Miembros de la Clase y del Ejemplar

Cuando se declara una variable miembro como unFloat en MiClase:
class MiClase {
    float unFloat;
}
declara una variable de ejemplar. Cada vez que se crea un ejemplar de la clase, el sistema crea una copia de todas las variables de ejemplar de la clase. Se puede acceder a una variable del ejemplar del objeto desde un objeto como se describe en Utilizar Objetos.

Las variables de ejemplar están en constraste con las variables de clase (que se declaran utilizando el modificador static). El sistema asigna espacio para las variables de clase una vez por clase, sin importar el número de ejemplares creados de la clase. Todos los objetos creados de esta clase comparten la misma copia de las variables de clase de la clase, se puede acceder a las variables de clase a través de un ejemplar o través de la propia clase.

Los métodos son similares: una clase puede tener métodos de ejemplar y métodos de clase. Los métodos de ejemplar operan sobre las variables de ejemplar del objeto actual pero también pueden acceder a las variables de clase. Por otro lado, los métodos de clase no pueden acceder a las variables del ejemplar declarados dentro de la clase (a menos que se cree un objeto nuevo y acceda a ellos através del objeto). Los métodos de clase también pueden ser invocados desde la clase, no se necesita un ejemplar para llamar a los métodos de la clase.

Por defecto, a menos que se especifique de otra forma, un miembro declarado dentro de una clase es un miembro del ejemplar. La clase definida abajo tiene una variable de ejemplar -- un entero llamado x -- y dos métodos de ejemplar -- x() y setX() -- que permite que otros objetos pregunten por el valor de x:

class UnEnteroLlamadoX {
    int x;
    public int x() {
        return x;
    }
    public void setX(int newX) {
        x = newX;
    }
}
Cada vez que se ejemplariza un objeto nuevo desde una clase, se obtiene una copia de cada una de las variables de ejemplar de la clase. Estas copias están asociadas con el objeto nuevo. Por eso, cada vez que se ejemplariza un nuevo objeto UnEnteroLlamadoX de la clase, se obtiene una copia de x que está asociada con el nuevo objeto UnEnteroLlamadoX.

Todos los ejemplares de una clase comparten la misma implementación de un método de ejemplar; todos los ejemplares de UnEnteroLlamadoX comparten la misma implementación de x() y setX(). Observa que estos métodos se refieren a la variable de ejemplar del objeto x por su nombre. "Pero, ¿si todos los ejemplares de UnEnteroLlamadoX comparten la misma implementación de x() y setX() esto no es ambigüo?" La respuesta es no. Dentro de un método de ejemplar, el nombre de una variable de ejemplar se refiere a la variable de ejemplar del objeto actual (asumiendo que la variable de ejemplar no está ocultada por un parámetro del método). Ya que, dentro de x() y setX(), x es equivalente a this.x.

Los objetos externos a UnEnteroLlamadoX que deseen acceder a x deben hacerlo a través de un ejemplar particular de UnEnteroLlamadoX. Supongamos que este código estuviera en otro método del objeto. Crea dos objetos diferentes del tipo UnEnteroLlamadoX, y selecciona sus valores de x a diferente valores Y luego lo muestra:

. . .
UnEnteroLlamadoX miX = new UnEnteroLlamadoX();
UnEnteroLlamadoX otroX = new UnEnteroLlamadoX();
miX.setX(1);
otroX.x = 2;
System.out.println("miX.x = " + miX.x());
System.out.println("otroX.x = " + otroX.x());
. . .
Observa que el código utilizado en setX() para seleccionar el valor de x para miX pero solo asignando el valor otroX.x directamente. De otra forma, el código manipula dos copias diferentes de x: una contenida en el objeto miX y la otra en el objeto otroX. La salida producida por este código es:
miX.x = 1
otroX.x = 2
mostrando que cada ejemplar de la clase UnEnteroLlamadoX tiene su propia copia de la variable de ejemplar x y que cada x tiene un valor diferente.

Cuando se declara una variable miembro se puede especificar que la variable es una variable de clase en vez de una variable de ejemplar. Similarmente, se puede especificar que un método es un método de clase en vez de un método de ejemplar. El sistema crea una sola copia de una variable de clase la primera vez que encuentra la clase en la que está definida la variable. Todos los ejemplares de esta clase comparten la misma copia de las variables de clase. Los métodos de clase solo pueden operar con variables de clase -- no pueden acceder a variables de ejemplar defindas en la clase.

