类的加载笔记
一 、类的生命周期:
加载->验证->准备->解析->初始化
1、加载:把类的二进制.class文件读入内存,把它放在运行时的数据区的方法区内,然后再堆区创建这个类对应的Class对象。
(1)类的加载:
从本地加载.class文件
从网络下载.class文件
从.jar、.rar文件中提取.class文件
等等。
(2)类加载器:
(1)java虚拟机自带的类加载器,包括启动类加载器、扩展类加载器和系统类加载器
(2)用户自定义的类加载器,它是java.lang.ClassLoader的子类
【注意】:一个类并不是在首次主动使用时才加载,Java虚拟机允许预先加载。
2、验证
保证加载的数据正确
3、准备
在准备阶段,虚拟机为类静态变量分配内存并设置默认值
4、解析
解析阶段,虚拟机把符号引用替换为直接引用、
5、初始化
为类的静态变量赋初值(声明时初始化或者在静态代码块中初始化;注意:final static表示常量,而且只能声明时赋值且必须赋值),初始化的过程包括:
(1)如果类没有加载和连接,先加载和连接。
(2)此类存在直接父类,那么先初始化父类(这保证主动使用此类时,此类和它的所有父类都初始化了)
(3)有初始化语句(声明时初始化或者静态代码块中初始化),就依次执行这些语句。
【注意a】:只有程序主动使用一个类或者接口时,它才会初始化,主动使用包括:
(1)创建类的对象(包括new创建、反射、clone()、反序列化)
(2)调用类方法
(3)访问类变量或者为其赋值
(4)调用类的反射方法Class.forName("X"),会初始化X类
(5)初始化此类的子类
(6)声明为启动类(即java X,X类)
【注意b】:初始化一个接口时,不会初始化其父接口;初始化一个类时,不会初始化其实现接口
【注意c】:对于编译时常量,也不会初始化
【注意d】:只有类或者接口的静态属性和静态方法是在本类、接口中定义的,主动访问才会初始此类或者接口,否则只初始化父类或者实现接口。
【注意e】:ClassLoader的loadClass()方法不会初始化被调用的类,仅仅是加载
二、类的加载器
1、根加载器Bootstrap:加载虚拟机核心类库,如java.lang.*,它加载sun.boot.class.path指定的目录中的类。它没有父加载器,用null表示。
2、扩展类加载器:它从java.ext,dirs属性指定的目录加载类或者从jre/lib/ext目录中加载类,它是java.lang.ClassLoader的子类,而且父加载器是根加载器
3、系统类加载器:从classpath目录中加载类,它是java.lang.ClassLoader的子类,还是用户自定义的类加载器的默认父加载器,而且其父加载器是扩展类加载器。通过ClassLoader.getSystemClassLoader()得到
4、用户自定义的类加载器,继承java.lang.ClassLoader类。可以通过Class对象.getClassLoader()获得此类的类加载器
【注意】:类加载器的父子关系并不是类的继承关系,一对父子类加载器可能都是加载类的实例对象。除了根加载器外,每个类加载器都有且只有唯一一个父类加载器
5、Java中类的加载是父加载器委托机制
假设有两个自定的类加载器classLoader1和classLoader2,classLoader2是classLoader1的父类对象,要求classLoader2加载一个类A,那么classLoader2尝试从自己的命名空间(就是这个类加载器及其父加载器所加载的类的集合,命名空间中不允许存在完整类名完全相同的两个类)中查找此类是否已经加载,如果已经加载,就返回;没有就交给classLoader1,classLoader1又会向上传递给系统类加载器,后者又请求扩展类加载器加载,而它又会请求根加载器加载;如果根加载器加载成功就向下传递给classLoader2,否则又有子类加载,……,如果classLoader2的父加载器都不能加载,那么classLoader2尝试加载,不能加载则抛出ClassNotFoundException异常。
6、我们知道java中有四种访问级别,其中protected是包内和子类可见,默认级别是包内可见。如果我们定义一个类java.lang.MyClass,那么在MyClass中能访问java.lang.*核心库的默认级别成员吗吗?如果可以,那么就会出问题,知道核心甚至修改细节。实际上不可能访问的,因为java.lang.MyClass与系统核心库的类加载器不同,所以是无法访问的,这样更安全。
7、数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建。数组类的类加载器由 Class.getClassLoader() 返回,该加载器与其元素类型的类加载器是相同的;如果该元素类型是基本类型,则该数组类没有类加载器。
8、自定义的类加载器:
继承ClassLoader,指定父加载器(在构造方法调用父类的构造方法super()说明父加载器为系统加载器;调用super(null)表示父加载器为根加载器);覆盖findClass()方法(一般需要调用protected final Class<?> defineClass(String name,byte[] b,int off,int len)方法(defineClass 方法将一个 byte 数组转换为 Class 类的实例))。
【注意】:
1、如果被加载的类中使用了另一个类,那么这个类由谁加载?也是使用父加载器委托机制。
2、同一个运行时包的类可以互相引用的。
3、子加载器加载的类可以引用父加载器加载的类,反之不行。
4、如果两个类加载器无父子关系,那么它们命名空间的类不能互相引用的。
5、处于不同命名空间的类要互相引用,可以使用反射的方式。