viernes, 8 de febrero de 2008

PROGRAMAS EN JAVA

programa en java

II.1. FUNDAMENTOS
A. Introducción
Java es un lenguaje orientado a objetos, que se deriva en alto grado de C++, de tal forma que puede ser considerado como un C++ nuevo y modernizado o bien como un C++ al que se le han amputado elementos heredados del lenguaje estructurado C.
B. Tokens
Un token es el elemento más pequeño de un programa que es significativo para el compilador. Estos tokens definen la estructura de Java.
Cuando se compila un programa Java, el compilador analiza el texto, reconoce y elimina los espacios en blanco y comentarios y extrae tokens individuales. Los tokens resultantes se compilan, traduciéndolos a código de byte Java, que es independiente del sistema e interpretable dentro de un entorno Java.
Los códigos de byte se ajustan al sistema de máquina virtual Java, que abstrae las diferencias entre procesadores a un procesador virtual único.
Los tokens Java pueden subdividirse en cinco categorías: Identificadores, palabras clave, constantes, operadores y separadores.
a.) Identificadores
Los identificadores son tokens que representan nombres asignables a variables, métodos y clases para identificarlos de forma única ante el compilador y darles nombres con sentido para el programador.
Todos los identificadores de Java diferencian entre mayúsculas y minúsculas (Java es Case Sensitive o Sensible a mayúsculas) y deben comenzar con una letra, un subrayado(_) o símbolo de dólar($). Los caracteres posteriores del identificador pueden incluir las cifras del 0 al 9. Como nombres de identificadores no se pueden usar palabras claves de Java.
Además de las restricciones mencionadas existen propuestas de estilo. Es una práctica estándar de Java denominar:
• Las clases: Clase o MiClase.
• Las interfaces: Interfaz o MiInterfaz.
• Los métodos: metodo() o metodoLargo().
• Las variables: altura o alturaMedia.
• Las constantes: CONSTATE o CONSTANTE_LARGA.
• Los paquetes: java.paquete.subpaquete.
Sin entrar en más detalle en la siguiente línea de código se puede apreciar la declaración de una variable entera (int) con su correspondiente identificador:
int alturaMedia;
b.) Palabras clave
Las palabras claves son aquellos identificadores reservados por Java para un objetivo determinado y se usan sólo de la forma limitada y específica. Java tiene un conjunto de palabras clave más rico que C o que C++, por lo que sí está aprendiendo Java con conocimientos de C o C++, asegúrese de que presta atención a las palabras clave de Java.
Las siguientes palabras son palabras reservadas de Java:
abstact boolean break byte byvalue
case cast catch char class
const continue default do double
else extends false final finally
float for future generic goto
if implements import inner instanceof
int interface long native new
null operator outer package private
protected public rest return short
static super switch syncroniced this
throw throws transient true try
var void volatile while
Tabla 2: Palabras reservadas Java
Las palabras subrayadas son palabras reservadas pero no se utilizan. La definición de estas palabras clave no se ha revelado, ni se tiene un calendario respecto a cuándo estará alguna de ellas en la especificación o en alguna de las implementaciones de Java.
c.) Literales y constantes
Los literales son sintaxis para asignar valores a las variables. Cada variables es de un tipo de datos concreto, y dichos tipos de datos tienen sus propios literales.
Mediante determinados modificadores (static y final) podremos crear variables constantes, que no modifican su valor durante la ejecución de un programa. Las constantes pueden ser numéricas, booleanas, caracteres (Unicode) o cadenas (String).
Las cadenas, que contienen múltiples caracteres, aún se consideran constantes, aunque están implementadas en Java como objetos.
Veamos un ejemplo de constante declarada por el usuario:
final static int ALTURA_MAXIMA = 200;
Se puede observar que utilizamos final static, para que la variable sea total y absolutamente invariable.
d.) Operadores
Conocidos también como operandos, indican una evaluación o computación para ser realizada en objetos o datos, y en definitiva sobre identificadores o constantes. Los operadores admitidos por Java son:
+ ^ <= ++ %=
>>>= - ~ >= -
&= . * && <<
== <<= [ / ||
>> += ^= ] %
! >>> = != (
& < *= ) |
> ?!! /= >>
Tabla 3: Operadores Java
Así por ejemplo el siguiente fragmento de código incrementa el valor de una variable en dos unidades, mediante la utilización del operador aritmético + que se utiliza para la suma:
int miNumero=0;
miNumero = miNumero + 2;
En el apartado "II.3 Operadores" de este tutorial aprenderemos que en Java hay formas más sencillas de hacer esto mismo, y estudiaremos el significado de cada uno de estos operadores.
e.) Separadores
Se usan para informar al compilador de Java de cómo están agrupadas las cosas en el código.
Los separadores admitidos por Java son: { } , : ;
f.) Comentarios y espacios en blanco
El compilador de Java reconoce y elimina los espacios en blanco, tabuladores, retornos de carro y comentarios durante el análisis del código fuente.
Los comentarios se pueden presentar en tres formatos distintos:
Formato Uso
/*comentario*/ Se ignoran todos los caracteres entre /* */. Proviene del C
//comentario Se ignoran todos los caracteres detrás de // hasta el fin de línea. Proviene del C++
/**comentario*/ Lo mismo que /* */ pero se podrán utilizar para documentación automática.
Tabla 4: Formatos de comentarios Java
Por ejemplo la siguiente línea de código presenta un comentario:
int alturaMinima = 150; // No menos de 150 centímetros
Ambito de las variables
El ámbito de una variable es el área del programa donde la variable existe y puede ser utilizada. Fuera de ese ámbito la variable, o bien no existe o no puede ser usada (que viene a ser lo mismo).
El ámbito de una variable miembro (que pertenece a un objeto) es el de la usabilidad de un objeto. Un objeto es utilizable desde el momento en que se crea y mientras existe una referencia que apunte a él. Cuando la última referencia que lo apunta sale de su ámbito el objeto queda 'perdido' y el espacio de memoria ocupado por el objeto puede ser recuperado por la JVM cuando lo considere oportuno. Esta recuperación de espacio en memoria se denomina 'recogida de basura' y es descrita un poco más adelante.
El ámbito de las variables locales es el bloque de código donde se declaran. Fuera de ese bloque la variable es desconocida.
Ejemplo:
{
int x; // empieza el ámbito de x. (x es conocida y utilizable)
{
int q; // empieza el ámbito de q. x sigue siendo conocida.
. . .
} // finaliza el ámbito de q (termina el bloque de código)
. . . // q ya no es utilizable
} // finaliza el ámbito de x
Recogida de basura
Cuando ya no se necesita un objeto simplemente puede dejar de referenciarse. No existe una operación explícita para 'destruir' un objeto o liberar el área de memoria usada por él.
La liberación de memoria la realiza el recolector de basura (garbage collector) que es una función de la JVM. El recolector revisa toda el área de memoria del programa y determina que objetos pueden ser borrados porque ya no tienen referencias activas que los apunten. El recolector de basura actua cuando la JVM lo determina (tiene un mecanismo de actuación no trivial).
En ocasiones es necesario realizar alguna acción asociada a la acción de liberar la memoria asignada al objeto (como por ejemplo liberar otros recursos del sistema, como descriptores de ficheros). Esto puede hacerse codificando un método finalize que debe declararse como:
protected void finalize() throws Throwable { }
Nota: las clausulas protected y throws se explican en capítulos posteriores.
El método finalize es invocando por la JVM antes de liberar la memoria por el recolector de basura, o antes de terminar la JVM. No existe un momento concreto en que las áreas de memoria son liberadas, sino que lo determina en cada momento la JVM en función de sus necesidades de espacio.



