[1] El ciclo de vida de la clase

 

El ciclo de vida de una clase Java se refiere a todo el proceso de un archivo de clase (un archivo compilado a partir de un archivo fuente Java) desde la carga hasta la descarga.

 

Tabla de contenido

1. Cargar

2. Conectar

(1) Verificación:

(2) Preparación:

(3) Análisis:

3. Inicialización

4. Utilice

(1) Creación de instancias de objetos:

(2) Recolección de basura:

(3) El final del objeto:

5. Descarga de clases

1. Cargar

Escribimos un archivo fuente de Java y generamos un archivo con el sufijo .class después de la compilación. Esto se combina con el archivo de código de cuatro bytes, y la máquina virtual Java reconoce este archivo. El ciclo de vida de Java es el archivo de clase desde la carga hasta proceso de extinción.

Con respecto a la carga, de hecho, el archivo de clase del archivo de origen encuentra la información de la clase y la carga en el área de métodos, y luego crea una instancia de un objeto java.lang.Class en el área del montón como la entrada para la información de esta clase. en el área de métodos.

Sin embargo, esta función se implementa fuera de la JVM. La razón principal es dejar que la aplicación decida cómo obtener esta clase. La forma de implementarla en diferentes máquinas virtuales no es necesariamente la misma. La máquina virtual hotspot se carga cuando es necesario. Hay otros que vienen precargados primero. (Encuentra y carga los datos binarios de la clase)

Cargador de clases:

1. Cargador que viene con la máquina virtual Java

    a. Cargador de clases raíz (escrito en C ++, los programadores no pueden obtener esta clase en código Java)

    b. Cargador de extensiones, implementado con código Java

    c. Cargador del sistema (cargador de aplicaciones), implementado con código Java

2. Cargador de clases definido por el usuario

     Subclase de java.lang.ClassLoader

     Los usuarios pueden personalizar la forma en que se carga la clase

El cargador de clases no necesita esperar a que una clase se "use activamente por primera vez" antes de cargarla. Pero llamar al método loadClass de la clase ClassLoader para cargar una clase no es un uso activo de la clase y no provocará la inicialización de la clase.

 

2. Conectar

Generalmente se intercalará con la fase de carga y la fase de inicialización El proceso consta de tres partes: verificación, preparación y análisis.

(1) Verificación :

Determinar si la clase se ajusta a las especificaciones del lenguaje java, si hay duplicación de atributos y comportamientos, y si la herencia es razonable. En resumen, se trata de asegurar que el jvm se pueda ejecutar (para asegurar la corrección de la clase )

(2) Preparación :

Lo principal es asignar memoria para las variables miembro modificadas por static y establecer el valor inicial predeterminado ( asignar memoria para las variables estáticas de la clase e inicializarla al valor predeterminado )

Los valores iniciales predeterminados son los siguientes:

1. El valor inicial predeterminado de los ocho tipos de datos básicos es 0

2. El valor inicial predeterminado del tipo de referencia es nulo.

3. Aquellos con modificación final estática se asignarán directamente, por ejemplo: static final int x = 10; el valor predeterminado es 10.

(3) Análisis :

La tarea de esta etapa es convertir las referencias de símbolos en el grupo constante en referencias directas.Para decirlo sin rodeos, JVM convertirá todos los nombres de clase o interfaz, nombres de campo y nombres de métodos en direcciones de memoria específicas . (Convierta la referencia de símbolo en la clase en una referencia directa . ) Con una referencia directa, el destino referenciado se ha cargado en la memoria .

 

3. Inicialización

( Dar el valor inicial correcto a la variable estática de la clase ) y ejecutarlo solo una vez

Tanto la declaración de variables estáticas como la inicialización de bloques de código estático pueden considerarse como la inicialización de variables estáticas La inicialización de variables estáticas y bloques de código estático de una clase está en orden. El orden es que los archivos de clase se inicialicen de arriba a abajo. Si la clase principal aún no se ha inicializado, entonces la clase principal se inicializa primero.

