细数浅拷贝和深拷贝

2023-01-11 0 247

Java 第一类复本是为第一类表达式的一类形式,单纯而言是建立两个和原第一类完全相同的第一类,康孔县的第一类是原第一类的两个复本,复试官贼拉讨厌在复试的这时候应证你浅复本和深复本的基本原理。即使它牵涉到第一类的提及亲密关系,涉及到 Java 是fork却是传达提及亲密关系,这通常而言是复试的重点项目。因此在聊深复本和浅复本以后,他们嘿嘿聊聊聊提及亲密关系。

有关提及

在 Java 中,除基本上正则表达式(五类六种正则表达式)以外,还存有提及正则表达式。通常采用 = 号做表达式操作形式的这时候,对基本上正则表达式,事实上是复本的它的值,但对第一类而言,只不过表达式的而已那个第一类的提及,也是将原第一类的提及传达往后,但她们事实上却是对准的同一第一类。如下表所示标识符右图

public class Food{ String name; int num; String taste; constructor() get and set() toString() }

试验类:

public static void main(String[] args) { int i1 = 10; int i2 = i1; // 基本上正则表达式的复本,复本值 System.out.println(“i2 = “ + i2); Food milk = new Food(“milk”,1,“fragrance”); Food food = milk; System.out.printf(“food = “ + food); System.out.println(“milk = “ + milk); // milk 和 food 都对准同一堆内存第一类 }

如果用图表示的话,应该是下面这样的:

细数浅拷贝和深拷贝
不用纠结 Java 中到底是值传达却是提及传达这种无意义的争论中,你只要记得对基本上数据类型,传达的是正则表达式的值,而对提及类型而言,传达的是第一类的提及,也是第一类的地址就可以了。

有关浅复本和深复本

浅复本和深复本只不过是在提及的那个基础上来做区分的,如果在复本的这时候,只对基本上正则表达式进行复本,对提及正则表达式而已进行了提及的传达,没有真正的建立两个新的第一类,这种复本形式就认为是浅复本。反之,在对提及正则表达式进行复本的这时候,建立了两个新的第一类,并且复制其内的成员变量,这种复本形式就被认为是深复本。

浅复本

那么如何实现浅复本(Shallow copy)呢?很单纯,是在需要复本的类上实现 Cloneable 接口并重写其 clone() 方法就可以了。

下面他们对 Food 类进行修改,他们让他实现 Cloneable 接口,并重写 clone() 方法。

public class Food implements Cloneable{ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }

然后在试验类中的标识符如下表所示

Food milk = new Food(“milk”,1,“fragrance”); Food food = (Food)milk.clone(); System.out.println(“milk = “ + milk); System.out.println(“food = “ + food);

可以看到,现在的 food 第一类是由 milk 第一类复本出来的,那么此时的 food 第一类和 milk 第一类是同一第一类吗?他们通过打印,可以看到这两个第一类的原生 hashcode。

milk = com.cxuan.objectclone.Food@3cd1a2f1 food = com.cxuan.objectclone.Food@4d7e1886

可以发现,food 和 milk 并不是同一第一类,那 milk 中还有三个属性值,这三个属性值在 food 中是不是也一样呢?为了验证那个猜想,他们重写了 toString 方法。

@Override public String toString() { return “Food{“ + “name=” + name + \ + “, num=” + num + “, taste=” + taste + \ + }; }

然后再次打印 food 和 milk ,可以观察到如下表所示结果

milk = Food{name=milk, num=1, taste=fragrance} food = Food{name=milk, num=1, taste=fragrance}

嗯哼,虽然看起来”cxuan 哥”和”cuan 哥”是两种完全不同的称呼!但她们却有一类共同的能力:写作!

他们却是通过图示而言明一下:

细数浅拷贝和深拷贝

这幅图看出门道了么?在堆区分别出现了两个 Food 第一类,这同时表明 clone 方法会重康孔县两个第一类并为其分配一块内存区域;虽然出现了两个第一类,但两个第一类中的属性值是一样的,这也是换汤不换药,虽然汤和药是不同的东西(第一类),但她们都溶于水(属性值)。

深复本

虽然浅复本是一类换汤不换药的说法,但是在 Java 世界中却是有一类说法是。。。。。。是啥来着?

词穷了。。。。。。

细数浅拷贝和深拷贝

哦对,还有一类改头换面的形式,它是他们所熟悉的深复本(Deep copy),嘿嘿抛出一下深复本的定义:在进行第一类复本的基础上,对第一类的成员变量也依次复本的形式被称为深复本。

哈哈哈哈,这故作高深的深复本原来是在浅复本的基础上再复制一下它的属性值啊,我还以为是啥高深的东西呢!上标识符!

他们先增加两个饮品类 Drink 。

public class Drink implements Cloneable { String name; get and set() @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } toString() }