Para especificar que una variable miembro es una variable de clase, se utiliza la palabra clave static. Por ejemplo, cambiemos la clase UnEnteroLlamadoX para que su variable x sea ahora una variable de clase:

class UnEnteroLlamadoX {
    static int x;
    public int x() {
        return x;
    }
    public void setX(int newX) {
        x = newX;
    }
}
Ahora veamos el mismo código mostrado anteriormente que crea dos ejemplares de UnEnteroLlamadoX, selecciona sus valores de x, y muestra esta salida diferente:
miX.x = 2
otroX.x = 2
La salida es diferente porque x ahora es una variable de clase por lo que sólo hay una copia de la variable y es compartida por todos los ejemplares de UnEnteroLlamadoX incluyendo miX y otroX.

Cuando se llama a setX() en cualquier ejemplar, cambia el valor de x para todos los ejemplares de UnEnteroLlamadoX.

Las variables de clase se utilizan para aquellos puntos en lo que se necesite una sola copia que debe estar accesible para todos los objetos heredados por la clase en la que la variable fue declarada. Por ejemplo, las variables de clase se utilizan frecuentemente con final para definir constantes (esto es más eficiente en el consumo de memoria, ya que las constantes no pueden cambiar y sólo se necesita una copia).

Similarmente, cuando se declare un método, se puede especificar que el método es un método de clase en vez de un método de ejemplar. Los métodos de clase sólo pueden operar con variables de clase y no pueden acceder a las variables de ejemplar definidas en la clase.

Para especificar que un método es un método de clase, se utiliza la palabra clave static en la declaración de método. Cambiemos la clase UnEnteroLlamadoX para que su variable miembro x sea de nuevo una variable de ejemplar, y sus dos métodos sean ahora métodos de clase:

class UnEnteroLlamadoX {
    private int x;
    static public int x() {
        return x;
    }
    static public void setX(int newX) {
        x = newX;
    }
}
Cuando se intente compilar esta versión de UnEnteroLlamadoX, se obtendrán errores de compilación:
UnEnteroLlamadoX.java:4: Can't make a static reference to nonstatic variable x in class UnEnteroLlamadoX.
        return x;
               ^ 
UnEnteroLlamadoX.java:7: Can't make a static reference to nonstatic variable x in class UnEnteroLlamadoX.
        x = newX;
        ^
2 errors
Esto es porque los métodos de la clase no pueden acceder a variables de ejemplar a menos que el método haya creado un ejemplar de UnEnteroLlamadoX primero y luego acceda a la variable a través de él.

Construyamos de nuevo UnEnteroLlamadoX para hacer que su variable x sea una variable de clase:

class UnEnteroLlamadoX {
    static private int x;
    static public int x() {
        return x;
    }
    static public void setX(int newX) {
        x = newX;
    }
}
Ahora la clase se compilará y el código anterior que crea dos ejemplares de UnEnteroLlamadoX, selecciona sus valores x, y muestra en su salida los valores de x:
miX.x = 2
otroX.x = 2
De nuevo, cambiar x a través de miX también lo cambia para los otros ejemplares de UnEnteroLlamadoX.

Otra diferencia entre miembros del ejemplar y de la clase es que los miembros de la clase son accesibles desde la propia clase. No se necesita ejemplarizar la clase para acceder a los miembros de clase. Reescribamos el código anterior para acceder a x() y setX() directamente desde la clase UnEnteroLlamadoX:

. . .
UnEnteroLlamadoX.setX(1);
System.out.println("UnEnteroLlamadoX.x = " + UnEnteroLlamadoX.x());
. . .
Observa que ya no se tendrá que crear miX u otroX. Se puede seleccionar x y recuperarlo directamente desde la clase UnEnteroLlamadoX. No se puede hacer esto con miembros del ejemplar. Solo se puede invocar métodos de ejemplar a través de un objeto y sólo puede acceder a las variables de ejemplar desde un objeto. Se puede acceder a las variables y métodos de clase desde un ejemplar de la clase o desde la clase misma.


Ozito