Programación - RafaC - 2005/2006

Página Principal -


Tema 1: Aspectos imperativos del lenguaje


0 Antes de empezar

1 Primer programa

2 Variables (y constantes)

3 Entrada/Salida en modo texto

4 Tipos Primitivos

5 Instrucciones de control



Tabla A: Palabras Reservadas en Java

Tabla B: Rango de los tipos primitivos

Tabla C: Asociatividad de los operadores


Apéndice A: Instalación y uso de un compilador

Apéndice B: Uso de Javadoc

Apéndice C: Ficheros JAR






0 Antes de empezar


0.1 Objetivos
0.1 El secreto del éxito
0.2 Un poco de terminología






0.1 Objetivos

Estos apuntes pretenden ser una introducción al lenguaje de programación Java. Java es un lenguaje muy extenso, con muchas "caras", y ningún libro o colección de apuntes pueden pretender cubrir con suficiente profundidad todos los aspectos del lenguaje. De hecho, hoy en día es normal designar mediante la palabra "Java" no a un lenguaje sino a toda un conjunto de herramientas, metodologías, etc. basadas en el lenguaje. Por eso no está de más que tratemos de delimitar desde el principio cuáles son los objetivos, qué se pretende y qué no se pretende.

Lo que se pretende es mostrar en suficiente detalle los conceptos básicos del lenguaje. Para ello se parte de cero, tratando de hacer el aprendizaje lo más gradual y "suave" posible. El precio a pagar: no será posible cubrir todos los aspectos del lenguaje. Muchos de los aspectos más avanzados, y puede que para muchos más atractivos, del lenguaje no serán tratados. También se ignoran algunos trucos y técnicas comunes entre los programadores de Java que, aunque útiles, podrían confundir al principiante. Se pretende explicar sólo lo principal, pero bien, procurando que el lector entienda el porqué de cada paso. El objetivo es llegar a tener un conocimiento sólido, una buena base del lenguaje. Más adelante, si así lo queremos o lo necesitamos, estos conocimientos nos ayudarán a comprender libros dedicados a aspectos más específicos del lenguaje.

En los apéndices al final de cada capítulo mencionaremos algún aspecto más específico que consideremos curioso o interesante.

La lista siguiente incluye la mayor parte de los aspectos del lenguaje cubiertos por estos apuntes. Contiene muchos términos que posiblemente suenen de los más extraños (e incluso amenazadores) al que aún no sabe nada del lenguaje, pero puede resultar de utilidad para los que sí sepan algo y quieran evitar perder el tiempo.
Breve introducción a las sentencias de control (bucles, sentencias condicionales, etc.) y otros aspectos básicos como el manejo de excepciones.
Principios de programación orientada a objetos en Java: encapsulación, herencia, clases abstractas e interfaces.
Programación de interfaces gráficos: control de eventos, clases AWT y Swing (esta última sólo superficialmente), y desarrollo de Applets.
Utilización de los recursos básicos del lenguaje tales como vectores y arrays, y desarrollo de nuestros propios tipos abstractos de datos.

Esta otra lista (compuesta por términos aún más esotéricos) incluye parte de lo que no se puede encontrar en estos apuntes:
Programación de Servlets y JSP (Java Server Pages).
Programación concurrente.
Acceso a bases de datos (JDBC).
Java Beans.
Una vez las cosas en su sitio, podemos empezar a divertirnos con ellas.

0.1 El secreto del éxito

Nacimiento de Java
Java fue desarrollado en 1995 por Sun Microsystems. Es heredero directo de C++ y es por tanto un lenguaje orientado a objetos. Fue pensado en principio como un lenguaje sencillo para escribir programas de control de microprocesadores para electrodomésticos. Sin embargo ha sido con el desarrollo de internet y en particular con el crecimiento de las páginas WEB cuando el lenguaje ha alcanzado su máxima difusión, hasta llegar hoy en día a ser uno de los lenguajes más demandados. Pero ¿Por qué?. Para comprender las razones tenemos que entender cómo se ejecutan los programas Java en un ordenador. Aunque estos detalles no tienen que ver con la programación en si, no está de más conocerlos, aunque sea mínimamente.
Compiladores
El ordenador sólo entiende un lenguaje: el código máquina (o ensamblador). Es un lenguaje muy poco "natural" para los humanos. Consta de instrucciones básicas que hacen referencia a la estructura del microprocesador (por ejemplo del Pentium). Realizar programas de gran tamaño y complejidad directamente en código máquina es una tarea prácticamente imposible.

Es por esta razón por la que surgieron los llamados lenguajes de alto nivel (como Java, Pascal, Fortran, C, C++). Estos lenguajes son más cercanos al ser humano, y por tanto más adecuados para la programación de aplicaciones. De hecho llamar a estos lenguajes de alto nivel es demasiado optimista. En realidad están, por así decirlo "a medio camino" entre lo que a los humanos les gustaría poder escribir y lo que el ordenador admite realmente. Por ejemplo al humano le gustaría poder decir:

"Anda, calcúlame la raíz cuadrada de 2 y muéstrame el resultado por pantalla"

Mientras que el procesador del ordenador (que es quién realmente ejecuta el programa) sólo admitiría algo así como:

"0010011010010111010101000011010111010101010101011101111010011101000111111"

Un lenguaje de alto nivel como Java permite alcanzar cierto compromiso y así podemos escribir:

System.out.println(  Math.sqrt(2)  );

donde System.out.println significa "escribe en pantalla" y Math.sqrt(2) "calcula la raíz cuadrada de 2".

Pero ¿cómo hacer que el procesador admita esta instrucción en Java? Pues para eso hace falta utilizar un programa especial, llamado compilador . Este programa se encarga del "trabajo sucio": traduce el programa en el lenguaje de alto nivel (por ejemplo Java) al lenguaje del ordenador (por ejemplo a código máquina del procesador Pentium).

Viéndolo de forma simplificada podemos representar esta idea así:



Por supuesto el programador no tiene porqué preocuparse del funcionamiento interno del compilador: le basta con usarlo. Sin embargo, no viene mal echar un breve vistazo brevemente cual es la estructura interna de los compiladores tradicionales. En una primera (y muy burda) división, el compilador consta de dos fases consecutivas:
  1. Análisis. En esta fase se lee el programa original (al que se suele denominar código fuente) y se analiza para descubrir sus significado. Por ejemplo, al llegar a la instrucción System.out.println(  Math.sqrt(2)  ); , el compilador leerá el código letra a letra (S-y-s-t-e-m-...) reconociendo en primer lugar la palabras "System". Si nos equivocamos y escribimos en nuestro programa Sostem.out.println(  Math.sqrt(2)  ); , el compilador detectará en esta fase que "Sostem" no corresponde a nada conocido, y nos dará un mensaje de error. Si en cambio el compilador no detecta ningún error la primera fase habrá finalizado con éxito. El resultado de esta primera fase es una representación interna del programa fuente (a la que el programador no tiene, en principio, acceso) y que la segunda fase tomará a su vez como entrada.

    El análisis de un programa por parte de un compilador no es una tarea complicada (hay técnicas muy conocidas para automatizar esta parte), pero es farragosa, y a menudo lenta: el compilador debe leer todo el código fuente e ir examinando cada posibilidad para ver cual es el significado de lo escrito por el programador.
     
  2. Generación de código máquina. En esta segunda fase se genera ya el código máquina a partir de la representación interna producida en el paso anterior. La generación de código no es una tarea trivial: cuando se diseña el lenguaje hay que pensar en cómo se representarán en el código máquina del microprocesador todas las características que forman parte del lenguaje. Cuanto más complejo o de más "alto nivel" sea el lenguaje, más difícil será traducirlo a código máquina. Sin embargo, una vez que todas estas decisiones están tomadas y se ha conseguido escribir esta parte del compilador, su ejecución sí es veloz, es decir se tarda menos en generar código que en hacer el análisis del código fuente (fase 1).
