类加载过程解读

1.前提

什么时候会类加载,当程序创建第一个对类的静态成员的引用时,就会加载这个类。(构造方法就是隐式的静态方法)。

并且在类加载中会多次提到类的初始化,要明确类的初始化是类加载的一部分。

在java虚拟机中,从未强制规定过类什么时候加载,但明确说明了在哪几种情况下必须要初始化,而初始化之前必须的过程是(加载,验证,解析,准备)。所以很多书中讨论类加载总会说在调用什么的时候类会初始化。这种说法可能会让人感到有些混淆,但实际上能走到初始化这一步,前面4步必须要做,因此其实并不矛盾。

类加载了 不一定就会初始化。比如当用.class来获得一个class对象时 就不会初始化该类,而只是加载。

2.大致过程

类加载开始经过了 加载->验证->准备->解析->和初始化。

1)加载:加载就是通过类的全限定名获得类的二进制流,然后将二进制流转化为方法区的数据结构,在内存中生成一个代表该类的Class对象。

2)验证:就是保证Class文件符合java虚拟机的要求,并不会损害虚拟机的安全。

3)准备:就是给类变量赋初值,即给static的变量赋值 引用类型为null 基本类型为0。

4)解析:(我也不是很清楚 只能说个大概) 解析就是将符号引用转化为java虚拟机可以获取的内存地址,或指针的过程。

符号引用其实就是class文件中会有一个常量池存储各种常量包括类的全限定名,字段、方法的名称和描述符等等,这些东西实际上像一个map一样,描述符是CONSTANT_Class_info 类似这种东西的字符串,它只是一个java虚拟机的约定,虚拟机看到这个字符串就可以判断出它是个什么类型并在内存中生成对应的类型。(这段有待考证,只是初步认识)这个过程就是解析。

最后才是初始化。

3.初始化

3.1初始化对于类变量和实例变量的不同处理

首先初始化时,java虚拟机才真正开始运行定义了的java代码。编译期间,编译器就会开始收集类变量和static语句块。并将他们合并成为一个static方法,并执行且只会执行一次。如果存在继承关系那会保证父类的static方法在子类执行之前执行。

对于实例变量,实际上是被java收集了我们写好的实例变量的赋值语句合并到构造方法中执行的。

3.2初始化的惰性

上代码

public class AboutInitialize {
    public static void main(String[] args) {
        Class clza1 = a1.class;    //这是单纯的调用.class得到class文件一定会引起类加载,但是不会触发初始化
        System.out.println(".class is End");
        System.out.println(a1.a1str);
        System.out.println(".a1str is End");
        System.out.println(a1.a1str1);
        System.out.println(".a1str1 is End");
        System.out.println(a2.a2str);
        System.out.println(".a2str is End");
        try {
            Class a3clz = Class.forName("RTTI.a3");  //实例化一定会初始化
            System.out.println("after create a3ref");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("未找到a3类");
        }
        System.out.println(a3.a3int);
    }

}
class a1{
    public static final String a1str= "str";  //这是一个static final修饰的编译器常量 在编译时就可以确定,不需要初始化就可以获取,这是就不会触发类的初始化
    public static final String a1str1 = String.valueOf(123); //对它的访问将会强制初始化因为它不是编译器常量
    static {
        System.out.println("a1被初始化了");
    }
}
class a2{
    public static String a2str = "a2str";  //如果一个static不是final的 那么会在它被读取之前为这个域分配存储空间
    并为这个域进行初始化
    static { 
        System.out.println("a2被初始化了");
    }
}
class a3{
    public static int a3int = 3;
    static {
        System.out.println("a3被初始化了");
    }
}

输出结果:

.class is End
str
.a1str is End
a1被初始化了
123
.a1str1 is End
a2被初始化了
a2str
.a2str is End
a3被初始化了
after create a3ref
3

大家可以按需注释,来进行各种测试。注意:static块只能在程序执行期间执行一次。

就是这样了~~~

参考自:https://www.cnblogs.com/jimxz/p/3974939.html
               java编程思想

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值