今天看了下effective java 中的clone的使用,对其有了一点了解,下面就把自己
理解的写下来,以便以后运用~~~~
java中要实现对象的拷贝,就可以用Object的clone()方法,对于自己的写的类要实现cloneable
接口,并且覆盖父类的clone()方法。
传统的对象赋值如:
public class Test{
private String str;
public Test(String str){
this.str = str;
}
public void setStr(String str){
this.str = str;
}
public String getStr(){
return this.str;
}
public static void main(String args[]){
Test t1 = new Test("www");
Test t2 = t1;
t2.setStr("ddd");
System.out.println(t1.getStr());//输出ddd
System.out.println(t2.getStr());//输出ddd
}
}
上面 t2 = t1只是把让t1和t2同时指向了同一块
内存,对于t2的改变也会影响到t1,那如果我想要t2的改变不影响t1应该怎么办呢?
其实很简单,这就可以用到Object类的clone()方法,同时实现Cloneable接口改进后的代码,如下:
public class Test implements Cloneable{
private String str;
public Test(String str){
this.str = str;
}
public void setStr(String str){
this.str = str;
}
public String getStr(){
return this.str;
}
public static void main(String args[]) throws CloneNotSupportedException{
Test t1 = new Test("www");
Test t2 = (Test) t1.clone();
t2.setStr("ddd");
System.out.println(t1.getStr());//输出www
System.out.println(t2.getStr());//输出ddd
}
}
这样t2是一个新的对象引用,就类似于Test t2 = new Test("www"),t1和t2 指向不同的内存空间,对t2设置 值不会影响到t1。
但是这样做又会产生另一个问题,看下面的代码:
public class Test implements Cloneable{
private String str;
private HashMap<Integer, String> map = new HashMap<Integer, String>();
public Test(String str){
this.str = str;
map.put(1, "1111");
map.put(2, "2222");
map.put(3, "3333");
}
public void setStr(String str){
this.str = str;
}
public String getStr(){
return this.str;
}
public HashMap<Integer, String> getMap(){
return this.map;
}
public static void main(String args[]) throws CloneNotSupportedException{
Test t1 = new Test("www");
Test t2 = (Test) t1.clone();
t2.setStr("ddd");
System.out.println(t1.getStr());//输出www
System.out.println(t2.getStr());//输出ddd
t2.getMap().put(4, "4444");
System.out.println(t1.getMap().get(4));//输出4444(我们设想的值应该是null)
System.out.println(t2.getMap().get(4));//输出4444
}
}
可以看到上面的t1中的输出并不是null,而是4444,这是为什么呢???
其实Object类中的clone()方法,可以简单的理解为复制一个内部元素值相同的对象出来,如上面的程序,t1和t2中的map成员变量所指向的内存地址是一样的,所以你可以任意其中一个改变map的值,这样
他们就不指向同一内存空间,但是你如果去修改map中的键值的话,他们两个是同时指向同一个空间的,一个改变,另一个也会改变。
解决方法如下:
public class Test implements Cloneable{
private String str;
private HashMap<Integer, String> map = new HashMap<Integer, String>();
public Test(String str){
this.str = str;
map.put(1, "1111");
map.put(2, "2222");
map.put(3, "3333");
}
public void setStr(String str){
this.str = str;
}
public String getStr(){
return this.str;
}
public HashMap<Integer, String> getMap(){
return this.map;
}
public static void main(String args[]) throws CloneNotSupportedException{
Test t1 = new Test("www");
Test t2 = (Test) t1.clone();
t2.setStr("ddd");
System.out.println(t1.getStr());//输出www
System.out.println(t2.getStr());//输出ddd
t2.getMap().put(4, "4444");
System.out.println(t1.getMap().get(4));//输出null
System.out.println(t2.getMap().get(4));//输出4444
}
@Override public Test clone(){
Test obj = null;
try {
obj = (Test) super.clone();
obj.map = (HashMap<Integer, String>) obj.map.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
重写clone()方法并且把成员变量也clone()一份,这样就可以解决上面的问题,对象数组也是这样处理的,事实上只要你用的对象的clone()方法,最好都重写clone()方法
@Override public Test clone(){
Test obj = null;
try {
obj = (Test) super.clone();
//clone其他类成员对象
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
哎,总算写完了,JDK中的clone这个方法有很多问题的,所以很多大牛们都是不推荐用的,基本的问题留给以后慢慢发掘了~~~