Java 浅复制和深复制
目录
在学习原型模式中,里面有知识点浅复制和深复制,进行深入一点的学习:
java创建对象的方式:
1,使用new创建对象
new操作符是分配内存。程序执行到new操作符时,进行对象的初始化,先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。
2,使用clone方法来复制对象
clone在第一步也是分配内存,调用clone方法时,分配的内存和源对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域, 填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。
浅复制和深复制
浅复制:复制引用
要想获取对象的内存地址应使用System.identityHashCode()方法
代码:
public static void main(String[] args){
testShallowObject();
}
public static void testShallowObject(){
System.out.println("====== Object =======");
Shape shape1 = new Shape("pink","circle");
Shape shape2 = shape1;
System.out.println("init: shape1: "+ shape1.toString());
System.out.println("init: shape2: "+ shape2.toString());
shape2.setColor("green");
shape2.setType("rectangle");
System.out.println("change: shape1: "+ shape1.toString());
System.out.println("change: shape2: "+ shape2.toString());
System.out.println("内存地址: shape1: "+ System.identityHashCode(shape1));
System.out.println("内存地址: shape2: "+ System.identityHashCode(shape2));
}
结果:
====== Object =======
init: shape1: Shape{color='pink', type='circle'}
init: shape2: Shape{color='pink', type='circle'}
change: shape1: Shape{color='green', type='rectangle'}
change: shape2: Shape{color='green', type='rectangle'}
给shape2设置属性的时候,把shape1的内容也改变了。这个是复制引用。
new Shape()是创建了一个真正的对象, shape1和shape2只是两个对象的引用。代码中打印地址可以发现地址值是相同的,既然地址都是相同的,那么肯定是同一个对象。shape1和shape2只是指向了一个相同的对象的两个不同的引用而已。可以把这种现象叫做引用的复制。内存中的地址情景如图:
深复制:复制对象
使用clone()方法进行复制
代码:
public static void main(String[] args){
testDeepObject();
}
public static void testDeepObject(){
System.out.println("====== Object =======");
Shape shape1 = new Shape("pink","circle");
System.out.println("========= clone =========");
Shape shape3 = (Shape) shape1.clone();
System.out.println("init: shape1: "+ shape1.toString());
System.out.println("init: shape3: "+ shape3.toString());
shape3.setColor("blue");
shape3.setType("triangle");
System.out.println("change: shape1: "+ shape1.toString());
System.out.println("change: shape3: "+ shape3.toString());
System.out.println("内存地址: shape1: "+ System.identityHashCode(shape1));
System.out.println("内存地址: shape3: "+ System.identityHashCode(shape3));
}
结果:
====== Object =======
========= clone =========
init: shape1: Shape{color='pink', type='circle'}
init: shape3: Shape{color='pink', type='circle'}
change: shape1: Shape{color='pink', type='circle'}
change: shape3: Shape{color='blue', type='triangle'}
内存地址: shape1: 258952499
内存地址: shape3: 603742814
给shape3设置属性的时候,把shape1的内容没有变化。这个是复制对象。
new Shape()是创建了一个真正的对象, 而(Shape) shape1.clone()复制了对象。shape1和shape3是两个不同的对象。代码中打印地址可以发现地址值是不同的。shape1和shape3分别指向了不同的两个对象,可以把这种现象叫做对象的复制。内存中的地址情景如图:
Shape:代码
public class Shape implements Cloneable {
private String color;
private String type;
public Shape(String color, String type) {
this.color = color;
this.type = type;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
@Override
public String toString() {
return "Shape{" +
"color='" + color + '\'' +
", type='" + type + '\'' +
'}';
}
}
Clone的用法和说明
(1)clone方法将对象复制了一份并返回给调用者。
一般而言,clone()方法满足:
1,对任何的对象x,都有x.clone() !=x 克隆对象与原对象不是同一个对象
2,对任何的对象x,都有x.clone().getClass()= =x.getClass()克隆对象与原对象的类型一样。
3,如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。
(2)为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。
在派生类中覆盖基类的clone()方法,并声明为public。 在派生类的clone()方法中,调用super.clone()。 在派生类中实现Cloneable接口。
说明:
(1)为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
(2)那么clone类为什么还要实现Cloneable接口呢?点击Cloneable接口会发现,
public interface Cloneable {
}
Cloneable接口是不包含任何方法的!
其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对Object类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出java.lang.CloneNotSupportedException异常。
总结:
了解了什么是浅复制和深复制,在使用对象类型,进行复制的时候,就更容易清楚要注意什么。
对象类型的深复制具体处理看 java 深复制处理