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排除)。 - 例:包含
Thread、Context、View、Bitmap等通常不适合/不能直接序列化。
- 类里包含的字段类型也必须可序列化(或用
- 性能一般
- Java 序列化反射/元数据开销大,Android 上用于频繁 IPC 或大量对象传递会慢、占内存。
- 不适合跨进程/跨版本长期存储
- 进程间用 Binder 时更推荐
Parcelable(Android 原生为 IPC 设计)。 - 长期落盘/对外协议更推荐 JSON/ProtoBuf 等明确 schema 的格式。
- 进程间用 Binder 时更推荐
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 可省;涉及“长期存储/跨版本读取”,建议显式加上并管理版本策略。