En este punto conviene recordar que en general diferentes microprocesadores tienen diferentes códigos máquina asociados, y que éstos en general no son compatibles entre sí. Habitualmente olvidamos esto porque estamos acostumbrados a trabajar con ordenadores cuyos microprocesadores son compatibles (los Intel x86, Pentium, etc.). Pero si somos usuarios, por ejemplo de Mac, sabremos que los programas que se ejecutan en nuestro PC no se pueden ejecutar en el Mac. La razón no es tanto el uso de sistemas operativos diferentes sino que los códigos máquinas de los microprocesadores no son compatibles. Igualmente un programa de un PC de los llamados "32 bits" no corre en un antiguo PC de "16 bits" por incompatibilidad entre los códigos máquina.

Por tanto, si queremos, por ejemplo, programar en C++ en nuestro ordenador, lo primero que tenemos que hacer (aparte de aprender C++) es conseguir un compilador de C++ que esté desarrollado para nuestro microprocesador. Si por ejemplo (y por error) conseguimos un compilador de C++ que genera código para ordenadores con procesadores SPARC, aunque logremos compilar nuestro programa, éste no podrá ejecutarse satisfactoriamente.

Esto en realidad no es ningún problema ... siempre y cuando no pensemos en aplicaciones para internet. Vamos a ver por qué.
Programar en la WEB
Cuando visitamos una página html en internet, se desarrolla el siguiente proceso:
  1. Nuestro ordenador (el "cliente") se conecta con el ordenador ("servidor") en el que se encuentra la página que deseamos, y se la solicita.
  2. El servidor envía (utilizando una copia del protocolo http) una copia de la página solicitada hacia nuestro ordenador.
  3. Nuestro ordenador recibe la página, la procesa y nos muestra utilizando el navegador correspondiente (Netscape, explorer, etc.). A partir de ahora, y siempre cuando no pulsemos en ningún enlace la página (en realidad una copia) está físicamente en nuestro ordenador; podríamos incluso interrumpir la conexión con internet y seguiríamos teniendo la página en el explorador, con la posibilidad de movernos por ella libremente.
Una página html básica sólo consiste en tres elementos: texto, imágenes y enlaces a otras páginas. Sin embargo, desde el principio los programadores de páginas web querían diseñar páginas más interactivas, capaces de responder a acciones del usuario
Y esto, evidentemente, equivalía a incluir programas como parte de las páginas html. Sin embargo, ¿cómo hacerlo? En principio hay dos posibilidades obvias:
Mandar al usuario el programa, ya compilado, junto con la página html. Pero hemos dicho que el programa ya compilado está en código máquina, y que el código máquina sólo es válido para un procesador determinado ¿qué pasa si el usuario tiene un ordenador con un procesador diferente? Una solución sería que el cliente informara al servidor acerca de qué procesador tiene, y el servidor le enviara el programa compilado para su procesador particular. Pero esto obligaría a actualizar el servidor cada vez que aparezca un tipo de procesador nuevo y no es nada práctico.

Mandar al usuario el programa fuente, sin compilar. El navegador compilaría entonces el programa y lo mostraría al usuario. Esto requiere que el navegador incluya un compilador, pero esto no es un problema si se "convence" a las empresas que distribuyen los navegadores. El problema es que los programas fuentes ocupan bastante espacio, y la compilación (sobre todo en la fase 1) es un proceso largo, que retrasaría enormemente la ejecución del programa. De nuevo la solución no es práctica.
Fue entonces cuando se pensó en una posible solución que combinaba y mejoraba las dos posibilidades anteriores. Y esta fue la razón (o una de las razones) del éxito de Java.
Uno de los secreto del éxito: los bytecodes
La solución que se usa hoy en día se basa en una mezcla las dos ideas expuestas en el apartado anterior. Curiosamente los creadores de Java pensaron en esta solución pero no para páginas web sino para electrodomésticos.
El programa que queremos enviar con la página html se compila en el servidor, pero no para un código máquina concreto sino para una especie de código intermedio universal llamado bytecodes. Este proceso se hace sólo una vez, antes de poner la página a disposición del público.

Cuando el usuario solicita la página, ésta viaja al ordenador cliente acompañada de los correspondientes bytecodes. El navegador del usuario debe tener un pequeño compilador (en realidad un intérprete porque traduce y ejecuta instrucción a instrucción) llamado máquina virtual de Java (MVJ) para ejecutar este programa.
Lo bueno de esta solución es que el código que viaja (bytecodes) ya ha pasado la primera fase de la compilación; que como hemos dicho es la más lenta. Ocupa menos espacio que el código fuente original y está con (casi) seguridad libre de errores por haber superado la fase 1. El intérprete incluido en el navegador sólo tiene que traspasar el código en bytecodes a código para su procesador concreto. Como los bytecodes ya son algo similar a un código máquina la traducción no es complicada y se hace de forma veloz.

Se obtiene una ventaja que explica parte del éxito de Java: La parte más difícil, el compilador de Java a bytecodes, es ahora independiente del procesador (ya que no genera código para ningún procesador real): al cambiar del sistema sólo hay que construir un nuevo intérprete de bytecodes lo que, como hemos dicho, es mucho más sencillo.


En particular los navegadores más usuales (explorer, Mozilla, Netscape) incluyen intérpretes de bytecodes.

Ahora también resulta más fácil de entender por qué se oye a menudo hablar de juegos en Java para teléfonos móviles. El creador del juego no tiene por qué preocuparse de todos los modelos de teléfono móvil del mercado: sólo de escribir el juego y compilarlo generando los bytecodes. Es cada fabricante del teléfono el que se encarga de incluir un pequeño intérprete de bytecodes, lo que garantiza que su teléfono será capaz de ejecutar juegos en java. Por tanto cuando "bajamos" un juego o una aplicación en Java de internet para nuestro teléfono, lo que estamos descargando son sencillamente programas traducidos a bytecodes.
Más razones para el éxito
Acabamos de ver que algunas de las razones más importantes del éxito del lenguaje no tienen nada que ver con el lenguaje como tal, sino con cómo se compilan los programas. Pero el lenguaje también tiene características que han sido fundamentales para que los programadores se "animaran" a usarlo. Algunas de estas características son:
Java es un lenguaje orientado a objetos. Este tipo de lenguajes, entre los que también se incluye C++, tiene numerosas ventajas sobre los llamados lenguajes imperativos no orientados a objetos a los que pertenecen por ejemplo Pascal, Fortran y C (en un tema posterior veremos estas ventajas).

