Java中一个类可以拥有多个静态实例吗?

3

我是新手,请不要介意如果你觉得问题很蠢。我正在研究单例模式代码。我稍微改了一下它(我的问题与单例无关,而且我已经删除了单实例检查)。我的问题是:尽管在Java中类实例只能有一个,但为什么输出中会有两个静态类“instance”(请看哈希值)?我知道“new”关键字会给出新的内存地址(这就是哈希值打印出来的信息),但静态类实例不应该是一个吗?所以我得到了两个哈希值用于打印对象实例,静态变量k具有相同的值,这是可以接受的。

public class Singleton {

    private  static Singleton instance;
    static int k;


    public static Singleton getInstance(){
        try{
            instance = new Singleton();

            System.out.println(instance);
        }catch(Exception e){
            throw new RuntimeException("Exception occured in creating singleton instance");
        }
        return instance;
    }

    public static void main(String[] ar) {
        Singleton c1=Singleton.getInstance();
        c1.k=1;
        Singleton c2=Singleton.getInstance();
        c2.k=2;
        System.out.println(c1.k);
        System.out.println(c2.k);

    }
}

输出:

Singleton@15db9742
Singleton@6d06d69c
2
2

你在这里想要问什么? - Amit
2
你的代码不是单例模式,每次调用 getInstance() 都会产生一个新的实例。 - Mark Rotteveel
static 意味着只有一个变量实例。它与您创建多少对象实例无关。 - shmosel
2
在Java中,不存在所谓的“静态类实例”;您的问题似乎基于缺乏对Java基础知识的理解。你只是在你的类中有一个包含你创建的最后一个实例的静态字段。你得到了两个不同的实例,因为这是你的代码所做的:每次调用getInstance()方法时都会创建一个新实例。 - Mark Rotteveel
1
再一次强调,这是基本的Java知识。这只是一个字段,可以在任何时候被重新分配。与非静态字段不同的是,该字段由类拥有,而不是该类的实例拥有。只要该定义在主体中而不是方法体中,static Singleton s=new Singleton()是允许的。 - Mark Rotteveel
显示剩余4条评论
5个回答

4

你的变量instance在两个对象之间共享,但是当你调用instance = new Singleton();时,它指向的对象会发生改变。

我猜你想要的是这样的。

public class Singleton {
    public static Singleton instance; //field is made public so we can print it from main class (just to debug)
    static int k;


    public static Singleton getInstance(){
        try{
            instance = new Singleton();
        }catch(Exception e){
            throw new RuntimeException("Exception occured in creating singleton instance");
        }
        return instance;
    }

    public static void main(String[] ar) {
        Singleton c1=Singleton.getInstance();
        c1.k=1;
        Singleton c2=Singleton.getInstance();
        c2.k=2;

        //notice that both of the instance variables of c1 and c2 are printed
        //after we've initialized all of them.
        System.out.println(c1.instance);
        System.out.println(c2.instance);

        System.out.println(c1.k);
        System.out.println(c2.k);

    }
}

在这里,两个实例变量将得到相同的值

输出

Singleton@6d06d69c
Singleton@6d06d69c
2
2

这个想法是在所有对象的instance变量初始化后打印其值。最近的初始化将覆盖所有先前的初始化。


是的,完全正确!你解决了我的疑惑。但是为什么静态实例可以通过new关键字创建呢? - Vishal
@Vishal 这取决于需求。假设在你的类中,你想找出它被初始化的次数。在这种情况下,静态实例非常方便,因为我们可以创建一个名为 private static int counter = 0 的字段,并在类的构造函数中递增它。由于此 counter 变量将在所有对象之间共享,相同的变量将计算所有实例。如果我们不使其静态,则每次初始化对象时,都会创建一个 counter 的新实例,并且它将从零开始。 - Raman Sahasi
是的,你正解决了我的疑惑。但是为什么允许使用new来初始化静态变量呢?我的意思是,由于对象不应在静态上下文中使用,在这里我正在使用new Singleton()来初始化“instance”。 此外,可以直接使用Singleton.class。 不允许使用static Singleton s = new Singleton(),这是正确的。 - Vishal
不行。我们不能使用Singleton.class进行初始化,它表示类引用并且不会初始化对象。关键是静态变量需要一个对象来保持。这就是为什么我们使用new来初始化它的原因。区别在于,如果使用new初始化static变量,则同一对象将在所有实例之间共享,而如果我们不将其设置为静态,则所有新对象都将以null开始。 - Raman Sahasi
好的,明白了。但是在主函数中,相同的代码“static Singleton s=new Singleton()”会导致编译错误吗? - Vishal
1
@Vishal 是的,这是Java语言规范所强制的限制。在Java中,您不能将任何字段声明为静态字段。只有类变量(字段)和方法可以是静态的。请参见此答案以了解原因:static variable in static member function in JAVA - Raman Sahasi

2

你的单例模式存在问题,每次调用 getInstance 方法都会生成一个新的实例,而不是检查静态对象 "instance" 是否为 null。

这就是为什么你会遇到这个问题的原因:

Singleton@15db9742 
Singleton@6d06d69c

这清晰地展示了类Singleton的两个实例。


1

如上所述,你的问题在于你没有保护你的实例化。

另外,为了更加确保在多线程环境下不会实例化两个类,需要在方法 getInstance 上添加关键字 "synchronized"。

  public static synchronized Singleton getInstance(){ 
  try{ 
  if(instance == null) { 
      instance = new Singleton();
  }
      System.out.println(instance);
}catch(Exception e){
    throw new RuntimeException("Exception occured in creating singleton instance");
}
return instance;

}


0

问题在于你的 getInstance 方法总是创建一个新的实例对象。

也就是说,虽然你的实际实例是 static,即类属性,但每次调用 getInstance 时都会被覆盖。

为了解决这个问题,你应该检查 instance 是否为 null,只有在这种情况下才创建一个新对象。

if(instance == null)
  instance = new Stingleton();

return instance;

0
这是因为缺少一个保护措施,只有创建单例一次。
public static Singleton getInstance(){
  try{
    if(instance == null)
    {
      instance = new Singleton();
    }

    System.out.println(instance);
  } catch(Exception e){
   throw new RuntimeException("Exception occured in creating singleton instance");
  }
  return instance;
}

如果没有这个保护,仍然只有一个静态实例,但是第二次会覆盖这个实例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接