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 numéricas a 0.
- Las booleanas a false.
- Las char al caracter nulo (hexadecimal 0).
- Las referencias a null.
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
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
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.
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
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 explicarán 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.
Sobrecarga de métodos
Una misma clase puede tener varios métodos con el mismo nombre siempre que se diferencien en el tipo o número de los argurmentos. Cuando esto sucede se dice que el método está sobrecargado. Por ejemplo, una misma clase podría tener los métodos:
int metodoSobrecargado() { . . .}
int metodoSobrecargado(int x) { . . .}
Sin embargo no se puede sobrecargar cambiando sólo el tipo del valor devuelto. Por ejemplo:int metodoSobrecargado(int x) { . . .}
int metodoSobrecargado() { . . .}
void metodoSobrecargado() { . . .} // error en compilación
con esta definición, en la expresión metodoSobrecargado() la JVM no sabría que método invocar.void metodoSobrecargado() { . . .} // error en compilación
Se puede sobrecargar cualquier método miembro de una clase, así como el constructor.
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;
}
}
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;
}
}
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);
}
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
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.
REFERENCIAS:arrakis