Serializable序列化、反序列化

本文介绍了Java序列化的过程,如何使用ObjectOutputStream将对象写入文件,以及ObjectInputStream从文件读取对象。重点讲解了serialVersionUID在避免类定义变动导致的序列化不兼容问题中的作用。
摘要由CSDN通过智能技术生成

序列化

        序列化就是将一个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;

  • 26
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值