是否可以在没有外部类的情况下序列化匿名类?

Ada*_*ker 9 java serialization rmi anonymous-class

我在网上进行了一项小型研究并审查了该网站上的相关主题,但答案是矛盾的:有些人说这是不可能的,有些人说这是可能的,但很危险.

目标是将匿名类的对象作为RMI方法的参数传递.由于RMI要求,此类必须是可序列化的.这没问题,很容易使类Serializable.

但我们知道内部类的实例包含对外部类的引用(而匿名类是内部类).因此,当我们序列化内部类的实例时,外部类的实例被序列化以及字段.这里是问题出现的地方:外部类不可序列化,更重要的是 - 我不想序列化它.我想要做的只是发送匿名类的实例.

简单示例 - 这是一个RMI服务,其方法接受Runnable:

public interface RPCService {    
    Object call(SerializableRunnable runnable);
}
Run Code Online (Sandbox Code Playgroud)

以下是我想要调用该方法的方法

void call() {
     myRpcService.call(new SerializableRunnable() {             
         @Override
         public Object run {
             System.out.println("It worked!");
         }
     }        
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我想要做的是向另一方发送"动作" - 系统A描述应该在系统B上运行的代码.这就像用Java发送脚本一样.

如果可能的话,我可以很容易地看到一些危险的后果:例如,如果我们从Runnable访问字段或捕获外部类的最终变量 - 我们将遇到麻烦,因为调用者实例不存在.另一方面,如果我在Runnable中使用安全代码(编译器可以检查它),那么我没有看到禁止此操作的原因.

因此,如果有人知道,如何在匿名类中正确覆盖方法writeObject()readObject()方法,或者如何引用外部类transient或者解释为什么在java中不可能,它将非常有用.

UPD 另一个需要考虑的重要事项:外部类不存在于将执行该方法的环境中(系统B),这就是为什么应该完全排除有关它的信息以避免NoClassDefFoundError.

Ste*_*n C 7

你可以尝试制作Caller.call()一种static方法.

但是,仍然需要在反序列化序列化实例的上下文中提供匿名类.这是不可避免的.

(很难想象匿名类可用的情况,但封闭类不是.)


所以,如果有人可以展示,我如何在我的匿名类中正确覆盖writeObject和readObject方法......

如果你制作Caller.call()静态,那么你会这样做,就像你想要的那样,我想.(我相信你可以找到适合自己的例子.)


实际上,(模块化匿名类可用性问题)它的工作原理.这里,该static main方法替代了一种static Classer.call()方法.该程序编译并运行,表明在静态方法中声明的匿名类可以被序列化和反序列化.

import java.io.*;

public class Bar {

    private interface Foo extends Runnable, Serializable {}

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

        Runnable foo = new Foo() {
            @Override
            public void run() {
                System.out.println("Lala");
            }
        };

        Thread t = new Thread(foo);
        t.start();
        t.join();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(foo);
        oos.close();
        Foo foofoo = (Foo) new ObjectInputStream(
            new ByteArrayInputStream(baos.toByteArray())).readObject();

        t = new Thread(foofoo);
        t.start();
        t.join();
    }
}
Run Code Online (Sandbox Code Playgroud)

另一个需要记住的重要事项是:在Caller执行方法的环境中不存在类,因此我希望在序列化期间排除有关它的所有信息以避免NoClassDefFoundError.

没有办法避免这种情况.远程JVM中的反序列化抱怨的原因是类描述符包含对外部类的引用.反序列化方需要解析该引用,即使您设法破坏了引用,即使您从未在反序列化对象中显式或隐式使用合成变量.

问题是远程JVM的类加载器在加载内部类的类文件时需要知道外部类的类型.验证需要它.它需要反思.垃圾收集器需要它.

没有解决方法.

(我不确定这是否也适用于static内部阶级......但我怀疑它确实如此.)


尝试在没有外部类的情况下序列化匿名Runnable实例不仅指序列化问题,还指在另一个环境中执行任意代码的可能性.很高兴看到JLS参考,描述这个问题.

没有JLS参考.JLS中未指定序列化和类加载器.(类初始化是......但这是一个不同的问题.)

可以通过RMI在远程系统上运行任意代码.但是,您需要实现RMI动态类加载才能实现此目的.这是一个参考:

请注意,将远程类的动态类加载添加到RMI会引入重大的安全问题.你必须考虑类加载器泄漏之类的问题.