17
public class Main {

    public static void main(String[] args) {
        System.out.println(B.x);
    }

}
class A {
    public static String x = "x";
}
class B extends A {
    static {
        System.out.print("Inside B.");
    }
}

问题:为什么输出会是:x. 但不是:Inside B.x

4

5 回答 5

10

引用B.x发出以下字节码:

getstatic       #3   <Field int B.x>

根据Java 虚拟机规范

Java 虚拟机指令 anewarray、checkcast、getfield、 getstatic、instanceof、invokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual、ldc、ldc_w、multianewarray、new、putfield 和 putstatic对运行时常量池进行符号引用。执行任何这些指令都需要 解析其符号引用

因此 JVM 应该将符号引用B.x解析为. 字段分辨率是这样指定的

要将未解析的符号引用从 D 解析到类或接口 C 中的字段,必须首先解析由字段引用给出的对 C 的符号引用(第 5.4.3.1 节)。

...

解析字段引用时,字段解析首先尝试在 C 及其超类中查找引用的字段:

如果 C 用字段引用指定的名称和描述符声明一个字段,则字段查找成功。声明的字段是字段查找的结果。

否则,字段查找将递归地应用于指定类或接口 C 的直接超接口。

否则,如果 C 具有超类 S,则将字段查找递归应用于 S。

否则,字段查找将失败。

换句话说,JVM 将解析B.xA.x. 这就是为什么只A需要加载类。

于 2012-11-20T14:27:15.903 回答
7

因为B.x实际上是A.x这样,所以只A需要加载类。

于 2012-11-20T14:22:40.043 回答
4

Java 语言规范,Java SE 7 版的第 12.4 节“类和接口的初始化”规定:

类的初始化包括执行其static初始化程序和类中声明的静态字段(类变量)的初始化程序。

[…]

static对字段的引用(第 8.3.1.1 节)只会初始化实际声明它的类或接口,即使它可能通过子类、子接口或实现接口的类的名称来引用。

So although — contrary to claims in some of the answers above — class B does have to be loaded, in order to determine that B.x is declared in A, class B is not initialized (i.e., its static initializers are not actually run) until you do something more specific to B.

于 2012-11-20T16:45:51.603 回答
2

Class B扩展A,其中有一个public static variable x您在调用时正在访问的B.x

如果您期望Inside B.输出,则必须创建该类的对象。执行所有静态代码块。或将该静态代码块移至类A:-)

当 JVM 加载类时,它将所有静态块分组并按照它们声明的顺序执行它们。

编辑来源):简短的回答是静态在 Java 中不是继承的。相反,类中声明的静态成员(受“访问”限制)在派生类的命名空间中直接可见,除非它们被派生类中的声明“隐藏”。

因此,如果静态只属于类,为什么它会渗透到派生类?它不应该只保留在定义它的类中吗?

于 2012-11-20T14:21:51.007 回答
2

它实际上不需要加载B,直到它直接访问静态成员B。请注意,此代码:

public class TestMain {
    public static void main(String[] args) {
        System.out.println(B.x);
        System.out.println(B.y);
    }

    static class A {
        public static String x = "x";
    }

    static class B extends A {
        public static String y = "y";
        static {
            System.out.print("Inside B.");
        }
    }
}

将输出:

x
Inside B.y

因为在访问B某些内容之前不需要加载。B

这是关于这个主题的一个很好的链接。来自文章,“别忘了,这段代码将在 JVM 加载类时执行。JVM 将所有这些块组合成一个静态块然后执行。这里有几点我想提一下:”

于 2012-11-20T14:33:36.683 回答