Java:基于对象流、字节流、文件流的Serializable和深拷贝

本文介绍了Java中的Serializable接口,用于对象的序列化和反序列化,以实现对象状态的保存和恢复。通过代码示例展示了如何在实践中应用此接口,以及如何结合字节流和对象流实现深拷贝。同时,讨论了`transient`关键字在对象序列化过程中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 Serializable接口概述

有时候我们有一种需求,保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且想把保存的对象状态再读出来,这样既可以实现对象的传递。
实现这一需求的方式有很多,比如,使用objectMapper序列化为Json字符串/对象,再进行传递或者保存,Java的设计指出也考虑到了这种需求,就设计出了Serializable接口,该接口没有必要实现的方法,默认继承了该接口后会就能自动调用java默认的序列化和放序列化。

2 Serializable接口代码实践

定义一个类用于序列化:UserInfo .java

@Getter
@Setter
public class UserInfo implements Serializable{ //实现Serializable接口才能被序列化
    private String userName;
    private String usePass;
    private transient int userAge;//使用transient关键字修饰的变量不会被序列化
    public UserInfo() {
        userAge=20;
    }
    public UserInfo(String userName, String usePass, int userAge) {
        super();
        this.userName = userName;
        this.usePass = usePass;
        this.userAge = userAge;
    }

    @Override 
    public String toString() {
        return "UserInfo [userName=" + userName + ", usePass=" + usePass + ",userAge="+(userAge==0?"NOT SET":userAge)+"]";
    }

	 /**
     * 静态方法,序列化对象到文件
     * @param fileName
     */
    public static void serialize(String fileName){
        try {
			//对象输出流和文件输出流结合使用,实现将对象持久化/序列化到本地
            ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(fileName));
            out.writeObject("序列化的日期是:");//序列化一个字符串到文件
            out.writeObject(new Date());//序列化一个当前日期对象到文件
            UserInfo userInfo=new UserInfo("郭大侠","961012",21);
            out.writeObject(userInfo);//序列化一个会员对象
            out.close();  //资源释放要放到finally中,这里并不规范   
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

	 /**
     * 静态方法,从文件中反序列化对象
     * @param fileName
     */
    public static void deserialize(String fileName){
        try {
            ObjectInputStream in=new ObjectInputStream(new FileInputStream(fileName));
            
            String str=(String) in.readObject();//刚才的字符串对象
            Date date=(Date) in.readObject();//日期对象
            UserInfo userInfo=(UserInfo) in.readObject();//会员对象
            
            System.out.println(str);
            System.out.println(date);
            System.out.println(userInfo);
            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

主方法,运行实验一下:

class Main{
	public static main(String[] args){
		UserInfo.serialize("test");
		sleep(2*60*1000)
        UserInfo.deserialize("test");//这里userAge取读不到是因为使用了transient修饰,所以得到的是默认值
        
        /**
         * UserInfo的无参构造中给userAge属性赋值蛋反序列化得到的结果还是一样。
         * 得出结论:
         * 当从序列化的文件中读出某个类的实例时,实际上并不会执行这个类的构造函数,   
         * 而是载入了一个该类对象的持久化状态,并将这个状态赋值给该类的另一个对象。  
         */
    }
	}
}

原始输出:

序列化的日期是:
Sun Dec 15 11:13:05 CST 2019
UserInfo [userName=郭大侠, usePass=961012,userAge=NOT SET]

3 基于字节流、对象流的深拷贝

public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        UserInfo userInfo1 = new UserInfo("123", "456", 111);
        System.out.println(userInfo1);
        UserInfo userInfo2 = deepClone(userInfo1);
        System.out.println(userInfo2);
    }


    @SuppressWarnings("unchecked")
    public static <T> T deepClone(T object) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(object);

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        return (T) objectInputStream.readObject();
        
        
        /*
        原始输出:
        UserInfo [userName=123, usePass=456,userAge=111]
        UserInfo [userName=123, usePass=456,userAge=NOT SET]
         */
    }
}

从原始输出可以看出,transient 关键字可以可以抑制Object输入输出流对实例被transient 修饰属性的读写。

4 总结

以上两种应用场景,本质上Object输入输出流和其他输入输出流的配合使用:

  1. 和字节数组输入输入输出流配合使用,实现了对象的深拷贝
  2. 和文件输入输出流的配合使用,实现了对象持久化到的本地文件再进行读取的功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值