【问题标题】:Why static block are executed later?为什么静态块稍后执行?
【发布时间】:2014-03-24 07:11:26
【问题描述】:

附注:

This question has been edited a few times as my previous code doesn't demonstrate the problem. There are some answers which may not make perfect sense against the edited question

我有一个名为Son.java的公共课程

package com.t;

public class Son extends Father {

    static int i;

    static {
        System.out.println("son - static");
        i = 19;
    }

    {
        System.out.println("son - init-block"); 
    }

    public static void main(String[] args) {
        //Son s = new Son();
        int a[] = new int[2];
        System.out.println(a[5]);
    }

}

class Father {

    static {
        System.out.println("f - static");
    }
    {
        System.out.println("f - init-block");
    }
}

当我第一次运行程序时:

输出是:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
    at com.t.Son.main(Son.java:19)
f - static
son - static

然后当我运行这个程序时(输出顺序是random

输出是:

f - static
son - static
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
    at com.t.Son.main(Son.java:19)

我读到static 块在类初始化时执行。

但是为什么这里先出现异常,然后执行静态块呢?

我也在使用Eclipse 来运行我的程序。 谁能解释一下?

【问题讨论】:

  • 在您的情况下,“a”索引的最大值为 1。因为数组的索引从 0 开始。
  • 您确实应该提供一个完整的示例,其他人可以尝试重现该问题。
  • main方法属于哪个类?
  • @Thinker:我们甚至还没有一个场景。你还没有提供一个完整的程序。我们甚至不知道什么类包含main!请编辑您的问题,以便我们可以简单地将一段代码复制到文本编辑器中,编译并运行。听起来你已经得到了,但出于某种原因,你决定从你的问题中删掉其中的一部分:(
  • 好吧,我认为标准输出和标准错误的缓冲发生在操作系统级别,而不是在 Java 中。所以这使得这是一个关于操作系统的问题,而不是 Java 问题。这可能归结为在您运行代码时操作系统正在执行的其他操作。根据我在 07:29:23Z 的评论,这不是由 Java 定义的。

标签: java static


【解决方案1】:

异常不会首先发生,您只是首先看到异常的打印输出。

如果首先发生异常,您将永远看不到输出的其余部分。

原因是您在程序中同时输出了System.err(来自您的异常)和System.out。这些打印到屏幕上的顺序没有定义,因此您可以按不同的顺序获取它们。

【讨论】:

  • 确实如此。异常打印到标准错误,而静态块打印到标准输出。它以“随机”顺序输出,因为它取决于运行该事物的任何事物来决定哪个实际首先击中控制台。
【解决方案2】:

未捕获异常的堆栈跟踪打印在System.err 中,这是一个无缓冲的流。您将文本打印到System.out,这是一个缓冲流,并且无法预测缓冲区是否在堆栈跟踪打印之前或之后被刷新。

如果你把你所有的打印语句都改成System.err,那么输出的顺序就会变成打印的顺序,而且总是一样的顺序。

【讨论】:

    【解决方案3】:

    @Keppil 的回答已经确定了。


    我只是想指出一些事情......呃......有趣。

    OP 是这样说的:

    我正在使用 Eclipse 运行我的程序。

    下意识的反应是说“那不相关”……但在这种情况下,我认为它>>是放大,因为输出将发送到 Eclipse“控制台”面板。

    当应用程序从命令行运行时,输出到 stderr 和 stdout 可能会合并到操作系统内核中某处的输出流中。如果没有,控制台程序可能会使用select 系统调用来处理来自两个源的输入......并赋予一个流优先于另一个,因为这是编码它的简单方法。因此,您会期望输出以基本一致的顺序出现在控制台上,即使顺序是不确定的。

    但是当应用程序写入 Eclipse 控制台时,Eclipse 可能使用单独的线程来读取每个流。假设两个线程都在read 系统调用中被阻塞,并且输入在两个流上大致同时到达,则由线程调度程序决定哪个线程首先被唤醒。与 select ... 或内核中的流合并的行为相比,这将是不可预测的。

    无论哪种方式,我的观察是重新排序的 stdout / stderr 输出在 Eclipse 控制台中比在使用“本机”控制台时更为普遍。

    【讨论】:

      【解决方案4】:

      正如提问者所说,main 属于Son 类并且正在扩展Father。稍微修改了一下代码,就可以编译了。

          class Father {
          static{
              System.out.println("f - static");
      
          }
      }
      
      public class Son extends Father {
          static {
              System.out.println("son - static");
          }
      
      public static void main(String[] args) throws ArrayIndexOutOfBoundsException{
              int a[] = new int[2];
              System.out.println(a[3]);
          }
      }
      

      输出是:-

      f - static
      son - static
      Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
          at kanwal.Son.main(Son.java:20)
      

      它完全按照它应该的方式工作。

      编辑:- 这个答案是在 OP 编辑​​问题之前做出的。

      【讨论】:

      • 您的回答没有解释为什么输出有时是一个顺序,有时是另一个顺序......这是 OP 真正询问的内容。跨度>
      • 答案与OP的第三次编辑一致。当前版本的问题不是 OP 开始的。它作为问题顶部的注释提及。
      • 嗯,它肯定不会像现在这样回答问题。所以到目前为止,反对票是有效的。 (随时更新您的答案......或删除它以从 2 次反对票中取回 -4。您当然会失去剩余的 +6,但这是您的选择。)
      • 我会顺其自然,看看这个答案能获得多少反对票,即我对这个问题的回答有多任性。(尽管 OP 已经明确提到它)。这个问题的答案(截至目前)不在我的知识范围内。因此,我没有编辑它。
      猜你喜欢
      • 2018-08-25
      • 1970-01-01
      • 2014-08-01
      • 1970-01-01
      • 2022-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多