Expresiones
Los operadores, variables y las llamadas a métodos pueden ser combinadas en secuencias conocidas como expresiones. El comportamiento real de un programa Java se logra a través de expresiones, que se agrupan para crear sentencias.
Una expresión es una serie de variables, operadores y llamadas a métodos (construida conforme a la sintaxis del lenguaje) que se evalúa a un único valor.
Entre otras cosas, las expresiones son utilizadas para realizar cálculos, para asignar valores a variables, y para ayudar a controlar la ejecución del flujo del programa. La tarea de una expresión se compone de dos partes: realiza el cálculo indicado por los elementos de la expresión y devuelve el valor obtenido como resultado del cálculo.
Los operadores devuelven un valor, por lo que el uso de un operador es una expresión.
Por ejemplo, la siguiente sentencia es una expresión:
int contador=1;
contador++;
La expresión contador++ en este caso particular se evalúa al valor 1, que era el valor de la variable contador antes de que la operación ocurra, pero la variable contador adquiere un valor de 2.
El tipo de datos del valor devuelto por una expresión depende de los elementos utilizados en la expresión. La expresión contador++ devuelve un entero porque ++ devuelve un valor del mismo tipo que su operando y contador es un entero. Otras expresiones devuelven valores booleanos, cadenas...
Una expresión de llamada a un método se evalúa al valor de retorno del método; así el tipo de dato de la expresión de llamada a un método es el mismo que el tipo de dato del valor de retorno de ese método.
Otra sentencia interesante sería:
in.read( ) != -1 // in es un flujo de entrada
Esta sentencia se compone de dos expresiones:
1. La primera expresión es una llamada al método in.read(). El método in.read() ha sido declarado para devolver un entero, por lo que la expresión in.read() se evalúa a un entero.
2. La segunda expresión contenida en la sentencia utiliza el operador !=, que comprueba si dos operandos son distintos. En la sentencia en cuestión, los operandos son in.read() y -1. El operando in.read() es válido para el operador != porque in.read() es una expresión que se evalúa a un entero, así que la expresión in.read()!=-1 compara dos enteros. El valor devuelto por esta expresión será verdadero o falso dependiendo del resultado de la lectura del fichero in.
Como se puede observar, Java permite construir sentencias (expresiones compuestas) a partir de varias expresiones más pequeñas con tal que los tipos de datos requeridos por una parte de la expresión concuerden con los tipos de datos de la otra.
D. Bloques y ámbito
En Java el código fuente está dividido en partes separadas por llaves, denominas bloques. Cada bloque existe independiente de lo que está fuera de él, agrupando en su interior sentencias (expresiones) relacionadas.
Desde un bloque externo parece que todo lo que está dentro de llaves se ejecuta como una sentencia. Pero, ¿qué es un bloque externo?. Esto tiene explicación si entendemos que existe una jerarquía de bloques, y que un bloque puede contener uno o más subbloques anidados.
El concepto de ámbito está estrechamente relacionado con el concepto de bloque y es muy importante cuando se trabaja con variables en Java. El ámbito se refiere a cómo las secciones de un programa (bloques) afectan el tiempo de vida de las variables.
Toda variable tiene un ámbito, en el que es usada, que viene determinado por los bloques. Una variable definida en un bloque interno no es visible por el bloque externo.
Las llaves de separación son importantes no sólo en un sentido lógico, ya que son la forma de que el compilador diferencie dónde acaba una sección de código y dónde comienza otra, sino que tienen una connotación estética que facilita la lectura de los programas al ser humano.
Así mismo, para identificar los diferentes bloques se utilizan sangrías. Las sangrías se utilizan para el programador, no para el compilador. La sangría (también denominada indentación) más adecuada para la estética de un programa Java son dos espacios:
{

// Bloque externo

int x = 1;

{

// Bloque interno invisible al exterior

int y = 2;

}

x = y; // Da error: Y fuera de ámbito

}




