首先,我们要明白栈溢出是由什么导致的?
我们都知道堆栈是内存中连续的一段存储空间,用来保存一些临时数据。正如您现在所想,线程的堆栈里存储着正在执行的方法、原始数据类型、变量、对象指针和返回值。这些都会消耗内存,如果线程的堆栈大小超出内存分配大小就会报错:StackOverflowError。
接下来先看一段代码:
public class A {
private String a1;
protected String a2;
public String a3;
String a4;
public void testA() {
System.out.println("类A的方法!!!");
}
}
public class C extends A{
C c = new C();
public void testC() {
c.a2 = "类A的变量a2,类C修改了 ";
c.a3 = "类A的变量a3,类C修改了 ";
c.a4 = "类A的变量a4,类C修改了 ";
}
}
public class Test {
public static void main(String[] args) {
C c = new C();
c.testC();
}
}
这几段代码都是在同一个包里面执行的,这几段代码看着是没有问题的。但是一旦执行就会包栈溢出错误。
通过断点调试(将断点设置在Test类中创建C类对象的地方),我们可以发现类C和类A之间在来回的循环调用。那么问题就来了两个类之间循环调用就会产生变量、原始数据类型等一些数据。就如我们开始提到的一样,线程的堆栈里面就会存储临时变量和原始数据等,并且这两个类是循环调用没有停止,所以最后的结果是堆栈空间使用完导致栈溢出错误。
现在我们就会有这样的问题,为什么这两个类(A类和C类)之间会循环调用?
通过设置断点进行调试:在Test类中C类创建对象我们会进入到C类里面进行一系列的初始化,然而C类是继承类,在这里我们要知道子父类之间的初始化顺序:父类首先初始化。所以我们有进入到了A类(也就是父类里面)进行初始化。A类初始化完之后就有回到了C类进行执行语句,我们可以看到C类的第一个语句是创建C类对象,创建C类对象我们需要初始化变量,然而C类是继承类那么要先初始化A类。这样循环的创建对象初始化子父类的变量就会导致栈溢出。