Static en Java

Buenas tardes, en esta entrada veremos como desarrollar código que declare, inicialice y use diferentes tipos de objetos y variables static.


  • Variables static y Métodos

    El modificador static ha tenido un profundo impacto en el comportamiento de método o variable que hemos estado tratando como un concepto enteramente separado de otros modificadores. Para entender la manera en la que el miembro static trabaja, miraremos primero la razón por la que los usaremos. Imaginemos que tenemos una clase de utilidad con un método que siempre se ejecuta de la misma manera; su única función es retornar un número aleatorio, por ejemplo. No importaría que instancia de la clase hubiera realizado el método (siempre se comportaría de la misma manera). En otras palabras, el comportamiento del método no tiene dependencia del estado del objeto (los varobles de las variables de instancia). Entonces ¿Por qué necesitamos un objeto cuando el método nunca será una instancia específica? ¿Por qué no solo preguntamos a la clase por sí misma que ejecute el método?:

    Vamos a imaginar otro escenario: Supongamos que queremos mantener una cuenta de todas las instancias de una clase particular que se han instanciado. ¿Dónde podríamos actualmente guardar la variable? No funcionará si lo guardamos como una variable de instancia dentro de la clase la cual estamos rastreando, porque la cuenta sería inicializada a su valor por defecto con cada nueva instancia. La respuesta a ambos escenarioses usar el modificador static. Las variables y métodos marcados como static pertenecen a la clase en lugar de cualquier instancia. De hecho, podemos usar métodos static o variables sin tener instancias de ningúna clase. Solo necesitamos tener la clase disponible para poder invocar el método static o acceder a una variable static, las cuales también pueden ser accedidas sin tener ninguna instancia de la clase. Pero si hay instancias, una variable static de la clase será compartida por todas las instancias de la clase, solo hay una copia.

    El siguiente código declara y usa un variable static que actúa como contador:

    		public class Frog {
    		    static int frogCount = 0; // Declara e inicializa
    		                              // una variable static
    		    
    		    public Frog(){
    		        frogCount += 1; // Modifica el valor en el constructor
    		    }
    		    
    		    public static void main (String[] args){
    		        new Frog();
    		        new Frog();
    		        new Frog();
    		        System.out.println("Frog count is now " + frogCount);
    		    }
    		}
    		

    En el código anterior, la variable static frogCount se establece a 0 cuando la clase Frog se carga por primera vez en la JVM, antes de que cualqueir instancia de Frog haya sido creada. Allí donde se cree una instancia de Frog, el constructor de Frog se ejecutará y incrementará la variable static frogCount. Cuando este código se ejecuta, se crean 3 instancias de Frog en main(), y el resultado es:

    		Frog count is now 3
    		

    Ahora imaginemos que estaría pasando si frogCount fuera una variable de instancia (O en otras palabras, no static):

    		public class Frog {
    		    int frogCount = 0; // Declara e inicializa
    		                       // una variable de instancia
    		    
    		    public Frog(){
    		        frogCount += 1; // Modifica el valor en el constructor
    		    }
    		    
    		    public static void main (String[] args){
    		        new Frog();
    		        new Frog();
    		        new Frog();
    		        System.out.println("Frog count is now " + frogCount);
    		    }
    		}
    		

    Cuando intentamos ejecutar este código, debería crear 3 instancias de Frog en main(), pero el resultado es un error de compilación. No podemos lograr que este código compile de esta menera.

    La JVM no sabe cual variable de instancia frogCount de los objetos Frog estamos intentando acceder. El problema es que main() por sí mismo es un método static, y por lo tanto no se está ejecutando en contra de cualquier instancia de la clase, mas bien en la clase. Un método static no puede acceder a una variable no-static, porque no es una instancia. Esto no es decir que no existan instancias de la clase vivas en la pila, mas bien incluso si hay, el método static no sabe nada sobre ninguno de ellos. Esto mismo se aplica también para los métodos de instancia: un método static no puede directamente invocar un método que sea nonstatic. Pensemos en static = clase, nonstatic = instancia. Haciendo el método llamado por la JVM (main()) un método static significa que la JVM no tiene que crear una instancia de nuestra clase solo para iniciar el código.

  • Accediendo a variables y métodos Static

    Puesto que no necesitamos tener una instancia para poder invocar un método static o una variable static, entonces ¿Cómo invocamos o usamos un miembro static?¿Cuál es la sintaxis?:

    		class Frog {
    		    int frogSize = 0;
    		    public int getFrogSize(){
    		        return frogSize;
    		    }
    		    public Frog(int s){
    		        frogSize = s;
    		    }
    		    public static void main (String[] args){
    		        Frog f = new Frog(25);
    		        System.out.println(f.getFrogSize()); // Accedemos al método de instancia
    		                                             // usando f
    		    }
    		}
    		

    En el código anterior, instanciamos un Frog, el cual asignamos a la variable de referencia f, y entonces usamos la referencia f para invocar al método en la instancia de Frog que acabamos de crear. En otras palabras, el método getFrogSize() está siendo invocado en un objeto Forg específico en la pila.

    Pero este enfoque (usando referencia a un objeto) no es apropiado para acceder a un método static, porque puede no haber instancias de al clase. Por lo que, la manera de aceder a un método static (o variable) es usar el operador . en el nombre de la clase, en vez de usarlo en la referencia a la instancia, como el ejemplo siguiente:

    		class Frog {
    		    static int frogCount = 0; // Declara e inicializa
    		                              // la variable static
    		    
    		    public Frog(){
    		        frogCount += 1;
    		    }
    		}
    		
    		class TestFrog{
    		    public static void main (String[] args){
    		        new Frog();
    		        new Frog();
    		        new Frog();
    		        System.out.println("frogCoount: " + Frog.frogCount); // Accede a la 
    		                                                             // variable estática
    		    }
    		}
    		

    Pero para que sea realmente confuso, el lenguaje Java permite tambien usar una variable de referencia de un objeto acceder al miembro static:

    		Frog f = new Frog();
            int frogs = f.frogCount;
    		

    En el código anterior, instanciamos un Frog, le asignamos el nuevo objeto Frog a la variable de referencia f, y entonces usamos la referencia a f para invocar un método static. Pero aunque estemos usando una instancia específica de Frog para acceder al método static, las reglas no han cambiado. Esto es meramente un truco en la sintaxis para permitir el uso de la variable de referencia a un objeto (pero no al objeto al cual se refiere) para recoger un método static o una variable, pero el miembro static se mantiene inconsciente sobre la instancia particular que se ha usado para invocar el miembro static.


Con esto finalizamos esta pequeña explicación sobre los elementos marcados con el modificador static, el cual puede ser un poco dificil de entender hasta que vemos realmente lo que es, para qué se usa y como se usa.

Sin más, cualquier aporte o corrección son bienvenidos.

Saludos!!!