Clases
Las clases son el mecanismo por el que se pueden crear nuevos Tipos en Java. Las clases son el punto central sobre el que giran la mayoría de los conceptos de la Orientación a Objetos.
Una clase es una agrupación de datos y de código que actua sobre esos datos, a la que se le da un nombre.
Una clase contiene:
• Datos (se denominan Datos Miembro). Estos pueden ser de tipos primitivos o referencias.
• Métodos (se denominan Métodos Miembro).
La sintaxis general para la declaración de una clase es:
modificadores class nombre_clase {
declaraciones_de_miembros ;
}
Por ejemplo:
class Punto {
int x;
int y;
}
Los modificadores son palabras clave que afectan al comportamiento de la clase. Los iremos viendo progresivamente en los sucesivos capítulos.
4.2. Objetos, miembros y referencias
Un objeto es una instancia (ejemplar) de una clase. La clase es la definición general y el objeto es la materialización concreta (en la memoria del ordenador) de una clase.
El fenómeno de crear objetos de una clase se llama instanciación.
Los objetos se manipulan con referencias. Una referencia es una variable que apunta a un objeto. Las referencias se declaran igual que las variables de Tipos primitivos (tipo nombre). Los objetos se crean (se instancian) con el operador de instanciación new.
Ejemplo:
Punto p;
p = new Punto();
La primera línea del ejemplo declara una referencia (p) que es de Tipo Punto. La referencia no apunta a ningún sitio. En la segunda línea se crea un objeto de Tipo Punto y se hace que la referencia p apunte a él. Se puede hacer ambas operaciones en la misma expresión:
Punto p = new Punto();
A los miembros de un objeto se accede a través de su referencia. La sintaxis es:
nombre_referencia.miembro
En el ejemplo, se puede poner:
p.x = 1;
p.y = 3;
Se puede visualizar gráficamente los datos primitivos, referencias y objetos de la siguiente forma:
• Datos primitivos: int i = 5;
• Referencias y objetos:
Punto p = new Punto();
p.x = 1;
p.y = 3;





