1. 类初始化:
什么时候会发生类初始化?
[1] 类的主动引用 (一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类
- new 一个类的对象
- 调用类的静态成员(除了finial常量) 和静态方法
- 使用java.lang.reflect 包的方法对类进行反射调用
- 当初始化一个类。如果父类没有初始化,则先回初始化它的父类
[2] 类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会初始化,如:当通过子类引用父类的静态变量,不会导致子类初始化。
- 通过数组定义类引用,不会触发此类的初始化。
- 引用常量不会触发此类的初始化(常量是在链接阶段存入调用的常量池)
package com.gs.reflection;
//测试类什么时候会初始化
public class Test06 {
static {
System.out.println("Main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
System.out.println("===========会产生类的初始化================");
//1. 主动引用
Son son = new Son();
//2.反射也会产生主动引用
Class.forName("com.gs.reflection.Son");
//3.调用类的静态成员
System.out.println(Son.m);
System.out.println("===========不会产生类的初始化================");
//不会产生引用的方法
//4.子类访问父类的静态域 ===>只有父类会初始化
System.out.println(Son.b);
//5.通过数组定义引用 ===>不会初始化
Son[] array = new Son[5];
//6.引用常量 ===>不会初始化
System.out.println(Son.M);
//7.调用finial时,不会进行类的初始化
System.out.println(Son.M);
}
}
class Father{
static int b = 2;
static {
System.out.println("父类被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
m = 300;
}
static int m = 100;
static final int M = 1;
}
代码解析:
上面的代码1~7逐一测试的话你会发现 1~ 3在进行调用的时候,不仅会对子类进行初始化,还会对父类初始化(父类的初始化先于子类的初始化),调用4的时候,只有父类会初始化,子类不会。而调用5~7时,父类和子类都不会初始化。这也很好地印证了上面的结论。
2. 类加载器的作用
[1] 类加载的作用: 将class字节码内容加载到内存中,并将这些静态数据转化为方法区内的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象。作为方法区中类数据访问入口。
[2] 类缓存:标准的JavaSE类的加载器可以按要求查找类,但一旦某个类加载到类加载器中,它将为维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
类加载器的作用就是用来把类(class) 装载进内存的。JVM规范定义了如下类型的加载器。
测试代码
package com.gs.reflection;
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException {
//1.获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//2.获取系统类加载器的父类加载器 --->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//3.获取扩展类加载器的父类加载器 -->跟加载器(C/C++)(无法获取的)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
//测试当前类是哪个加载器加载的 (是由系统加载器加载的)
ClassLoader classLoader = Class.forName("com.gs.reflection.Test07").getClassLoader();
System.out.println(classLoader);
//测试JDK内置的类是哪个类加载器 (是由跟加载器加载的)
ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader1);
//如何获得系统加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
//双亲委派机制
/**如果你自定义了java.lang.String的包,它并不会运行,它会一直向上找是否
* 有相同的包。如果有,你这个包将不会被加载
**/
}
}
运行结果