lambda函数序列化

@FunctionalInterface

public interface MyFunctionextends Function,Serializable{

}


public static void main(String[] args)throws IOException, ClassNotFoundException {

//第一种

    String dd =map((Function&Serializable) T::aa, "dd");

    System.out.println("result~~~~~~~"+dd);

    //第二种 在方法指定是序列化函数

    String yy =map1(T::aa, "dd");

    System.out.println("result~~~~~~~"+yy);

}

public static Stringaa(String pa){

System.out.println(pa);

    return pa +"~~~~~";

}

static  <U> U map(Function<String, ?extends U> function,String a)throws IOException, ClassNotFoundException {

System.out.println(function);

    serialize(function, new FileOutputStream("lambda.obj"));

    Function func = (Function)deserialize(new FileInputStream("lambda.obj"));

    U apply = func.apply(a);

    System.out.println(apply);

    return apply;

}

static U map1(MyFunction function,String a)throws IOException, ClassNotFoundException {

System.out.println(function);

    serialize(function, new FileOutputStream("lambda.obj"));

    Function func = (Function)deserialize(new FileInputStream("lambda.obj"));

    U apply = func.apply(a);

    System.out.println(apply);

    return apply;

}

public static void serialize(Object obj, OutputStream out)throws IOException {

ObjectOutputStream objOut =new ObjectOutputStream(out);

    objOut.writeObject(obj);

    out.flush();

    out.close();

}

public static Objectdeserialize(InputStream in)throws IOException, ClassNotFoundException {

ObjectInputStream objIn =new ObjectInputStream(in);

    Object result = objIn.readObject();

    objIn.close();

    return result;

}

2. lambda序列化

如果我们使用这个命令运行TestLambda:java -cp . -Djdk.internal.lambda.dumpProxyClasses=. TestLambda

程序运行完成后,会在当前目录生成两个class文件,TestLambda\$$Lambda$1.class和TestLambda\$$Lambda$4.class。

writeReplace方法的作用是在该类实例序列化时,并不会真正序列化该类的对象,而是将writeReplace方法返回的对象进行序列化。所以,lambda对象序列化的时候,真正序列化的对象是SerializedLambda对象。

该SerializedLambda对象记录如下信息:

        (1).lambda定义所在的Class对象                                                               

        (2).lambda实现的函数式接口的全限定名

        (3).lambda实现的函数式接口的方法名

        (4).lambda实现的函数式接口的方法签名

        (5).implMethodKind(这个含义我也没弄明白)

        (6).lambda函数的真正实现方法所在的类名,和(1)中的Class对象相同,这里是类的全限定名

        (7).lambda函数的真正实现方法的方法名

        (8).lambda函数的真正实现方法的方法签名

        (9).替换类型参数的函数式接口方法签名

        (10).捕获的参数

最终,这些信息,也就是上面代码中new SerializedLambda对象传进去的参数,才是被序列化记录的信息,这些信息都是在用于lambda的反序列化。

3.lambda反序列化

既然lambda序列化时记录的是SerializedLambda对象,那么我们来看SerializedLambda对象是怎么最终变成lambda对象的。

在SerializedLambda类的源码中,我们可以看到有一个方法readResolve,这个方法的作用是在该类对象被反序列化时,将调用readResolve方法,该方法的返回对象才是反序列化返回的对象。

从代码中可以看出,反序列化SerializedLambda对象时,readResolve方法将会从capturingClass中寻找名为"$deserializeLambda$"的方法。由上一节可知,capturingClass就是定义lambda对象所在的类对象,而$deserializeLambda$方法也是编译器生成的隐藏方法,通过 javap -s -p -v -c -l TestLambda.class 查看字节码,可以看到确实存在方法$deserializeLambda$。

由于这个方法字节码比较长,就不全部贴出来分析了,在这个方法中,会对上一节序列化记录的信息进行校验,校验正确,程序会调用invokeDynamic指令生成代表lambda的内部类,并且创建该内部类的对象,然后返回作为反序列化的最终对象。

简单来说,lambda对象的反序列化,就是先反序列化出SerializedLambda对象,然后调用readResolve方法生成真正的lambda对象。

4.总结

(1).编译器会在定义lambda函数的类中,生成静态方法$deserializeLambda$(java.lang.invoke.SerializedLambda),该方法知晓由SerializedLambda对象生成lambda对象的具体细节。

(2).lambda对象运行时生成的类,它有一个writeReplace方法,该方法将lambda的信息记录在一个SerializedLambda对象中,然后将这个SerializedLambda对象序列化。它记录了定义lambda所在的类,lambda实现的函数式接口等。

(3).SerializedLambda对象定义的readResolve方法,就是用于反序列化生成lambda对象的。

(4).lambda对象序列化整个流程:

lambda对象 --> writeReplace方法 --> SerializedLambda对象 --> 序列化的字节 --> SerializedLambda对象 --> readResolve方法 --> $deserializeLambda$方法 --> lambda对象。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容