java的深浅拷贝与绕过构造函数获取对象的神奇之旅

一、产生一个对象一定要执行构造函数吗?

当然不是!!!!

     1、通过new产生一个对象

(1)先看new操作符后的类型,知道类型,分配相应大小的内存空间
(2)再调用构造函数,填充对象的各个域(对象初始化)
(3)构造函数执行后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部可以使用这个引用操作操纵这个对象

     2、反射-------请移步查看类加载机制以及Java-Reflect(反射)

(其本质还是newInstance()去找正确的构造

     3、clone()----重点来啦

(1)clone第一步和new相似,都是分配内存
(2)调用clone()时,分配的内存和源对象(即调用clone()的对象)相同,然后再使用原有对象中对应的各个域,填充新对象的域。
(3)填充完成后,clone()返回,一个新的相同的对象被创建,同样可以把新对象引用发到外部

来段代码感受一下

public class Thing implements Cloneable {
	public Thing() {
		// TODO Auto-generated constructor stub
		System.out.println("构造函数被执行......");
	}
	
	@Override
	public Thing clone() {
		Thing thing = null;
		try {
			thing = (Thing) super.clone();
		} catch (Exception e) {
			// TODO: handle exception
		}
		return thing;
	}
}
(我们在构造函数输出一句话,验证clone对象时构造函数被执行了几次)
public class Client {
	/**
	 * 对象拷贝时构造函数不被执行,Object类的clone方法的原理:</p>
	 * 从内存中(对内存)一二进制流的方式进行拷贝,重新分配一个内存块</p>
	 * 在运行时刻,Object中的clone()识别出你要复制的是哪个对象,然后为此对象分配空间,并进行对象的复制,</p>
	 * 将原始对象的内容一一复制到新对象的存储空间。</p>
	 * !!native方法效率远高于java中的非native方法
	 * @param args
	 */
	public static void main(String[] args) {
		//产生一个对象
		Thing thing = new Thing();
		//拷贝一个对象
		Thing clonething = thing.clone();
		
		System.out.println(thing); //com.ashes.cloneTest.Thing@c17164
		System.out.println(clonething); //com.ashes.cloneTest.Thing@1fb8ee3----新内存块
	}
}

结果会是什么呢,我们的构造函数会被执行几次呢?

构造函数被执行......
com.ashes.cloneTest.Thing@de6ced
com.ashes.cloneTest.Thing@c17164

二、验证结果提问之什么是浅拷贝

看到此处,你可有疑问,为什么要实现Cloneable接口?clone()为什么是@Override?这个异常必须获吗?

1、浅拷贝

Object类提供的方法clone只是拷贝对象,其对象内部的数组,引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝。

      (1)被复制的类需要实现Cloneable接口(不实现的话在调用clone()会抛出 CloneNotSupportedException
该接口为标记接口(不含有任何方法)
/*** Eclipse Class Decompiler plugin, copyright (c) 2012 Chao Chen ([email protected]) ***/
package java.lang;

public abstract interface Cloneable {
}
     
     (2)覆盖clone(),访问修饰符设为public,方法中调用super.clone()得到需要的复制对象,native方法效率远高于非native方法哦。这也是为什么我们复制对象不新写一个类的关键原因。
     (3) CloneNotSupportedException  - 如果对象的类不支持  Cloneable  接口,则重写  clone  方法的子类也会抛出此异常,以指示无法复制某个实例。所以必须捕获异常。

2、深拷贝

clone()到底有什么功能,又有什么不足呢?
public class Thing implements Cloneable {
	
	//定义一个私有变量
	private ArrayList<String> arrayList = new ArrayList<String>();
	
	public Thing() {
		// TODO Auto-generated constructor stub
		System.out.println("构造函数被执行......");
	}
	
	@Override
	public Thing clone() {
		Thing thing = null;
		try {
			thing = (Thing) super.clone();
		} catch (Exception e) {
			// TODO: handle exception
		}
		return thing;
	}
	
	//设置HashMap的值
	public void setValue(String value) {
		this.arrayList.add(value);
	}
	
	//取得arrayList的值
	public ArrayList<String> getValue() {
		return this.arrayList;
	}
}

public class Client {
	public static void main(String[] args) {
		//定义一个对象
		Thing thing = new Thing();
		//设置一个值
		thing.setValue("Ashes");
		//拷贝一个对象
		Thing cloneThing = thing.clone();
		cloneThing.setValue("MushRoom");
		
		System.out.println("原对象  ====> " + thing.getValue());
		System.out.println("复制的新对象  ====>" + cloneThing.getValue());
	}
}

结果会和我们湘的一样吗???
构造函数被执行......
原对象  ====> [Ashes, MushRoom]
复制的新对象  ====>[Ashes, MushRoom]

(1)为什么我明明修改的是复制的新对象,但是原对象自己也改变了???

浅拷贝:是指在拷贝欧诺对象的时候,对于基本数据类型的变量会重新复制一份,而对于引用类型的变量,只是对引用进行拷贝,没有对指向的对象进行拷贝。
所以,在这里原始对象及其副本引用的是同一个对象。

深拷贝:是指在拷贝对象时,同时会对引用指向的对象进行拷贝。
因此,深浅拷贝的区别在于: 是否对对象中的引用变量所指向的对象进行拷贝。

再来回顾刚刚的代码
public class Thing implements Cloneable {
	
	//定义一个私有变量
	private ArrayList<String> arrayList = new ArrayList<String>();
	
	public Thing() {
		// TODO Auto-generated constructor stub
		System.out.println("构造函数被执行......");
	}
	
	@Override
	public Thing clone() {
		Thing thing = null;
		try {
			thing = (Thing) super.clone();
			thing.arrayList = (ArrayList<String>) this.arrayList.clone();
		} catch (Exception e) {
			// TODO: handle exception
		}
		return thing;
	}
	
	//设置HashMap的值
	public void setValue(String value) {
		this.arrayList.add(value);
	}
	
	//取得arrayList的值
	public ArrayList<String> getValue() {
		return this.arrayList;
	}
}
我只是简单的添加一行代码,结果又会有什么变化嘞?????
构造函数被执行......
原对象  ====> [Ashes]
复制的新对象  ====>[Ashes, MushRoom]


哇偶,我们的原始对象和副本是不是已经互不影响了,这是因为深拷贝的对对象中的引用变量所指向的对象进行拷贝了,原始对象和副本引用的不是同一个引用了,所以大家的修改自然不会相互影响。

猜你喜欢

转载自blog.csdn.net/Ashes18/article/details/78120195