java的深浅拷贝与绕过构造函数获取对象的神奇之旅
2017-09-28 10:05
567 查看
一、产生一个对象一定要执行构造函数吗?
当然不是!!!!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 (cnfree2000@hotmail.com) ***/ 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]
哇偶,我们的原始对象和副本是不是已经互不影响了,这是因为深拷贝的对对象中的引用变量所指向的对象进行拷贝了,原始对象和副本引用的不是同一个引用了,所以大家的修改自然不会相互影响。
相关文章推荐
- java 的对象拷贝(有深浅拷贝两种方式,深拷贝实现的两种方式(逐层实现cloneable接口,序列化的方式来实现))
- javaseday31补充(反射 三种获取字节码对象 获取空参有参对象 进行构造函数和无参有参函数使用)
- Java-获取Class对象的名称
- 2种方法实现java对象的深拷贝
- Java:使用synchronized和Lock对象获取对象锁
- 使用Java对两个对象的属性进行拷贝
- Java反射之通过反射获取一个对象的方法信息(实例代码)
- 集合框架----Java管理对象神奇之Object类的equals和hashcode
- java基础-反射2(反射,反射操作对象,Class对象的使用,类型信息的获取)
- 十、构造函数和析构函数(四) 拷贝构造函数、默认拷贝构造函数、拷贝构造函数调用几种情况、深拷贝浅拷贝、构造函数和=操作符区别、禁止对象拷贝
- 黑马程序员_Java基础_面向对象(概述、类与对象关系、成员变量、封装private、构造函数和构造代码块、this关键字)
- java反射(3)获取Class对象属性方法和构造器
- java-获取.csv文件里的数据,并且获取文件夹下所含有对象的个数
- 将Java对象中属性值为null获取到
- Java使用JNDI技术获取DataSource对象
- java 序列化实现对象的深拷贝
- java对象的内存布局(二):利用sun.misc.Unsafe获取类字段的偏移地址和读取字段的值
- JAVA对象拷贝
- [编写高质量代码:改善java程序的151个建议]建议43 避免对象浅拷贝; 建议44:推荐使用序列化实现对象的深拷贝
- java学习面向对象之父子构造函数初始化