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虚拟机系列
最新文章
- byte数据的常用操作函数[转发]
- 研究kisso跨域登录的心得
- 一个很详细的web.xml讲解
- Git commit 常见用法
- 课程3——程序结构关键字
- 使用Javascript实现跳转页面和打开新窗口的方法
- delphi 动态建立WebBrower
- Oracle core03_ACID
- jQuery模拟点击A标记
- [HNOI2014]米特运输
- 微信小程序wx.getLocation()获取经纬度及JavaScript SDK调用腾讯地图API获取某一类地址
- CentOS 与Ubuntu 下配置IP地址
- tp3.2.3运用phpexcel将excel文件导入mysql数据库
- MT【112】单变量化
- HttpClient(三)-- 抓取图片
- 深入浅出MongoDB应用实战开发
- selenium测试(Java)-- 验证信息(八)
- python使用cx_Oracle连接oracle
- CentOS 7_64位系统下搭建Hadoop_2.8.0分布式环境
- 进阶系列(8)——匿名方法与lambda表达式
热门文章
- HDU 5225 枚举
- IIS 7.0 的 ASP.NET 应用程序生命周期概述
- fastjson&;gson
- Alpha事后诸葛(团队)
- 玩下软工项目,第一轮--全局Context的获取,SQLite的建立与增删改查,读取用户通话记录信息
- Notepad++如何多视图(分屏)显示
- mysql中一些表选项
- jQuery表单验证组件BootstrapValidator
- .netMVC Vue axios 获取数据
- 【.Net】C# 根据绝对路径获取 带后缀文件名、后缀名、文件名、不带文件名的文件路径