【问题标题】:Returning in a static initializer返回静态初始化器
【发布时间】:2011-02-06 02:51:19
【问题描述】:

这不是有效的代码:

public class MyClass
{
    private static boolean yesNo = false;

    static
    {
        if (yesNo)
        {
            System.out.println("Yes");
            return; // The return statement is the problem
        }
        System.exit(0);
    }
}

这是一个愚蠢的例子,但在静态类构造函数中我们不能return;。 为什么?这有充分的理由吗?有人知道更多吗?

所以我应该做return的原因是要在那里结束构建。

谢谢

【问题讨论】:

  • 这些被称为静态初始化器,而不是静态构造器。写这篇文章是为了帮助搜索。

标签: java return static-constructor class-constructors


【解决方案1】:

你应该返回什么?在静态初始化程序中没有调用者,所以就我看来,返回没有意义。首次加载类时会执行静态初始化程序。

【讨论】:

    【解决方案2】:
    • 程序流总是可以被构造成不需要return。 (在您的示例中,将 System.exit(0) 放在 else 子句中会达到预期的效果)

    • 如果你真的需要它,你可以将代码移动到一个静态方法中,然后从初始化器中调用它:

    .

    static {
        staticInit();
    }
    
    private static void staticInit() {
        if (yesNo) {
            System.out.println("Yes");
            return;
        }
        System.exit(0);
    }
    

    请注意,这不是一个静态构造函数,它是一个静态初始化器。什么都没有构建。

    【讨论】:

    • 我知道你可以用一个简单的else 来解决它。但我说这是一个非常愚蠢的例子。但这是真的!
    • @Martijn Courteaux 是的,我明白这一点。这就是为什么它在括号中,只是我概括的一个例子。
    【解决方案3】:

    来自JSL regarding static initializers

    “静态初始化程序能够突然完成(第 14.1 节,第 15.6 节)并带有检查异常(第 11.2 节)是编译时错误。如果静态初始化程序无法完成,这是编译时错误通常(第 14.21 节)。”

    Abrupt completion(以及其他):“无值返回”、“返回给定值”等。

    因此,静态初始化程序中的 return 语句是“突然完成”并产生编译时错误。

    【讨论】:

    • 不过,JSL 没有说明原因,这就是这个问题的意义所在。
    • 我正要发布同样的内容,但我试图思考:为什么静态初始化程序的“突然完成”如此糟糕以至于他们将其变成编译错误?
    • @Oak:你是对的,没有理由。规范有时就像公理。
    【解决方案4】:

    我认为原因是初始化器与字段初始化一起携带(在实例初始化器的情况下与构造器一起使用)。换句话说,JVM 只识别一个地方来初始化静态字段,因此所有的初始化——无论是否在块中——都必须在那里完成。

    因此,例如,当您编写一个类时:

    class A {
        static int x = 3;
        static {
            y = x * x;
        }
        static int z = x * x;
    }
    

    那么实际上就好像你写了:

    class A {
        static int x, y, z;
        static {
            x = 3;
            y = x * x;
            z = x * x;
        }
    }
    

    如果你看反汇编就可以确认这一点:

    static {};
      Code:
       0:   iconst_3
       1:   putstatic       #5; //Field x:I
       4:   getstatic       #5; //Field x:I
       7:   getstatic       #5; //Field x:I
       10:  imul
       11:  putstatic       #3; //Field y:I
       14:  getstatic       #5; //Field x:I
       17:  getstatic       #5; //Field x:I
       20:  imul
       21:  putstatic       #6; //Field z:I
       24:  return
    

    因此,如果您在静态初始化程序中间的某处添加“返回”,它也会阻止计算 z。

    【讨论】:

    • 我正要写这个的时候看到这个。另请注意,一个类中可以有多个静态初始化程序块。
    • 非常好的答案,奥克。它还说明了为什么在静态初始化程序中认为突然完成如此糟糕(正如 Joe 在评论中指出的那样)以产生编译时错误。
    【解决方案5】:

    我知道静态初始化器的规则是它们只执行一次,在加载类字节码之后和执行任何静态方法或从类中实例化第一个对象之前。 JLS 保证此初始化将已完成。为了确保这个保证是真实的,JLS 还指定代码不能被突然终止(正如另一个答案中明确给出的那样)。

    请注意,可以在不初始化的情况下加载字节码;请参阅Class.forName(String, boolean, ClassLoader) 方法。如果boolean 参数是false,那么这将加载类但不会初始化它。程序员仍然可以进行一些反思以发现有关该类的信息,而无需对其进行初始化。但是,一旦您尝试通过调用静态方法或实例化实例来直接使用该类,那么 JVM 将首先进行初始化。

    如果任何静态初始化程序突然终止 - RuntimeException 可能会发生这种情况,则该类将处于无效状态。第一次,JVM 会抛出一个ExceptionInInitializeError(注意这是一个Error,这意味着它被认为是内部故障)。从那时起,将无法使用该类 - 尝试调用静态方法或实例化对象,您将获得 NoClassDefFoundError

    在不重新启动 JVM 的情况下从这种情况中恢复的唯一方法是,如果您使用 ClassLoaders 并且可以用失败的类替换类加载器并在不同的环境中重建类或重新初始化程序(可能是不同的系统属性) ,但程序必须为这种情况做好充分准备。

    【讨论】:

      【解决方案6】:

      我会重新排序语句,使其更简单/更短。永远不会有 if/else 的两个分支都需要返回的情况。

      static { 
          if (!yesNo) 
             System.exit(0); // silently exiting a program is a bad idea!"
          System.out.println("Yes"); 
      } 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-04-16
        • 1970-01-01
        • 1970-01-01
        • 2014-10-06
        • 1970-01-01
        • 1970-01-01
        • 2010-12-11
        • 2011-07-18
        相关资源
        最近更新 更多