序列化
序列化就是将一个Java对象转换为字节流写入文件。
一个java对象要能序列化,必须实现一个特殊的接口---java.io.Serializable接口,例如ArrayList<E>类,就实现了java.io.Serializable接口。
那么要实现把一个Java对象转换为字节流写入文件,我们可以使用ObjectOutputStream。为什么不用BufferedOutputStream呢?因为BufferedOutputStream的write()方法只能传入字节数组,而无法传入一个Java对象,ObjectOutputStream既可以写入基本类型,如:int、boolean,也可以写入String,还可以写入Object。
public class Demo01_ObejectOutputStream {
public static void main(String[] args) {
ArrayList<String> list=new<String>();
list.add("关羽");
list.add("貂蝉");
list.add("吕布");
list.add("张飞");
list.add("妲己");
//创建输出流
try(ObjectOutputStreamoos=new ObjectOutputStream(
new BufferedOutputStream(
new OutputStream("D:\\IO流\\oos.bin")))){
//将集合对象,写入文件
oos.writeObject(list);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//BufferedOutputStream的Write()方法
//ObjectOutputStream的Write()方法
反序列化
ObjectInoutStream 负责从一个字节流读取Java对象:
public class Demo02_ObejectInputStream {
public static void main(String[] args) {
//创建输入流
try (ObjectInoutStream ois=new ObjectInputStream(
new BufferedInputStream(
new FileInputStream("D:\\IO流\\oos.bin")))){
//从文件中读取对象
ArrayList list=(Arraylist)ois.readObject();
System.out.println(list);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出结果:[关羽, 貂蝉, 吕布, 张飞, 妲己]
ObjectInoutStream除了能读取基本类型和String类型外,调用readObject()可以直接返回一个Object对象,当我们想将它转化为特定类型时,需要进行强制类型转换,下面是一个普通的自定义类,当我们想实现将一个Order对象转换为字节流写入文件,即序列化时,Order这个类一定要实现Serializable接口,否则无法进行序列化或反序列化:
public class Order implements Serializable{
private String orderNo;
private double pay;
private double sale;
//当我们不想将某一个成员变量序列化或反序列化时,可以用transient关键字
修饰.
private transient double code;
//有参构造方法
public Order(String orderNo,double pay,double sale) {
this.orderNo=orderNo;
this.pay=pay;
this.sale=sale;
}
public Order(String orderNo,double pay,double sale,double code) {
this(orderNo,pay,sale);
this.code=code;
}
//重写toString方法
@Override
public String toString() {
return "Order [orderNo=" + orderNo + ", pay=" + pay + ",
sale=" + sale + "]";
}
//getter和setter方法
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public double getPay() {
return pay;
}
public void setPay(double pay) {
this.pay = pay;
}
public double getSale() {
return sale;
}
public void setSale(double sale) {
this.sale = sale;
}
public double getCode() {
return code;
}
public void setCode(double code) {
this.code = code;
}
序列化:
public class Demo01_ObejectOutputStream {
public static void main(String[] args) {
//新建一个订单对象
Order o=new Order("123",52,0.85,.79);
//创建输出流
try (ObjectOutputStream oos=new ObjectOutputStream(
new BufferedOutputStream(
new FileOutputStream("D:\\IO流\\order.bin")))){
//将订单对象写入文件
oos.writeObject(o);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
反序列化:
public class Demo02_ObejectInputStream {
public static void main(String[] args) {
//创建输入流
try (ObjectInputStream ois=new ObjectInputStream(
new BufferedInputStream(
new FileInputStream("D:\\IO流\\order.bin")))){
//从文件中读取对象
Order o=(Order)ois.readObject();
System.out.println(o);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
在进行序列化时不会出现任何问题,但是在进行反序列化时,会出现以下错误:
(截图截不下,大致错误如下:date4_07.Order; local class incompatible: stream classdesc serialVersionUID = 4548777618395762843, local class serialVersionUID = 6078288131602943035) ,大致意思为stream classdesc serialVersionUID = 4548777618395762843,而local class serialVersionUID = 6078288131602943035,两个serialVersionUID不匹配,所以出现java.io.InvalidClassException。
出现这种错误是因为Order对象序列化时会生成一个serialVersionUID,但是我们在修改Order类时,Order类中也会为我们自动生成一个默认的serialVersionUID,每次改变,这个默认的serialVersionUID都会改变,那么在进行反序列化时,Order类中的serialVersionUID和序列化时生成的serialVersionUID不匹配,就会出现错误。
为了避免这种class定义变动导致的不兼容问题,Java的序列化允许class定义一个特殊的serialVersionUID静态变量,用于标识Java类的序列化的“版本”,注意:在我们增加或修改了类中的字段时,就要重新生成一个serialVersionUID。
当我们生成一个和序列化时生成的serialVersionUID一样的值时,Order类就会使用我们指定的serialVersionUID,而不是自己默认生成一个serialVersionUID,这样,序列化时生成的serialVersionUID和当前类的serialVersionUID相同,就不会报错。
public class Order implements Serializable{
private static final long serialVersionUID = 6078288131602943035L;
private String orderNo;
private double pay;
private double sale;
//...
我们观察Order类会发现,我们调用的是第二个有参构造方法--Order o=new Order("123",52,0.85,.79),传入了code这个变量,但是没有打印出code,就是因为使用了transient关键字修饰code,当我们不想将某一个成员变量序列化或反序列化时,可以用transient关键字修饰。
private transient double code;