Es importante señalar que en el ejemplo, p no es el objeto. Es una referencia que apunta al objeto.
Los métodos miembro se declaran dentro de la declaración de la clase, tal como se ha visto en el capítulo anterior. Por ejemplo:
class Circulo {
Punto centro; // dato miembro. Referencia a un objeto punto
int radio; // dato miembro. Valor primitivo
float superficie() { // método miembro.
return 3.14 * radio * radio;
} // fin del método superficie
} // fin de la clase Circulo
El acceso a métodos miembros es igual que el que ya se ha visto para datos miembro. En el ejemplo:
Circulo c = new Circulo();
c.centro.x = 2;
c.centro.y = 3;
c.radio = 5;
float s = c.superficie();
Es interesante observar en el ejemplo:
• Los datos miembro pueden ser tanto primitivos como referencias. La clase Circulo contiene un dato miembro de tipo Punto (que es el centro del círculo).
• El acceso a los datos miembros del Punto centro se hace encadenando el operador . en la expresión c.centro.x que se podría leer como 'el miembro x del objeto (Punto) centro del objeto (Circulo) c'.
• Aunque el método superficie no recibe ningún argumento los paréntesis son obligatorios (Distinguen los datos de los métodos).
• Existe un Objeto Punto para cada instancia de la clase Circulo (que se crea cuando se crea el objeto Circulo).
Conceptos básicos. Resumen
• Una Clase es una definición de un nuevo Tipo, al que se da un nombre.
• Una Clase contiene Datos Miembro y Métodos Miembro que configuran el estado y las operaciones que puede realizar.
• Un Objeto es la materialización (instanciación) de una clase. Puede haber tantos Objetos de una Clase como resulte necesario.
• Los Objetos se crean (se les asigna memoria) con el Operador new.
• Los Objetos se manipulan con Referencias.
• Una Referencia es una Variable que apunta a un Objeto.
• El acceso a los elementos de un Objeto (Datos o métodos) se hace con el operador . (punto) : nombre_referencia.miembro



Noción de constructor
Cuando se crea un objeto (se instancia una clase) es posible definir un proceso de inicialización que prepare el objeto para ser usado. Esta inicialización se lleva a cabo invocando un método especial denominado constructor. Esta invocación es implícita y se realiza automáticamente cuando se utiliza el operador new. Los constructores tienen algunas características especiales:
• El nombre del constructor tiene que se igual al de la clase.
• Puede recibir cualquier número de argumentos de cualquier tipo, como cualquier otro método.
• No devuelve ningún valor (en su declaración no se declara ni siquiera void).
El constructor no es un miembro más de una clase. Sólo es invocado cuando se crea el objeto (con el operador new). No puede invocarse explicitamente en ningún otro momento.
Continuando con los ejemplos del capítulo anterior se podría escribir un constructor para la clase Punto, de la siguiente forma:
class Punto {
int x , y ;
Punto ( int a , int b ) {
x = a ; y = b ;
}
}
Con este constructor se crearía un objeto de la clase Punto de la siguiente forma:
Punto p = new Punto ( 1 , 2 );

