[英]Why is getClass() called when we create an object for Inner class?
我正在研究内部类的工作原理及其字节码,我正在跟踪堆栈,但不明白为什么调用 getClass()?
我发现了一个关于 Lambda 函数的类似问题,但无法理解。
我确实试图理解没有空检查是必需的,在 JDK 8 之后它被一个名为 requiredNoNull 的静态函数所取代。
代码 :
class Outer{
class Inner{
}
public static void main(String args[]){
Outer.Inner obj = new Outer().new Inner();
}
}
字节码:
public static void main(java.lang.String[]);
Code:
0: new #2 // class Outer$Inner
3: dup
4: new #3 // class Outer
7: dup
8: invokespecial #4 // Method "<init>":()V
11: dup
12: invokevirtual #5 // Method java/lang/Object.getClass:()Ljava/lang/Class;
15: pop
16: invokespecial #6 // Method Outer$Inner."<init>":(LOuter;)V
19: astore_1
这是一个伪装的空检查,仅此而已。 不过,在这种特殊情况下,它并不是真正需要的,并且未来的javac
进行一些优化 - 请看下面的示例。
可能这会更好地解释这个问题(使用 java-12,这里getClass
hack 已被Objects::requireNonNull
取代):
public class Outer {
class Inner {
}
public void left() {
Outer.Inner inner = new Outer().new Inner();
}
public void right(Outer outer) {
Outer.Inner inner = outer.new Inner();
}
}
left
方法将编译为不使用Objects::requireNonNull
东西(您可以自己查看字节码),因为Outer
的创建就地发生并且编译器可以肯定地告诉new Outer()
实例不是null
。
另一方面,您将Outer
作为参数传递,编译器无法确定传递的实例不为 null,因此Objects::requireNonNull
将出现在字节码中。
据我了解, @Eugene 的回答是绝对正确的。 我决定用简单的话添加一个解释。 希望它会帮助某人。
答: JDK8 中的编译器使用对Object.getClass
调用来在必要时生成 NullPointerExceptions。 在您的示例中,此检查是不必要的,因为new Outer()
不能为 null,但编译器不够聪明,无法确定它。
在 JDK 的更高版本中,空检查被更改为使用更具可读性的Objects.requireNotNull
。 编译器也得到了改进以优化多余的空检查。
解释:
考虑这样的代码:
class Outer{
class Inner{
}
public static void main(String args[]){
Outer.Inner obj = ((Outer) null).new Inner();
}
}
这段代码抛出了一个 NullPointerException 异常,这是应该的。
问题是,NPE 仅从 Java 的角度来看是合乎逻辑的。 字节码级别不存在构造函数。 编译器生成的字节码或多或少相当于以下伪代码:
class Outer {
public static void main(String[] args) {
Outer tmp = (Outer) null;
Outer$Inner obj = new; //object created
obj."<init>"(tmp);
}
}
class Outer$Inner {
//generated field
private final Outer outer;
//generated initializer
void "<init>"(Outer outer) {
this.outer = outer;
}
}
如您所见,构造函数被替换为一个方法。 并且该方法本身不会检查它的参数是否为 null,因此不会抛出异常。
因此,编译器必须添加额外的空检查以生成NullPointerException
。 在 Java 8 之前,实现这一目标的快速而肮脏的方法是发出对getClass
的调用:
Outer tmp = (Outer) null;
tmp.getClass(); //generates an NPE
你怎么能检查这确实是原因:
Outer
类。Outer.class
删除对Object.getClass
的调用。作为Outer类中存在的Inner类的定义,JVM需要首先加载Outer类。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.