sirve settitle que para java jvm classloader

para que sirve settitle en java



¿Cómo se resuelven las dependencias entre bloques estáticos? (7)

Recientemente me encontré con esto en el trabajo. Aunque no estoy seguro de que realmente sea una buena idea, no entiendo cómo el compilador maneja los bloques estáticos.

Aquí hay un ejemplo:

Considere que tiene las clases A y B :

public class A { public final static List<Integer> list; static { list = new ArrayList<>(); } } public class B { public final static int dependsOnA; static { dependsOnA = A.list.size(); } }

Y una clase principal que solo lee B.dependsOnA .

El bloque estático en B depende del que está en A , ya que usa la variable estática de la list .

Ahora, el código se ejecuta correctamente y no se NullPointerException en el tiempo de ejecución. Pero, ¿cuál es el mecanismo que garantiza que la list se inicialice antes de que se utilice potencialmente en otra parte?


Aquí tenemos algunas explicaciones de Static Block en Java

Si llama primero a la clase A, se llama a la estática A y existe una lista y lo hará cuando B la llame.

Si llama primero a la clase B, se llama a B static, en cascada a la llamada A, llamando a su bloque estático, donde se crea una lista.

Pudimos ver que es la forma más complicada: B> B.static> A> A.static> A.list exists


Durante la ejecución del bloque static de B , el tiempo de ejecución encuentra a A por primera vez e invocará el bloque static de A antes de A.list a A.list .


El "mecanismo" es el cargador de clases de la JVM, que asegurará que se ejecuten los bloques de inicialización de una clase (con un bloqueo global en toda la JVM) antes de devolver el flujo de control al lugar donde se hizo referencia por primera vez a la clase. Primero cargará la clase A solo después de referenciarse, en este caso cuando el bloque init de B A.list referencia a A.list .


El mecanismo se describe en detalle here , pero los cinco puntos más importantes son:

  1. Antes de que se haga referencia a una clase, debe inicializarse.
  2. Si la inicialización de una clase ya ha comenzado (o si ha finalizado), no se intenta de nuevo.
  3. Antes de inicializar una clase, todas sus superclases y superinterfaces deben inicializarse primero.
  4. Los inicializadores estáticos dentro de una sola clase se ejecutan en orden textual.
  5. Las interfaces implementadas se inicializan en el orden en que aparecen en la cláusula de implements .

Estas reglas definen completamente el orden en que se ejecutan los bloques estáticos.

Su caso es bastante simple: antes de acceder a B.dependsOnA , B necesita ser inicializado (regla 1), el inicializador está tratando de acceder a A.list , lo que desencadena la inicialización de la clase A (nuevamente la regla 1).

Tenga en cuenta que no hay nada que le impida crear dependencias circulares de esta manera, lo que hará que sucedan cosas interesantes:

public class Bar { public static int X = Foo.X+1; public static void main(String[] args) { System.out.println( Bar.X+" "+Foo.X); // } } class Foo { public static int X = Bar.X+1; }

El resultado aquí es 2 1 porque la forma en que ocurre la inicialización es esta:

  1. La inicialización de la Bar comienza.
  2. Bar.X el valor inicial de Bar.X , lo que requiere inicializar a Foo primero
  3. Comienza la inicialización de Foo .
  4. El valor inicial de Foo.X se evalúa, pero como la inicialización de Bar ya está en progreso, no se inicializa de nuevo, se usa el valor "actual" de Bar.X , que es 0, por lo que Foo.X se inicializa a 1.
  5. Volvimos a evaluar el valor de Bar.X s, Foo.X es 1, por lo que Bar.X convierte en 2.

Esto funciona incluso si ambos campos fueron declarados final .

La moraleja de la historia es tener cuidado con los inicializadores estáticos que se refieren a otras clases en la misma biblioteca o aplicación (referirse a clases en una biblioteca de terceros o biblioteca de clases estándar es seguro ya que no se referirán a su clase).


Este es el trabajo del cargador de clases. La carga de clase en Java comienza con el cargador de clases de arranque . Este cargador de clases primero carga todas las clases en la biblioteca java estándar, el rt.jar .

Luego se invoca el cargador de clases de extensión . Esto carga todas las clases desde los archivos jar de extensión instalados en un directorio ext de JVM. Ahora, finalmente, se invoca el classloader classpath.

El classloader classpath comienza a cargar clases desde la clase principal, la clase que tiene el método principal definido. Una vez que está cargado, ejecuta cualquier inicializador estático en esa clase. Mientras que en la ejecución del inicializador, si encuentra cualquier clase que no está cargada, pausará la ejecución del bloque estático, cargará primero la clase y finalmente reanudará la ejecución de ese bloque estático.

Por lo tanto, no hay posibilidad de que se produzcan llamadas a clases no cargadas. Veamos esto con tu propio ejemplo, cuyo código es el siguiente:

class A { public final static List<Integer> list; static { System.out.println("Loaded Class A"); list = new ArrayList<>(); } } class B { public final static int dependsOnA; static { System.out.println("Loaded Class B"); dependsOnA = A.list.size(); } }

Aquí en este ejemplo, en realidad no hay un método principal, por lo que estas clases no se cargarán en la memoria. Supongamos que agreguemos la siguiente clase principal al código anterior.

class C { static { System.out.println("Loaded Class C"); } public static void main(String[] args) { System.out.println(B.dependsOnA); } }

Veamos qué produciría esto en la salida: http://ideone.com/pLg3Uh

Loaded Class C Loaded Class B Loaded Class A 0

Es decir, primero se carga la clase C, porque tenía el método principal. Una vez que se carga, se invoca el inicializador estático de la clase C. Observe, sin embargo, que el método principal se invoca después de que se carga el bloque estático de la clase C.

Ahora, el método principal, dependsOnA el valor de dependsOnA de la clase B. Ahora, el cargador de clases deja de ejecutar esa declaración, carga la clase B y ejecuta su bloqueo estático, que a su vez asigna la variable dependsOnA al valor de la clase B número de elementos en la lista de clase A, que no está cargado.

Entonces, el cargador de clases salta desde allí, carga la clase ahora e invoca el bloque estático de la clase A, y se crea una lista. Ahora, dado que no hay más clases para cargar, el cargador de clases regresa al bloque estático de la clase B y la asignación está completa. Ahora, finalmente, el control ahora está con el método principal, y el valor de dependsOnA se imprime en la consola.

Espero que esto ayude.


No importa cómo se escriba el código, un bloque static es un bloque static y se ejecutará como parte de la JVM cargando una clase.

Cuando dices B.dependsOnA , la clase B comienza a getearse cargada por la JVM y el bloque static en B se llama en algún lugar durante este proceso. Cuando dices dependsOnA = A.list.size(); , la clase A comienza a ser cargada por la JVM y el bloque static en A se ejecutará en algún lugar durante este proceso que inicializa la list . La declaración list.size() solo se ejecutará una vez que la clase A haya sido cargada completamente por la JVM. Posteriormente, la JVM solo puede terminar de cargar la clase B después de que el bloque estático en B finalice.


El trabajo es un cargador de clases JVM muy simple, que asegurará que se ejecuten bloques estáticos de clase cuando se hace referencia por primera vez a la clase.
1.Si tiene instrucciones ejecutables en el bloque estático, JVM ejecutará automáticamente estas declaraciones cuando la clase se cargue en JVM.
2.Si está refiriendo algunas variables / métodos estáticos de los bloques estáticos, estas sentencias se ejecutarán después de que la clase se cargue en JVM igual que anteriormente, es decir, ahora se aplicarán las variables / métodos estáticos referidos y el bloque estático ambos.