Java类的加载与生命周期

一、概要:

类的生命周期从类的 加载、连接、初始化 开始,到类的 卸载结束;

二、几个阶段:

加载:查找并加载类的二进制数据。(把类的.class文件的二进制数据读入内存,存放在运行时数据区的方法区;类加载的最终结果是产生 堆区中描述对应类的Class对象);

连接:包括验证、准备和解析三个子阶段;

   验证:确保补加载类的正确性;

   准备:为类的静态变量分配内存,并设定默认值;

   解析:把类中的符号引用转换为直接引用;

初始化:给类中的静态变量赋予正确的初始值;

  

类的加载时机:并不需要等到某个类被主动使用时才加载,虚拟机会预测某个类将要被使用就加载它;

类的初始化时机

  a. 创建类(new、反射、克隆、反序列化)

  b. 使用静态方法、非静态变量

  c. Class.forName("ATest");  获取描述类的Class对象;

  另:初始化子类,父类被先初始化。虚拟机的启动类,使用命令 java Sample(也算是创建类);

  注:

    使用能在编译期能得知的final static修饰的常量,不会导致类的初始化;

    public static final int a = 2*3;//编译时常量,不会导致类初始化;

    public static final int a b = (int)(Math.random()*10)/10+1; // 不是编译时常量,会初始化;

    只有当程序访问的静态变量或静态方法的确在当前类或接口中定义时,才可以看作是对类或接口的主动使用;(如用子类引用调用父类静态方法或变量,只会初始化父类

    调用ClassLoader.loadClass()方法加载一个类时,只会加载,但不会初始化;

子类父类初始化过程:

先对这个类进行加载和连接-> 如果有直接父类,先加载连接初始化这个父类->重复以上步骤直到所有父类初始化,初始化当前类;

(先加载连接当前类,再加载连接初始化父类,再初始化当前类)

class Base {
    static {
        System.out.println("init Base");
    }
}

class Sub extends Base {
    static {
        System.out.println("init Sub");
    }
}

public class ATest extends Sub {
    static {
        System.out.println("init ATest");
    }

    public static void main(String args[]) {
        System.out.println("main methos");
    }
}
结果:
init Base
init Sub
init ATest
main methos
View Code

三、类加载器

  除了Java虚拟机自带的根类加载器以外,其余的类加载器有且只有一个父加载器;

  Java虚拟机自带以下几种加载器:

  (Bootstrap)类加载器:没有父类加载器。负责加载虚拟机核心类,sun.boot.class.path路径下类库,java.lang.*; 实现依赖于底层操作系统,没有继承java.lang.ClassLoader;

  扩展(Extension)类加载器:父加载器为根加载器;加载java.ext.dirs下的类库 和 JDK目录下jre/lib/ext目录下类库;继承于java.lang.ClassLoader;

  系统(System)类加载器:也称应用类加载器,父加载器为扩展类加载器;加载classpath路径下指定的类库;继承于java.lang.ClassLoader,也是自定义加载器的默认父类

父亲委托机制

  类加载过程中,会先从最顶层加载器(一般是根加载器)开始往下,先判断父类加载器能不能加载,能加载则往下传递返回加载的类;不能加载则继续往下判断,如果都不能加载,则会抛出ClassNotFoundException异常;

  加载器之间的父子关系实际上是指加载器对象之间的包装关系,而不是类之间的继承关系;

命名空间:

  命名空间由该加载器及所有父加载器所加载的类组成;

  唯一性:在同一个命名空间中,不会出现类的完整名字(包名+类名)相同的两个类;

  同一命名空间有以下关系:

    同一命名空间的类是相互可见的;(可见是指可以直接使用其类名,不可见的类之间可以通过反射来获取类信息);

    子加载器的命名空间所含所有父加载器的命名空间,所有子类加载器的类能看见父加载器的类;但父加载器不能看见子加载器的类;

运行时包: 

  包名相同,且属于同一个类加载器;

  同一运行时包的类才能包可见;如用户自己定义了一个java.lang.Spy,并由自己定义的类加载器加载,由于java.lang.Spy和核心类库java.lang.*由不同的类加载器加载,它们不在同一运行时包,Spy不能访问java.lang.*下的包可见类;

用户自定义类加载器:

  扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法;

  可通过 this.getClass().getClassLoader()获取类对应的加载器

类的卸载:(清除对应类的Class对象 和 类的二进制数据结构)

  由Java虚拟自带的三个类加载器加载的类不能卸载;(虚拟机会始终引用这3个类加载器,类加载器中有一个集合,又会一直引用其加载的类的Class对象)

  用户自定义的类加载器加载的类可以被卸载;

  

  

 整理自孙卫琴《Java面象对象编程》

原文地址:https://www.cnblogs.com/mzzcy/p/7234199.html