故事的起源,是一段代码:
public static void main(String[] args) {
I i = C::n;
I i2 = C::n;
I i3 = C::n;
System.out.println(i.getClass());
System.out.println(i2.getClass());
System.out.println(i3.getClass());
}
@FunctionalInterface
public interface I {
void m();
}
public static class C {
static void n() {
System.out.println("hello");
}
}
在JDK1.8之后,java终于也有了自己的一套用起来比较简洁方便的函数式编程的方式,其中lambda表达式就是其中比较关键的一步。
正常来说,java会预先编译好我们的java文件,将用到的class按需load到内存中。
不过随着时代的发展,各种第三方知名框架或类库,许多核心的实现更依赖于后续程序运行期间动态生成的一些class。
我们知道,在java中,真正可以干活的一定需要是某个类,而不是接口。在JDK8中诞生的lambda的运行背后也是如此,它也是会在声明了一个表达式之后,默默的动态生成了某个内部类。
你可以尝试分析一下,这段代码的最后打印的class会是几个呢?
运行结果:
class com.peng.run_time_data_area.T05_InvokeDynamic$$Lambda$1/1078694789
class com.peng.run_time_data_area.T05_InvokeDynamic$$Lambda$2/1023892928
class com.peng.run_time_data_area.T05_InvokeDynamic$$Lambda$3/558638686
是3个。 通过这段输出结果,我们知道了,每次lambda的声明,确实都会动态新产生一个Lambda的内部类。
这时候你回想一下你之前如果用过lambda表达式,有没有这种操作:就是在一个for循环里面进行声明了一个lambda表达式。 我觉得这种操作一般用lambda的人,都可能会有。
就是类似这种写法:
for (int j = 0; j < 5; j++) {
I theLambda = C::n;
System.out.println(theLambda.getClass());
}
按照我们刚才的理论,每次声明都会动态产生一个内部类,那我如果在一个for循环里声明,那岂不是会生成n个内部对象,而其实它们的作用是一样的,我是不是应该声明到外面去呢?
以前我也是这么想的,但一次偶然的机会,我发现和我想的不太一样。
看上面代码输出结果:
class com.peng.jvm.c4_RuntimeDataAreaAndInstructionSet.T05_InvokeDynamic$$Lambda$4/1149319664
class com.peng.jvm.c4_RuntimeDataAreaAndInstructionSet.T05_InvokeDynamic$$Lambda$4/1149319664
class com.peng.jvm.c4_RuntimeDataAreaAndInstructionSet.T05_InvokeDynamic$$Lambda$4/1149319664
class com.peng.jvm.c4_RuntimeDataAreaAndInstructionSet.T05_InvokeDynamic$$Lambda$4/1149319664
class com.peng.jvm.c4_RuntimeDataAreaAndInstructionSet.T05_InvokeDynamic$$Lambda$4/1149319664
呀? 奇怪,怎么都是一个对象呢。
这个lambda并没有像我想象的一样,产生了多个对象,而是这些循环都使用的是一个lambda的对象。
我猜测这大概是JDK for循环中对lambda做出的优化。
觉得很有意思,特此记录一下。