饿汉式的非延迟实例化对象怎么理解

AceShot 2015-02-21 12:59:13

class Singleton{
private static Singleton instance = new Singleton();

private Singleton(){}

public static Singleton getInstance(){
return instance;
}
}


在没有类初始化之前new Singleton()应该是不会被实例化。是由当<cinit>方法触发即类初始化进行时才会new。
而类主动初始化仅有的如下几种情况,详见《深入理解java虚拟机》7.2节类加载的时机
1):new一个类的实例对象
2):对类的静态变量进行读取、赋值操作的。
3):直接调用类的静态方法。
4):反射调用一个类的方法。
5):初始化一个类的子类的时候,父类也相当于被程序主动调用了(如果调用子类的静态变量是从父类继承过来并没有复写的,那么也就相当于只用到了父类的东东,和子类无关,所以这个时候子类不需要进行类初始化)。
6):直接运行一个main函数入口的类。

这里显然使用的是3调用静态方法,这时类初始化,new操作进行。

如果按照我这种想法的话,懒汉式照样也是在getInstance()时触发的第一次new,那么两者new的时间可以说是相近的,可为何说饿汉式是非懒加载,而懒汉式是懒加载(延迟实例化的)

不知道我想的错在哪里。求高手指点。
...全文
200 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
skgary 2015-02-22
  • 打赏
  • 举报
回复
引用 5 楼 sum_rain 的回复:
[quote=引用 4 楼 still_rain 的回复:] 同样在《深入浅出Java虚拟机》第7章,楼主你看的那段话的下面: Page 154 ”在首次主动使用时初始化“这个规则直接影响着装载、连接和初始化类的机制。在首次主动使用时,其类型必须被初始化。然后,在类型能被初始化之前,它必须已经被连接了,而在它能被连接之前,它必须已经被装载了。Java虚拟机的实现可以根据需要在更早的时候装载以及连接类型,没有必要一直等到该类型的首次主动使用才去装载和连接它。 Page 155 类装载器(启动型或者用户自定义的)并不需要一直等到某个类型”首次主动使用“时再去装入它。Java虚拟机规范允许类装载器缓存Java类型的二进制表现形式,在预料某个类型将要被使用时就装载它,或者把这些类型装载到一些相关的分组里面。 一般的JVM实现为了加快执行效率,就是按上面的原则实现的。在这个基础上,你再来理解饿汉式是非懒加载,而懒汉式是懒加载(延迟实例化的),不知道楼主你能理解否?
没错,装载,准备,连接,解析等操作是可以提前完成的,但这些操作并不会触发new操作,因为new操作肯定是在<cinit>中进行中,即类初始化这一个步骤中完成的。 而所谓lazy就是指延迟实例化,也必然的是new这个操作呀。[/quote] 仔细想一下2楼提到的。
爱摸鱼de老邪 2015-02-22
  • 打赏
  • 举报
回复
同样在《深入浅出Java虚拟机》第7章,楼主你看的那段话的下面: Page 154 ”在首次主动使用时初始化“这个规则直接影响着装载、连接和初始化类的机制。在首次主动使用时,其类型必须被初始化。然后,在类型能被初始化之前,它必须已经被连接了,而在它能被连接之前,它必须已经被装载了。Java虚拟机的实现可以根据需要在更早的时候装载以及连接类型,没有必要一直等到该类型的首次主动使用才去装载和连接它。 Page 155 类装载器(启动型或者用户自定义的)并不需要一直等到某个类型”首次主动使用“时再去装入它。Java虚拟机规范允许类装载器缓存Java类型的二进制表现形式,在预料某个类型将要被使用时就装载它,或者把这些类型装载到一些相关的分组里面。 一般的JVM实现为了加快执行效率,就是按上面的原则实现的。在这个基础上,你再来理解饿汉式是非懒加载,而懒汉式是懒加载(延迟实例化的),不知道楼主你能理解否?
爱摸鱼de老邪 2015-02-22
  • 打赏
  • 举报
回复
好吧,被你打败了,一个在getInstance方法之前执行初始化,一个在getInstance方法之中再去执行初始化。你姑且认为是一样的吧。
skgary 2015-02-22
  • 打赏
  • 举报
回复
lazy不lazy是相对的。 的确,就2楼的例子来说,这两种情况差不多,因为这两个类里就只有getInstance方法,你用也就是用getInstance方法。 但你可以想像一下另外一种情况,2楼lazy的class中还有其他 函数,还有其他的静态变量。当你任何一个地方要用这个Singleton2的时候,可以不初始化Singleton2实例,而可以直接使用,但Singleton1是不行的。
AceShot 2015-02-22
  • 打赏
  • 举报
回复
上面有个小问题,getInstane是invokestatic,这个会触发类初始化。从代码可以看出invokestatic是通过getstatic获得类变量的
AceShot 2015-02-22
  • 打赏
  • 举报
回复
引用 6 楼 still_rain 的回复:
给你看看两种单例模式生成的字节码吧,你自己也可以用javap试试。 饿汉模式Singleton1:


public class Singleton1 {
  public static Singleton1 getInstance();
    Code:
       0: getstatic     #2                  // Field INSTANCE:LSingleton1;
       3: areturn

