一、产生一个对象一定要执行构造函数吗?
当然不是!!!!
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]