记一次JAVA类加载问题
什么是类加载器
负责读取Java字节码,并转换成 java.lang.class 类的一个实例;
类加载器与类的”相同“判断
类加载器除了用于加载类外,还可用于确定类在 Java虚拟机中的唯一性;
即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的;
通俗一点来讲,要判断两个类是否“相同”,前提是这个类必须被同一个类加载器加载,否则这两个类不相同;
这里指的“相同”,包括类的Class对象的equals方法、isAssignableFrom() 方法、isInstance()方法、instanceof() 关键字等判断出来的结果;
类加载器的种类:
- 启动类加载器: Bootstrap ClassLoader,加载JAVA_HOME\lib,加载 -Xbootclasspath 参数限定的类
- 扩展类加载器:Extension ClassLoader,加载\lib\ext,或者被 java.ext.dirs 系统变量指定的类;
- 应用程序类加载器: Application ClassLoader , 加载 ClassPath 中的类库;
- 自定义类加载器: 通过继承ClassLoader 实现;
类加载过程
类加载分为三个步骤: 加载,连接,初始化
加载:
根据一个类的全限定名来读取此类的二进制字节流到JVM内部;将字节流所代表的静态存储结构转换为方法区的运行时数据结构,转换成一个与目标对应的java.lang.Class 对象;
连接:
验证:
验证阶段主要包括四个检验过程:文件格式验证、元数据验证、字节码验证和符号引用验证;
准备:
为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实力变量将不再此操作范围内);
解析:
将常量池中所有的符号引用转为直接引用
初始化
在连接的准备阶段,类变量已赋过一次系统要求的初始值,int 赋值为0 ,而在初始化阶段,则是根据程序员自己写的逻辑去初始化类变量和其他资源
在这里插入代码片
public static int value1 = 5;
public static int value2 = 6;
static {
value2 = 66;
}
在准备阶段 value1 和 value2 都等于 0;在初始化阶段 value1 和 value2 等于 5 和 6;
-
所有类变量初始化语句和静态代码块都会在编译时被前端编译器放在收集器里头,存放到一个特殊方法中,这个方法就是 方法,即类/接口初始化方法,该方法只能在类加载的过程中由JVM调用
-
编译器收集的顺序是由于语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量;
-
如果超类(父类)还没有被初始化,那么优先对超类初始化,但在 方法内部不会显示调用超类的 方法,由JVM 负责保证一个类的 方法执行之前,它的超类 方法已经被执行;
-
JVM必须确保一个类的初始化过程中,**如果是多线程需要同时初始化它,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程;(所以可以利用静态内部类实现线程安全的单例模式)
-
如果一个类没有声明任何的类变量,也没有静态代码块,那么可以没有类 方法;
-
接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成()方法。但接口与类不同的是,执行接口的() 方法不需要先执行父接口的 () 方法,只有当父接口中定义的变量使用时,父接口才会初始化。另外,接口的实现类在初始化时也一样不会执行接口的 方法。
何时触发类的初始化
- 为一个类型创建一个新的对象实例;
- 调用一个类型的静态方法时(即在字节码中执行 invokestatic 指令);
- 调用一个类型或接口的静态字段,或者对这些静态字段执行赋值操作时(即在字节码中,执行 getstatic 或者 putstatic 指令),不过用final修饰的静态字段除外,它被初始化为一个编译常量表达式;
- 调用JavaAPI 中的反射方法时(比如调用 java.lang.Class 中的方法,或者 java.lang.reflect 包中其他类的方法;
- 初始化一个类的派生类时( Java虚拟机规范明确要求初始化一个类时,它的超类必须提前完成初始化操作,接口例外)
- JVM 启动包含main方法的启动类时;
不会触发初始化的几种情况:
- 通过子类引用父类的静态字段,不会导致子类初始化(下面例子有说明)
- 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会定义常量的类的初始化;
- 通过数组定义来引用类,不会触发此类的初始化:(SuperClass类已在本文开篇定义)
在这里插入代码片
public class ConstClass {
static {
System.out.println("ConstClass init !");
}
public static final String HELLOWORLD = "hello world";
}
class NotInitialization{
public static void main(String[] args) {
System.out.println(ConstClass.HELLOWORLD);
}
}
在这里插入代码片
import java.util.Random;
public class testMain {
/**
* @author: weizhenglong
* @since 2019-07-25
*/
static {
System.out.println("A");
}
public static void main(String[] args) {
System.out.println(Children.hello);
System.out.println("--------");
System.out.println(Parent1.hello);
System.out.println("--------");
new Children();
System.out.println("--------");
System.out.println(Children11.test);
System.out.println("--------");
}
}
class Parent1{
public static String hello="B";
static {
System.out.println("C");
}
{
System.out.println("D");
}
}
class Children extends Parent1{
public static final String hello="E";
static {
System.out.println("F");
}
}
interface Parent11{
String test="AA";
public static Thread THREAD=new Thread(){
{
System.out.println("H");
}
};
}
interface Children11 extends Parent11{
public static int test=new Random().nextInt(10);
public static Thread THREAD=new Thread(){
{
System.out.println("N");
}
};
}
执行结果:
A
E
C
B
F
D
N
6
[参照] :(https://www.cnblogs.com/aspirant/p/7200523.html)