usar error como java stack-overflow

java - error - como usar stack overflow



¿Por qué esta declaración no arroja un StackOverflowError? (3)

La variable NIL primero recibe el valor null y luego se inicializa una vez de arriba a abajo. No es una función y no se define de forma recursiva. Cualquier campo estático que use antes de que se inicialice tiene el valor predeterminado y su código es el mismo que

public static Node { public static Node NIL; static { NIL = new Node(null /*Node.NIL*/, null /*Node.NIL*/); } public Node(Object one, Object two) { // Assign values to fields } }

Esto no es diferente a escribir

NIL = null; // set implicitly NIL = new Node(NIL, NIL);

Si definió una función o método como este, obtendría una StackoverflowException

Node NIL(Node a, Node b) { return NIL(NIL(a, b), NIL(a, b)); }

Acabo de ver este extraño fragmento de código en otra pregunta. Pensé que daría como resultado un StackOverflowError , pero no ...

public class Node { private Object one; private Object two; public static Node NIL = new Node(Node.NIL, Node.NIL); public Node(Object one, Object two) { this.one = one; this.two = two; } }

Pensé que iba a explotar, debido a que Node.NIL referencia a sí mismo para construir.

No puedo entender por qué no.


La clave para entender por qué no causa una inicialización infinita es que cuando se inicializa el Node clase, la JVM lo rastrea y evita la reinicialización durante una referencia recursiva a la clase dentro de su inicialización original. Esto se detalla en esta sección de la especificación del idioma :

Debido a que el lenguaje de programación Java es multiproceso, la inicialización de una clase o interfaz requiere una sincronización cuidadosa, ya que algún otro hilo puede estar tratando de inicializar la misma clase o interfaz al mismo tiempo. También existe la posibilidad de que la inicialización de una clase o interfaz se solicite de forma recursiva como parte de la inicialización de esa clase o interfaz ; por ejemplo, un inicializador variable en la clase A podría invocar un método de una clase B no relacionada, que a su vez podría invocar un método de la clase A. La implementación de la máquina virtual Java es responsable de cuidar la sincronización y la inicialización recursiva mediante el uso de siguiente procedimiento.

Entonces, mientras el inicializador estático está creando la instancia estática NIL , la referencia a Node.NIL como parte de la llamada del constructor no vuelve a ejecutar el inicializador estático nuevamente. En cambio, solo hace referencia a cualquier valor que tenga la referencia NIL en ese momento, que es null en este caso.


NIL es una variable estática. Se inicializa una vez, cuando se inicializa la clase. Cuando se inicializa, se crea una única instancia de Node . La creación de ese Node no desencadena la creación de ninguna otra instancia de Node , por lo que no hay una cadena infinita de llamadas. Pasar Node.NIL a la llamada del constructor tiene el mismo efecto que pasar null , ya que Node.NIL aún no se inicializa cuando se llama al constructor. Por lo tanto, public static Node NIL = new Node(Node.NIL, Node.NIL); es lo mismo que public static Node NIL = new Node(null, null); .

Si, por otro lado, NIL era una variable de instancia (y no se pasó como argumento al constructor de Node , ya que el compilador le habría impedido pasarlo al constructor en ese caso), se inicializaría cada vez se creó una instancia de Node , que crearía una nueva instancia de Node , cuya creación inicializaría otra variable de instancia NIL , lo que llevaría a una cadena infinita de llamadas de constructor que terminaría en Error .