zoukankan      html  css  js  c++  java
  • JVM学习四:JVM之类加载器之初始化分析

    在经过了前面的加载  和 连接分析之后,这一节我们进入重要的初始化分析过程:

                   

    一、认识初始化

    初始化:这个似乎与上面的初始化为默认值有点矛盾,我们再看一遍:为累的静态变量赋予正确的初始值,上面是赋予默认值,这里是赋予正确的初始值,什么是正确的初始值,就是用户给赋予的值。我们来看一个例子

    class Test{
    private static int a = 1;
    
    }

    我们知道,这个类加载好之后,a的值就是1,但实际是这样子的,类在加载的连接阶段,将a初始化为默认值0(int的默认值是0),然后在初始化阶段将a的值赋予为正确的初始值1. 我们看到最终a的值是等于1,但是实际的运行中是有一个将0赋予a的过程,这个过程放生在连接的准备阶段。类的初始化还有另外的一种形式,代码如下:

    class Test{
    
    private static int a ;
    
    static{
    
    a=1;
    
      }
    
    }

    这里强调一点,这个时候还是没有类的实例生成的,这点一定要注意!《深入java虚拟机第二版》里面有一个图阐述了对应的关系,如下:

    静态变量的声明语句,以及静态代码块都被看做类的初始化语句,Java虚拟机会按照初始化语句在类文件中的先后顺序来依次执行它们。

    二、初始化步骤

    1.假如这个类还没有被加载和连接,那就先进行加载和连接。【加载和连接执行完成后不一定会执行初始化,要符合主动使用才会进行初始化,否则只会执行加载和连接】
    2.假如类存在直接的父类,并且这个父类还没有被初始化,那就先初始化直接的父类。
    3.假如类中存在初始化语句,那就依次执行这些初始化语句。

    三、主动使用和被动使用【初始化时机】

     Java程序对类的使用方式可分为2种,主动使用和被动使用。所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用时”才初始化他们。

    主动使用的六种情况:

    1.创建类的实例。

    new Test();

    2.访问某个类或接口的静态变量,或者对该静态变量赋值。

    int b = Test.a;
    Test.a = b;

    3.调用类的静态方法

    Test.doSomething();

    4.反射

    Class.forName(“com.mengdd.Test”);

    5.初始化一个类的子类

    class Parent{
    }
    class Child extends Parent{
          public static int a = 3;
    }
    Child.a = 4;

    6.Java虚拟机启动时被标明为启动类的类

    java com.mengdd.Test

    除了以上六种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化 (除了上述6种情况以外,都不会执行初始化,只会执行加载和连接)

    这个时候我们再来看第一节学习中那段比较诡异的代码:

    class Singleton{
    
       
    
        private static Singleton singleton=new Singleton();
    
        private static int counter1;
    
        private static int counter2 = 0;
    
       
    
        public Singleton() {
    
           counter1++;
    
           counter2++;
    
        }
    
        public static int getCounter1() {
    
           return counter1;
    
        }
    
        public static int getCounter2() {
    
           return counter2;
    
        }
    
    
        public static Singleton getInstance(){
    
           return singleton;
    
        }
    
       
    
    }
    
    
    public class ClassLoaderTest {
    
    
        @SuppressWarnings("static-access")
    
        public static void main(String[] args) {
    
           Singleton singleton=Singleton.getInstance();
    
           System.out.println("counter1:"+singleton.getCounter1());
    
           System.out.println("counter2:"+singleton.getCounter2());
    
           System.out.println(singleton.getClass().getClassLoader());
    
        }
    
    }

    我们调用Singleton singleton=Singleton.getInstance();调用Singleton的静态方法,相当于主动使用了类Singleton,因此Singleton被初始化!
    当我们看到显示的是:

    private static Singleton singleton=new Singleton();
    
        private static int counter1;
    
        private static int counter2 = 0;

    这样的时候,顺序执行,先赋予初始值,singleton为null,counter1为0,counter2为0,然后顺序对singleton赋予正确的值newSingleton(),执行构造函数,counter1增加变为1,然后counter2变为1,然后继续执行初始化,将counter2赋值为正确的值,将counter2修改为0,因此运行结果是1、0

    反过来

    private static int counter1;
    
        private static int counter2 = 0;
    
        private static Singleton singleton=new Singleton();

    先赋予初始值counter1为0,counter为0,singleton为null,然后对counter2赋值为正确的值,counter2为0,然后对singleton执行初始化赋予正确的值new Singleton(),执行构造函数,counter1为1,counter2为1,因此执行结果是1、1

    到此,是不是感觉前面的问题豁然开朗了呢?

    参考资料:

    圣思园张龙老师深入Java虚拟机系列

  • 相关阅读:
    SQL入门经典(九) 之自定义函数
    SQL入门经典(八) 之存储过程
    SQL入门经典(七) 之脚本和批处理
    SQL入门经典(六) 之视图
    超越阿里云,华为云网络服务竟然这么厉害!
    shanchushanchu
    2020-2022年中国云计算市场预测
    如何使用egit将本地代码提交到托管平台
    一个项目经理对主流项目管理工具的对比:禅道VS华为软件开发云
    从SVN到Git最强指南
  • 原文地址:https://www.cnblogs.com/pony1223/p/8641181.html
Copyright © 2011-2022 走看看