¿Cuándo se inicializan las variables estáticas?

87

Me pregunto cuándo se inicializan las variables estáticas a sus valores predeterminados. ¿Es correcto que cuando se carga una clase, se crean (asignan) variables estáticas, luego se ejecutan los inicializadores estáticos y las inicializaciones en las declaraciones? ¿En qué momento se dan los valores predeterminados? Esto conduce al problema de la referencia hacia adelante.

También, si puede explicar esto en referencia a la pregunta sobre ¿Por qué los campos estáticos no se inicializan a tiempo? y especialmente la respuesta dada por Kevin Brock en el mismo sitio. No puedo entender el tercer punto.

Ankit
fuente
2
Por favor, podría editar su pregunta para incluir la cita a la que se refiere.
Oliver Charlesworth
1
¿Ha leído la especificación del lenguaje Java? Es un documento bastante legible, deliberadamente. Si ha leído eso, puede que comprenda lo que está pasando. Si no, puede hacer una pregunta más específica como mínimo ...
Maarten Bodewes
Creo que esta sesión de preguntas y respuestas es un duplicado de stackoverflow.com/questions/3499214 .
Stephen C

Respuestas:

72

De Ver métodos de variables estáticas de Java :

  • Es una variable que pertenece a la clase y no al objeto (instancia)
  • Las variables estáticas se inicializan solo una vez, al inicio de la ejecución. Estas variables se inicializarán primero, antes de la inicialización de cualquier variable de instancia.
  • Una única copia para ser compartida por todas las instancias de la clase.
  • Se puede acceder a una variable estática directamente por el nombre de la clase y no necesita ningún objeto.

Las variables de instancia y clase (estáticas) se inicializan automáticamente a los valores predeterminados estándar si no las inicializa intencionalmente. Aunque las variables locales no se inicializan automáticamente, no puede compilar un programa que no pueda inicializar una variable local o asignar un valor a esa variable local antes de que se utilice.

Lo que el compilador realmente hace es producir internamente una rutina de inicialización de una sola clase que combina todos los inicializadores de variables estáticas y todos los bloques de código de inicializadores estáticos, en el orden en que aparecen en la declaración de clase. Este único procedimiento de inicialización se ejecuta automáticamente, una sola vez, cuando la clase se carga por primera vez.

En caso de clases internas , no pueden tener campos estáticos

Una clase interna es una clase anidada que no se declara explícita o implícitamente static.

...

Las clases internas no pueden declarar inicializadores estáticos (§8.7) o interfaces miembro ...

Las clases internas no pueden declarar miembros estáticos, a menos que sean variables constantes ...

Consulte JLS 8.1.3 Clases internas e instancias adjuntas

finalLos campos en Java se pueden inicializar por separado de su lugar de declaración, sin embargo, no puede ser aplicable a los static finalcampos. Vea el ejemplo a continuación.

final class Demo
{
    private final int x;
    private static final int z;  //must be initialized here.

    static 
    {
        z = 10;  //It can be initialized here.
    }

    public Demo(int x)
    {
        this.x=x;  //This is possible.
        //z=15; compiler-error - can not assign a value to a final variable z
    }
}

Esto se debe a que solo hay una copia de las staticvariables asociadas con el tipo, en lugar de una asociada con cada instancia del tipo, como ocurre con las variables de instancia y si intentamos inicializar el ztipo static finaldentro del constructor, intentará reinicializar el static finalcampo de tipo. zporque el constructor se ejecuta en cada instanciación de la clase que no debe ocurrir en finalcampos estáticos .

León
fuente
5
In case of static inner classes, they can not have static fieldsparece un error tipográfico. Las clases internas no son estáticas.
Daniel Lubarov
Sin embargo, debes usar en lugar de Aunque
Suraj Jain
Cuando inicia una JVM y carga una clase por primera vez (esto lo hace el cargador de clases cuando se hace referencia a la clase por primera vez de alguna manera), cualquier bloque o campo estático se 'carga' en la JVM y se vuelve accesible.
nhoxbypass
1
Desafortunadamente, esta respuesta contiene algunas inexactitudes fácticas sobre cuándo se inicializan las estáticas. Consulte stackoverflow.com/a/3499322/139985 .
Stephen C
15

Ver:

El último en particular proporciona pasos de inicialización detallados que detallan cuándo se inicializan las variables estáticas y en qué orden (con la advertencia de que finallas variables de clase y los campos de interfaz que son constantes en tiempo de compilación se inicializan primero).

No estoy seguro de cuál es tu pregunta específica sobre el punto 3 (¿asumiendo que te refieres al anidado?). La secuencia detallada indica que esto sería una solicitud de inicialización recursiva, por lo que continuará la inicialización.

Dave Newton
fuente
11

Los campos estáticos se inicializan cuando el cargador de clases carga la clase. Los valores predeterminados se asignan en este momento. Esto se hace en el orden en que aparecen en el código fuente.

Dave
fuente
10

El orden de inicialización es:

  1. Bloques de inicialización estáticos
  2. Bloques de inicialización de instancias
  3. Constructores

Los detalles del proceso se explican en el documento de especificación de JVM .

Óscar López
fuente
6

variable estática

  • Es una variable que pertenece a la clase y no al objeto (instancia)
  • Las variables estáticas se inicializan solo una vez, al inicio de la ejecución (cuando Classloader carga la clase por primera vez).
  • Estas variables se inicializarán primero, antes de la inicialización de cualquier variable de instancia.
  • Una única copia para ser compartida por todas las instancias de la clase.
  • Se puede acceder a una variable estática directamente por el nombre de la clase y no necesita ningún objeto
aleroot
fuente
4

Comenzando con el código de la otra pregunta:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static final Object obj = new Object();
  public MyClass() {
    System.out.println(obj); // will print null once
  }
}

Una referencia a esta clase comenzará la inicialización. Primero, la clase se marcará como inicializada. Luego, el primer campo estático se inicializará con una nueva instancia de MyClass (). Tenga en cuenta que myClass recibe inmediatamente una referencia a una instancia de MyClass en blanco . El espacio está ahí, pero todos los valores son nulos. El constructor ahora se ejecuta e imprime obj, que es nulo.

Ahora volvamos a inicializar la clase: objse hace una referencia a un nuevo objeto real, y ya está.

Si esto fue compensado por una declaración como: el MyClass mc = new MyClass();espacio para una nueva instancia de MyClass se asigna nuevamente (y la referencia se coloca en mc). El constructor se ejecuta de nuevo y se imprime de nuevo obj, que ahora no es nulo.

El verdadero truco aquí es que cuando usas new, como en, WhatEverItIs weii = new WhatEverItIs( p1, p2 ); weiiinmediatamente se le da una referencia a un poco de memoria anulada. La JVM luego continuará para inicializar valores y ejecutará el constructor. Pero si de alguna manera hace referencia weii antes de que lo haga, al hacer referencia a él desde otro hilo o al hacer referencia a la inicialización de la clase, por ejemplo, está viendo una instancia de clase llena de valores nulos.

RalphChapin
fuente
1
Una clase no se marca como inicializada hasta que se completa la inicialización; de lo contrario, no tendría sentido. Marcar como inicializado es casi el último paso que se da. Consulte JLS 12.4.2 .
Dave Newton
@DaveNewton: Una vez que algo hace referencia a la clase y comienza a inicializarla, todas las referencias posteriores tratarán la clase como inicializada. No intentarán inicializarlo y no esperarán a que se inicialice. Por tanto, los campos que parecen no ser nulos desde el inicio de un programa pueden ser nulos durante un tiempo. No entender esto es lo que está causando toda la confusión. Creo que es más simple decir que una clase no inicializada está "marcada" como inicializada en la primera referencia, todas las demás referencias la tratan como inicializada, y por eso sucede esto.
RalphChapin
Una corrección al comentario anterior: como se describe en JLS 12.4.2 de Dave Newton, la clase está bloqueada mientras se inicializa. Otros temas serán esperar a la clase a ser inicializado. Sin embargo, eso no afecta este caso, que sucede en un solo hilo.
RalphChapin
4

La variable estática se puede inicializar de las siguientes tres formas, como sigue, elija la que desee

  1. puedes inicializarlo en el momento de la declaración
  2. o puede hacerlo haciendo un bloque estático, por ejemplo:

    static {
            // whatever code is needed for initialization goes here
        }
    
  3. Existe una alternativa a los bloques estáticos: puede escribir un método estático privado

    class name {
        public static varType myVar = initializeVar();
    
        private static varType initializeVar() {
            // initialization code goes here
        }
    }
    
ajay verma
fuente