序列化 Serializable

Serializable 是 Java/Android 里的“对象可被序列化”的标记接口:让对象能被转换成字节流(写入文件/数据库/网络传输),以及在需要时再还原成对象。它在 Android 开发里最常见的场景是:把对象放进 Intent / Bundle 做页面跳转传参。

核心概念

  • Serializable 是“标记接口”(没有方法),实现它表示该类允许被 Java 序列化机制处理。
  • 序列化:对象 → 字节流;反序列化:字节流 → 对象。
  • 典型用途:Intent.putExtra() / Bundle.putSerializable() 传对象;或持久化到磁盘。

Android 里怎么用(Intent/Bundle)

// 1) 定义可序列化的类
public class User implements java.io.Serializable {
    private static final long serialVersionUID = 1L;

    public String name;
    public int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

// 2) 传递
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("user", new User("Alice", 18));
startActivity(intent);

// 3) 接收
User user = (User) getIntent().getSerializableExtra("user");

必须注意的坑

  • serialVersionUID
    • 建议显式声明:private static final long serialVersionUID = 1L;
    • 作用是“版本兼容”。类结构变更(字段增删改)时,反序列化可能失败(InvalidClassException)。有 UID 能更可控,但并不保证随意改结构都兼容。
  • 成员也要可序列化
    • 类里包含的字段类型也必须可序列化(或用 transient 排除)。
    • 例:包含 ThreadContextViewBitmap 等通常不适合/不能直接序列化。
  • 性能一般
    • Java 序列化反射/元数据开销大,Android 上用于频繁 IPC 或大量对象传递会慢、占内存。
  • 不适合跨进程/跨版本长期存储
    • 进程间用 Binder 时更推荐 Parcelable(Android 原生为 IPC 设计)。
    • 长期落盘/对外协议更推荐 JSON/ProtoBuf 等明确 schema 的格式。

Serializable vs Parcelable(Android 面试高频点)

  • Serializable
    • 优点:写起来最省事,实现接口就行
    • 缺点:性能较差、内存开销偏大
    • 适用:对象小、偶尔页面传参,开发效率优先
  • Parcelable
    • 优点:性能好,Android IPC/Bundle 传递更高效
    • 缺点:代码更繁琐(不过 Kotlin 有 @Parcelize 可大幅简化)
    • 适用:频繁传递、大对象、跨进程、性能敏感

最佳实践(Android 里常用结论)

  • Activity/Fragment 间传参:小对象可以用 Serializable,但更推荐 Parcelable(尤其是列表/频繁跳转)。
  • 不要序列化 Context/View 这类与生命周期绑定的对象。
  • 对于“要长期存储/要跨端”的数据,不要依赖 Serializable,用 JSON/数据库/Proto 更稳。

如果你用的是 Kotlin,我也可以顺带给你一个 @Parcelize 的对比写法(通常更符合 Android 现在的主流做法)。

在 Kotlin 里让 data class User(val name: String) 支持 Serializable,最直接就是实现 java.io.Serializable

import java.io.Serializable

data class User(val name: String) : Serializable

可选但常用:加上 serialVersionUID(放在 companion object 里,用 @JvmField 暴露成真正的静态字段):

import java.io.Serializable

data class User(val name: String) : Serializable {
    companion object {
        @JvmField
        val serialVersionUID: Long = 1L
    }
}

然后就可以通过 Intent/Bundle 传:

// 发送
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("user", User("Alice")) // Serializable
startActivity(intent)

// 接收
val user = intent.getSerializableExtra("user") as? User

补充一个更符合新 API 的写法(Android 13+ 有类型安全重载):

  • intent.getSerializableExtra("user", User::class.java)

不用写 serialVersionUID 吗?

可以不写。

serialVersionUID 只在“反序列化时要做版本一致性校验/兼容”这类场景更关键:例如你把对象序列化到磁盘、发到网络、或者进程/版本之间长期保存后再读回来。你现在常见的用法(同一次运行里 Activity/Bundle 传参)通常不会遇到类版本不一致的问题,所以不写也基本没影响。

什么时候建议写:

  • 你会把序列化后的数据持久化(文件/数据库)并在以后版本读取
  • 你会跨模块/跨版本/跨进程传递,并且对象类结构可能演进
  • 你希望避免 Kotlin/编译器生成的默认 serialVersionUID 因结构变化导致的反序列化失败不可控

写法(Kotlin):

import java.io.Serializable

data class User(val name: String) : Serializable {
    companion object {
        @JvmField
        val serialVersionUID: Long = 1L
    }
}

结论:页面跳转传参、小对象、同版本同进程用,serialVersionUID 可省;涉及“长期存储/跨版本读取”,建议显式加上并管理版本策略。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容