Java fue desarrollado después de C++ y corrige gran parte de los inconvenientes de este lenguaje (aunque introduce otros). Se puede ver en este sentido como una evolución con respecto a C++.

Una de las diferencias con respecto a C++ es la gestión de la memoria dinámica. En C++ (y en C) el programador tiene responsabilidad sobre el uso de la memoria adicional que pueda requerir durante la ejecución (los amados/odiados punteros): debe solicitar la memoria, usarla -con precaución- y no olvidar liberarla. En Java el manejo de memoria dinámica es más sencillo, y los errores se detectan más fácilmente. En particular la liberación de la memoria, ya no es responsabilidad del programador sino del lenguaje, que cuando detecta que un fragmento de memoria ha quedado libre, llama al recolector de basura que se encarga de marcar el fragmento como disponible.

Una de las razones más importantes: en Java la programación de interfaces gráficos es parte del lenguaje estándar. En C++, por ejemplo, el lenguaje no incluye ninguna referencia a cómo hacer interfaces gráficos ni sobre cuáles deben ser las librerías gráficas. Sin embargo hoy en día todas las aplicaciones reales trabajan en modo gráfico (ventanas, tipos de letra, etc.). Como consecuencia, cada compañía (Borland, Microsoft) desarrolló sus propias librerías, llegando en la práctica a haber distintos "dialectos" del lenguaje. En Java, al ser más moderno, se decidió desde el principio incluir como parte del lenguaje todas las características gráficas. Así el programador no depende de la compañía concreta que ha hecho el compilador; basta con que aprenda Java estándar. Además desde el comienzo Sun ofreció su compilador, con entorno gráfico y depurador, de forma gratuita. Esto ha "animado" (¿obligado?) a otras compañías a ofrecer a su vez sus compiladores, al menos en la versión básica, de forma gratuita.
No puede ser todo tan bonito...
Pues no, no lo es. Java también tiene su "lado oscuro". Defectos y problemas que podemos resumir en los siguientes puntos:
Poca velocidad. Comparado C e incluso con C++, la ejecución de programas en Java es habitualmente más lenta. Esto es debido en parte a algunas características específicas del lenguaje (como la "recogida de basura" mencionada), y en parte a que cada vez que se ejecuta un programa hay que convertir los bytecodes en código de la máquina, proceso más costoso que ejecutar código máquina directamente.

No es adecuado para controlar hardware.. En efecto, Java intenta, como hemos dicho, ser independiente del procesador de la máquina en la que se está ejecutando. Por ello no dispone de instrucciones de acceso a los puertos de entrada/salida del ordenador y no permite controlar periféricos o acceder a hardware. Es, por tanto, poco adecuado para hacer aplicaciones que tengan que ver con, por ejemplo, robótica.

El aprendizaje es, al comienzo, más difícil que en otros lenguajes. Como veremos enseguida incluso el programa más corto incluye desde el principio gran cantidad de términos extraños. Al principio debemos aceptarlos sin más, aunque poco a poco iremos aclarándolos todos.


0.2 Un poco de terminología

A los informáticos les encantan los acrónimos. Son cortos, designan expresiones complicadas... y sobre todo sirven para sentirse superiores al resto de los mortales que, pobres, no conocen su profundo significado. Vamos a repasar algunos de los más usuales en Java:
JSDK (Java Software Development Kit, SDK para los amigos, si los hubiere): Entorno de desarrollo (compilador y otras herramientas) que permite la creación de aplicaciones en tecnología Java. No incluye ningún interfaz gráfico; son todo comandos de línea, es decir que deben ser tecleados desde una consola. Para evitar esto se suele utilizar en la práctica un entorno gráfico (como Eclipse o JCreator) que es el que se encargará de "llamar" al compilador de JSDK. A menudo se emplea la variante J2SDK para indicar que se trata del JSDK correspondiente a la segunda revisión del lenguaje, que es el que se usa actualmente.
JRE (Java Runtime Environment): Se necesita para ejecutar un programa Java previamente compilado (es decir, es el intérprete de los famosos bytecodes).
JVM (Java Virtual Machine): Similar a JRE, pero se suele utilizar más para referirse al intérprete de bytecodes incluido como parte de un navegador.
API (Application Programming Interface): Se usa para referirse al conjunto de funcionalidades que ofrece un lenguaje (o un sistema operativo). En el caso de Java se refiere al conjunto de clases que ofrece el lenguaje al programador y que incluyen la posibilidad de usar componentes gráficos (botones, ventanas, etc), vectores, operaciones matemáticas (clase Math) y un larguísimo etcétera.
J2SE (Java 2 Standard Edition), y J2EE (Java 2 Enterprise Edition): Se llama J2SE al núcleo del lenguaje actual, incluyendo el J2SDK y el JRE. Se llama J2EE a una ampliación del J2EE que incluye un API más extenso, pensado, sobre todo, para el desarrollo de programas Java como servidores de páginas HTML. En estos apuntes nos ocuparemos de J2SE pero no de J2EE.
Applet: No es un acrónimo, sino el nombre que se da al programa Java diseñado para ser parte de una página HTML (HTML ... ¡otro acrónimo!).
Esto por supuesto no es más que un mínimo catálogo del variado bestiario que adorna los libros de Java, donde el inquieto aventurero podrá encontrar otras pintorescas criaturas tales como EJBs (Enterprise Java Beans), JSPs (Java Server Pages), JDBC (Java DataBase Connection), JACC (Java Authorization Contract For Containers), RMI (Remote Method Invocation), JMS (Java Message Service), JND (Java Naming and Directory), JTA (Java Transaction API) o JTS (Java Transaction Service). Casi todas, como las bestias medievales, son sumamente nombradas pero raramente explicadas, lo que nos llevará a dudar de su existencia.


1 Primer programa

Nuestro primer programa se limita a mostrar una línea de texto en pantalla:

Hola.java

public class Hola {
 
    public static void  main(String [] Args)  {
        System.out.println("¡Soy pequeñín....mímame!");
    }    
}