Esta etapa es el proceso de asignación de variables estáticas (variables de clase) y el proceso de inicialización de bloques de código estático, es decir, solo se pueden inicializar aquellos modificados por static . El orden de ejecución es:

El dominio estático de la clase principal o el bloque de código estático, y luego el dominio estático de la subclase o el bloque de código estático de la subclase

 

4. Utilice

Todavía hay tres pasos en el uso de la clase: creación de instancias de objetos, recolección de basura y terminación de objetos

(1) Creación de instancias de objetos :

Es para ejecutar el contenido del constructor en la clase. Si la clase tiene una JVM de clase padre, el constructor de la clase padre se ejecutará primero en forma de visualización o implícita, abriendo espacio en la memoria del montón para las variables de instancia de la clase principal, y proporcione el valor inicial predeterminado, y luego asigne el valor real a la variable de instancia en sí de acuerdo con el contenido del código del constructor, luego abra espacio para la variable de instancia de esta clase, asigne el valor predeterminado y luego asigne el valor usando el constructor. Luego, haga referencia a la variable para obtener la primera dirección del objeto y llame a la variable de instancia y al método operando el objeto

(2) Recolección de basura :

Cuando ya no se hace referencia al objeto, la máquina virtual lo marcará con una marca de basura especial (antes de Java 1.2, el contador de referencia se usaba para marcar si se requiere recolección de basura; después de 1.2, el algoritmo de búsqueda raíz se usa para determinar si la recolección de basura no lo es), Esperando la recolección de GC en el montón

(3) El final del objeto :

Después de que el objeto es reciclado por el GC, el objeto ya no existe y la vida del objeto llega a su fin.

 

5. Descarga de clases

Es decir, el ciclo de vida de la clase ha llegado al último paso, y ya no hay una referencia a la clase en el programa, la clase será recolectada como basura por la JVM y la vida termina ...

La desinstalación de clases debe cumplir las siguientes tres condiciones al mismo tiempo:

1. Todas las instancias de esta clase se han reciclado.

2. El ClassLoder que cargó esta clase ha sido reciclado.

3. No se hace referencia al objeto java.lang.Class correspondiente a esta clase y no hay forma de acceder a esta clase a través de la reflexión en ninguna parte.

El cargador de clases raíz, el cargador de clases de extensión y el cargador de clases del sistema con los que viene la JVM, la propia JVM siempre hará referencia a estos cargadores de clases, por lo que no se formará la condición 2. Estos cargadores de clases siempre se referirán a los objetos de clase que cargan, por lo que no se formará la condición 3.

Entonces, las únicas clases que se descargarán son las cargadas por el cargador de clases personalizado.

ejemplo:

class A{

    static int a;//类变量

    String name;

    int id;

    //静态代码块

    static{

        a=10;

        System.out.println("这是父类的静态代码块"+a);

    }

    //构造代码块

    {

        id=11;

        System.out.println("这是父类的构造代码块id:"+id);

    }

    A(){

        System.out.println("这是父类的无参构造函数");

    }

    A(String name){

        System.out.println("这是父类的name"+name);

    }

}

class B extends A{

    String name;

    static int b;

    static{

        b=12;

        System.out.println("这是子类的静态代码块"+b);

    }

     B(String name) {

        super();

        this.name = name;

        System.out.println("这是子类的name:"+name);

    }

}

public class Test666 {

public static void main(String[] args) {

    B bb=new B("GG");

}

}

El resultado es el siguiente:

Este es el bloque de código estático de la clase padre 10

Este es un bloque de código estático de la subclase 12

Este es el ID de bloque de código de construcción de la clase principal: 11

Este es el constructor sin parámetros de la clase padre

Este es el nombre de la subclase: GG

 

 

Hay dos formas de utilizar clases en programas Java:

  (1) Uso activo

  (2) Uso pasivo

