单例模式之类的加载

来源:互联网 发布:mac safari 编辑:程序博客网 时间:2024/06/04 17:50

单例模式中经常提到懒汉式和饿汉式,最大的区别就是在创建对象的时间不一样,饿汉是在类加载的时候就创建了对象,懒汉是在使用时候才创建对象

同时单例模式保证只有一个对象,那么请问类的加载的时机是什么?类加载都做了什么?

对象在内存中都做什么?以下是我的理解

首先明确类的加载模式

类什么时候加载

类的加载是通过类加载器(Classloader)完成的,它既可以是饿汉式[eagerly load](只要有其它类引用了它就加载)加载类,也可以是懒加载[lazy load](等到类初始化发生的时候才加载)。不过我相信这跟不同的JVM实现有关,然而他又是受JLS保证的(当有静态初始化需求的时候才被加载)。

类什么时候初始化

加载完类后,类的初始化就会发生,意味着它会初始化所有类静态成员,以下情况一个类被初始化:

  1. 实例通过使用new()关键字创建或者使用class.forName()反射,但它有可能导致ClassNotFoundException。
  2. 类的静态方法被调用
  3. 类的静态域被赋值
  4. 静态域被访问,而且它不是常量
  5. 在顶层类中执行assert语句

反射同样可以使类初始化,比如java.lang.reflect包下面的某些方法,JLS严格的说明:一个类不会被任何除以上之外的原因初始化。

类是如何被初始化的

现在我们知道什么时候触发类的初始化了,他精确地写在Java语言规范中。但了解清楚 域(fields,静态的还是非静态的)、块(block静态的还是非静态的)、不同类(子类和超类)和不同的接口(子接口,实现类和超接口)的初始化顺序也很重要类。事实上很多核心Java面试题和SCJP问题都是基于这些概念,下面是类初始化的一些规则:

  1. 类从顶至底的顺序初始化,所以声明在顶部的字段的早于底部的字段初始化
  2. 超类早于子类和衍生类的初始化
  3. 如果类的初始化是由于访问静态域而触发,那么只有声明静态域的类才被初始化,而不会触发超类的初始化或者子类的初始化即使静态域被子类或子接口或者它的实现类所引用。
  4. 接口初始化不会导致父接口的初始化。
  5. 静态域的初始化是在类的静态初始化期间,非静态域的初始化时在类的实例创建期间。这意味这静态域初始化在非静态域之前。
  6. 非静态域通过构造器初始化,子类在做任何初始化之前构造器会隐含地调用父类的构造器,他保证了非静态或实例变量(父类)初始化早于子类
初始化例子

这是一个有关类被初始化的例子,你可以看到哪个类被初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
 * Java program to demonstrate class loading and initialization in Java.
 */
publicclass ClassInitializationTest {
 
    publicstatic void main(String args[]) throwsInterruptedException {
 
        NotUsed o = null;//this class is not used, should not be initialized
        Child t = newChild(); //initializing sub class, should trigger super class initialization
        System.out.println((Object)o == (Object)t);
    }
}
 
/**
 * Super class to demonstrate that Super class is loaded and initialized before Subclass.
 */
classParent {
    static{ System.out.println("static block of Super class is initialized"); }
    {System.out.println("non static blocks in super class is initialized");}
}
 
/**
 * Java class which is not used in this program, consequently not loaded by JVM
 */
classNotUsed {
    static{ System.out.println("NotUsed Class is initialized "); }
}
 
/**
 * Sub class of Parent, demonstrate when exactly sub class loading and initialization occurs.
 */
classChild extendsParent {
    static{ System.out.println("static block of Sub class is initialized in Java "); }
    {System.out.println("non static blocks in sub class is initialized");}
}
 
Output:
staticblock of Super classis initialized
staticblock of Sub classis initialized in Java
nonstaticblocks in superclass is initialized
nonstaticblocks in sub classis initialized
false

从上面结果可以看出:

  1. 超类初始化早于子类
  2. 静态变量或代码块初始化早于非静态块和域
  3. 没使用的类根本不会被初始化,因为他没有被使用

再来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * Another Java program example to demonstrate class initialization and loading in Java.
 */
 
publicclass ClassInitializationTest {
 
    publicstatic void main(String args[]) throwsInterruptedException {
 
       //accessing static field of Parent through child, should only initialize Parent
       System.out.println(Child.familyName);
    }
}
 
classParent {
    //compile time constant, accessing this will not trigger class initialization
    //protected static final String familyName = "Lawson";
 
    protectedstatic String familyName = "Lawson";
 
    static{ System.out.println("static block of Super class is initialized"); }
    {System.out.println("non static blocks in super class is initialized");}
}
 
Output:
staticblock of Super classis initialized
Lawson

分析:

  1. 这里的初始化发生是因为有静态域被访问,而且不一个编译时常量。如果声明的”familyName”是使用final关键字修饰的编译时常量使用(就是上面的注释代码块部分)超类的初始化就不会发生。
  2. 尽管静态与被子类所引用但是也仅仅是超类被初始化



0 0
原创粉丝点击