【Java】入门笔记01-IO流实现深拷贝

本文介绍了浅拷贝与深拷贝的区别,以及如何使用IO流(如ByteArrayOutputStream和ObjectOutputStream/InputStream)实现深拷贝,重点讲述了序列化和反序列化的概念及注意事项,包括InvalidClassException异常处理。

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

浅拷贝与深拷贝

简单阐述

  • 浅拷贝:仅仅拷贝对象本身,而不拷贝对象包含的引用指向的对象
  • 深拷贝:不仅拷贝对象本身,而且拷贝对象包含的引用指向的对象

两者的区别

在需要拷贝的对象中包含引用对象时,是否将引用对象也进行拷贝(重新在堆空间中创建一个对象)
浅拷贝与深拷贝原理简单图解

相关IO流简介

ByteArrayOutputStream

  • 在创建它的实例时,可以创建字节数组输出流,同时内存会为该输出流创建一个byte型的数组缓冲区,将捕获的数据转换成字节数组放在内存缓冲区中

ByteArrayInputStream

  • 可以将内存缓冲区中的字节数组转化为输入流,其中缓冲区会随着数据的不断写入而自动增长,可使用 toByteArray() 和 toString() 获取数据

PSByteArrayOutputStreamByteArrayInputStream不需要关闭流。即使关闭,对应的方法还是可以使用,因为它们是内存读写流,字节数组是它的成员变量,当数组不再使用时会被Java的垃圾回收机制回收。

ObjectOutputStream

  • 构造方法中传递字节输出流,保存基本类型数据或对象,实现序列化(见后)
  • 常搭配.writeObject将对象写入文件

ObjectInputStream

  • 读取基本类型数据或对象,实现反序列化
  • 常搭配.readObject将文件中数据还原成对象

序列化简要说明

简单描述

序列化是指把对象转换为字节序列的过程,而反序列化是指把字节序列恢复为对象的过程。

好处

可将任何实现了Serializable接口的对象转化为字节数据,以实现数据的持久化(永久地保存到硬盘上),也可实现实现远程通信(即在网络上传送对象的字节序列)

注意

  • 要序列化的对象不能被statictransient修饰
  • 要序列化的类和要引用类型属性必须实现SerializableExternalizable两个接口之一

InvalidClassException异常

  • 若把一个对象序列化后又进行修改,在未序列化的情况下又将其反序列化,就会出现该异常。

  • 解决方法:在创建对象时,提前给对象创建一个序列号

    private static final long serialVersionUID=1L;
    

IO流实现深拷贝

示例

例如若要为Point类实现深拷贝,可以这样写:

class Point implements Serializable {//设置Point类
    int x, y;

    void setXY(int m, int n) {
        x = m;
        y = n;
    }
    
    public Point myCopy() throws IOException, ClassNotFoundException {//深拷贝方法实现
		// 保存对象为字节数组——序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
		// 从字节数组中读取拷贝对象——反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (Point) ois.readObject();
    }
}

public class Reference {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Point p1, p2;
        p1 = new Point();
        p2 = new Point();
        System.out.println("p1 的引用: " + p1);
        System.out.println("p2 的引用: " + p2);
        p1.setXY(1111, 2222);
        p2.setXY(-100, -200);
        Point p3 = p2.myCopy();//深拷贝方法的使用
        System.out.println("p3 的引用: " + p3);//检验深拷贝结果
        System.out.println("p1 的 x, y 坐标: " + p1.x + ", " + p1.y); 
        System.out.println("p2 的 x, y 坐标: " + p2.x + ", " + p2.y);
        System.out.println("p3 的 x, y 坐标: " + p3.x + ", " + p3.y);//检验深拷贝结果
        p1 = p2;//浅拷贝
        p1.setXY(0, 0);
        System.out.println("p1 的引用: " + p1);//浅拷贝结果
        System.out.println("p2 的引用: " + p2);
        System.out.println("p3 的引用: " + p3);//检验深拷贝结果
        System.out.println("p1 的 x, y 坐标: " + p1.x + ", " + p1.y);//浅拷贝结果
        System.out.println("p2 的 x, y 坐标: " + p2.x + ", " + p2.y);
        System.out.println("p3 的 x, y 坐标: " + p3.x + ", " + p3.y);//检验深拷贝结果
    }
}
本地运行结果
p1 的引用: Point@b4c966a
p2 的引用: Point@1d81eb93
p3 的引用: Point@33833882
p1 的 x, y 坐标: 1111, 2222
p2 的 x, y 坐标: -100, -200
p3 的 x, y 坐标: -100, -200
p1 的引用: Point@1d81eb93
p2 的引用: Point@1d81eb93
p3 的引用: Point@33833882
p1 的 x, y 坐标: 0, 0
p2 的 x, y 坐标: 0, 0
p3 的 x, y 坐标: -100, -200
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值