Constructor no-args.
Si una clase no declara ningún constructor, Java incorpora un constructor por defecto (denominado constructor no-args) que no recibe ningún argumento y no hace nada.
Si se declara algún constructor, entonces ya no se puede usar el constructor no-args. Es necesario usar el constructor declarado en la clase.
En el ejemplo el constructor no-args sería:
class Punto {
int x , y ;
Punto ( ) { }
}
Sobrecarga de constructores.
Una clase puede definir varios constructores (un objeto puede inicializarse de varias formas). Para cada instanciación se usa el que coincide en número y tipo de argumentos. Si no hay ninguno coincidente se produce un error en tiempo de compilación.
Por ejemplo:
class Punto {
int x , y ;
Punto ( int a , int b ) {
x = a ; y = b ;
}
Punto () {
x = 0 ; y = 0;
}
}
En el ejemplo se definen dos constructores. El citado en el ejemplo anterior y un segundo que no recibe argumentos e inicializa las variables miembro a 0. (Nota: veremos más adelante que este tipo de inicialización es innecesario, pero para nuestro ejemplo sirve).
Desde un constructor puede invocarse explicitamente a otro constructor utilizando la palabra reservada this . this es una referencia al propio objeto. Cuando this es seguido por parénteses se entiende que se está invocando al constructor del objeto en cuestión. Puedes ver el uso más habitual de this aquí. El ejemplo anterior puede reescribirse de la siguiente forma:
class Punto {
int x , y ;
Punto ( int a , int b ) {
x = a ; y = b ;
}
Punto () {
this (0,0);
}
}
Datos estáticos
Un dato estático es una variable miembro que no se asocia a un objeto (instancia) de una clase, sino que se asocia a la clase misma; no hay una copia del dato para cada objeto sino una sola copia que es compartida por todos los objetos de la clase.
Por ejemplo:
class Punto {
int x , y ;
static int numPuntos = 0;

Punto ( int a , int b ) {
x = a ; y = b;
numPuntos ++ ;
}
}
En el ejemplo numPuntos es un contador que se incrementa cada vez que se crea una instancia de la clase Punto.
Observese la forma en que se declara numPuntos, colocando el modificador static delante del tipo. La sintaxis general para declarar una variable es:
[modificadores] tipo_variable nombre;
static es un modificador. En los siguientes capítulos se irán viendo otros modificadores. Los [ ] en la expresión anterior quieren decir que los modificadores son opcionales.
El acceso a las variables estáticas desde fuera de la clase donde se definen se raliza a través del nombre de la clase y no del nombre del objeto como sucede con las variables miembro normales (no estáticas). En el ejemplo anterior puede escribirse:
int x = Punto.numPuntos;
No obstante también es posible acceder a las variables estáticas a través de una referencia a un objeto de la clase. Por ejemplo:
Punto p = new Punto();
int x = p.numPuntos;
Las variables estáticas de una clase existen, se inicializan y pueden usarse antes de que se cree ningún objeto de la clase.
Métodos estáticos
Para los métodos, la idea es la misma que para los datos: los métodos estáticos se asocian a una clase, no a una instancia.
Por ejemplo:
class Punto {
int x , y ;
static int numPuntos = 0;

Punto ( int a , int b ) {
x = a ; y = b;
numPuntos ++ ;
}

static int cuantosPuntos() {
return numPuntos;
}
}
La sintaxis general para la definición de los métodos es, por tanto, la siguiente:
[modificadores ] Tipo_Valor_devuelto nombre_método ( lista_argumentos ) {
bloque_de_codigo;
}
El aceso a los métodos estáticos se hace igual que a los datos estáticos, es decir, usando el nombre de la clase, en lugar de usar una referencia:
int totalPuntos = Punto.cuantosPuntos();
Dado que los métodos estáticos tienen sentido a nivel de clase y no a nivel de objeto (instancia) los métodos estáticos no pueden acceder a datos miembros que no sean estáticos.
El método main
Un programa Java se inicia proporcionando al intérprete Java un nombre de clase. La JVM carga en memoria la clase indicada e inicia su ejecución por un método estático que debe estar codificado en esa clase. El nombre de este método es main y debe declararse de la siguiente forma:
static void main ( String [] args)
• Es un método estático. Se aplica por tanto a la clase y no a una instancia en particular, lo que es conveniente puesto que en el momento de iniciar la ejecución todavía no se ha creado ninguna instancia de ninguna clase.
• Recibe un argumento de tipo String []. String es una clase que representa una cadena de caracteres ( se verá más adelante),
• Los corchetes [] indican que se trata de un array que se verán en un capítulo posterior.
No es obligatorio que todas las clases declaren un método main . Sólo aquellos métodos que vayan a ser invocados directamente desde la línea de comandos de la JVM necesitan tenerlo. En la práctica la mayor parte de las clases no lo tienen.
Inicializadores estáticos
En ocasiones es necesario escribir código para inicializar los datos estáticos, quizá creando algún otro objeto de alguna clase o realizando algún tipo de control. El fragmento de código que realiza esta tarea se llama inicializador estático. Es un bloque de sentencias que no tiene nombre, ni recibe argumentos, ni devuelve valor. Simplemente se declara con el modificador static .
La forma de declarar el inicializador estático es:
static { bloque_codigo }
Por ejemplo:
class Punto {
int x , y ;
static int numPuntos;

static {
numPuntos = 0;
}

Punto ( int a , int b ) {
x = a ; y = b;
numPuntos ++ ;
}

static int cuantosPuntos() {
return numPuntos;
}
}
Nota: El ejemplo, una vez más, muestra sólo la sintaxis y forma de codificación. Es innecesario inicializar la variable tal como se verá más adelante. Además podría inicializarse directamente con: static int numPuntos = 0;

