如果你看一下字节码
Consumer<String> println = System.out::println;
Run Code Online (Sandbox Code Playgroud)
Java 8更新121生成的字节代码是
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
DUP
INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
POP
INVOKEDYNAMIC accept(Ljava/io/PrintStream;)Ljava/util/function/Consumer; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
// arguments:
(Ljava/lang/Object;)V,
// handle kind 0x5 : INVOKEVIRTUAL
java/io/PrintStream.println(Ljava/lang/String;)V,
(Ljava/lang/String;)V
]
ASTORE 1
Run Code Online (Sandbox Code Playgroud)
getClass()正在调用该方法System.out,结果被忽略.
这是间接空引用检查吗?
当然,如果你跑
PrintStream out = null;
Consumer<String> println = out::println;
Run Code Online (Sandbox Code Playgroud)
这会触发NullPointerException.
这是基于这个问题.考虑这个示例,其中方法Consumer基于lambda表达式返回:
public class TestClass {
public static void main(String[] args) {
MyClass m = new MyClass();
Consumer<String> fn = m.getConsumer();
System.out.println("Just to put a breakpoint");
}
}
class MyClass {
final String foo = "foo";
public Consumer<String> getConsumer() {
return bar -> System.out.println(bar + foo);
}
}
Run Code Online (Sandbox Code Playgroud)
我们知道,在进行函数式编程时引用lambda中的当前状态并不是一个好习惯,原因之一是lambda将捕获封闭的实例,在lambda本身超出范围之前不会进行垃圾收集.
但是,在这个与final字符串相关的特定场景中,似乎编译器可能只是在返回的lambda中包含了constant(final)字符串foo(来自常量池),而不是MyClass在调试时将所有实例括起来,如下所示(放置在的System.out.println).是否与lambdas编译为特殊invokedynamic字节码的方式有关?
我只是试着设置一个变量,告诉我超级构造函数已经完成而没有浪费我的代码.所以我记得如何初始化类变量; 在超级构造函数之后但在类构造函数之前.但是如果你看一下这个例子,有一些奇怪的事情:
public class Init {
public Init() {
System.out.println("Init instance of " + this.getClass().getSimpleName());
System.out.println("syso Init:");
this.syso(false);
}
protected void syso(boolean childCall) {
System.out.println("should not be printed because the method is overwitten");
}
public static class Child extends Init {
private final boolean finalTrueA = true;
private final boolean finalTrueB;
private final boolean finalTrueC;
private boolean nonFinalTrueA = true;
private boolean nonFinalTrueB;
private boolean nonFinalTrueC;
{
this.finalTrueB = true;
this.nonFinalTrueB = true;
}
public Child() {
super();
this.finalTrueC = …Run Code Online (Sandbox Code Playgroud) 我反编译了 Java(实际上是 Dalvik)字节码。在方法的开头,我直接访问实例成员的字段(即不通过 getter)。
似乎 Java 调用Object.getClass()了访问的实例成员 ( mOther),但没有在任何地方使用结果。这是某种检查吗?为什么需要这个电话?我怀疑这是因为我直接访问了一个字段(在该类中定义),但我没有看到连接。
Java代码和反编译后的字节码如下。
(请注意,最后一条指令lifeTime作为常量加载,0x0001因为 in MyOtherClass,我有lifeTime一个public final字段,并且当前是从代码初始化的。)
MyOtherClass other = mOther;
if (mAge >= other.lifeTime) { // lifeTime is initialized to 0x0001
end();
return;
}
.line 53
move-object/from16 v0, p0
iget-object v0, v0, Lcom/example/engine/MyClass1;->mOther:Lcom/example/engine/MyOtherClass;
move-object/from16 v16, v0
.line 54
.local v16, other:Lcom/example/engine/MyOtherClass;
move-object/from16 v0, p0
iget v0, v0, Lcom/example/engine/MyClass1;->mAge:I
move/from16 v18, v0
// Why is Object->getClass() called?
invoke-virtual/range {v16 .. v16}, …Run Code Online (Sandbox Code Playgroud)