然后更改一下 Food 类,即使 Drink 也算是 Food ,因此他们在 Food 类中增加对 Drink 的提及,然后再修改 get set 、toString 、clone 、构造方法,修改后的 Food 类标识符如下表所示

public class Food implements Cloneable{ String name; int num; String taste; Drink drink; public Food(String name, int num, String taste,Drink drink) { this.name = name; this.num = num; this.taste = taste; this.drink = drink; } get and set @Override protected Object clone() throws CloneNotSupportedException { Food food = (Food)super.clone(); food.drink = (Drink) drink.clone(); return super.clone(); } @Override public String toString() { return “Food{“ + “name=” + name + \ + “, num=” + num + “, taste=” + taste + \ + “, drink=” + drink + }; } }

可以看到最大的改变是 clone 方法,他们在 clone 方法中,实现了对 Food 第一类的复本,同时也实现了对 Drink 第一类的复本,这是他们上面所说的复制第一类并复制第一类的成员变量。

然后他们进行一下 Deep Copy的试验:

public static void main(String[] args) throws CloneNotSupportedException { Drink drink = new Drink(“milk”); Food food = new Food(“humberge”,1,“fragrance”,drink); Food foodClone = (Food)food.clone(); Drink tea = new Drink(“tea”); food.setDrink(tea); System.out.println(“food = “ + food); System.out.println(“foodClone = “ + foodClone.getDrink()); }

运行完成后的输出结果如下表所示:

food = Food{name=humberge, num=1, taste=fragrance, drink=Drink{name=tea}} foodClone = Drink{name=milk}

可以看到,他们把 foodClone 复本出来之后,修改 food 中的 drink 变量,却不会对 foodClone 造成改变,这就说明 foodClone 已经成功实现了深复本。

用图示表示的话,应该是下面这样的:

细数浅拷贝和深拷贝

这是深复本之后的内存分配图,现在可以看到,food 和 foodClone 完全是两个不同的第一类,它们之间不存有纽带亲密关系。

他们上面主要探讨实现第一类复本的形式是第一类实现 Cloneable 接口,并且调用重写之后的 clone 方法,在 Java 中,还有一类实现第一类复本的形式是采用 序列化。

序列化

采用序列化的形式主要是采用 Serializable 接口,这种形式还以解决多层复本的问题,多层复本是提及类型里面又有提及类型,层层嵌套下去。采用 Serializable 的关键标识符如下表所示

public Person clone() { Person person = null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this); // 将流序列化成第一类 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); person = (Person) ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return person; }

采用序列化可以实现深复本,它的基本原理是将二进制字节流内容写到两个文本或字节数组,然后是从那个文本或者字节数组中读取数据,原第一类写入那个文本或者字节数组后再复本给 clone 第一类,原第一类的修改不会影响 clone 第一类,即使 clone 第一类是从文本或者字节数组中读取的。

如何选择复本形式

到现在他们已经把浅复本和深复本都介绍完了,那么如何选择浅复本和深复本呢?下面是几点注意事项⚠️

如果第一类的属性都是基本上正则表达式,那么可以采用浅复本。如果第一类有提及类型,那就要基于具体的需求来选择浅复本却是深复本。如果第一类嵌套层数比较多,推荐采用 Serializable 接口实现深复本。如果第一类提及任何这时候都不会被改变,那么没必要采用深复本,只需要采用浅复本就行了。如果第一类提及经常改变,那么就要采用深复本。没有一成不变的规则,一切都取决于具体需求。

其他复本形式

除第一类的复本,Java 中还提供了其他的复本形式

比如数组的复本,你可以采用 Arrays.copyof 实现数组复本,还可以采用默认的 clone 进行复本,不过这两者都是浅复本。

public void test() { int[] lNumbers1 = new int[5]; int[] rNumbers1 = Arrays.copyOf(lNumbers1, lNumbers1.length); int[] lNumbers2 = new int[5]; int[] rNumbers2 = lNumbers2.clone(); }

除基本上数组正则表达式之外的复本,还有第一类的复本,不过用法基本上是一样的。

集合也可以实现复本,即使集合的底层就采用的是数组,因此用法也是一样的。

一些说明

针对 Cloneable 接口,有下面三点采用说明

如果类实现了 Cloneable 接口,再调用 Object 的 clone() 方法可以合法地对该类实例进行按字段复制。如果在没有实现 Cloneable 接口的实例上调用 Object 的 clone() 方法,则会导致抛出CloneNotSupporteddException。实现此接口的类应该采用公共方法重写 Object 的clone() 方法,即使 Object 的 clone() 方法是两个受保护的方法。

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务