Android Studio使用Eclipse类跨平台读取实例报ClassNotFoundException的解决方法
这个问题的核心原因很明确:Java对象序列化时,会把类的全限定名(包名+类名)作为标识写入序列化文件。当你把类从Eclipse项目复制到Android Studio时,大概率修改了包名(比如Eclipse里是com.example.MySerializableClass,Android里改成了com.my.android.app.MySerializableClass),两边的类全限定名不一致,反序列化时JVM自然找不到对应的类,抛出ClassNotFoundException。
下面给你几种可行的解决思路,按推荐程度排序:
1. 改用通用序列化格式(最推荐)
Java自带的序列化机制本身就对类结构、包名极度敏感,跨项目复用很容易出问题。换成JSON或XML这类通用格式,完全摆脱包名限制,只要两个类的字段名、类型匹配就能正常序列化/反序列化,还具备可读性强、调试方便的优点。
比如用Gson实现(Eclipse和Android端都可以引入Gson库):
保存对象到文件(Eclipse/Android通用)
Gson gson = new Gson(); // 把对象转成JSON字符串 String objJson = gson.toJson(yourClassInstance); // 写入文件 try (FileWriter writer = new FileWriter("saved_object.json")) { writer.write(objJson); } catch (IOException e) { e.printStackTrace(); }
从文件读取对象(Eclipse/Android通用)
// 从文件读取JSON字符串 String jsonContent = new String(Files.readAllBytes(Paths.get("saved_object.json"))); Gson gson = new Gson(); // 转成目标类实例 YourClass obj = gson.fromJson(jsonContent, YourClass.class);
2. 保持类的全限定名一致
如果必须使用Java序列化,最简单的办法是让Eclipse和Android Studio里的类完全共用同一个包名。比如Eclipse里类的包名是com.yamil.myapp.MyClass,那Android Studio里也把这个类放在com.yamil.myapp包下,不要修改包名。这样序列化时写入的类标识和反序列化时查找的类标识完全匹配,就能正常读取。
注意:除了包名,还要确保两个类的字段结构完全一致(字段名、类型、访问修饰符尽量匹配),并且显式声明相同的serialVersionUID,避免JVM自动生成不同的版本号导致InvalidClassException:
// 两个类里都要加这行,值必须完全相同 private static final long serialVersionUID = 123456789L;
3. 自定义类名映射(兼容已有序列化文件)
如果已经有大量用Java序列化保存的文件,且无法修改包名,可以通过自定义ObjectInputStream来在反序列化时替换类名,把Android项目的包名映射到Eclipse项目的包名(反之亦然)。
比如在Eclipse应用中,读取Android保存的文件时,用以下自定义流:
public class ClassMappingObjectInputStream extends ObjectInputStream { public ClassMappingObjectInputStream(InputStream in) throws IOException { super(in); } @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { // 把Android的类名替换成Eclipse的类名 if (desc.getName().equals("com.android.yamil.app.MyClass")) { return Class.forName("com.eclipse.yamil.app.MyClass"); } // 其他类正常处理 return super.resolveClass(desc); } }
然后用这个流读取文件:
try (ClassMappingObjectInputStream ois = new ClassMappingObjectInputStream(new FileInputStream("saved_object.ser"))) { MyClass obj = (MyClass) ois.readObject(); // 处理对象 } catch (Exception e) { e.printStackTrace(); }
同理,如果需要Android应用读取Eclipse保存的文件,也可以在Android端实现类似的自定义ObjectInputStream,把Eclipse的类名映射到Android的类名。
内容的提问来源于stack exchange,提问作者Yamil Essus