Vamos a ir analizando el programa línea a línea:
public class Hola { En esta primera línea se indica el nombre de la clase que estamos definiendo. public class indica que el fichero contiene una clase que se va a poder utilizar desde otras. A estas palabras reservadas les sigue el nombre que queremos poner a la clase, en este caso Hola. Aunque no es obligatorio, es una norma habitual en Java escribir siempre el nombre de una clase con un identificador que comience por una letra mayúscula. Además, cada clase debe, (casi) obligatoriamente, formar parte de un fichero con el mismo nombre que la clase y con extensión .java. Aunque más adelante veremos que la cosa puede llegar a ser un poco más complicada, conviene en principio que recordemos la siguiente correspondencia:
Cada clase se almacena en un fichero que lleva su nombre (y extensión .java).
Cada fichero .java contiene una única clase pública, cuyo nombre corresponde con el del fichero (sin la extensión).

Aviso: ¡Las mayúsculas sí importan en Java! Si después de escribir la clase Hola la utilizamos escribiendo hola en otro lugar, el compilador dará error indicando que el identificador no ha sido definido.
Aunque en los temas posteriores veremos con detalle los conceptos de clases y paquetes no está de más que veamos una primera (incompleta y burda) aproximación:
Clases Las clases representan conjuntos de objetos que quedan caracterizados por unas características (atributos) y un comportamiento (dado por sus métodos) determinados.


Observación (para los que sepan C++): En C++ las clases se dividen en dos ficheros, el fichero de cabecera (extensión .h) y el fichero de implementación (extensión .cpp). En Java la cabecera y la implementación forman un único fichero con extensión .java
Paquetes La unidad de código de más alto nivel en Java es el paquete. Cada paquete agrupa una o más clases lógicamente relacionadas. Es habitual que las clases que forman un programa se agrupen en varios paquetes. Normalmente para cada paquete se creará un directorio o carpeta (con el nombre coincidente con el paquete) que contendrá todas las clases (ficheros .java) del paquete. A diferencia de las clases es "tradición" poner a los paquetes nombres que empiecen por la letra minúscula.

Cuando la aplicación es muy complicada interesa ampliar la jerarquía, y hacer paquetes que contienen otros paquetes y así repetidas veces hasta llegar a los paquetes más simples que sólo contienen clases. Aunque nosotros no haremos esto en nuestros ejemplos al ser éstos demasiado simples, conviene conocer esta posibilidad por las clases del API de Java que utilizaremos sí están organizadas en paquetes y subpaquetes.

Observación: Si no asignamos una clase a un paquete particular, Java la asigna a un paquete genérico por defecto. Este es el caso de la clase Hola.
Tras la declaración del nombre de clase viene el símbolo { que indica que comienza la declaración de los componentes de la clase. La declaración de la clase se dará por terminada cuando se encuentre el símbolo } correspondiente a este { inicial.

public static void main(String [] Args) { Así comienza la declaración del método main, único componente de la clase Hola.El método main siempre debe ser declarado con la misma cabecera:


    public static void  main(String [] Args)  {

Podemos explicar brevemente cada una de las partes de esta declaración:
public Esta palabra significa que el método puede utilizarse desde fuera de la clase. Pero...¿tendría sentido escribir un método que no pudiera utilizarse fuera de la clase? Sí, cuando se trata de métodos auxiliares que serán utilizados desde otros métodos de la clase pero no queremos (por seguridad o por estética) que puedan ser "vistos" fuera de la clase.

static La palabra static significa que para utilizar este método no necesitamos declarar un objeto de la clase. Veremos que en general para utilizar una clase es necesario tener un objeto de dicha clase (una variable de ese tipo, dicho con otras palabras) como ocurría con el objeto líneas de la clase Vector, pero el método main es un método especial ya que es el punto de comienzo de la aplicación.

void Todos los métodos deben devolver un resultado de algún tipo. En la cabecera hay que indicar dicho tipo. Sin embargo algunos métodos, como este realmente no necesitan devolver ningún valor. En este caso se declaran como de tipo void.

String[] args Los métodos pueden recibir argumentos, es decir valores que se le pasan como en el caso de las funciones matemáticas seno y coseno, que tienen como argumento el valor del que queremos calcular el seno o el coseno. En el caso de main este argumento es una lista (representada en Java por []) de cadenas de caracteres (String en Java). Estas cadenas de caracteres se utilizan para que el usuario pueda pasar información al programa al inicio.
En Java, igual que en C y C++ se utilizan las llaves ({ }) para agrupar código, en este caso el cuerpo del método. Este cuerpo está formado por una única instrucción:

System.out.println("¡Soy pequeñín....mímame!"); Instrucción que, como todas en Java, terminan en ;. En este caso se llama al método println que escribe por pantalla una cadena de caracteres. El método pertenece al objeto out definido en la clase System que siempre puede usarse sin necesidad de utilizar una declaración import.

Pregunta 1.a Llevados por la emoción decidimos escribir un programa un poco más grande, al que llamamos "Crece.java":

public class crece {
 
    public static void  main(String [] Args)  {
        System.out.println("Ya ocupo ");
        System.out.println("dos líneas ");
    }    
}
Sin embargo al intentar compilarlo nos da el siguiente error:
Crece.java:1: class crece is public, should be declared in a file crece.java
class crece {
^
1 error
¿qué significa?


Aunque el programa Hola.java es correcto lo habitual es escribir comentarios que expliquen el código:

Hola.java
/**
 * Primera clase en Java.
 * @version 1.0, 24/12/04
 * @author Rafa Caballero
 */
public class Hola {
 
    /** 
     * Método principal. Escribe por pantalla una línea de texto
     * @param Args Argumentos pasados por línea de comandos. No utilizados
     */
    public static void  main(String [] Args)  {
        /* 
           Mostramos una línea por pantalla en modo texto.
           Más adelante aprenderemos a hacerlo mediante una ventana gráfica.
         */  
        System.out.println("¡Soy pequeñín....mímame!");
    }    
} // fin de la clase


Este código hace exactamente lo mismo que el programa inicial sólo que incluye comentarios. Los comentarios no alteran el funcionamiento del programa, se incluyen para que éste sea más comprensible y para generar documentación automática. Java incluye 3 tipos de comentarios:
  1. Comenzando con // . En este caso el compilador ignora todo lo que va desde // hasta el final de línea.
  2. Comenzando con /* y terminando con */. Estos comentarios pueden ocupar múltiples líneas.
  3. Comenzando con /** y terminando con */. Igual que el anterior, sólo que estos comentarios pueden ser utilizados por la herramienta javadoc para generar documentación sobre la clase.

2 Variables (y constantes)

2.1 ¿Qué es una variable?
2.2 Declaración de variables
2.3 Operación de asignación
2.4 Constantes


2.1 ¿Qué es una variable?

Nos podemos imaginar la variable como una "caja" (unas direcciones de memoria) con un nombre y un valor:

|    |
|  'b'   |   (10325-10326)
| ____ |
    x   
En esta figura, x es el nombre de una variable que contiene la letra 'b' y que se "aloja" en las direcciones de memoria (10325-10326). En los lenguajes llamados "tipados" como Java, las variables tienen, además, un tipo. El tipo de la variable determina el conjunto de variables que ésta puede tomar, así como las operaciones que se le pueden aplican. En el ejemplo anterior el tipo de la variable x sería carácter, o como se escribe en Java, char.
Por tanto una variable en Java tiene:
Un nombre, que fija el programador.
Un tipo, que también fija el programador.
Un valor, que va variando a voluntad del programador.
Unas direcciones de memoria, en las que se almacena la variable, y que no son decididas por el programador.
Normalmente nos olvidaremos de las direcciones de memoria, al no tener control sobre ellas, pero es importante saber que andan por ahí para entender ciertas particularidades del lenguaje que veremos más adelante.


2.2 Declaración de variables

La forma general de una declaración de variables es:

tipoVariable nombreVariable =valorInicial;

La inicialización =valorInicial es opcional. Si se hace el valor valorInicial debe ser del mismo tipo (o un tipo compatible) con tipoVariable. El nombre de la variable puede ser cualquier identificador (igual que el nombre de un método o una clase).

Observación: Los identificadores son secuencias de letras, números y pueden contener símbolos como '$', '_' o '#', pero siempre deben empezar por una letra. A diferencia de otros lenguajes, los identificadores en Java pueden contener ñ y letras acentuadas.
Las normas de "buena educación" indican que una variable siempre debe empezar por minúscula (salvo en ocasiones si se trata de una constante, en cuyo caso se suele escribir en mayúsculas). Por ejemplo, sabiendo que en Java los números enteros corresponden con el tipo int y los caracteres con el tipo char, podemos escribir:

        int año = 2005;
        int suma=0;
        int número;
        char letra;
    
Java también permite que varias variables del mismo tipo se declaren separadas por comas:

        int número,b=3,c=4,suma=0,d;
        char letra;
    
Un último aspecto a mencionar es que en Java las declaraciones de una variable se pueden hacer en cualquier lugar:

public class Principal {
    
    public static void main(String[] args) {
        int a=5;
        System.out.println("El valor de a es: "+a);
        int r = 25; // r se declara aquí
        System.out.println("El valor de r es: "+r);
    
    } // main   
}
    
El operador + sirve aquí para concatenar el contenido de la variable al final de la cadena de caracteres.

2.3 Operación de asignación

La operación de asignación para todos los tipos definidos en Java. La forma general de esta operación es:

nombreVariable = expresión;

En Java (como en C y C++) la asignación no es una instrucción sino una expresión.

Observación: Una expresión es una secuencia de símbolos que puede ser reducida en tiempo de ejecución a un valor.
En el caso de la asignación ese valor es el que contiene la variable de la parte izquierda:

public class Principal {    
    public static void main(String[] args) {
        int a,b;
        
        a = (b=5)+1;
        System.out.println(a);
        System.out.println(b);     
  } // main   
}

Pregunta 1.b ¿Qué escribirá el programa?
En particular esto permite la llamada asignación múltiple:

public class Principal {
    public static void main(String[] args) {
        int a,b,c;
        
        a = b = c = 1;
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);    
   } // main   
}

Pregunta 1.c ¿Qué escribirá el programa?
A menudo y por comodidad utilizaremos esta asignación múltiple.

Observación: La igualdad es una operación que asocia por la derecha (ver tabla C).

2.4 Constantes

Las constantes son variables que no pueden modificarse. En Java se declaran anteponiendo la palabra reservada final a la declaración de la variable:

final tipoVariable nombreVariable = valorInicial;
Ej.:

      final char AMAYÚSCULA = 'a';
      final double N = 299792.495;  
Es necesario incluir la inicialización junto con la declaración, porque la operación de asignación no está permitida más que en la declaración:

      final double PI = 3.141592;
      
      double a;
      a = PI +1; // esto es válido
      PI = a +1; //NOOOO!!!!    
    

3 Entrada/Salida en modo texto

3.1 Salida en modo texto
3.2 Entrada en modo texto


3.1 Salida en modo texto

La salida de datos en modo texto se puede hacer en Java fácilmente a través del método println() de System.out. Ya hemos visto que una instrucción como
System.out.println("El que sabe no habla, el que habla no sabe");
Hace que aparezca por pantalla la secuencia de caracteres escrita entre comillas. En lugar de escribir la secuencia de caracteres directamente, también nos puede interesar almacenarla primero en una variable. El tipo de las secuencias de caracteres en Java se llama String. El siguiente código declara una variable de este tipo y muestra su contenido por pantalla:
String s = "Nocturno y dulce revuelo";
System.out.println(s);
Es importante observar aquí que el nombre de la variable no va entre comillas; si hubiéramos escrito System.out.println("s"); en lugar de la frase el código mostraría por pantalla simplemente s.

println() es tan flexible que nos permite igualmente escribir enteros, caracteres o números reales. Por ejemplo

System.out.println(124);
System.out.println('a');
System.out.println(1.618033);
muestra por pantalla:
124
a
1.618033
También podemos combinar diferentes tipos utilizando +:

public class Prueba {    
    public static void main(String[] args) {
    String s1 = "Entonces, quedamos a las ";
    int hora1 = 11;
    String s2 ="?";
    String s3 = "No, mejor a las ";
    double hora2 = 11.45;

    System.out.println(s1+hora1+s2);
    System.out.println(s3+hora2+". Vale"+s2);
  } // main   
}

Pregunta 1.d ¿Qué escribirá el programa?
Para acabar, decir que se puede usar print en lugar de println si se quiere que no se introduzca un salto de línea tras escribir.

3.2 Entrada en modo texto

Hemos visto que la salida de datos es muy sencilla. En cambio, lograr que un programa en Java recoja los datos de entrada introducidos por el usuario es un poco más complicado. Veamos un ejemplo:

Ligón.java
// para leer de teclado necesitamos la clase Scanner
import java.util.Scanner;
    
public class Ligón {
    
    public static void main(String[] args) {
        
     // sc se usará para leer los datos
     Scanner sc = new Scanner(System.in);
     
     // preguntar la edad
     System.out.print("¿Cuántos años tienes?  ");
 
     // leer un entero
     int edad = sc.nextInt();
     
     System.out.println("...pues aparentas "+(edad-3));
  } // main   
}


Una salida de este programa:
¿Cuántos años tienes?  22
...pues aparentas 19
Este ejemplo incluye muchas novedades:

import java.util.Scanner; Para leer de teclado se debe utilizar un objeto de la clase Scanner. Esta clase no es de las incluidas de forma automática y por eso debe incluirse mediante la palabra clave import. Si la clase está definida en otro paquete se escribe el nombre "cualificado", precedido por el nombre del paquete. En este caso la clase Scanner es parte del paquete util que es a su vez parte de la clase java. Si se desean incluir varias clases se incluirán varias sentencias import:
     import java.util.Scanner;
     import java.util.Vector;
Si se quieren incluir varias (o todas) clases del mismo paquete se puede usar el simbolo *:
     import java.util.*; // importa Scanner, Vector y muchas más
Para ver qué clases contiene cada paquete conviene consultar la ayuda de Java.

Scanner sc = new Scanner(System.in); Esta instrucción declara e inicializa un objeto sc de tipo Scanner. En los capítulos posteriores veremos los objetos en detalle, pero aquí tenemos un primer ejemplo de como se crea un objeto mediante new. El parámetro System.in indica de dónde se leerá, en este caso de la entrada estándar pero igualmente se podría leer, por ejemplo de un fichero de texto de nombre "datos.txt":
      Scanner sc = new Scanner(new File("datos.txt"));
int edad = sc.nextInt(); La llamada al método nextInt(); devuelve el siguiente entero que se puede leer del objeto de tipo Scanner. Además de enteros se pueden leer valores de otros tipos. Para leer una línea completa se usará el método nextLine(); que devuelve un valor de tipo String:
import java.util.Scanner;
    
public class Principal {
    
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);

        System.out.print("¿Cómo te llamas? ");
        String nombre = sc.nextLine(); // leer una cadena de caracteres
        System.out.println("Es un placer conocerte "+nombre);
  } // main   
}
Pregunta 1.e ¿Qué escribirá el programa?

Observación: A menudo en el terminal de texto las letras acentuadas, la ñ y otros símbolos del alfabeto español no se muestran adecuadamente. Esto se solucionará más adelante, cuando introduzcamos los programas con entrada/salida gráfica.

4 Tipos Primitivos

4.1 ¿Qué es un tipo?
4.2 Tipos Primitivos
4.3 Tipos enumerados
4.4 Conversiones de tipo


4.1 ¿Qué es un tipo?

En principio se tiende a pensar en un tipo como un conjunto de valores, por ejemplo el tipo int estaría formado por el conjunto de números enteros aceptados por el lenguaje Java. Sin embargo esta visión es demasiado restrictiva. Si nos limitamos a usar los valores podemos hacer pocas cosas. Los valores sólo son útiles cuando se combinan con las operaciones adecuadas. Por ello emplearemos la siguiente definición:

tipo = Valores + operaciones que manejan esos valores

Así el tipo int está formado por los valores enteros admitidos por el lenguaje Java junto con las operaciones +,-,*,/,=, ....

Observación: Hay veces que se usa el mismo símbolo para operaciones análogas entre diferentes tipos. Por ejemplo, + representa la suma tanto de números enteros como reales. Sin embargo, debemos recordar que se trata de dos operaciones diferentes, aunque estén representadas por el mismo símbolo. Se dice en este caso que el símbolo (por ejemplo +) está sobrecargado.

4.2 Tipos Primitivos

En Java existen 8 tipos primitivos predefinidos. Vamos a ver los valores que componen cada uno de ellos y las operaciones que admiten:

Lógicos
Valores
Los únicos valores de este tipo son las constantes true y false. Se utilizan principalmente en el control del programa: para decidir qué instrucciones se van a ejecutar o no dependiendo de condiciones, para fijar la condición de repetición de un bucle, etc.
Operaciones

Ejemplo

    // operaciones entre booleanos
    boolean  b1,b2;

    int x=5, y=x+3;
    b1= true && (x == y);  //      
    y = x;
    b2 = !b1 || (x != y);  //      
    System.out.println(b1); 
    System.out.println(b2); 
    

Pregunta 1.f ¿Qué escribirá este fragmento de programa?
Los valores lógicos se pueden leer de teclado utilizando el método nextBoolean(); de la clase Scanner:
        Scanner sc = new Scanner(System.in);
        boolean b = sc.nextBoolean();
Enteros
Hay 4 (sub)tipos de enteros: byte, short, int y long. La diferencia entre ellos es el rango de valores que admiten, y que se puede consultar en la tabla B al final del tema. El más usado es el tipo int, pero las operaciones que vamos a ver son válidas para todos ellos.

Valores
Los números enteros se pueden escribir : Si se quiere indicar que un valor es de tipo long se añade tras el último dígito una letra L (minúscula o mayúscula, no importa).
Operaciones
Números Reales
Números reales: float y double. Los literales que tienen un punto decimal son, por defecto, de tipo double, aunque si por el contexto se necesita indicar que un valor es de este tipo se puede escribir seguido de la letra d o D. Los literales del tipo float van seguidos de una F o f. Ej.: 123.45f En memoria tienen una longitud de 32 (float) y 64 (double) bits, respectivamente.

Valores
Los números reales deben incluir bien un punto decimal '.' o una letra 'e' minúscula (notación exponencial): 5., .54, 1.33, -4e2, 5e-8. Por defecto estos valores son double, pero si se quiere indicar explícitamente se puede añadir inmediatamente tras el número una 'f' o 'F' o bien 'd' o 'D' (float o double).
Operaciones
Las operaciones son las mismas que en el caso de los números enteros con la única de que ahora '/' representa la división con decimales. Pueden leerse de teclado utilizando los métodos nextDouble(),nextFloat() de la clase Scanner.
Caracteres
Valores
{....'a','b',....,'z', '#', '$', 'ñ', 'á', .....'[', '@',.....}
Los caracteres corresponden a la tabla Unicode, más extensa que la tabla ASCII usada por otros lenguajes: la tabla ASCII extendida tiene 256 caracteres mientras que la tabla Unicode está compuesta por 65536 caracteres, incluyendo Kanji, cirílico, etc. A cada carácter le corresponde un código único entre 0 y 65535, código que se puede ver forzando la conversión del carácter a entero (que se hace automáticamente):

Ejemplo
    char c='?';
    int codigoUnicode = c;

    System.out.println(codigoUnicode); // escribe el valor 936
    
Un carácter se puede representar de tres formas:
Operaciones
Sobre un carácter se pueden aplicar las operaciones válidas para los enteros, que actuarán sobre el código Unicode del carácter. Es importante saber que en la tabla Unicode los códigos de la 'A', 'B', 'C' ... van consecutivos, y que lo mismo sucede con 'a', 'b', 'c', ..., y con '0',...,'9'.

4.3 Tipos enumerados

Además de estos tipos básicos, el usuario también puede definir tipos enumerados, que desde el punto de vista del lenguaje también se tratan como tipos básicos, aunque en realidad se traduzcan por objetos internamente. Vamos a introducirlos brevemente. Los tipos enumerados son una novedad en Java 1.5, y permiten al usuario definir tipos formados por un número finito de valores. En un primer vistazo, los tipos enumerados se parecen mucho a los tipos enumerados de otros lenguajes de "la familia" como C, C++ y C#. Por ejemplo, se puede declarar un tipo que sirva para representar las estaciones del año. Las variables de este tipo sólo podrán tomar cuatro valores: PRIMAVERA, VERANO, OTOÑO, e INVIERNO:

EjEnumerados.java
public class EjEnumerados {
    // declaramos así el tipo enumerado
    public enum Estación {PRIMAVERA, VERANO, OTOÑO, INVIERNO};
        
    public static void main(String[] args) {
        Estación mia; // declaramos una variable del tipo Estación
        
        mia = Estación.PRIMAVERA; // le damos un valor
        
        System.out.println("Mi estación preferida es: "+mia);
    }   
}

Como se ve los valores de tipo enumerado se pueden escribir por pantalla, lo que supone una mejora con respecto a C y C++.

4.4 Conversiones de tipo

En una asignación, el tipo del lado derecho no tiene que ser necesariamente el mismo que el de la variable; basta con que sea un tipo compatible:

Ejemplo:

        double a=5;
        int b=6;

        a = 5.4;        
        System.out.println(a);
        
        a = b;        
        System.out.println(a);
    
Todas estas operaciones son correctas.
En cambio si hacemos:

        double a=5;
        int b=6;

        b = 5.4;        
        System.out.println(b);
        
        b = a;        
        System.out.println(b);
    
Obtenemos un error de tipo en tiempo de compilación.

Idea: Recordando la imagen de las variables como "cajas", podemos imaginarnos que a cajas diferentes corresponden cajas de distintos tamaños. Así un valor real (double) debe ser una caja en la que debe caber tanto un real como un entero, ya que los enteros son también números reales, pero no al revés.
Sin embargo, también es posible realizar conversiones de tipo explícita de un tipo "mayor" a un tipo "menor" (con la inevitable pérdida de precisión), escribiendo

(tipoMenor) expresiónDeTipoMayor

Como por ejemplo:

        double a=5.99999;
        int b;

        b = (int)5.8;        
        System.out.println(b);
        
        b = (int)a;        
        System.out.println(b);
    

Pregunta 1.i ¿Qué escribirá este fragmento de programa?

Observación: A la operación de conversión entre tipos se le suele llamar en terminología de lenguajes de programación cast.

Aviso: Un error común es pensar que al hacer un cast entre tipos numéricos los valores se "redondean" al valor más cercano del tipo destino, pero en realidad se truncan, es decir se pierde su parte decimal.
Por supuesto hay tipos incompatibles entre los que no hay posible conversión. Por ejemplo si intentamos compilar:

public class Principal {
    
    public enum Estación {PRIMAVERA, VERANO, OTOÑO, INVIERNO};
        
    public static void main(String[] args) {
        Estación mia; 
        mia = Estación.PRIMAVERA;
        int a = (int) mia;        
    }   
}
obtendremos un error: inconvertible types.


5 Estructuras de Control

5.1 Instrucción de selección if
5.2 El operador ?
5.3 Instrucción de selección múltiple switch
5.4 Bucle while
5.5 Bucle for
5.6 Bucle do ... while


5.1 Instrucción de selección if

Sintaxis:

if (expresión booleana)
    Instrucción1
[else
    Instrucción2]


Notas:

Ejemplo:

import java.util.Scanner;
    
public class Principal {
    
    // muestra el mayor de entre dos enteros leídos de teclado
    public static void main(String[] args) {
        
        Scanner sc = new Scanner(System.in);
        int x=sc.nextInt(),  y=sc.nextInt();

        if (x>y) 
             System.out.println(x);
        else 
             System.out.println(y);

  } // main   
}
        
Ejemplo:

import java.util.Scanner;
    
public class Principal {
    
    // lee dos enteros de teclado y los muestra ordenados de menor a mayor
    public static void main(String[] args) {
        
        Scanner sc = new Scanner(System.in);
        int x=sc.nextInt(),  y=sc.nextInt();

        int máximo, mínimo;

        if (x>y) {  
            máximo = x;
            mínimo = y;
        } else {
            máximo = y;
            mínimo = x;
        }
        System.out.println(mínimo+" "+máximo);

  } // main   
}
        

Ejemplo: Máximo de 3 valores

import java.util.Scanner;
    
public class Principal {
    
    // calcula el mayor de 3 enteros
    public static void main(String[] args) {
        
        Scanner sc = new Scanner(System.in);
        int m, x=sc.nextInt(),  y=sc.nextInt(), z=sc.nextInt();

        // queremos que m tome el mayor de los valores entre x, y , z
        if (x>y) 
            // el mayor es x o z
            if (x>z) 
                m = x;
            else
                m = z;
        else // el mayor es y o z
            if (y>z) 
                m = y;
            else
                m = z;

        System.out.println( "El mayor es"+ m);        

  } // main   
}

Ejemplo: If sin parte else.

import java.util.Scanner;

public class Principal {
    
    // lee dos enteros de teclado y los ordena de mayor a menor
    public static void main(String[] args) {

        // leemos los valores a comparar
        Scanner sc = new Scanner(System.in);
        int x=sc.nextInt(),  y=sc.nextInt();

        // si x>y no hacemos nada, pero si x<=y intercambiamos los valores 
        if (x<=y) {
             // intercambiar x con y. Pregunta 1.j: escribir el código
             ......
             
        }
        // mostramos los valores ordenados  
        System.out.println(x+" "+y);
    }   
}


Pregunta 1.k ¿Cómo cambiará el comportamiento del programa anterior cambiamos la instrucción System.out.println(x+" "+y); por System.out.println(x+y);?

5.2 El operador ?

En Java (y en C++ y C) la instrucción condicional if se puede escribir en forma de expresión, utilizando el símbolo ? para separar la condición de la parte if y el símbolo : antes de la parte else, como muestra el siguiente ejemplo:

Ejemplo:

Scanner sc = new Scanner(System.in);
int x=sc.nextInt(),  y=sc.nextInt();
int z;


// queremos que z tome el mayor de los valores entre x e y
z = x > y ? x : y;   // también if x>y then z=x else z=y

System.out.println("El mayor es: "+z);

5.3 Instrucción de selección múltiple switch

Esta instrucción se utiliza cuando queremos distinguir entre distintos valores (más de 2, sino se utiliza if) que puede tomar una misma expresión. Es similar a la expresión case de Pascal.
Sintaxis:

switch(expr) {
case valor1: ... // instrucciones
   break;
case valor2: ... // instrucciones
   break;
...
... // otros casos
...
default: // instrucciones
}


La expresión se evalúa y se ejecutan las instrucciones que corresponden al valor. Si ninguno de los casos se cumple se ejecutan las órdenes de la parte default (que es opcional).

Ejemplo:

int mes; 
Scanner sc = new Scanner(System.in);
System.out.print("Introduce un valor entre 1 y 12: ");
mes = sc.nextInt();

switch(mes) {
case 1:
  System.out.println("Enero");
  break;
case 2:
  System.out.println("Febrero");
  break;
case 3:
  System.out.println("Marzo");
  break;
case 4:
  System.out.println("Abril");
  break;
case 5:
  System.out.println("Mayo");
  break;
case 6:
  System.out.println("Junio");
  break;
case 7:
  System.out.println("Julio");
  break;
case 8:
  System.out.println("Agosto");
  break;
case 9:
  System.out.println("Septiembre");
  break;
case 10:
  System.out.println("Octubre");
  break;
case 11:
  System.out.println("Noviembre");
  break;
case 12:
  System.out.println("Diciembre");
  break;
default:   // en otro caso ...
  System.out.println("Ese valor no corresponde a ningún mes!!!");
} // switch

Aviso: Si no se introduce el break se ejecutan las instrucciones del caso siguiente.

Ejemplo:

int mes=2;

switch(mes) {
case 1:
  System.out.println("Enero");
  break;
case 2:
  System.out.println("Febrero");
case 3:
  System.out.println("Marzo");
  break;
.....
// el programa escribirá:
// Febrero
// Marzo

Esto es útil, en ocasiones, para agrupar casos:
Ejemplo:
Scanner sc = new Scanner(System.in);

int mes, año;
int díasMes;
System.out.print("Mes (entre 1 y 12): "); mes = sc.nextInt();
System.out.print("Año"); año = sc.nextInt();

// cálculo de los días que tiene el mes:
switch(mes) {
case 4:
case 6:
case 8:
case 10:
   díasMes = 30;
  break;
case 2:
  if ( ((año % 4 == 0) && (año % 100 != 0)) || (año % 400 == 0) )
     díasMes = 29;
  else
     díasMes = 28;
  break;
default:   // en otro caso 31
   díasMes = 31;
  } // switch

5.4 Bucle while

Esta instrucción repetitiva (bucle), tiene la siguiente sintaxis:

while (expresión booleana)
    Instrucción



La instrucción se repite mientras la condición se evalúe a true. En particular, si la primera vez ya no se cumple, la instrucción se ejecutará 0 veces.

Ejemplo: Comprobar si un número es primo.


import java.util.Scanner;

public class Principal {
    
    // comprueba si un número dado es primo
    public static void main(String[] args) {

        // leemos el valor del que se quiere saber si es primo
        Scanner sc = new Scanner(System.in);
        int x=sc.nextInt();;

        // utilizamos una variable que indica si puede ser primo
        boolean primo = x >1 ? true : false ; 
        // otra variable para almacenar un posible divisor
        int i=2;
        
        while (i<x && primo) {
            if (x % i == 0) 
               primo = false; // i divide a x
            else
               i++; // para comprobar el siguiente    
        }
        
        // mostramos el resultado
        if (primo)
           System.out.println(x+" es primo");
        else
           System.out.println(x+" no es primo");
           
    }   
}



Aviso: Un error habitual es olvidar modificar las variables que se encuentran en la condición del bucle. Esto da lugar a programas que no terminan.

Pregunta 1.l ¿Cómo se podría mejorar la condición del bucle para que éste diera el menor número de iteraciones ("vueltas") posible?

Pregunta 1.m Sustituye la instrucción "if" por otra (u otras) que sólo usen el operador "?" y que produzcan el mismo resultado

5.5 Bucle for

Su sintaxis es:

for (expresión1; condición_booleana; expresión2)
    Instrucción


Descripción:
  1. Primero se ejecuta la expresión1
  2. Se comprueba la condición booleana. Si es true se continua por el paso 3, y en caso contrario fin del bucle.
  3. Ejecutar la Instrucción (que puede ser una instrucción compuesta).
  4. Se ejecuta la expresión2 y se va al paso 2.

Observación: La expresión1 suele contener la inicialización de las variables que intervienen en el bucle. La expresión2 suele contener las instrucciones necesarias para actualizar parte de dichas variables.
Ejemplo:

// contando del 1 al 10
int i;
for (i=1; i<=10; i++)
    System.out.print(i); // escribir i

Ejemplo: Programa que escribe todos los divisores de un número dado

import java.util.Scanner;

public class Principal {
    // divisores de uno dado
    public static void main(String[] args) {
    
        Scanner sc = new Scanner(System.in);
        
        System.out.print("Introduzca el número: ");
        int n=sc.nextInt();;
        
        System.out.print("Factores de "+n+": ");
        for (int i = 1; i<=n; i++)
            if (n % i == 0) // i divide a n
                System.out.print(i+" ");
        
        System.out.println();
    }   
}
Ejemplo de ejecución del programa:
Introduzca el número: 2005
Factores de 2005: 1 5 401 2005

¿Cuándo usar while y cuándo for? En general se utiliza for cuando el número de iteraciones se puede determinar antes de comenzar, es decir no depende de valores que se obtienen en el bucle, y while en otro caso.

5.6 Bucle do...while

Similar al bucle while, pero con la particularidad de que se repite al menos una vez. Su sintaxis es:

do
    Instrucción1
while condiciónBooleana


Ejemplo: Preguntando al usuario que si quiere grabar antes de salir

import java.util.Scanner;

public class Principal {
    
    // preguntando al usuario que se quiere salir
    public static void main(String[] args) {

        
        Scanner sc = new Scanner(System.in);
 
        String respuesta;   

        do {
            System.out.println("¿Grabar los datos antes de salir?");
            respuesta = sc.nextLine();
 
        } while (!respuesta.equals("si") && !respuesta.equals("no"));

        if (respuesta.equals("si"))
                .... // grabar
    }   
}

 


Observación: Los valores de tipo String no se deben comparar con == porque String no es un tipo primitivo. Para ver si un String es igual a otro se usa el método equals, que compara objetos tal y como ilustra el ejemplo.

 



Tabla A: Palabras Reservadas

Se llaman palabras reservadas a aquellas ocupadas por la sintaxis del lenguaje y que no puede utilizar el usuario para sus propósitos. Por ejemplo, no podemos llamar a una variable for o return, al ser esta una palabra reservada del lenguaje. La lista de palabras reservadas en el lenguaje es:

abstract do implements private this
boolean double import protected throw
break else instanceof public throws
byte extends int return transient
case false interface short true
catch final long static try
char finally native strictfp void
class float new super volatile
continue for null switch while
default if package synchronized enum

Ya hemos visto algunas, y al final del curso conoceremos casi todas.

Tabla B: Rango de los tipos primitivos

Aquí tenemos una lista con todos los tipos primitivos y el rango de valores que pueden tomar.

TipoDescripciónRango
byte Entero con signo -128 a 127
short Entero con signo -32768 a 32767
int Entero con signo -2147483648 a 214783647
long Entero con signo -922117036854775808 a +922117036854775808
float Real con signo ±1.40239846e-45 a ±3.40282347e+38
double Real de doble precisión ±4.94065645841246544e-324 a ±1.79769313486231570e+308
char Carácter Unicode \u0000 a \uFFFF
boolean Valor lógico true o false

Tabla C: Asociatividad de los Operadores

Asociatividad Operador
Izq. a Der. *, /, %
Izq. a Der. +, -
Izq. a Der. <<, >>, >>>
Izq. a Der. <, >, <=, >=, instanceof
Izq. a Der. ==, !=,
Izq. a Der. &
Izq. a Der. ^
Izq. a Der. |
Izq. a Der. &&
Izq. a Der. ||
Der. a Izq. ?:
Der. a Izq. ++, --, +, -, ~, !, (tipo de dato)
Der. a Izq. =, *=, /=, %=, +=, -=
Der. a Izq. <<=, >>=, >>>=, &=, ^=, !=



Página Principal-


2005-2006 Rafael Caballero