  static {};
    Code:
       0: new           #3                  // class Singleton1
       3: dup
       4: invokespecial #4                  // Method "<init>":()V
       7: putstatic     #2                  // Field INSTANCE:LSingleton1;
      10: return
懒汉模式Singleton2:

public class Singleton2 {
  public static synchronized Singleton2 getInstance();
    Code:
       0: getstatic     #2                  // Field instance:LSingleton2;
       3: ifnonnull     16
       6: new           #3                  // class Singleton2
       9: dup
      10: invokespecial #4                  // Method "<init>":()V
      13: putstatic     #2                  // Field instance:LSingleton2;
      16: getstatic     #2                  // Field instance:LSingleton2;
      19: areturn

  static {};
    Code:
       0: aconst_null
       1: putstatic     #2                  // Field instance:LSingleton2;
       4: return
}
饿汉模式的new是否在getInstance时再去操作?

static {};
    Code:
       0: new           #3                  // class Singleton1
       3: dup
       4: invokespecial #4                  // Method "<init>":()V
       7: putstatic     #2                  // Field INSTANCE:LSingleton1;
      10: return
new是在static静态代码块中执行的,但这个代码块将会生成<cinit>方法,这正好说明了new是发生在类初始化这一个步骤中的(即<cinit>),但是类初始化这一个步骤的触发唯有靠我最先提到的那几种方式,这里就是通过getstatic静态方法,通过getInstance来触发类初始化,进而new发生了。
爱摸鱼de老邪 2015-02-22
  • 打赏
  • 举报
回复
给你看看两种单例模式生成的字节码吧,你自己也可以用javap试试。 饿汉模式Singleton1:


public class Singleton1 {
  public static Singleton1 getInstance();
    Code:
       0: getstatic     #2                  // Field INSTANCE:LSingleton1;
       3: areturn

  static {};
    Code:
       0: new           #3                  // class Singleton1
       3: dup
       4: invokespecial #4                  // Method "<init>":()V
       7: putstatic     #2                  // Field INSTANCE:LSingleton1;
      10: return
懒汉模式Singleton2:

public class Singleton2 {
  public static synchronized Singleton2 getInstance();
    Code:
       0: getstatic     #2                  // Field instance:LSingleton2;
       3: ifnonnull     16
       6: new           #3                  // class Singleton2
       9: dup
      10: invokespecial #4                  // Method "<init>":()V
      13: putstatic     #2                  // Field instance:LSingleton2;
      16: getstatic     #2                  // Field instance:LSingleton2;
      19: areturn

  static {};
    Code:
       0: aconst_null
       1: putstatic     #2                  // Field instance:LSingleton2;
       4: return
}
饿汉模式的new是否在getInstance时再去操作?
AceShot 2015-02-22
  • 打赏
  • 举报
回复
引用 4 楼 still_rain 的回复:
同样在《深入浅出Java虚拟机》第7章,楼主你看的那段话的下面: Page 154 ”在首次主动使用时初始化“这个规则直接影响着装载、连接和初始化类的机制。在首次主动使用时,其类型必须被初始化。然后,在类型能被初始化之前,它必须已经被连接了,而在它能被连接之前,它必须已经被装载了。Java虚拟机的实现可以根据需要在更早的时候装载以及连接类型,没有必要一直等到该类型的首次主动使用才去装载和连接它。 Page 155 类装载器(启动型或者用户自定义的)并不需要一直等到某个类型”首次主动使用“时再去装入它。Java虚拟机规范允许类装载器缓存Java类型的二进制表现形式,在预料某个类型将要被使用时就装载它,或者把这些类型装载到一些相关的分组里面。 一般的JVM实现为了加快执行效率,就是按上面的原则实现的。在这个基础上,你再来理解饿汉式是非懒加载,而懒汉式是懒加载(延迟实例化的),不知道楼主你能理解否?
没错,装载,准备,连接,解析等操作是可以提前完成的,但这些操作并不会触发new操作,因为new操作肯定是在<cinit>中进行中,即类初始化这一个步骤中完成的。 而所谓lazy就是指延迟实例化,也必然的是new这个操作呀。
skgary 2015-02-21
  • 打赏
  • 举报
回复
楼主,你能不能把问题用英文表述一下,实在是看不懂问题之所在。
AceShot 2015-02-21
  • 打赏
  • 举报
回复
引用 1 楼 skgary 的回复:
楼主,你能不能把问题用英文表述一下,实在是看不懂问题之所在。
ok. Lazy initialization

public class Singleton1 {
    private static Singleton1 INSTANCE = new Singleton1();
    private Singleton1() {}
    public static Singleton1 getInstance() {
        return INSTANCE;
    }
}
Eager initialization

public class Singleton2{
    private static Singleton2 instance = null;
    private Singleton2() { }
    public static synchronized Singleton2 getInstance() {
        if (instance == null) {
            instance = new Singleton2();
        }
        return instance;
    }
}
Briefly, how to understand Singleton2 is not a lazy initialization? In my mind, I think the time of creating the instance between the two examples is the same, all on calling the method getInstance Ask for consultation,thanks.

62,616

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