java中的clone( ) -- 浅拷贝 | 深拷贝

clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。


clone 与 "="的区别

public static void main(String[] args) throws CloneNotSupportedException {
		User user = new User("hl", "1234");
		User user2 = user;
		System.out.println(user == user2); //true
		
		User user3 = (User) user.clone();
		System.out.println(user == user3); //false
	}

“=” : 引用指向同一个对象(内存地址)

clone : 开辟新内存,指向不同对象(内存地址)

这里写图片描述

例1:

tatic void test1(){
		int[] array = {1,2,3,4};
		int[] clone = array.clone();
		array[2] = 6;
		System.out.println(Arrays.toString(array)); // [1, 2, 6, 4]
		System.out.println(Arrays.toString(clone)); // [1, 2, 3, 4]
		
		clone[2] = 7;
		System.out.println(Arrays.toString(array)); // [1, 2, 6, 4]
		System.out.println(Arrays.toString(clone)); // [1, 2, 7, 4]
		
	}

例2:

static void test3(){
		int[][] array = {{1,2},{3,4,5}};
		int[][] clone = array.clone();
		
		array[0][0] = 6;

		for (int[] is : array) {
			System.out.print(Arrays.toString(is)); //[6, 2][3, 4, 5]
		}
		System.out.println("");
		for (int[] is : clone) {
			System.out.print(Arrays.toString(is)); //[6, 2][3, 4, 5]
		}

表象 : 例1中改变原数组的值 不会引起克隆数组的变化,而例2克隆数组会随着原数组的变化而发生变化 .

java克隆时,如果克隆的对象的各属性都是简单数据项,那么克隆出来的结果无误,也就是浅拷贝。
如果对象的属性中存在引用的话,那克隆出来的对象会和原对象指向相同的引用地址,这时候想克隆,需要深拷贝。

clone并不能直接作用于二维数组。
因为java中没有二维数组的概念,只有数组的数组。
所以二维数组int array[][] 中存储的实际上是两个一维数组的引用。当调用clone函数时,是对这两个引用进行了复制。

浅拷贝?

public class Person implements Cloneable {

	private int age;
	
	private Head head;
	
	public Person(Head head) {
		super();
		this.head = head;
	}
	

	public Person() {
		super();
	}



	static class Head{
		private Mouth mouth;
		private int num; 
		
		public Head(Mouth mouth) {
			super();
			this.mouth = mouth;
		}

		public Head() {
			super();
		}
		
		
	}
	
	static class Mouth{
		
	}
	
	public static void main(String[] args) throws CloneNotSupportedException {
		Person person = new Person(new Head(new Mouth()));
		Person clone = (Person)person.clone();
		
		//1
		System.out.println(person == clone); //false
		
		//2
		clone.age = 4;
		System.out.println(person.age +"-" +clone.age); //0-4
		
		//3
		System.out.println(person.head == clone.head); //true
		
		//4
		System.out.println(person.head.num +"---" + person.head.num); //null---null
		person.head.num = 1;
		System.out.println(person.head.num +"---" + person.head.num); //1---1
		clone.head.num = 2;
		System.out.println(person.head.num +"---" + person.head.num); //2---2
		
		//5
		System.out.println(person.head.mouth == clone.head.mouth); //true
	}
}

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值 [ 对应代码中2,4 ];如果属性是内存地址(引用类型),拷贝的就是内存地址,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。[ 对应代码中3,5 ]

浅拷贝

这里写图片描述

如何实现深拷贝?

深拷贝

这里写图片描述

1. 实现Cloneable接口,重写对象的clone方法

public class Person implements Cloneable { //实现Cloneable接口

	private Head head;
	
	public Person(Head head) {
		super();
		this.head = head;
	}
	
	public Person() {
		super();
	}

	static class Head implements Cloneable{ //实现Cloneable接口
		private Mouth mouth;
		
		public Head(Mouth mouth) {
			super();
			this.mouth = mouth;
		}

		public Head() {
			super();
		}
		
		//重写Head对象的clone方法
		@Override
		protected Object clone() throws CloneNotSupportedException {
			// TODO Auto-generated method stub
			return super.clone();
		}
		
	}
	
	static class Mouth {
		
	}
	
	public static void main(String[] args) throws CloneNotSupportedException {
		Person person = new Person(new Head(new Mouth()));
		Person clone = (Person)person.clone();
		
		
		//1
		System.out.println(person == clone); //false
		
		//2
		System.out.println(person.head == clone.head); //false
		
		//3
		System.out.println(person.head.mouth == clone.head.mouth); //true
	}
	
	//重写Person对象的clone方法
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		Person person = new Person(new Head(new Mouth()));
		person.head = (Head) head.clone();
		return person;
	}
}

如果要实现对象的深拷贝,必须重写该对象的clone方法.如果一个对象中引用的对象又引用另一个对象 如:Person -> Head -> Mouth 则必须重写Person和Head的clone方法,以此类推有n个对象嵌套引用则需要重写n-1个对象的clone方法,因此用这种方法实现彻底的深拷贝基本不可能.

2. 利用序列化

public class SerializableTest {

	public static void main(String[] args) throws ClassNotFoundException, IOException, CloneNotSupportedException {
		Person1 person = new Person1(new Head());
		Person1 cloneT = (Person1) person.cloneT();  //序列化
		Person1 clone = (Person1) person.clone();  //重写clone()
		
		System.out.println("cloneT--"+ (person == cloneT)); //false
		System.out.println("cloneT--" + (person.head == cloneT.head)); //false
		System.out.println("------------------------------------");
		System.out.println("clone--"+ (person == clone)); //false
		System.out.println("clone--" + (person.head == clone.head)); //true
	}
}

class Person1 implements Serializable,Cloneable{
	
	private static final long serialVersionUID = 1L;
	
	public Head head;

	public Person1(Head head) {
		super();
		this.head = head;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return super.clone();
	}
	
	public Object cloneT() 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 ois.readObject();
	}
}

class Head implements Serializable{

	private static final long serialVersionUID = 1L;
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liangh.orz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值