Cuando se declaran varios constructores para una misma clase estos deben distinguirse en la lista de argumentos, bien en el número, bien en el tipo.
Esta característica de definir métodos con el mismo nombre se denomina sobrecarga y es aplicable a cualquier método miembro de una clase como veremos más adelante.

Datos estáticos
Un dato estático es una variable miembro que no se asocia a un objeto (instancia) de una clase, sino que se asocia a la clase misma; no hay una copia del dato para cada objeto sino una sola copia que es compartida por todos los objetos de la clase.
Por ejemplo:
class Punto {
int x , y ;
static int numPuntos = 0;

Punto ( int a , int b ) {
x = a ; y = b;
numPuntos ++ ;
}
}
En el ejemplo numPuntos es un contador que se incrementa cada vez que se crea una instancia de la clase Punto.
Observese la forma en que se declara numPuntos, colocando el modificador static delante del tipo. La sintaxis general para declarar una variable es:
[modificadores] tipo_variable nombre;
static es un modificador. En los siguientes capítulos se irán viendo otros modificadores. Los [ ] en la expresión anterior quieren decir que los modificadores son opcionales.
El acceso a las variables estáticas desde fuera de la clase donde se definen se raliza a través del nombre de la clase y no del nombre del objeto como sucede con las variables miembro normales (no estáticas). En el ejemplo anterior puede escribirse:
int x = Punto.numPuntos;
No obstante también es posible acceder a las variables estáticas a través de una referencia a un objeto de la clase. Por ejemplo:
Punto p = new Punto();
int x = p.numPuntos;
Las variables estáticas de una clase existen, se inicializan y pueden usarse antes de que se cree ningún objeto de la clase.
Métodos estáticos
Para los métodos, la idea es la misma que para los datos: los métodos estáticos se asocian a una clase, no a una instancia.
Por ejemplo:
class Punto {
int x , y ;
static int numPuntos = 0;

Punto ( int a , int b ) {
x = a ; y = b;
numPuntos ++ ;
}

static int cuantosPuntos() {
return numPuntos;
}
}
La sintaxis general para la definición de los métodos es, por tanto, la siguiente:
[modificadores ] Tipo_Valor_devuelto nombre_método ( lista_argumentos ) {
bloque_de_codigo;
}
El aceso a los métodos estáticos se hace igual que a los datos estáticos, es decir, usando el nombre de la clase, en lugar de usar una referencia:
int totalPuntos = Punto.cuantosPuntos();
Dado que los métodos estáticos tienen sentido a nivel de clase y no a nivel de objeto (instancia) los métodos estáticos no pueden acceder a datos miembros que no sean estáticos.
El método main
Un programa Java se inicia proporcionando al intérprete Java un nombre de clase. La JVM carga en memoria la clase indicada e inicia su ejecución por un método estático que debe estar codificado en esa clase. El nombre de este método es main y debe declararse de la siguiente forma:
static void main ( String [] args)
• Es un método estático. Se aplica por tanto a la clase y no a una instancia en particular, lo que es conveniente puesto que en el momento de iniciar la ejecución todavía no se ha creado ninguna instancia de ninguna clase.
• Recibe un argumento de tipo String []. String es una clase que representa una cadena de caracteres ( se verá más adelante),
• Los corchetes [] indican que se trata de un array que se verán en un capítulo posterior.
No es obligatorio que todas las clases declaren un método main . Sólo aquellos métodos que vayan a ser invocados directamente desde la línea de comandos de la JVM necesitan tenerlo. En la práctica la mayor parte de las clases no lo tienen.
Inicializadores estáticos
En ocasiones es necesario escribir código para inicializar los datos estáticos, quizá creando algún otro objeto de alguna clase o realizando algún tipo de control. El fragmento de código que realiza esta tarea se llama inicializador estático. Es un bloque de sentencias que no tiene nombre, ni recibe argumentos, ni devuelve valor. Simplemente se declara con el modificador static .
La forma de declarar el inicializador estático es:
static { bloque_codigo }
Por ejemplo:
class Punto {
int x , y ;
static int numPuntos;

static {
numPuntos = 0;
}

Punto ( int a , int b ) {
x = a ; y = b;
numPuntos ++ ;
}

static int cuantosPuntos() {
return numPuntos;
}
}
Nota: El ejemplo, una vez más, muestra sólo la sintaxis y forma de codificación. Es innecesario inicializar la variable tal como se verá más adelante. Además podría inicializarse directamente con: static int numPuntos = 0;

