我知道这是毫无意义的:我发现它很有趣,我想更多地询问当你创建一个继承自身的类时会发生什么的机制,导致堆栈溢出崩溃。令人惊讶的是,Java允许您开始构建这样的构造。
我只是在猜测,但JVM是否会在实例化之前将其自身置于一个无限循环中试图解析该类,或者它是否实际上无休止地实现了该类的多个副本?
我应该更具体;我正在使用一个内部类来从封闭类派生。
public class Outside {
private int outsideValue;
public class Inside extends Outside {
private int insideValue;
public Inside(int val) {
insideValue = val;
}
}
public Outside() {
Inside o = new Inside(0);
}
}
public class Main {
public static void main(String args[]) {
Outside o = new Outside();
}
}
答案 0 :(得分:8)
请注意,由于Inside
扩展了Outside
,因此它对super()
进行了隐式调用,这是Outside
的构造函数(在转而调用Inside
)的构造函数,因此它就会出现。
您发布的代码在概念上与以下程序没有区别:
class A {
B b = new B();
}
class B extends A {
}
public class Test {
public static void main(String[] args) {
new A(); // Create an A...
// ... which creates a B
// ... which extends A thus implicitly creates an A
// ... which creates a B
// ...
}
}
答案 1 :(得分:2)
在最终形式中,这个问题与循环继承和内部类无关。它只是由未绑定的递归构造函数调用引起的无限递归。通过以下简单示例可以显示相同的效果:
public class A {
public A() {
new A();
}
}
请注意,此代码完全有效,因为Java不对递归调用应用任何限制。
在你的情况下,由于继承,它稍微复杂一些,但如果你还记得子类的构造函数隐式调用超类的构造函数,那么应该很清楚这些调用会形成无限递归。
答案 2 :(得分:1)
尝试像eclipse这样的IDE,它不允许你这样做。即给出这样的错误。
检测到周期:类型Test无法扩展/实现自身或其自己的成员类型之一
答案 3 :(得分:1)
扩展自己会产生循环继承错误(java不允许)。您的代码示例已编译并且有效。
由于Vladimir Ivanov's持久性,我将修复我的编辑。
由于以下原因,您的代码会抛出StackOverflowError
。
Inside o = new Inside(0);
由于Inside
扩展Outside
,Inside
首先隐式调用super()
方法(因为您自己没有调用它)。 Outside()
构造函数初始化Inside o
并且循环再次运行,直到堆栈已满并且溢出(堆栈内的Inside
和Outside
太多了。)
希望这有助于弗拉基米尔·伊万诺夫。
答案 4 :(得分:1)
尝试进入循环继承链时,java编译器不会进入无限循环。毕竟,每个继承链都是最终有限的图形(并且从计算上讲,它具有非常少量的节点和边缘。)更准确地说,从子类A到(最终)超类Z的继承图必须是一条线(不是但是,相反,编译器可以很容易地确定它是否是一行。
程序确定这样一个小图是否是循环的,或者它是否是一行,这并不需要花费太多时间,这就是编译器的作用。因此,编译器不会进入无限循环,并且JVM永远不会耗尽堆栈空间,因为1)编译器既不在JVM上运行,也不会在JVM上执行(因为没有任何东西可以编译而且编译器永远不会调用在这种情况下JVM无论如何。)
我不知道任何允许这种循环继承图的语言(但我已经做了11年除了Java之外什么都没做,所以我对除Java以外的任何东西的记忆都是糊涂的。)此外,我看不到使用这种结构(在建模或现实生活中)。但理论上可能很有趣。
修改强>
好的,我运行了你的代码,确实会导致堆栈溢出。你是对的。我将不得不坐下来真正研究这个以理解为什么编译器允许这样的构造。
很好找!!!!
答案 5 :(得分:1)
如果我们更改一下,您发布的示例可能会出现问题:
public class Outside {
public class Inside extends Outside {
public Inside(int val) {
}
}
private Inside i;
public Outside() {
i = new Inside();
}
}
但这与Inside
是Outside
的内部类的事实并不完全相关,它可能与单独的顶级类相同。
答案 6 :(得分:0)
你可以通过以下方式得到答案:
Class.forName("MyClass");
这种方式得到解决但未实例化。因此,如果解决方案本身导致崩溃,你可以查看。
我想这取决于您使用的JVM。
答案 7 :(得分:0)
当我尝试编译时:
class A extends A {
}
我明白了:
$ javac A.java
A.java:1: cyclic inheritance involving A
class A extends A {
^
1 error
所以Java不会让你做这种事情。有关信息,请java version "1.6.0_24"