请考虑以下代码。
public class Action {
private static int i=1;
public static void main(String[] args) {
try{
System.out.println(i);
i++;
main(args);
}catch (StackOverflowError e){
System.out.println(i);
i++;
main(args);
}
}
}
我正确地认识到4338
。捕获StackOverflowError
输出后连接如下。
4336
4337
4338 // up to this point out put can understand
433943394339 // 4339 repeating thrice
434043404340
4341
434243424342
434343434343
4344
4345
434643464346
434743474347
4348
434943494349
435043504350
在此处考虑Live演示。它正常工作到i=4330
。其实这是怎么发生的?
供参考:
我做了以下代码来实现这里发生的事情。
public class Action {
private static int i = 1;
private static BufferedWriter bw;
static {
try {
bw = new BufferedWriter(new FileWriter("D:\\sample.txt"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
bw.append(String.valueOf(i)+" ");
try {
i++;
main(args);
} catch (StackOverflowError e) {
bw.append(String.valueOf(i)+" ");
i++;
main(args);
}
}
}
现在上一期不存在。现在i
的值最多为16824744
正确并且进一步运行。我正在跳,这可能会达到i=2,147,483,647
(int的最大值)的值而没有问题。
println()
存在一些问题。下面也有类似的答案。但为什么?
实际原因是什么?
答案 0 :(得分:52)
请注意433943394339
中缺少换行符。它表明在System.out.println()
内发生了错误。
这里的要点是System.out.println()
需要一些堆栈空间才能工作,因此StackOverflowError
会抛出System.out.println()
。
以下是带有标记点的代码:
public static void main(String[] args) {
try{
System.out.println(i); // (1)
i++;
main(args); // (2)
}catch (StackOverflowError e){
System.out.println(i); // (3)
i++;
main(args); // (4)
}
}
让我们假设当i = 4338
时,在递归的N级发生了什么:
4338
。 输出4338\n
i
会增加到4339
4339
,但System.out.println()
在打印换行符之前会抛出StackOverflowError
。 输出4339
StackOverflowError
在级别N + 1处被捕获,语句(3)尝试打印4339
并且由于同样的原因再次失败。 输出4339
4339
并成功(正确打印换行符)。 输出4339\n
i
递增,控制流程再次在(4)在此之后,情况会以4340
重复。
我不确定为什么有些数字在没有换行符的序列之间打印出来,可能与System.out.println()
的内部工作和它使用的缓冲区有关。
答案 1 :(得分:17)
我怀疑正在发生的是:
答案 2 :(得分:4)
根据我的测试:
当try Block抛出Exception时,i
在catch块中具有相同的值(因为它没有因异常而增加)
然后在catch块中抛出相同的异常,并且它再次被catch块捕获!
我尝试过以下代码
try {
System.out.println("Try " + i);
i++;
main(args);
} catch (StackOverflowError e) {
System.out.println("\nBefore");
System.out.println("Catch " + i);
i++;
System.out.println("After");
main(args);
}
输出
Try 28343
Try 28344
Before
Before
Before
Before
Catch 28344
After
Try 28345
Try 28346
Try 28347
Try 28348
Before
Before
Before
Before
Catch 28348
After
Try 28349
当try块抛出异常时,它被catch块捕获但当它再次转到System.out.println("Catch " + i);
异常被抛出4次(在我的eclipse中)没有打印System.out.println("Catch " + i);
与上面的输出一样,我通过打印“之前”文本进行了测试,在打印之前打印了四次System.out.println("Catch " + i);
答案 3 :(得分:3)
如果执行println
(或其中一个方法)导致堆栈溢出,您将从封闭i
的catch子句中打印相同的main
值化身。
确切的行为是不可预测的,因为它取决于仍然可用的堆栈空间。
答案 4 :(得分:1)
正如其他答案已经解释的那样,它与System.out.println有关,需要在堆栈上留出额外空间,从而使自己陷入StackOverflowError。
在这里尝试使用此代码来查看一些不同的行为,这些行为表明在某些时候会有异常被抛出,以至于i ++不再出现。
public class Action {
private static int i = 1;
private static StringBuffer buffer = new StringBuffer();
public static void main(String[] args) {
try {
print();
main(args);
}
catch (StackOverflowError e) {
print();
main(args);
}
}
private static void print() {
buffer.append(i).append("\n");
i++;
if (i % 1000 == 0) {
System.out.println("more: " + buffer);
buffer = new StringBuffer();
}
}
}
要学习的重要内容是:永远不要捕获错误,因为它们确实表明JVM存在严重错误,无法正常处理。
答案 5 :(得分:1)
底线是StackOverflowError
是错误,不是例外。你正在处理一个错误,而不是异常。所以程序在进入catch块时已经崩溃。关于程序的奇怪行为,下面是基于来自this官方doc的java缓冲区的解释:< / p>
例如,autoflush PrintWriter对象会刷新缓冲区 每次调用println或format
System.out.println()
在内部调用缓冲的PrintStream
。您不会从缓冲区中丢失任何数据,它会在填满之后或在您明确调用flush时将所有数据写入输出(在您的情况下为终端)。
回到这个场景,它取决于填充堆栈的内部动态以及能够从main()
中的catch捕获执行的打印语句数量,并且这些字符数被写入缓冲区。在执行第一次尝试之后,即在第一次出现堆栈溢出的情况下,第一个System.out.println()无法打印新行,因此它用剩余的字符刷新缓冲区。
答案 6 :(得分:0)
axtavt答案非常完整,但我想补充一点:
您可能知道堆栈用于存储变量的内存,基于此,当您达到限制时无法创建新变量,System.out.println确实需要一些堆栈资源
787 public void More ...println(Object x) {
788 String s = String.valueOf(x);
789 synchronized (this) {
790 print(s);
791 newLine();
792 }
793 }
然后在调用打印后,错误不允许您甚至调用newLine,它在打印时再次中断。基于此,您可以通过更改代码来确保这种情况:
public class Action {
static int i = 1;
public static void main(String[] args) {
try {
System.out.print(i + "\n");
i++;
main(args);
} catch (StackOverflowError e) {
System.out.print(i + " SO " + "\n");
i++;
main(args);
}
}
}
现在你不会要求堆栈处理新行,你将使用常量“\ n”,你可以在异常打印行中添加一些调试,并且你的输出在同一行中没有多个值: / p>
10553
10553 SO
10553 SO
10554
10554 SO
10554 SO
10555
10556
10557
10558
它会一直被破坏,直到获得一些资源来分配新数据并传递给下一个i值。