当我们声明一个类:
public class MyTest{
MyTest a;
}
MyTest类中有一个类型是本身的成员变量test,这样声明并不会出现什么问题。但是成员变量是可以初始化的,如果我们一开始就给test初始化会出现什么问题:
public class MyTest{
MyTest a = new MyTest();
}
用上面这个声明,我们调用一下:
public class Main{
public static void main(String[] args) {
MyTest demo = new MyTest();
}
}
我们在外部声明一个MyTest类型的变量demo并初始化,这时候运行会出现错误:java.lang.StackOverflowError,栈溢出,为什么?
一步一步来看一下,首先demo有一个属性是a,当执行MyTest demo = new MyTest();
的时候,demo的属性也会初始化,因此会执行MyTest a = new MyTest();
,发现没有,当执行MyTest a = new MyTest();
的时候,是不是跟执行MyTest demo = new MyTest();
的效果是一样的,a也有自己的属性也叫a(暂且叫做a’),这个a‘也要被初始化,也要执行MyTest a' = new MyTest();
a’也有自己的属性a’‘,a’‘有属性a’‘’,a’‘‘有属性a’’‘’…这样就进入了无线递归,因此就会出现栈溢出。
但是
如果声明是这样的:
public class MyTest{
static MyTest a = new MyTest();
}
会出现栈溢出吗?
static修饰了a,那么a就是一个静态属性,静态属性的生命周期跟类一样,并且在内存中仅有一份,我们还是在外部调用:
public class Main{
public static void main(String[] args) {
MyTest demo = new MyTest();
}
}
这时候,当执行MyTest demo = new MyTest()
时候,其实静态属性a会比demo先被加载,也就是说,static MyTest a = new MyTest()
先被执行,此时a已经被实例化了,接下来就实例化demo,那么实例化demo的时候,会再实行static MyTest a = new MyTest()
吗,答案是不会。因为a是静态资源,仅会被加载一次,因此这样子的声明不会出现栈溢出。
这也就是为什么单例模式中,要将属性声明为static。
以下是饿汉式的单例模式的简单声明:
public class Singleton {
private final static Singleton INSTANCE = new Singleton(); //用于引用全局唯一的单例对象,在一开始就创建好
private Singleton() {} //不允许随便new,需要对象直接找getInstance
public static Singleton getInstance(){ //获取全局唯一的单例对象
return INSTANCE;
}
}