您似乎将看似不同的问题作为一个问题提出。这可能是您困惑的主要原因。此外,在处理语言规范时,我们需要在名称方面保持精确,以便最大限度地减少混淆。
静态内容是网站组织中使用的术语。当您引用 静态内容 时,您似乎暗示了 Java 类的静态字段和方法(统称为 静态成员)。让我们使用术语静态成员来清楚。
从以上几点,我们可以断定静态内容属于java.lang.Class实例,但为什么在任何规范的任何地方都没有写清楚?
§15.11 尝试使用表达式Primary . Identifier 指定通过PrimaryName 和标识符 访问静态成员。
在最简单的情况下,规范说:Primary 的类型必须是引用类型 T,否则会发生编译时错误。这意味着静态成员将通过定义成员的类型的 name 来访问。
如果class A { static int b; } 为什么不能通过 A.class.b 访问 b?
出于与上述相同的原因,A.class 不是定义静态字段b 的引用type。类型是A,因此b 只能作为A.b 访问。
作为 Java 程序员,我们要感谢 Java 的设计者,让我们可以像 A.b 一样访问像 A 的 b 这样的静态成员,不是吗?
一个稍长一点的解释必须处理 Java 的反射能力,特别是类 java.lang.Class。 这是 Java 中唯一以类为实例的类。是的,java.lang.Class 的实例是类。因此,java.lang.String 是java.lang.Class 的实例,就像字符串String s = "foo"; 是java.lang.String 类的实例一样。程序:
public class Main {
public static void main(String[] args) {
String s = "foo";
System.out.println(String.class instanceof Class); // statically
System.out.println(Class.class.isInstance(String.class)); // dynamically
System.out.println(s instanceof String); // statically
System.out.println(String.class.isInstance(s)); // dynamically
}
}
打印:
true
true
true
true
一个问题可能仍然存在,那就是A.class 到底是什么? spec defines it 作为类文字:
类字面量计算为由当前实例的类的定义类加载器(第 12.2 节)定义的命名类型(或 void)的 Class 对象。
所以,任何可以在 String.class 上调用的方法(请记住,String.class 是这里的接收者)是在 其类上定义的方法,即类 @987654345 @。因此,您可以像调用str.length() 一样执行String.class.isInstance(str),其中str 是String 的一个实例(而length() 是在String 类上定义的所谓实例方法;实例方法和类方法的区别在这里无关紧要)。
那么如何证明静态变量和方法属于 Class 对象呢?
从上面的 1) 开始,即它们只能通过声明它们的类型名称(即 class 或 interface)访问。这里有一个小皱纹。静态成员也可以通过定义它们的类型的实例来访问,下面的示例可能会让您感到惊讶。这个程序是做什么的:
public class Main {
static int foo = 22;
public static void main(String[] args) {
Main m = null;
System.out.println(m.foo); // ** Don't Do This **
}
}
不,它不会抛出 NullPointerException。通过定义静态成员的类的实例,允许(尽管不鼓励)访问静态成员。由于javac 不像其他人那样理解 Java 语言规范,它会做规范想要做的事情。
但请注意,如果您使用定义类型或该类型的实例,它将获取完全相同的静态字段值并在运行时调用完全相同的静态方法。这足以证明静态成员确实是定义它们的类型(或类)的特征。