类型生命周期(一)

来源:互联网 发布:淘宝上的酷狗会员没了 编辑:程序博客网 时间:2024/06/04 18:55

一切从一段诡异的代码说起,据说这个代码是Java程序员都会犯的错误,代码如下:

class Singleton {    public static Singleton singleton = new Singleton();    public static int a;    public static int b = 0;    private Singleton() {        super();        a++;        b++;    }    public static Singleton GetInstence() {        return singleton;    }}public class MyTest {    /**     * @param args     */    public static void main(String[] args) {        Singleton mySingleton = Singleton.GetInstence();        System.out.println(mySingleton.a);        System.out.println(mySingleton.b);    }}
其实这段代码已经在很多地方被转载了,想必大家也都知道答案。也可以参照这篇文章看一下具体的分析(http://blog.csdn.net/csh624366188/article/details/7958101)我也是在网上看了分析才知道了答案,借此机会就把类型的生命周期学习了一下,在此小做一个总结。

类型的生命周期。

Java class 文件以标准的二进制形式来表现java类型。

一个Java类的生命周期,指从它进入虚拟机开始一直到从虚拟机卸载。这中间要包括开始阶段的装载,连接,初始化,以及占对象生命周期绝大部分的对象实例化,垃圾收集,和对象终结,然后是Java类型生命周期的结束,也就是从虚拟机中卸载。如下图:

1. 开始阶段

开始阶段的工作就像上图所示一样包括 装载,链接还有初始化,这三个阶段必须按照顺序进行,其中第二个阶段的第三步-解析,可以在初始化之后再进行。

1.1 装载

装载阶段有三个基本动作组成,要装载一个类型,Java虚拟机必须:

- -通过类的完全限定名,产生一个代表该类型的二进制数据流

-- 解析这个数据流为方法区内的内部数据结构

-- 创建一个表示该类型的java.lang.Class实例

注: 类装载器不一定等到类首次使用的时候再去装载类,Java虚拟机规范允许类装载器缓存类,或者把这些类装配到某些相关的分组中去。如果一个类装载器,在预先装载类的工程中遇到错误,在装载阶段是不会报错的,它会等到类首次主动使用的时候才报告错误。如果类一直没有被主动使用,那么类装载器将不会报告这个错误。

1.2 链接

装载过程包括三个步骤,验证,准备,解析,前面也做了说明,解析是可以在初始化之后进行的,其他的两个步骤必须按照顺序执行。下面分别对这三个步骤做简单的说明。

1.2.1验证

这个步骤的工作主要是用来确认类型符合Java语言的语义,并且它不会危及Java虚拟机的完整性。

不同的虚拟机有不同的实现,Java虚拟机规范列出了这个阶段可以抛出的一样,什么异常在什么条件下必须抛出。

1.2.2 准备

在准备阶段Java虚拟机为类变量分配内存,设置默认值。注意这儿说的是类变量,什么是类变量呢,简单点来说就是声明成static的变量,而且设置的是默认值,并不是初始化的值,Java虚拟机会根据变量的类型为每个类变量设置一个默认值。

    private static int test1 = 9;  //类变量,根据下面的表格int类型的默认值是0,所以在准备阶段,test1就会被设置成默认值0    private int test2 = 10;       //这个不是类变量,这个被称作实例变量

下面的这个表格给出了Java虚拟机,根据不同类型设置的默认值:

类型默认初始值int0long0Lshort(short)0char'\u0000'byte(byte)0booleanfalsereferencenullfloat 0.0fdouble0.0d









1.2.3 解析

解析过程就是在类型的常量池中寻找类,接口,字段,方法的符号引用,并把这些引用替换成直接引用的过程。

1.3 初始化

类初始化就是给类变量赋予正确的初始值的过程。以便在类或者接口首次被主动使用的时候,能够取得正确的数据。这里涉及到一个概念就是主动使用,我们先来解释一下什么叫做类的主动使用。共有六种情况符合主动使用的要求:

-- 当创建某个类的实例时(或者通过在字节码中执行new命令,或者通过不明的创建,反射、克隆、反序列化);
-- 当调用某个类的静态方法时;
-- 当使用某个类的静态字段或者给该字段赋值时,使用final定义的静态字段除外,它被初始化为一个编译时的常量;
-- 当条用API中的某些反射方法时,比如Class类的方法,或者java.lang.reflect包中类的方法时;
-- 当初始化它的子类时(某个类初始化时,要求他的超类已经被初始化了);
-- 当虚拟机启动时,某个被标明为启动类的类(即含有main方法的类)。

在Java代码中,一个正确初始值是通过类变量初始化语句或者静态初始化语句给出的。类变量初始化语句静态初始化语句合并在一起被称为类初始化方法。

class InitializationProcess {    private static int test1 = 9;    //类变量初始化语句    private static int test2 = 10;   //类变量初始化语句   //静态初始化语句    static {        test1++;        test2++;    }

一般情况下初始化一个类包含两个步骤:

1. 如果一个类存在直接超类的话,而且超类没有被初始化,先初始化超类。

2. 如果一个类存在初始化的方法,就调用给初始化方法。

文章写到这儿,关于类开始阶段的理论和概念都解释完了,但是光有这些理论还是远远不够的,给大家转载一篇很有意思的文章,是关于类的初始化顺序和执行顺序的,我看完了之后从中获益良多,也希望对大家有所帮助。

http://greateryang.blog.163.com/blog/static/81953375201232621031508/


原创粉丝点击