使用案例
匿名内部类使用
java
public class AnonymousInnerClassTest {
public static void main(String[] args) {
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("Hello");
}
}
可以看到编译器帮助生成了一个 class 文件,这个 class 文件的内容和我们自己定义一个类没有任何区别,如下图所示: 

Lambda表达式使用
java
public class LambdaTest {
public static void main(String[] args) {
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("Hello");
}
}
可以看到编译器并未帮助生成 class 文件: 
原理解读
匿名内部类原理
匿名内部类本质上是一个语法糖。当代码中的某个类只在一个地方使用,不需要复用的时候,这个时候就可以通过匿名内部类语法来简化使用。它本质上和通过 Java 类文件定义没有区别,只是编译器在编译的时候自动帮你生成了 class 文件。
上面案例中的代码等价如下:
java
public static void main(String[] args) {
Consumer<String> consumer = new AnonymousInnerClassTest$1();
consumer.accept("Hello");
}
可以通过查看字节码文件来证实 
Lambda表达式原理
通过查看字节码文件可以看到,编译器会生成一个 lambda$main$0() 的私有静态方法,它的方法执行体就是 lambda 表达式的内容。如下图所示: 
等价的代码如下:
java
public class LambdaTest {
public static void main(String[] args) {
// Consumer<String> consumer = s -> System.out.println(s);
// 这里被替换为了invokedynamic字节码的调用
invokedynamic #7, 0
consumer.accept("Hello");
}
// 这个方法是编译器生成的
private static void lambda$main$0(String s) {
System.out.println(s);
}
}
当 main() 方法中第一次执行到 invokedynamic 指令时,JVM 会找到 invokedynamic 指令关联的引导方法,即 LambdaMetafactory.metafactory()。如下图所示: 

然后把 invokedynamic 指令后面的信息,连同对编译器生成的静态方法 lambda$main$0 的引用(通过一个叫MethodHandle的对象),一起传递给 metafactory()方法。LambdaMetafactory 接收到这些信息后,会像一个代码生成器一样,在内存中即时生成一个新类的字节码,等价的代码逻辑如下:
java
final class LambdaTest$$Lambda$1 implements java.util.function.Consumer {
// 对于无状态Lambda,可以是一个单例
private static final LambdaTest$$Lambda$1 INSTANCE = new LambdaTest$$Lambda$1();
private LambdaTest$$Lambda$1() {}
@Override
public void accept(Object s) {
LambdaTest.lambda$main$0((String) s);
}
}
metafactory() 方法并不直接返回这个类的实例,而是返回一个 CallSite 对象。这个 CallSite 对象内部持有一个「工厂」,这个工厂知道如何创建上面那个动态生成的 LambdaTest$$Lambda$1 类的实例。JVM通过 CallSite 返回的工厂,创建 LambdaTest$$Lambda$1 的一个实例。源码如下:
java
public static CallSite metafactory(MethodHandles.Lookup caller, String interfaceMethodName, MethodType factoryType,
MethodType interfaceMethodType, MethodHandle implementation, MethodType dynamicMethodType) throws LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
mf = new InnerClassLambdaMetafactory(Objects.requireNonNull(caller),
Objects.requireNonNull(factoryType),
Objects.requireNonNull(interfaceMethodName),
Objects.requireNonNull(interfaceMethodType),
Objects.requireNonNull(implementation),
Objects.requireNonNull(dynamicMethodType),
false,
EMPTY_CLASS_ARRAY,
EMPTY_MT_ARRAY);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}
java
abstract public class CallSite {
public abstract MethodHandle getTarget();
}
当执行 consumer.accept("Hello") 时,字节码是 invokeinterface,这就像一个普通的多态方法调用,会调用到我们动态生成的 LambdaTest$$Lambda$1 类的accept方法。如下图所示: 
上述过程的代码等价于下面的代码:
java
import java.lang.invoke.*;
import java.util.function.Consumer;
public class ManualLambdaFactory {
// 定义一个私有静态方法,模拟JVM自动生成的静态方法
private static void printString(String s) {
System.out.println(s);
}
public static void main(String[] args) throws Throwable {
// LambdaMetafactory.metafactory()方法有6个参数
// 这是 metafactory 的第1个参数
MethodHandles.Lookup caller = MethodHandles.lookup();
// 参数2: invokedName - 要实现的接口方法名
String invokedName = "accept";
// 参数3: invokedType - 工厂本身的类型
MethodType invokedType = MethodType.methodType(Consumer.class);
// 参数4: samMethodType - 函数式接口中抽象方法的签名
MethodType samMethodType = MethodType.methodType(void.class, Object.class);
// 参数5: implMethod - 指向我们实现体(printString)的方法句柄(MethodHandle,有点类似于C++中的方法指针)
MethodHandle implMethod = caller.findStatic(
ManualLambdaFactory.class, // 在哪个类里找
"printString", // 方法名叫什么
MethodType.methodType(void.class, String.class)
);
// 参数6: instantiatedMethodType - 实现方法的实际签名
MethodType instantiatedMethodType = MethodType.methodType(void.class, String.class);
// 核心步骤:手动调用 metafactory,模拟 invokedynamic 指令的行为
CallSite callSite = LambdaMetafactory.metafactory(
caller,
invokedName,
invokedType,
samMethodType,
implMethod,
instantiatedMethodType
);
// 调用这个工厂,可以得到Consumer实例
MethodHandle factory = callSite.getTarget();
// 调用工厂,创建 Consumer 实例
// 这里需要一个强制类型转换,因为 MethodHandle 返回的是 Object
Consumer<String> consumer = (Consumer<String>) factory.invokeExact();
consumer.accept("Hello, World!");
}
}
effectively final 变量
假设把上面的匿名内部类的代码修改为如下,这个时候 Idea 就会有提示说变量 a 必须是 final 或者 effectively final 的。也就是说在匿名内部类后面的代码不能再对变量 a 的值进行修改了。如下图所示:
java
public class AnonymousInnerClassTest {
public static void main(String[] args) {
String a = "hello";
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(a);
}
};
a = "nihao";
consumer.accept("Hello");
}
}

通过查看生成的匿名内部类的 class 文件可以看到,匿名内部类中引用的外部变量实际上是通过匿名内部类的构造函数中传递进去的,而 Java 中方法的参数传递都是值传递。假设在匿名内部类后面的代码修改了变量 a 的值,那么匿名内部类中是无法感知到这个值的变化的,那就可能造成两边的值是不一样的。 