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
- 2. Entorno
- 3. ¿Qué es static en Java?
- 4. Campos estáticos
- 5. Métodos estáticos
- 6. Clases y bloques estáticos
- 7. Imports estáticos
- 8. Conclusiones
- 9. Referencias
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
- 10 points about Static in Java.
- How to make a method thread-safe in Java?
- Java for Beginners: Static Variables — What Are They?
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?
Gracias, lo revisamos:)
Con todos los respetos, está redactado para gente que ya sabe lo que es la directriz static.
Creo que en el renglón 2, hace falta establecer los campos que van hacer estáticos y los no estáticos
Hola
Con todos los respetos, está redactado para gente que ya sabe lo que es la directriz static.
Thanks