Serializable学习

本文详细介绍了Java对象序列化的基本概念及其应用场景,包括序列化和反序列化的过程、transient关键字的作用以及如何自定义序列化行为。同时,还探讨了ArrayList在序列化过程中的特殊处理。

1.java对象序列化:是运行过程中持久化java对象。保存的是java对象的状态,即:对象的成员变量。而不会关注类的静态变量。


2.保存的是java对象的一串字节码。等要使用的时候,用反序列化将字节码转换为对象。


3.transient 英[ˈtrænziənt](短暂的,转瞬即逝的)用于修饰java变量。当在序列化过程中不需要对某个成员变量进行保持时,就用transient修饰。反序列化时被transient修饰的变量被设为默认值。int 型的是 0,对象型的是 null。


4.通过ObjectOutputStream和ObjectInputStream进行对象的序列化和反序列化。


5.ArrayList的序列化过程:
  在序列化过程中,如果被序列化的类中定义了writeObject 和 readObject 方法,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化。如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。
<think>我们正在讨论Java中的Serializable接口。根据引用,Serializable是一个标记接口,用于指示类的对象可以被序列化序列化是将对象状态换为字节流的过程,以便存储或传输,之后可以通过反序列化重建对象。作用:1.实现序列化:使对象能够被换为字节流,用于网络传输或持久化存储。2.支持远程方法调用(RMI):在RMI中,参数和返回值需要序列化。3.保存程序状态:可以将当前系统的状态保存下来,以便恢复。注意事项:-敏感字段可以使用`transient`关键字标记,避免被序列化。 -可以通过重写`writeObject`和`readObject`方法来自定义序列化过程。 -实现`Externalizable`接口可以更细粒度地控制序列化过程(但这里我们主要讨论Serializable)。使用示例:我们创建一个实现Serializable接口的类,并演示序列化和反序列化的过程。步骤:1.创建一个类,实现Serializable接口。2.创建该类的对象。3.使用ObjectOutputStream将对象序列化到文件。4.使用ObjectInputStream从文件反序列化回对象。注意:序列化时,每个类都有一个serialVersionUID,用于标识类的版本。如果类结构发生变化,最好显式声明一个serialVersionUID,以避免反序列化失败。示例代码: ```javaimport java.io.*;//实现Serializable接口publicclass Personimplements Serializable{//显式声明serialVersionUID(可选,但推荐)private staticfinal longserialVersionUID =1L;privateString name;private transientint age; //transient修饰的字段不会被序列化publicPerson(Stringname,int age) {this.name =name;this.age =age;}@OverridepublicString toString() {return"Person{name='" +name +"',age="+ age+ "}";}publicstatic voidmain(String[] args) {//序列化对象try (ObjectOutputStreamoos= newObjectOutputStream(new FileOutputStream("person.ser"))) {Personperson =new Person("Alice",30);oos.writeObject(person);System.out.println("序列化成功");}catch (IOException e) {e.printStackTrace();}//反序列化对象try(ObjectInputStream ois =new ObjectInputStream(newFileInputStream("person.ser"))){Person deserializedPerson =(Person) ois.readObject();System.out.println(deserializedPerson);//注意:age字段由于是transient,反序列化后为0(默认值)}catch (IOException |ClassNotFoundExceptione){e.printStackTrace();}} }```解释:-我们创建了一个Person类,实现了Serializable接口。 -使用transient修饰age字段,这样在序列化时不会保存age的值。 -在序列化时,将对象写入文件"person.ser"。 -在反序列化时,从文件中读取对象。由于age是transient,所以反序列化后age的值是0(对于int的默认值),而不是原来的30。引用说明: [^1]提到Serializable接口的作用是实现序列化。 [^2]指出Serializable是一个标记接口,没有任何方法,并提到与Externalizable的区别。 [^4]提到了使用transient关键字以及自定义序列化方法(如writeObject和readObject)来解决安全问题。[^5]提到了在RMI中使用序列化。根据引用,我们还可以知道,如果不显式定义serialVersionUID,Java运行时会根据类的细节自动生成一个。如果类结构发生变化(比如增加一个字段),自动生成的UID会改变,导致反序列化失败。因此,显式声明serialVersionUID是一个好习惯。如果需要自定义序列化过程(例如,对敏感字段进行加密),可以在类中定义以下方法(注意是私有方法):```javaprivate voidwriteObject(ObjectOutputStreamout)throws IOException{//可以在这里自定义序列化过程,例如对字段进行加密out.defaultWriteObject(); //调用默认的序列化方法}private voidreadObject(ObjectInputStreamin)throws IOException, ClassNotFoundException {//自定义反序列化过程in.defaultReadObject(); //调用默认的反序列化方法} ```这样,我们就可以在序列化和反序列化过程中插入自定义逻辑。相关问题:1.什么是序列化和反序列化?2.为什么需要显式声明serialVersionUID?3.如何避免序列化敏感字段?4.实现Serializable接口和Externalizable接口有什么区别?5.序列化在哪些场景下使用?我们已经在回答中涵盖了部分问题,但用户可以根据相关问题进一步学习。</think>### Java Serializable 接口详解 #### 一、作用与核心概念 1. **序列化机制** `Serializable` 是标记接口(无方法定义),用于指示类对象支持**序列化**(对象→字节流)和**反序列化**(字节流→对象)[^1][^2]。 数学表达:若类 $C$ 实现 `Serializable`,则存在映射 $f: C \rightarrow \text{byte[]}$ 和逆映射 $f^{-1}: \text{byte[]} \rightarrow C$。 2. **核心应用场景** - 网络传输:RMI(远程方法调用)中传递参数和返回值[^5] - 持久化存储:保存程序状态到文件或数据库 - 分布式计算:跨JVM传递对象 - 缓存机制:对象缓存到内存或磁盘 3. **安全机制** 敏感字段(如密码)可用 `transient` 关键字标记,避免被序列化[^4]: ```java private transient String password; // 不被序列化 ``` #### 二、使用示例 ```java import java.io.*; // 1. 实现Serializable接口 class User implements Serializable { // 2. 声明serialVersionUID(推荐) private static final long serialVersionUID = 1L; private String name; private transient int age; // 敏感字段,不序列化 public User(String name, int age) { this.name = name; this.age = age; } } public class SerializationDemo { public static void main(String[] args) { // 序列化对象 → 文件 try (ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("user.dat"))) { User user = new User("Alice", 30); oos.writeObject(user); // 序列化 } catch (IOException e) { e.printStackTrace(); } // 从文件反序列化 → 对象 try (ObjectInputStream ois = new ObjectInputStream( new FileInputStream("user.dat"))) { User restoredUser = (User) ois.readObject(); // 反序列化 System.out.println(restoredUser.getName()); // 输出 "Alice" System.out.println(restoredUser.getAge()); // 输出 0(因transient) } catch (Exception e) { e.printStackTrace(); } } } ``` #### 三、关键注意事项 1. **serialVersionUID** - 显式声明:$$ \text{private static final long serialVersionUID} = \text{唯一标识符} $$ - 作用:版本兼容性校验,未声明时JVM自动生成,类结构变化会导致反序列化失败[^3] 2. **序列化控制** - **默认行为**:自动序列化所有非`transient`字段 - **自定义控制**(重写方法): ```java private void writeObject(ObjectOutputStream out) throws IOException { // 自定义序列化逻辑 } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // 自定义反序列化逻辑 } ``` 3. **替代方案** - `Externalizable` 接口:完全自定义序列化过程[^2][^3] - 第三方库:JSON(如Gson)、Protocol Buffers等 #### 四、典型应用场景 1. **会话复制** 在分布式Web应用中复制HttpSession对象 2. **消息队列** 将Java对象序列化为消息体(如Kafka消息) 3. **深度克隆** 通过序列化/反序列化实现对象深拷贝 ```java ByteArrayOutputStream bos = new ByteArrayOutputStream(); new ObjectOutputStream(bos).writeObject(original); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); Object cloned = new ObjectInputStream(bis).readObject(); ``` > **安全警告**:反序列化未经验证的数据可能导致安全漏洞(如反序列化攻击),需配合输入验证或使用白名单机制[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值