对象拷贝:对象拷贝没有生成新对象,对象地址是一样的
A类
public class A implements Cloneable{
private Integer a;
private B b;
public int getA() {
return a;
}
public void setA(Integer a) {
this.a = a;
}
@Override
public String toString() {
return "A{" +
"a=" + a +
", b=" + b +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
B类
public class B {
private String b;
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
@Override
public String toString() {
return "B{" +
"b='" + b + '\'' +
'}';
}
}
public static void main(String[] args) throws CloneNotSupportedException {
// write your code here
B b=new B();
b.setB("haha");
A a=new A();
a.setA(1);
a.setB(b);
System.out.println("对象拷贝前a的值 "+"a: "+a);
A aa= a;
aa.setA(2);//修改b而未修改a
B bb=new B();
bb.setB("enen");
aa.setB(bb);
System.out.println("对象拷贝后a的值 "+"a: "+a);
}
结果为
对象拷贝前a的值 a: A{a=1, b=B{b='haha'}}
对象拷贝后a的值 a: A{a=2, b=B{b='enen'}}
我们发现对象拷贝并没有创建新的对象,而是引用a与aa都指向同一个地址
浅拷贝:简明的说,就是浅拷贝会创建一个新的对象,对于基本类型会拷贝值,而对于引用类型会拷贝地址,需要实现Cloneable接口并重写clone()方法
public static void main(String[] args) throws CloneNotSupportedException {
// write your code here
B b=new B();
b.setB("haha");
A a=new A();
a.setA(1);
a.setB(b);
System.out.println("浅拷贝拷贝前a的值 "+"a: "+a);
A aa= (A) a.clone();
aa.setA(2);//修改b而未修改a
B bb=aa.getB();
bb.setB("enen");
aa.setB(bb);
System.out.println("浅拷贝拷贝后a的值 "+"a: "+a);
}
结果为
浅拷贝拷贝前a的值 a: A{a=1, b=B{b='haha'}}
浅拷贝拷贝后a的值 a: A{a=1, b=B{b='enen'}}
我们发现浅拷贝确实是会创建一个新的对象,对于基本类型会拷贝值,对于引用类型则会拷贝地址,所以导致修改了aa的B,a的B也会跟着修改
深拷贝:浅拷贝会带来数据安全隐患,所以我们需要深拷贝,深拷贝需要对象里面的所有引用类型都要实现Cloneable接口并重写clone()方法
public class A implements Cloneable{
private Integer a;
private B b;
public int getA() {
return a;
}
public void setA(Integer a) {
this.a = a;
}
@Override
public String toString() {
return "A{" +
"a=" + a +
", b=" + b +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
A a= (A) super.clone();
B bb= (B) b.clone();
a.setB(bb);
return a;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B implements Cloneable {
private String b;
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
@Override
public String toString() {
return "B{" +
"b='" + b + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {
// write your code here
B b=new B();
b.setB("haha");
A a=new A();
a.setA(1);
a.setB(b);
System.out.println("深拷贝拷贝前a的值 "+"a: "+a);
A aa= (A) a.clone();
aa.setA(2);//修改b而未修改a
B bb=aa.getB();
bb.setB("enen");
aa.setB(bb);
System.out.println("深拷贝拷贝后a的值 "+"a: "+a);
}
结果为
深拷贝拷贝前a的值 a: A{a=1, b=B{b='haha'}}
深拷贝拷贝后a的值 a: A{a=1, b=B{b='haha'}}
我们发现深拷贝后,aa的修改并未影响到a的值
Cloneable相关
/**
* A class implements the <code>Cloneable</code> interface to
* indicate to the {@link java.lang.Object#clone()} method that it
* is legal for that method to make a
* field-for-field copy of instances of that class.
* <p>
* Invoking Object's clone method on an instance that does not implement the
* <code>Cloneable</code> interface results in the exception
* <code>CloneNotSupportedException</code> being thrown.
* <p>
* By convention, classes that implement this interface should override
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.
* <p>
* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface. Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.
*
* @author unascribed
* @see java.lang.CloneNotSupportedException
* @see java.lang.Object#clone()
* @since JDK1.0
*/
public interface Cloneable {
}
这个接口的说明我们去看文档
public interface Cloneable一个类实现Cloneable接口,以指示Object.clone()方法,该方法对于该类的实例进行现场复制是合法的。
在不实现Cloneable接口的实例上调用对象的克隆方法导致抛出异常CloneNotSupportedException 。
按照惯例,实现此接口的类应使用公共方法覆盖Object.clone (受保护)。 有关覆盖此方法的详细信息,请参阅Object.clone() 。
注意,此接口不包含clone方法。 因此,只能通过实现该接口的事实来克隆对象是不可能的。 即使克隆方法被反射地调用,也不能保证它成功。
从以下版本开始:
JDK1.0
另请参见:
CloneNotSupportedException , Object.clone()
要想用Object.clone()方法就得实现Cloneable接口,否则会报CloneNotSupportedException
反例如下:
Exception in thread "main" java.lang.CloneNotSupportedException: com.company.wh.A
at java.lang.Object.clone(Native Method)
at com.company.wh.A.clone(A.java:24)
at com.company.wh.Main.main(Main.java:11)
Object.clone()的文档里也很清楚的说明了这一点
protected Object clone()
throws CloneNotSupportedException创建并返回此对象的副本。 “复制”的精确含义可能取决于对象的类。 一般的意图是,对于任何对象x ,表达式:
x.clone() != x将是真实的,而且表达:
x.clone().getClass() == x.getClass()将是true ,但这些都不是绝对的要求。 通常情况是:
x.clone().equals(x)将是true ,这不是一个绝对的要求。
按照惯例,返回的对象应该通过调用super.clone获得。 如果一个类和它的所有超类(除了Object )遵守这个惯例,那将是x.clone().getClass() == x.getClass()的情况。
按照惯例,此方法返回的对象应该与此对象(正被克隆)无关。 为了实现这一独立性,可能需要修改super.clone返回的对象的一个或多个字段。 通常,这意味着复制构成被克隆的对象的内部“深层结构”的任何可变对象,并通过引用该副本替换对这些对象的引用。 如果一个类仅包含原始字段或对不可变对象的引用,则通常情况下, super.clone返回的对象中的字段通常不需要修改。
clone的方法Object执行特定的克隆操作。 首先,如果此对象的类不实现接口Cloneable ,则抛出CloneNotSupportedException 。 请注意,所有数组都被认为是实现接口Cloneable ,并且数组类型T[]的clone方法的返回类型是T[] ,其中T是任何引用或原始类型。 否则,该方法将创建该对象的类的新实例,并将其所有字段初始化为完全符合该对象的相应字段的内容,就像通过赋值一样。 这些字段的内容本身不被克隆。 因此,该方法执行该对象的“浅拷贝”,而不是“深度拷贝”操作。
Object类本身并不实现接口Cloneable ,因此在类别为Object的对象上调用clone方法将导致运行时抛出异常。
结果
这个实例的一个克隆。
异常
CloneNotSupportedException - 如果对象的类不支持Cloneable接口。 覆盖clone方法的子类也可以抛出此异常以指示实例无法克隆。
另请参见:
Cloneable
本来想看Object.clone()的源码,但发现是调用的本地方法
protected native Object clone() throws CloneNotSupportedException;
所以这个就结束了。。。
补充:
一直在思考,Integer,String等其实都是引用类型,也没见他们实现Cloneable接口,重写clone()方法,他们是怎么实现深浅拷贝的呢?
看到一个最好的答案,就是String,Integer等是Immutable Object,每一次赋值,指向的都是堆上的一个新对象