example - new icon java
Orden de inicializaciĆ³n y ejecuciĆ³n de Java (2)
A continuación se muestra un ejemplo que imprime el orden de cada paso durante la creación del objeto.
InstanceCreateStepTest.java:
import javax.annotation.PostConstruct;
/**
* Test steps of instance creation.
*
* @author eric
* @date Jan 7, 2018 3:31:12 AM
*/
public class InstanceCreateStepTest {
public static void main(String[] args) {
new Sub().hello();
System.out.printf("%s/n", "------------");
new Sub().hello();
}
}
class Base {
static {
System.out.printf("%s - %s - %s/n", "base", "static", "block");
}
{
System.out.printf("%s - %s - %s/n", "base", "instance", "block");
}
public Base() {
System.out.printf("%s - %s/n", "base", "constructor");
}
@PostConstruct
public void init() {
System.out.printf("%s - %s/n", "base", "PostConstruct");
}
public void hello() {
System.out.printf("%s - %s/n", "base", "method");
}
}
class Sub extends Base {
static {
System.out.printf("%s - %s - %s/n", "sub", "static", "block");
}
{
System.out.printf("%s - %s - %s/n", "sub", "instance", "block");
}
public Sub() {
System.out.printf("%s - %s/n", "sub", "constructor");
}
@PostConstruct
public void init() {
System.out.printf("%s - %s/n", "sub", "PostConstruct");
}
@Override
public void hello() {
// super.hello();
System.out.printf("%s - %s/n", "sub", "method");
}
}
Ejecución:
Simplemente invoque el método principal y luego verifique la salida.
Consejos:
- Los métodos marcados por
@PostConstruct
no se invocarán, a menos que lo invoque dentro de algún contenedor, comoSpring-boot
, ya que depende de esos contenedores para implementar una anotación como@PostConstruct
.
Estoy tratando de reconstruir el proceso de Inicialización y Ejecución en la JVM, pero el JLS es un poco obtuso en algunos detalles, por lo que si alguien quisiera aclarar algunos detalles, sería apreciado. Esto es lo que he podido descifrar hasta ahora.
Inicialización
Inicializar recursivamente las variables finales estáticas de la clase y sus interfaces que son constantes de tiempo de compilación.
Salga de la recursión procesando bloques estáticos y campos estáticos en orden textual.
Instanciación
Recursivamente inicialice las variables de instancia finales de la clase que son constantes de tiempo de compilación.
Salga de la recursión procesando bloques no estáticos y campos de instancia en orden textual anteponiéndolos a los constructores a medida que regresa.
De acuerdo, entonces ahora para las preguntas.
¿Se procesan las interfaces por orden de declaración?
¿Se procesan las interfaces en una pila recursiva separada?
a) En caso afirmativo, ¿las interfaces se procesan antes o después de las superclases?
b) En caso afirmativo, estoy en lo cierto al deducir que uno u otros (Interfaz o Superclase) obtiene sus campos constantes de tiempo de no compilación inicializados antes que las otras constantes de tiempo de compilación.
¿Qué papel desempeñan las llamadas al constructor superdeportivo no predeterminado en este proceso?
¿Me equivoco en alguna de mis conclusiones?
¿Me falta algún otro detalle clave?
Es importante distinguir entre la inicialización de una clase y la inicialización de un objeto.
Inicialización de clase
Una clase o interfaz se inicializa al primer acceso , asignando los campos constantes de tiempo de compilación, inicializando recursivamente la superclase (si no se inicializó), luego procesando los inicializadores estáticos (que incluyen los inicializadores para los campos estáticos que no son de compilación constantes).
Como habrás notado, la inicialización de una clase no desencadena, por sí misma, la inicialización de las interfaces que implementa. Por lo tanto, las interfaces se inicializan cuando se acceden por primera vez, generalmente leyendo un campo que no es una constante de tiempo de compilación . Este acceso puede ocurrir durante la evaluación de un inicializador, causando una inicialización recursiva.
También vale la pena señalar que la inicialización no se desencadena al acceder a los campos que son constantes de tiempo de compilación, ya que estos se evalúan en tiempo de compilación :
Una referencia a un campo que es una variable constante (§4.12.4) se debe resolver en tiempo de compilación al valor V denotado por el inicializador de la variable constante.
Si dicho campo es estático, no debe haber ninguna referencia al campo en el código en un archivo binario, incluida la clase o la interfaz que declaró el campo. Tal campo siempre debe aparecer como inicializado (§12.4.2); el valor inicial predeterminado para el campo (si es diferente de V) nunca se debe observar.
Si dicho campo no es estático, entonces no debe haber ninguna referencia al campo en el código en un archivo binario, excepto en la clase que contiene el campo. (Será una clase en lugar de una interfaz, ya que una interfaz solo tiene campos estáticos.) La clase debe tener un código para establecer el valor del campo en V durante la creación de la instancia (§12.5).
Inicialización de objetos
Un objeto se inicializa siempre que se crea un nuevo objeto , generalmente mediante la evaluación de una expresión de creación de instancia de clase. Esto procede de la siguiente manera:
Asigne los argumentos para el constructor a las variables de parámetros recién creados para esta invocación de constructor.
Si este constructor comienza con una invocación de constructor explícita (§8.8.7.1) de otro constructor en la misma clase (usando esto), entonces evalúe los argumentos y procese esa invocación de constructor recursivamente usando estos mismos cinco pasos. Si la invocación del constructor finaliza abruptamente, entonces este procedimiento se completa abruptamente por la misma razón; de lo contrario, continúe con el paso 5.
Este constructor no comienza con una invocación de constructor explícita de otro constructor en la misma clase (usando esto). Si este constructor es para una clase que no sea Object, entonces este constructor comenzará con una invocación explícita o implícita de un constructor de superclase (usando super). Evalúe los argumentos y procese esa invocación de constructor de superclase recursivamente utilizando estos mismos cinco pasos. Si la invocación del constructor finaliza abruptamente, entonces este procedimiento se completa abruptamente por el mismo motivo. De lo contrario, continúe con el paso 4.
Ejecute los inicializadores de instancia y los inicializadores de variable de instancia para esta clase, asignando los valores de los inicializadores de variable de instancia a las variables de instancia correspondientes, en el orden de izquierda a derecha en el que aparecen textualmente en el código fuente de la clase. Si la ejecución de cualquiera de estos inicializadores da como resultado una excepción, no se procesan más inicializadores y este procedimiento se completa abruptamente con la misma excepción. De lo contrario, continúe con el paso 5.
Ejecuta el resto del cuerpo de este constructor. Si la ejecución se completa abruptamente, entonces este procedimiento se completa abruptamente por el mismo motivo. De lo contrario, este procedimiento se completa normalmente.
Como podemos ver en el paso 3, la presencia de una llamada explícita al superconstructor simplemente cambia qué constructor de superclase se invoca.