Java 序列化并存储在JAR文件中时,什么可以修改SerialVersionUID?

Java 序列化并存储在JAR文件中时,什么可以修改SerialVersionUID?,java,serialization,jar,drools,Java,Serialization,Jar,Drools,我在序列化对象时遇到了一些问题(我使用JBoss Drools,希望存储KnowledgePackage的ArrayList) 当我序列化列表、将结果存储在文件中并对其进行反序列化时,不会出现任何问题,因此工作正常 但当我序列化列表,将结果存储在字节流中,然后将其保存在JarFile中时,我无法反序列化结果,因为出现以下错误: IOException during package import : java.util.ArrayList; local class incompatible: st

我在序列化对象时遇到了一些问题(我使用JBoss Drools,希望存储KnowledgePackage的ArrayList)

当我序列化列表、将结果存储在文件中并对其进行反序列化时,不会出现任何问题,因此工作正常

但当我序列化列表,将结果存储在字节流中,然后将其保存在JarFile中时,我无法反序列化结果,因为出现以下错误:

IOException during package import : java.util.ArrayList; local class incompatible: stream classdesc serialVersionUID = 8664875232659988799, local class serialVersionUID = 8683452581122892189
因此,我认为问题在于将序列化对象保存到Jarfile条目中时。我认为我这样做是正确的,因为以相同方式保存在Jarfile中的其他文件可以正确读取。 在使用了“cmp”和“hextump”之后,我发现将其保存在一个jar中会导致一个八位字节的变化,如果uuid,则内容相同

我真的很失望,说不出问题出在哪里

什么可以修改两个类之间的SerialVersionUID?除了另一个虚拟机版本


添加源代码: exportToJar->writerUserPackageEntry->writeEntry

/**
 * Writes content provided from a reader into a file contained in a jar.
 * 
 * @param output the output stream to write on
 * @param entryName the name of the file that will contain reader data
 * @param contentReader 
 * 
 * @return the zip entry that has been created into the jar
 */