Inicialización de variables
Desde el punto de vista del lugar donde se declaran existen dos tipos de variables:
• Variables miembro: Se declaran en una clase, fuera de cualquier método.
• Variables locales: Se declaran y usan en un bloque de código dentro de un método.
Las variables miembro son inicializadas automáticamente, de la siguiente forma:
• Las numéricas a 0.
• Las booleanas a false.
• Las char al caracter nulo (hexadecimal 0).
• Las referencias a null.
Nota: nulles un literal que indica referencia nula.
Las variables miembro pueden inicializarse con valores distintos de los anteriores en su declaración.
Las variables locales no se inicializan automáticamente. Se debe asignarles un valor antes de ser usadas. Si el compilador detecta una variable local que se usa antes de que se le asigne un valor produce un error. Por ejemplo:
int p;
int q = p; // error
El compilador también produce un error si se intenta usar una variable local que podría no haberse inicializado, dependiendo del flujo de ejecución del programa. Por ejemplo:
int p;
if (. . . ) {
p = 5 ;
}
int q = p; // error
El compilador produce un error del tipo 'La variable podría no haber sido inicializada', independientemente de la condición del if.

La referencia this
En ocasiones es conveniente disponer de una referencia que apunte al propio objeto que se está manipulando. Esto se consigue con la palabra reservada this. this es una referencia implicita que tienen todos los objetos y que apunta a si mismo. Por ejemplo:
class Circulo {
Punto centro;
int radio;
. . .
Circulo elMayor(Circulo c) {
if (radio > c.radio) return this;
else return c;
}
}
El método elMayor devuelve una referencia al círculo que tiene mayor radio, comparando los radios del Circulo c que se recibe como argumento y el propio. En caso de que el propio resulte mayor el método debe devolver una referencia a si mismo. Esto se consigue con la expresión return this.
La referencia null
Para asignar a una referencia el valor nulo se utiliza la constante null. El ejemplo del caso anterior se podría completar con:
class Circulo {
Punto centro;
int radio;
. . .
Circulo elMayor(Circulo c) {
if (radio > c.radio) return this;
else if (c.radio > radio) return c;
else return null;
}
}
Ocultamiento de variables
Puede ocurrir que una variable local y una variable miembro reciban el mismo nombre (en muchos casos por error). Cuando se produce esto la variable miembro queda oculta por la variable local, durante el bloque de código en que la variable local existe y es accesible. Cuando se sale fuera del ámbito de la variable local, entonces la variable miembro queda accesible. Observese esto en el ejemplo siguiente:
. . .
String x = "Variable miembro";
. . .
void variableOculta() {
System.out.println(x);
{
String x = "Variable local";
System.out.println(x);
}
System.out.println(x);
}
Nota: El uso de Strings se verá en un capítulo posterior, aunque su uso aquí resulta bastante intuitivo. La llamada System.out.println envia a la consola (la salida estándar habitual) las variables que se pasan como argumentos.
La llamada al método variableOculta() producirá la siguiente salida:
Variable miembro
Variable local
Variable miembro
Se puede acceder a la variable miembro oculta usando la referencia this. En el ejemplo anterior la expresión:
System.out.println(this.x);
siempre producirá la salida 'Variable miembro', puesto que this.x se refiere siempre a la variable miembro.
Ejecución condicional
El formato general es:
if (expresion_booleana)
sentencia
[else
sentencia]
sentencia (a todo lo largo de este capítulo) puede ser una sola sentencia o un bloque de sentencias separadas por ; y enmarcadas por llaves { y }. Es decir
if (expresion_booleana) {
sentencia;
sentencia;
. . .
}
else {
sentencia;
sentencia;
. . .
}
expresion_booleana es una expresión que se evalua como true o false (es decir, de tipo booleano). Si el resultado es true la ejecución bifurca a la sentencia que sigue al if . En caso contrario se bifurca a la sentencia que sigue al else.
Los corchetes en el formato anterior indican que la clausula else es opcional.
Iteraciones con while
Sintaxis formato 1:
while (expresion_booleana)
sentencia
Sintaxis formato 2:
do
sentencia
while (expresion_booleana)
La sentencia o bloque se sentencias (se aplica la misma idea que para el if-else) se ejecuta mientras que la expresion_booleana se evalue como true
La diferencia entre ambos formatos es que en el primero la expresión se evalua al principio del bloque de sentencias y en el segundo se evalua al final.
Iteraciones con for
El formato es:
for ( inicializacion ; expresion_booleana ; step )
sentencia
inicializacion es una sentencia que se ejecuta la primera vez que se entra en el bucle for . Normalmente es una asignación. Es opcional.
expresion_booleana es una expresión que se evalua antes de la ejecución de la sentencia, o bloque de sentencias, para cada iteración. La sentencia o bloque de sentencias se ejecutan mientras que la expresion_booleana se evalue como cierta. Es opcional.
step es una sentencia que se ejecuta cada vez que se llega al final de la sentencia o bloque de sentencias. Es opcional.
Una utilización clásica de un bucle de tipo for se muestra a continuación para evaluar un contador un número fijo de veces:
for ( int i = 1 ; i <= 10 ; i++ )
sentencia
La sentencia (o bloque de sentencias) se evaluará 10 veces. En cada ejecución (pasada) el valor de la variable i irá variando desde 1 hasta 10 (inclusive). Cuando salga del bloque de sentencias i estará fuera de su ámbito (porque se define en el bloque for).
Si se omiten las tres clausulas del bucle se obtiene un bucle infinito:
for ( ; ; )
sentencia
Obsérvese que se pueden omitir las clausulas pero no los separadores (;).
Evaluación múltiple
El formato es:
switch ( expresion_entera ) {
case valor_entero:
sentencia;
break;
case valor_entero:
sentencia;
break;
. . .
default:
sentencia;
}
Cuidado: en el switch la expresión que se evalua no es una expresión booleana como en el if-else, sino una expresión entera.
Se ejecuta el bloque case cuyo valor coincida con el resultado de la expresión entera de la clausula switch . Se ejecuta hasta que se encuentra una sentencia break o se llega al final del switch .
Si ningún valor de case coincide con el resultado de la expresión entera se ejecuta el bloque default(si está presente).
default y break son opcionales.
Devolución de control
El formato es:
return valor
Se utiliza en los métodos para terminar la ejecución y devolver un valor a quien lo llamó.
valor debe ser del tipo declarado en el método.
valor es opcional. No debe existir cuando el método se declara de tipo void. En este caso, la claúsula return al final del método es opcional, pero puede usarse para devolver el control al llamador en cualquier momento.
Expresiones
La mayor parte del trabajo en un programa se hace mediante la evaluación de expresiones, bien por sus efectos tales como asignaciones a variables, bien por sus valores, que pueden ser usados como argumentos u operandos en expresiones mayores, o afectar a la secuencia de ejecución de instrucciones.
Cuando se evalua una expresión en un programa el resultado puede denotar una de tres cosas:
• Una variable. (Si por ejemplo es una asignación)
• Un valor. (Por ejemplo una expresión aritmética, booleana, una llamada a un método, etc.)
• Nada. (Por ejemplo una llamada a un método declarado void)
Si la expresión denota una variable o un valor, entonces la expresión tiene siempre un tipo conocido en el momento de la compilación. Las reglas para determinar el tipo de la expresión varían dependiendo de la forma de las expresiones pero resultan bastante naturales. Por ejemplo, en una expresión aritmética con operandos de diversas precisiones el resultado es de un tipo tal que no se produzca pérdida de información, realizandose internamente las conversiones necesarias. El análisis pormenorizado de las conversiones de tipos, evaluaciones de expresiones, etc, queda fuera del ámbito de estos apuntes. En general puede decirse que es bastante similar a otros lenguajes, en particular C, teniendo en cuenta la característica primordial de Java de tratarse de un lenguaje con control fuerte de tipos.

curso java