【问题标题】:Behavior of static blocks with inheritance具有继承的静态块的行为
【发布时间】:2012-05-28 17:40:39
【问题描述】:

我正在尝试使用这样的静态块:

我有一个名为Base.java的基类

public class Base {

    static public int myVar;

}

还有一个派生类Derived.java

public class Derived extends Base {

    static
    {
        Base.myVar = 10;
    }
}

我的main函数是这样的:

public static void main(String[] args)  {
    System.out.println(Derived.myVar);
    System.out.println(Base.myVar);
}

这会将输出打印为0 0,正如我预期的10 0。有人可以解释这种行为吗?另外,如果我希望我的派生类设置静态变量的值,我该如何实现?

【问题讨论】:

  • 因为类没有初始化,所以静态块没有初始化。但是您取消注释(new Derived() )它将打印 10,10,但它永远不会打印 10,0

标签: java inheritance static static-block


【解决方案1】:

据我了解。您不调用任何Derived 属性(myVar 属于Base,而不是Derived)。并且 java 没有从Derived 运行静态块。如果你给Derived添加一些静态字段并访问它,那么java会执行所有的静态块。

class Base {

    static public int myVar;

}


class Derived extends Base {

    static public int myVar2;

    static
    {
        Base.myVar = 10;
    }
}


public class Main {
    public static void main( String[] args ) throws Exception {
        System.out.println(Derived.myVar2);
        System.out.println(Base.myVar);
    }
}

根据 java 规范,当类被初始化(并且静态块被执行)时:

12.4.1 初始化发生时 类或接口类型 T 将在以下任何一项第一次出现之前立即初始化:

• T 是一个类,并创建了一个 T 的实例。
• T 是一个类,并且调用了 T 声明的静态方法。
• 分配了一个由 T 声明的静态字段。
• 使用了由 T 声明的静态字段,并且该字段不是常量变量(第 4.12.4 节)。
• T 是一个顶级类(第 7.6 节),并且执行在词法上嵌套在 T(第 8.1.3 节)中的断言语句(第 14.10 节)。

【讨论】:

  • @Jigar 但您没有访问j
  • 所以我绝对不能强制使用可以通过继承类来静态设置的静态变量?
【解决方案2】:

在类初始化之前,静态初始化程序块不会运行。请参阅Java Language Specification 第 8.7 段(静态初始化程序)和 12.4.1(初始化发生时):

在类中声明的静态初始化器在类被执行时执行 初始化(第 12.4.2 节)。与类的任何字段初始值设定项一起 变量(第 8.3.2 节),静态初始化器可用于初始化 类的类变量。

这是一个直接来自 JLS 12.4.1 的类似示例:

class Super {
  static int taxi = 1729;
}
class Sub extends Super {
  static { System.out.print("Sub "); }
}
class Test {
  public static void main(String[] args) {
    System.out.println(Sub.taxi);
  }
}

此程序仅打印:

1729

因为 Sub 类永远不会被初始化;对 Sub.taxi 的引用 是对在 Super 类中实际声明的字段的引用,并且确实 不触发 Sub 类的初始化。

【讨论】:

  • @NikitaBeloglazov:在您的示例中,静态块有效,因为顶级类 Base 在引用 Derived 时被初始化。详见JLS《12.4.1 When Initialization Occurs》,初始化过程相当复杂:A reference to a static field (§8.3.1.1) causes initialization of only the class or interface that actually declares it, even though it might be referred to through the name of a subclass, a subinterface, or a class that implements an interface.
  • 是的,你是对的。但是您的示例可能会令人困惑,因为它看起来仅在创建类实例时才初始化类。
  • @NikitaBeloglazov:是的,我最好把我自己的例子删掉,只参考 JLS 中的例子,以免混淆。
  • 那么我必须调用new来初始化子类?
【解决方案3】:

myVar 有一个副本,父类和子类将共享相同的副本。 直到和除非子类被初始化。

【讨论】:

    【解决方案4】:

    当我们这样做时

    class Base {
    
        public static int myVar = 0;
        static {
            System.out.println("Base");
        }
    }
    
    class Derived extends Base {
    
        static {
            System.out.println("Derived");
            Base.myVar = 9;
    
        }
    }
    
    public class StaticBlock {
    
        public static void main(String[] args) {
    
            System.out.println(Base.myVar);
            System.out.println(Derived.myVar);
        }
    }
    

    输出将是 Base 0 0

    这意味着派生类的静态块没有执行..!!

    【讨论】:

    • 请检查输出。它打印:Base Derived 9 9
    【解决方案5】:

    这里是 Java 规范的链接——第 8.7 节讨论了静态初始化器。它提供了有关它们应该如何运行以及它们被调用的顺序的详细信息。 http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.7

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-03
      • 2022-08-20
      相关资源
      最近更新 更多