Todas las implementaciones de máquinas virtuales Java deben inicializar cada clase o interfaz "primer uso activo" por parte del programa Java (a partir de la carga). En otros casos, no se inicializará y solo realizará los pasos antes de la inicialización .

Solo el uso activo de clases internas estáticas iniciará el ciclo de vida de las clases internas, y el uso activo de clases externas solo iniciará el ciclo de vida de las clases externas.

Uso activo:

  ① Crea una instancia de la clase

  ② Acceda a una variable estática de una determinada clase o interfaz, o asigne un valor a la variable estática

  ③ Llamar al método estático de la clase

  ④ Reflexión (como Class.forName ("com.alibaba.Test"))

  ⑤ Inicializar una subclase de una clase

  ⑥ La clase que está marcada como la clase de inicio cuando se inicia la máquina virtual Java (Prueba de Java)

 

Los pasos de inicialización de la clase:

  (1) Si una clase no ha sido cargada o conectada, entonces cargue y conecte esta clase primero (no inicializada aquí)

  (2) Si la clase tiene una clase principal directa y la clase principal no se ha inicializado, inicialice primero la clase principal directa.

  (3) Si hay instrucciones de inicialización en la clase, ejecute directamente estas instrucciones de inicialización en orden

El "uso activo" de la subclase en el programa hará que se inicialice la clase padre; pero el uso "activo" de la clase padre no provocará que la subclase se inicialice (es imposible decir que generar un objeto del La clase de objeto causará que todos los niños en el sistema La clase se inicialice)

 

Nota: Llamar al método loadClass de la clase ClassLoader para cargar una clase no es un uso activo de la clase y no provocará la inicialización de la clase .

  Cuando la máquina virtual de Java para inicializar una clase, se lo pide a toda la clase principal se ha inicializado, pero esta regla no se aplica a la interfaz .

  Cuando se inicializa una clase, las interfaces que implementa no se inicializan primero.

  Cuando se inicializa una interfaz, su interfaz principal no se inicializa primero.

ejemplo:

package niuke;

public class TestStaticInitOrder {

    public static void main(String[] args){

        Singleton singleton =  Singleton.getInstance();

        System.out.println("counter1=" +  singleton.counter1);

        System.out.println("counter2=" +  singleton.counter2);

    }

}

class Singleton {

    private static Singleton singleton = new  Singleton();

    

    

    public static int counter1=0;

    

    public static int counter2;

    static{

        counter2 = 0;

    }

    

    private Singleton(){

        counter1++;

        counter2++;

    }

    

    public static Singleton getInstance(){

        return singleton;

    }

}   

Análisis: El programa comienza a ejecutarse, primero ejecuta el método principal, ejecuta la primera declaración del método principal, llama al método estático de la clase Singleton, aquí llamar al método estático de la clase Singleton es usar activamente la clase Singleton. Así que empieza a cargar la clase Singleton. En el proceso de carga de la clase Singleton, primero asigne valores predeterminados a las variables estáticas.

  Singleton = nulo

  contador1 = 0

  contador2 = 0

Después de asignarles los valores predeterminados, lo que se debe hacer es inicializar las variables estáticas e inicializar las variables que se han asignado en el momento de la declaración. Como mencionamos anteriormente, la inicialización se asigna de arriba a abajo del archivo de clase. Entonces, primero asigne un valor a Singleton, y para asignarlo, es necesario ejecutar su método de construcción, y luego ejecutar counter1 ++; counter2 ++; entonces aquí counter1 = 1; counter2 = 1; después de realizar esta inicialización, primero inicialice counter1, porque counter2 no está asignado, por lo que no hay inicialización

En este momento: contador1 = 0;

          contador2 = 1 ;

Luego, después de ejecutar el bloque de código estático:

        contador1 = 0 ;

        contador2 = 0 ;

Supongo que te gusta

Origin blog.csdn.net/Jack_PJ/article/details/87978585
Recomendado
Clasificación