ZipEntry writeEntry(JarOutputStream output, String entryName, ByteArrayInputStream input) {
    if (output == null || entryName == null || entryName.trim().length() == 0 || input == null) {
        throw new NullPointerException("Null argument passed");
    }

    ZipEntry entry = new ZipEntry(entryName);
    byte[] buffer = new byte[BUFFER_LENGTH];

    try {
        output.putNextEntry(entry);
        int nRead;

        while ((nRead = input.read(buffer, 0, BUFFER_LENGTH)) > 0) {
            output.write(buffer, 0, nRead);
        }

        output.closeEntry();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return entry;
}

/**
 * Export rules files to a serialized object (ArrayList<KnowledgePackage>) into 
 * an output stream, then write the output content as an entry of a jar.
 * 
 * @param os the output jar to write in
 */
void writeRulesPackageEntry(JarOutputStream os) {
    // serialize objects and write them to the output stream
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    RulesPackaging rulesPackaging = new RulesPackaging();
    rulesPackaging.exportResources(this.rules, output);

    // create a new input stream to read written objects from
    ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
    this.writeEntry(os, Product.ENTRY_RULES_PACKAGE, input);
}

/**
 * Creates a JarFile containing resources. 
 * 
 * @param filename the exported jar filename
 * @return the jar as an object, null if an error occured
 */
public JarFile exportToJar(String filename) {
    FileOutputStream fOs;
    JarOutputStream jOs;
    JarFile jar = null;

    try {
        fOs = new FileOutputStream(filename);
        jOs = new JarOutputStream(fOs);

        this.writeRulesPackageEntry(jOs);

        jOs.close();

        // construct a jar from the output jar
        jar = new JarFile(new File(filename));
    } catch (IOException e) {
        e.printStackTrace();
    }

    return jar;
}
/**
*将读取器提供的内容写入jar中包含的文件。
* 
*@param output要写入的输出流
*@param entryName将包含读取器数据的文件的名称
*@param contentReader
* 
*@返回已创建到jar中的zip条目
*/
ZipEntry writeEntry(JarOutputStream输出、字符串entryName、ByteArrayInputStream输入){
如果(输出==null | | entryName==null | | entryName.trim().length()==0 | |输入==null){
抛出新的NullPointerException(“传递了Null参数”);
}
ZipEntry条目=新ZipEntry(条目名称);
字节[]缓冲区=新字节[缓冲区长度];
试一试{
输出。输入(输入);
国际nRead;
而((nRead=input.read(buffer,0,buffer_LENGTH))>0){
输出写入(缓冲区,0,nRead);
}
output.closeEntry();
}捕获(IOE异常){
e、 printStackTrace();
}
返回条目;
}
/**
*将规则文件导出到序列化对象(ArrayList)中
*一个输出流,然后将输出内容作为jar的条目写入。
* 
*@param os要写入的输出jar
*/
无效WriterUserPackageEntry(JarOutputStream os){
//序列化对象并将其写入输出流
ByteArrayOutputStream输出=新建ByteArrayOutputStream();
RulesPackaging RulesPackaging=新的RulesPackaging();
rulespacking.exportResources(this.rules,output);
//创建新的输入流以从中读取写入的对象
ByteArrayInputStream输入=新的ByteArrayInputStream(output.toByteArray());
this.writeEntry(os、Product.ENTRY\u RULES\u PACKAGE、input);
}
/**
*创建包含资源的JAR文件。
* 
*@param filename导出的jar文件名
*@将jar作为对象返回,如果发生错误,则返回null
*/
公共JarFile exportToJar(字符串文件名){
文件输出流;
贾尔库特斯特雷姆·乔斯;
JarFile jar=null;
试一试{
fOs=新文件输出流(文件名);
jOs=新的输出流(fOs);
这是一个无需编写的PackageEntry(jOs);
jOs.close();
//从输出jar构造一个jar
jar=新JarFile(新文件(文件名));
}捕获(IOE异常){
e、 printStackTrace();
}
返回罐;
}

序列版本ID不会更改。它是编译时分配的
静态final
(我认为是基于源代码的散列),除非在源代码中明确分配了值

这里还有更多的内容

除此之外,您看到java.util.ArrayList的正确
serialVersionUID
是86834525811122892189L,它在源代码中显式分配,自1.2中引入该类以来一直保持不变

正如您所说,当字节流传输到JAR文件时,最有可能发生错误-请发布您用于执行此操作的代码

源代码发布后继续

我怀疑问题在于使用
java.io.InputStreamReader

从JavaDoc:

InputStreamReader是从 字节流到字符流:It 读取字节并将其解码为 使用指定字符集的字符。 它使用的字符集可能是 由名称指定或可以指定 显式,或平台的默认值 可以接受字符集


当我看到非文本流中涉及的字符集时,我总是感到怀疑,因为在解码过程中可能会修改流,因为字节序列与字符集中的字符不对应(看到编码问题发生时出现的小方形字符)。我会尝试直接从
java.io.ByteArrayInputStream
中读取字节,您正在
writerulesspackageentry(JarOutputStream)
中使用
java.io.InputStreamReader
进行包装。没有必要将流转换为
char[]

就像Nick建议的那样,问题很可能是您没有将流视为字节(从未更改),而是将其视为字符(可以更改)


话虽如此,关于序列化的另一个不错的参考资料是我在一百万年前(1997年)写的一本书中的一章,“掌握JavaBeans”。幸运的是,第11章,系列化,今天和当时一样重要。从

下载免费PDF是否有可能将早期版本序列化到JAR文件中,并且后续的序列化尝试无法覆盖它?然后检索该类早期版本的序列化数据,这将(正确地)抛出“不兼容”错误

我问这个问题的原因是,当我更新了一个序列化类但没有删除旧的持久缓存时,我使用我选择的缓存系统(EHCache)看到了类似的错误消息。