首先,要使一个Java对象能够被序列化,该对象所属的类必须实现`java.io.Serializable`接口。这个接口实际上是一个标记接口,没有定义任何方法。它的存在主要是出于安全考虑。如果没有这个接口,理论上任何Java对象都可以被序列化到磁盘上,这可能导致敏感信息泄露。通过实现`Serializable`接口,开发人员可以有选择性地决定哪些对象可以被序列化和反序列化,从而提高了安全性。
接下来,让我们看看如何进行序列化操作。假设我们有一个`Order`类,它包含订单的基本信息,如订单ID、订单编号、收件人姓名和收货地址。为了让这个对象可以被序列化,我们需要在类定义中添加`Serializable`接口。
```java import java.io.Serializable;
public class Order implements Serializable { private Long orderId; private String orderNo; private String consignee; private String deliveryAddress;
// getter 和 setter 方法 } ```
然后,使用`ObjectOutputStream`类,我们可以将`Order`对象转换为字节流并写入文件。以下是一个简单的示例:
```java import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.List;
public class SerializationExample { public static void main(String[] args) throws Exception { // 创建Order对象并添加到列表中 List
// 将对象列表序列化到文件 FileOutputStream fos = new FileOutputStream("orders.txt"); try (ObjectOutputStream os = new ObjectOutputStream(fos)) { os.writeObject(orders); } System.out.println("序列化成功"); } } ```
一旦对象被序列化到文件中,这些文件对于直接查看可能是不透明的,因为它们是以字节流的形式存储的。为了将这些字节流转换回Java对象,我们需要进行反序列化操作。
```java import java.io.FileInputStream; import java.io.ObjectInputStream; import java.util.ArrayList; import java.util.List;
public class DeserializationExample { public static void main(String[] args) throws Exception { List
// 输出反序列化后的对象信息 for (Order order : orders) { System.out.println(order.toString()); } } } ```
在进行序列化和反序列化时,有几个关键点需要注意。首先,静态变量和被`trANSIent`关键字修饰的变量不会被序列化。其次,如果字节流中的`serialVersionUID`与类定义中的`serialVersionUID`不一致,反序列化时会抛出异常。最后,序列化实际上实现了对象的深克隆,这意味着对象引用的每个对象都会被序列化。
总之,序列化和反序列化是Java编程中处理对象传输和存储的重要技术。通过实现`Serializable`接口,我们可以确保Java对象能够在不同的上下文之间安全、有效地传输。同时,理解和掌握序列化的细节对于开发安全、高效的Java应用程序至关重要。
java 对象经常需要在网络中以 socket 传输或者需要保存到文件中。这时不管 java 对象是文件、数据、图像还是其他格式,都可以转换为一个 byte[] 数组保存到文件或者通过网络传输。这种转换方式就叫做序列化。将文件或者网络传输中得到的 Byte[] 数组转换为 java 对象就叫做反序列化。
怎么使用
如果一个 Java 对象要能被序列化,必须实现一个特殊的 java.io.Serializable 接口
public interface Serializable {
}
Serializable 接口没有定义任何的方法,是一个空接口。为什么要有一个这样的接口?主要是因为安全。如果没有这个接口就代表着所有 java 对象都可以被序列化到磁盘上,然后通过反序列化看到所有属性的数据。有了这个 Serializable 就可以让开发人员选择 java 对象可以被序列化和反序列化,就增加了安全性。
序列化
下面例子是将一个 java 对象序列化后保存到文件。
import java.io.Serializable;
public class Order implements Serializable {
private Long orderId;
private String orderNo;
private String consignee;//收件人
private String deliveryAddress;//收货地址
//getter 和 setter
@Override
public String toString() {
return "OrderDTO{" +
"orderId=" + orderId +
", orderNo='" + orderNo + ''' + ", consignee='" + consignee + ''' + ", deliveryAddress='" + deliveryAddress + ''' +
'}';
}
}
把一个 Java 对象变为 byte[] 数组,需要使用 ObjectOutputStream。它负责把一个Java 对象写入一个字节流:
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) throws Exception {
OrderDTO orderDTO = new OrderDTO();
orderDTO.setOrderId(1L);
orderDTO.setOrderNo("123456");
orderDTO.setConsignee("李四");
orderDTO.setDeliveryAddress("xxx路xxx弄xxxx号");
OrderDTO orderDTO2 = new OrderDTO();
orderDTO2.setOrderId(2L);
orderDTO2.setOrderNo("78901");
orderDTO2.setConsignee("王五");
orderDTO2.setDeliveryAddress("yyy路yyy弄yyyy号");
List< OrderDTO > list = new ArrayList< >();
list.add(orderDTO);
list.add(orderDTO2);
FileOutputStream fos = new FileOutputStream("D:/order.txt");
try ( ObjectOutputStream os = new ObjectOutputStream(fos)){
os.writeObject(list);
}
System.out.println("序列化成功");
}
}
这个时候就将两个 OrderDTO 对象序列化到了 D:/order.txt 中。
反序列化
序列化文件在本地打开都是乱码的,这应该用反序列化将文件解析成对象。
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) throws Exception {
List< OrderDTO > list = new ArrayList< >();
FileInputStream fis = new FileInputStream("D:/order.txt");
try (ObjectInputStream is = new ObjectInputStream(fis)) {
list = (List< OrderDTO >)is.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
for (OrderDTO orderDTO : list){
System.out.println(orderDTO.toString());
}
}
}
输出结果:
OrderDTO{orderId=1, orderNo='123456', consignee='李四', deliveryAddress='xxx路xxx弄xxxx号'}
OrderDTO{orderId=2, orderNo='78901', consignee='王五', deliveryAddress='yyy路yyy弄yyyy号'}
注意点
- 静态变量和 transient 关键字修饰的变量不能被序列化
- 反序列化的时候,字节流中的 serialVersionUID 和实体类中的 serialVersionUID 的不一致会抛出异常。serialVersionUID 没有写的话,会被默认一个。
- 序列化实现了深克隆,对象引用的每一个对象数据也会被序列化。
总结
- 序列化必须实现 Serializable。
- serialVersionUID 不是必须的。