jvm学习 - 类加载和初始化相关的案例

Posted dengyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jvm学习 - 类加载和初始化相关的案例相关的知识,希望对你有一定的参考价值。

类的加載规则

总的顺序是:先父类后子类,先静态后动态,属性和代码块的初始化遵循正常的出场顺序无论是静态还是动态,但是他们总是先于构造器执行。

package ooptest;
 
public class StaticDemo6 {
 
    public static void main(String[] args) {
         new SB();
    }
    
 
}
 
class SA {
 
    D d;
 
    static {
        System.out.println("A 1"); // 1.先从父类的非静态开始
    }
 
    {
        System.out.println("A 2"); // 5.此处开始new对象(非静态相关)
        d = new D();// 6.顺序执行
    }
 
    public SA() {
        System.out.println("A 3"); // 10.此时调用完了自己的非静态代码块来到了构造器
    }
 
}
 
class SB extends SA {
    static C c = new C(); // 2.调用完了父类的静态相关来到子类的静态相关
 
    static {
        System.out.println("B 1"); // 4.接着按照顺序来调用自己的静态代码块 ,到此子类的所有静态都执行完毕接下来将会执行非静态相关
    }
 
    {
        System.out.println("B 2"); // 11.父类的构造器调用完成调用子类的非静态块
    }
 
    public SB() {
        System.out.println("B 3"); // 12.调用完了自己的非静态块调用自己的构造方法 
    }
 
}
 
class C {
    public C() {
        System.out.println("C"); // 3.C没有父类与静态直接调用自己的构造器  // 8.
    }
}
 
class D extends C {// 7. 来到了D但是D有自己的父类所以到达C类
    public D() {
        System.out.println("D");// 9.调用完了父类的构造器会来到子类的构造器
    }
}

注:当加载到一个静态属性的时候他的赋值对象为一个静态的对象,这个时候就会中断静态相关的加载,转而先去执行非静态相关的代码。这里还需要注意的是属性和代码块的加载遵循他们的先后出场顺序。如下代码

package staticment;
 
public class Text {
 
    public static int k = 0;
    public static Text t1 = new Text("t1"); // 1.这里的静态属性赋有一个非静态的对象 所以停止类加载转向所有的非静态初始化 
    public static Text t2 = new Text("t2");// 6.由于上一句静态代码以及相关的非静态代码执行完毕所以来到了下一句静态代码的执行
    public static int i = print("i");
    public static int n = 99;
 
    public int j = print("j");// 2.这是第一个非静态属性它的赋值调用print("j")方法 // 7.原理和1的执行过程类似
 
    {// 4.现在2.处的非静态属性终于初始化完毕,所以接着来到了非静态块此时 k = 2, i = 1, n = 1   
        print("构造块");
    }
 
    static {
        print("静态块");
    }
 
    public Text(String str) {// 5.非静态块执行完毕之后来到了构造方法(它本身也是非静态的)此时k = 3, i = 2, n = 2  
        System.out.println((++k) + ":" + str + " i = " + i + " n = " + n);
        ++i;
        ++n;
    }
 
    public static int print(String str) {// 3.来自2.的调用此时k = 1, i = 0, n = 0  
        System.out.println((++k) + ":" + str + " i = " + i + " n = " + n);
        ++n;
        return ++i;
    }
 
    public static void main(String[] args) {
        Text t = new Text("init");
    }
}

无论如何类的加载都

①先进行解析(也就是声明静态变量但是不去初始化),也就是将静态变量放入方法区并且标记,标记一个值0。相当于只定义没有赋值。

②当所有的解析都过去的时候才进行初始化,初始化就是按照出场顺序来执行静态代码块和检查静态变量那里是否赋值值,如果有值得话那么就赋值,没有的话那么就将标记值赋值给静态变量。

注意:标记状态的值相当于无值它不可以直接参加运算但是可以间接的使用标记的值。类名调用。

 

public class StaticDemo7 {
    
    public static void main(String[] args) {
        System.out.println(E.i);
        System.out.println(E.j);
    }
 
}
 
class E {
    
    
    static E e = new E();// 1
    static int i = 5;// 2
    static int j;// 3
    static {
        E.i++;
        E.j++;
    }
    
    public E() {
        i++;
        j++;
    }
    
}

①首先进行解析 i = 0,j = 0 //标记值

②停止静态的类加载,执行构造器中的方法,标记值发生了运算,i = 1,j = 1

③执行到2处,i = 5

④执行到3处j没有赋值,默认使用标记值1

⑤执行静态代码块i = 6,j = 2

静态变量

static修饰变量---静态变量/类变量。静态变量在类加载的时候加载到方法区,并且在方法区中被赋予默认值。由于静态变量先于对象出现,所以可以通过类名来调用静态变量,也可以通过对象调用。这个类的所有对象存储的是这个静态变量在方法区的地址,所以所有对象是共享这个静态变量。

注意:

1. 类是加载到方法区中---类中的所有的信息都会加载方法区中

2. 类是第一次使用的时候加载到方法区,加载之后不在移除 --- 意味着类只加载一次

静态变量能否定义到构造方法中?---不可以。--- 静态变量在类加载的时候加载到方法区;构造方法是在创建对象的时候调用,在栈内存中执行。

静态变量能否定义到构造代码块中?---不可以

注意:所有的静态只能定义在类中不能定义到代码块中,代码块也就意味着这个变量的作用域只是在当前代码块的作用域内。

双亲委托模式

从java虚拟机的角度来看,只存在两种类加载器,启动类加載器(bootStrap ClassLoader),由c++语言实现,和其他所有类加载器,由java语言实现,都继承与抽象类java.lang.ClassLoader

   在java开发人员看,其他类加載器包括(扩展类加载器和应用程序类加载器)

技术分享图片

双亲委派模型工作过程

如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。

加载流程

1.当Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成。  

2.当Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap ClassLoader去完成。  

3.如果Bootstrap ClassLoader加载失败(在<JAVA_HOME>lib中未找到所需类),就会让Extension ClassLoader尝试加载。  

4.如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。  

5.如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。  

6.如果均加载失败,就会抛出ClassNotFoundException异常。

为什么要双亲委托

Java类伴随其类加载器具备了带有优先级的层次关系,确保了在各种加载环境的加载顺序。  
保证了运行的安全性,防止不可信类扮演可信任的类。

 


以上是关于jvm学习 - 类加载和初始化相关的案例的主要内容,如果未能解决你的问题,请参考以下文章

JVM学习-类加载和类加载器

JVM基础学习之类的加载链接和初始化

从实战角度解读JVM:类加载机制+JVM调优实战+代码优化

JVM类加载原理学习笔记

JVM类生命周期概述:加载时机与加载过程

java虚拟机(JVM) 一 类加载过程