当前位置: 首页 > 知识库问答 >
问题:

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

山疏珂
2023-03-14

我在网站上做了一个小研究,并在这个网站上回顾了相关主题,但答案是矛盾的:有人说这是不可能的,有人说这是可能的,但很危险。

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

但是我们知道内部类的实例包含对外部类的引用(匿名类是内部类)。因此,当我们序列化内部类的实例时,外部类的实例也被序列化为一个字段。问题来了:外部类是不可序列化的,更重要的是——我不想序列化它。我想做的只是发送匿名类的实例。

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

public interface RPCService {    
    Object call(SerializableRunnable runnable);
}

下面是我想如何调用这个方法

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

如您所见,我想做的是向另一方发送一个“操作”——系统A描述应该在系统B上运行的代码。这就像在Java发送脚本。

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

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

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

共有3个答案

吴腾
2023-03-14

上面所述的示例不能在Java中工作,因为匿名内部类是在类Caller中声明的,并且您明确声明类Caller在RPC服务器上不可用(如果我理解正确的话)。请注意,对于JavaRPC,只有数据通过网络发送,类必须已经在客户端和服务器上可用。尊重您的示例是没有意义的,因为看起来您想要发送代码而不是数据。通常情况下,您将在服务器和客户端都可以使用的JAR中拥有可序列化的类,并且每个可序列化的类都应该具有唯一的序列化VersionUID。

梁丘书
2023-03-14

如果您足够疯狂,可以使用反射来查找包含对外部类的引用的字段,并将其设置为null

单耘豪
2023-03-14

您可以尝试使Caller.call()成为静态方法。

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

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

所以,如果有人能展示,我如何在我的匿名类中正确重写writeObject和readObject方法。。。

如果您使用调用者。call()static,然后您可以像处理命名类一样执行此操作。(我相信你自己也能找到这样的例子。)

事实上,(考虑匿名类可用性问题)它是有效的。这里,static main方法代替了静态类。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();
    }
}

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

这是无法避免的。远程JVM中的反序列化抱怨的原因是类描述符包含对外部类的引用。反序列化端需要解析该引用,即使您成功地关闭了该引用,并且即使您从未在反序列化对象中显式或隐式使用过合成变量。

问题是远程JVM的类加载器在为内部类加载类文件时需要知道外部类的类型。这是核查所需要的。这是反思的需要。垃圾收集器需要它。

没有解决办法。

(我不确定这是否也适用于静态内部类……但我怀疑它确实适用。)

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

对此没有JLS参考。JLS中未指定序列化和类加载器。(类初始化是……但这是另一个问题。)

可以通过RMI在远程系统上运行任意代码。然而,您需要实现RMI动态类加载来实现这一点。以下是参考资料:

  • http://www.cis.upenn.edu/~bcpierce/courses/629/jdkdocs/guide/rmi/spec/rmi arch。博士。html#280

请注意,将远程类的动态类加载添加到RMI会带来重大的安全问题。您必须考虑类加载器泄漏等问题。

 类似资料:
  • 我的问题是,有什么方法可以让我序列化/反序列化一个名为onlinePlayers的列表,该列表引用了“John1”的实例,它也碰巧在List allPlayers中,而不重复“John1”,同时仍然引用那个对象? 我猜当我反序列化allPlayers时,它将创建不同于原始对象的对象,所以onlinePlayers在反序列化后不可能仍然引用相同的对象。我是否应该编写一个自定义方法,在反序列化后将新创

  • 是否可以在没有实体的情况下使用JpaRepository?在这种情况下,将其替换为DTO。 如下示例所示 这种情况有替代方案吗? 注意:DTO已经映射,但我不想创建视图来将此DTO转换为实体。 我已经验证了这个主题,但没有重大进展,请使用无实体的JpaRepository交互样式 我在试这个 接口- 公共接口BffDTOInterface2{ } 我有这个错误

  • 我已经从源代码处构建并安装了另一个glibc,并且我想让现有的用C++编写的可执行文件与自定义glibc一起运行,以供实验之用。为了做到这一点,我尝试更改可执行文件的加载程序。首先,在/lib64下创建了一个名为的链接,其路径指向新的加载程序 其次,通过文本编辑器修改了可执行文件中的加载器路径,将“/lib64/ld-linux-x86-64.so.2”更改为“/lib64/ld_linux-x8

  • 我想使用并使其直接进入给定的url,而不是从ribbon配置中获取主机。 我知道在Spring,cloud-feign默认与ribbon和eureka一起出现。 根据这个:https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html#spring-cloud-ribbon-without-eure

  • 问题内容: 有关一些背景知识和参考,以下是一些Heroku文档页面的一些报价。 从Heroku Node.js支持>激活中: 当应用程序的根目录中有文件时,将使用Heroku Node.js buildpack 。 从Heroku Node.js支持>默认Web进程类型: 首先,Heroku寻找一个Procfile来指定您的进程类型。 如果在构建过程中应用程序的根目录中没有任何内容,则将通过运行[

  • 在Spring Boot的文档中,我只找到了使用Redis会话的例子,不使用Redis也能使用它吗?