La directiva static en Java

6
179015

En este tutorial veremos para qué sirve la directiva static en Java, a qué elementos se puede aplicar, sus características, beneficios y desventajas que pueden tener.

0. Índice de contenidos

1. Introducción

En este tutorial veremos para qué sirve la directiva static en Java, a qué elementos se puede aplicar, sus características, beneficios y desventajas que pueden tener.

Se propondrán una serie de ejemplos y snippets de código para ilustrar cada uno de los puntos mencionados.

2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • MacBook Pro Intel Core 2 Duo 8GB RAM
  • SO: Yosemite
  • IDE: Eclipse Mars 4.5.0
  • Java JDK 1.7

3. ¿Qué es static en Java?

Una clase, método o campo declarado como estático puede ser accedido o invocado sin la necesidad de tener que instanciar un objeto de la clase. Uno de los ejemplos típicos de uso de métodos y variables estáticas es la clase java.lang.Math:

public class MathTest {

    public static void main(String[] args) {
        double floorOfPi = Math.floor(Math.PI);
        System.out.println(floorOfPi);
    }
}

Cuya salida será el suelo matemático del número Pi:

3.0

4. Campos estáticos

Los campos de una clase declarados como estáticos son inicializados en el momento en que se carga la clase en memoria, respetando el orden de declaración. Los campos no estáticos no pueden ser accedidos desde un contexto no estático; este comportamiento resultará en un error en tiempo de compilación.

En conjunción con la directiva final (que evita la modificación del campo), las constantes de clase son definidas de esta manera.

Gracias a que las variables estáticas son compartidas entre todos los objetos de una clase, es posible implementar el patrón Singleton, guardando una referencia de la instancia:

public class SingletonTest {

    private static SingletonTest instance = null;

    protected SingletonTest() {
    }

    public static SingletonTest getInstance() {
        if (instance == null) {
            instance = new SingletonTest();
        }

        return instance;
    }
}

En este caso, el constructor por defecto se oculta con el modificador protected (que podría ser también private) para evitar la instanciación explícita desde fuera del contexto de la clase. Sin embargo, si la construcción es costosa y la clase es poco utilizada, el rendimiento se verá degradado.

Hay que tener en cuenta que, al igual que las variables transient, los campos estáticos no son serializadas. De esta manera, en el proceso de deserialización, los campos estáticos tomarán su valor por defecto (0.0 para los float, null para objetos, etc…).

5. Métodos estáticos

Debido a que los métodos estáticos son enlazados en tiempo de compilación mediante static binding usando la información del tipo, no es posible realizar sobreescritura (override) de métodos.

class Padre {

    public static void metodoEstatico() {
        System.out.println("Invocación desde el padre.");
    }
}

class Hijo extends Padre {

    public static void metodoEstatico() {
        System.out.println("Invocación desde el hijo.");
    }
}

public class BindingTest {

    public static void main(String[] args) {
        Padre p = new Hijo();
        p.metodoEstatico();
        Hijo h = new Hijo();
        h.metodoEstatico();
    }
}
Salida:
Invocación desde el padre.
Invocación desde el hijo.

En la salida podemos comprobar como el método que se invocará vendrá determinado por el tipo desde donde se invoque, aunque los objetos sean del mismo tipo (de la clase Hijo en este caso).

Otra de las consecuencias del static binding es que los métodos estáticos no pueden ser declarados como abstract, por el mismo motivo que la imposibilidad de la sobrecarga de métodos.

Uno de las características que produce más dolores de cabeza al tratar con métodos y variables estáticas es que, debido a su naturaleza, no son seguras para la programación con hilos. Es necesario hacer uso de las directivas synchronized de los métodos y de las variaciones atómicas de los tipos nativos con los campos.

6. Clases y bloques estáticos

Las clases internas pueden ser declaradas estáticas, de tal manera que aumenten la cohesión de la clase que las engloba. Hay que tener en cuenta que, como el resto de clases internas, el compilador crea un fichero .class por cada una de estas clases. Un ejemplo típico de clases internas estáticas son los comparadores de objetos:

public class Objeto {

    private int campo1;
    private String campo2;

    public Objeto(int campo1, String campo2) {
        this.campo1 = campo1;
        this.campo2 = campo2;
    }

    private static class ObjetoComparator implements Comparator<Objeto> {

        @Override
        public int compare(Objeto o1, Objeto o2) {
            return o2.campo1 - o1.campo1;
        }

    }
}

Es posible declarar bloques de código como estáticos, de tal manera que sean ejecutados cuando se cargue la clase. Este tipo de bloques se conocen como bloques de inicialización estáticos (static initializer block). Si no se declara un bloque de este tipo de forma explícita, el compilador Just-in-Time combina todos los campos estáticos en un bloque y los ejecuta durante la carga de clases. Aunque estos bloques no puedan lanzar checked exceptions, las unchecked pueden ocurrir, resultando en un ExceptionInInitializerError.

public class Objeto {

    private static int campo1;

    static {
        campo1 = 10;
    }
}

Los bloques de inicialización pueden ser, además, sustituidos por métodos estáticos:

public class Objeto {

    private static int campo1 = inicializaCampo();

    private static int inicializaCampo() {
        return 10;
    }

}

Como se observa en los ejemplos anteriores, esta funcionalidad permite dotar de cierta lógica la inicialización de variables de clase estáticas sin necesidad de incluirlo en su constructor.

7. Imports estáticos

Una de las características incluidas en Java 5 fué la capacidad de importar los métodos y variables estáticas de un módulo y acceder a ellos como si hubieran sido declarados en la propia clase. Es especialmente útil, y mejora la legibilidad, cuando se están definiendo test unitarios, ya que la mayoría de los métodos de aserción de JUnit son estáticos.

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class ImportEstático {

    @Test
    public void test() {
        assertEquals(1, 1);
    }

}

8. Conclusiones

La directiva static permite el acceso a métodos y variables de clase sin la necesidad de instanciar un objeto de dicha clase, permitiendo la inicialización de forma cómoda y durante la carga de clase. Además, los import estáticos mejoran la legibilidad de nuestro código, así como las clases estáticas internas la cohesión.

9. Referencias

6 COMENTARIOS

  1. Hola!

    En el apartado 4, creo que esta frase está al revés: » Los campos estáticos no pueden ser accedidos desde un contexto no estático; este comportamiento resultará en un error en tiempo de ejecución.»

    Quiero decir, que desde un método no estático, puedo acceder a PI, pero desde un método estático, no puedo acceder a miembros no estátitos, no?

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad