当我们想复制一个简单类型的数据时:
int a=5;
int b=a;
其他7种简单类型的复制也可以通过这种方式进行。
但当我们想复制一个对象时,就没有这么简单了,
有些初学者复制对象时应该会采用如下方法:
public class Test {
public static void main(String[] args) {
Student s1=new Student();
s1.setStuId(1);
s1.setStuName("caojie");
Student s2=s1;
System.out.println(s1);
System.out.println(s2);
}
}
class Student{
private int stuId;
private String stuName;
public int getStuId() {
return stuId;
}
public void setStuId(int stuId) {
this.stuId = stuId;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
@Override
public String toString() {
return "Student [stuId=" + stuId + ", stuName=" + stuName + "]";
}
}
如果这样的话,当你改变s2的值得时候,s1的值也会跟着改变,这是因为s2=s1这句话是将s1的引用赋给了s2,两个引用指向同一个内存单元,所以改变其中一个的值,两个对象的值都会发生改变。
那怎样对一个对象进行有效的复制呢?
Object类有一个clone方法,protected native Object clone() throws CloneNotSupportedException,
每个类的直接父类或间接父类都是Object,所以每个类都有clone方法,但这个方法是protected的,类外不能访问,所以每个类必须重写clone方法进行对象的复制。
浅复制:一般 步骤
1.被复制的类需要实现cloneable接口(不实现会抛CloneNotSupportedException异常,该接口是个标记接口,不含任何方法)
2.重写clone方法(将访问修饰符改成public,调用super.clone获得需要的复制对象)
修改后的代码是这样的:
class Student implements Cloneable{
private int stuId;
private String stuName;
public int getStuId() {
return stuId;
}
public void setStuId(int stuId) {
this.stuId = stuId;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student s1=null;
try{
s1=(Student)super.clone();//调用super.clone获得需要复制的对象
}catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return s1;
}
@Override
public String toString() {
return "Student [stuId=" + stuId + ", stuName=" + stuName + "]";
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student s1=new Student();
s1.setStuId(1);
s1.setStuName("caojie");
Student s2=(Student)s1.clone();
System.out.println(s1);
System.out.println(s2);
s2.setStuId(2);
System.out.println(s1);
System.out.println(s2);
}
}
打印结果:
Student [stuId=1, stuName=caojie]
Student [stuId=1, stuName=caojie]
Student [stuId=1, stuName=caojie]
Student [stuId=2, stuName=caojie]
打印System.out.println(s1==s2);输出结果为false
深复制
当我们在Studennt类中加入另一个类的引用,代码如下:
class Student implements Cloneable{
private int stuId;
private String stuName;
private Address addr;
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
public int getStuId() {
return stuId;
}
public void setStuId(int stuId) {
this.stuId = stuId;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student s1=null;
try{
s1=(Student)super.clone();//调用super.clone获得需要复制的对象
}catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return s1;
}
@Override
public String toString() {
return "Student [stuId=" + stuId + ", stuName=" + stuName + ", addr=" + addr + "]";
}
}
class Address{
private String addr;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Address [addr=" + addr + "]";
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr=new Address();
addr.setAddr("beijjj");
Student s1=new Student();
s1.setStuId(1);
s1.setStuName("caojie");
s1.setAddr(addr);
Student s2=(Student)s1.clone();
System.out.println(s1);
System.out.println(s2);
addr.setAddr("haha");
}
}
打印结果如下:
Student [stuId=1, stuName=caojie, addr=Address [addr=beijjj]]
Student [stuId=1, stuName=caojie, addr=Address [addr=beijjj]]
这样看复制结果是没有任何问题的
当我们改变重新改变地址时:s2.setAddr(addr);
class Student implements Cloneable{
private int stuId;
private String stuName;
private Address addr;
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
public int getStuId() {
return stuId;
}
public void setStuId(int stuId) {
this.stuId = stuId;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student s1=null;
try{
s1=(Student)super.clone();//调用super.clone获得需要复制的对象
}catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return s1;
}
@Override
public String toString() {
return "Student [stuId=" + stuId + ", stuName=" + stuName + ", addr=" + addr + "]";
}
}
class Address{
private String addr;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Address [addr=" + addr + "]";
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr=new Address();
addr.setAddr("beijjj");
Student s1=new Student();
s1.setStuId(1);
s1.setStuName("caojie");
s1.setAddr(addr);
Student s2=(Student)s1.clone();
System.out.println(s1);
System.out.println(s2);
addr.setAddr("haha");
s2.setAddr(addr);
System.out.println(s1);
System.out.println(s2);
}
}
打印结果如下:
Student [stuId=1, stuName=caojie, addr=Address [addr=beijjj]]
Student [stuId=1, stuName=caojie, addr=Address [addr=beijjj]]
Student [stuId=1, stuName=caojie, addr=Address [addr=haha]]
Student [stuId=1, stuName=caojie, addr=Address [addr=haha]]
可以发现,当我们改变其中一个对象的地址时,两个对象的值都会发生改变
这是因为浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。
所以,为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法,完整代码如下:
class Student implements Cloneable{
private int stuId;
private String stuName;
private Address addr;
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
public int getStuId() {
return stuId;
}
public void setStuId(int stuId) {
this.stuId = stuId;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student s1=null;
try{
s1=(Student)super.clone();//调用super.clone获得需要复制的对象
//浅复制
}catch (CloneNotSupportedException e) {
e.printStackTrace();
}
s1.addr=(Address)addr.clone();//深复制
return s1;
}
@Override
public String toString() {
return "Student [stuId=" + stuId + ", stuName=" + stuName + ", addr=" + addr + "]";
}
}
class Address implements Cloneable{
private String addr;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Address [addr=" + addr + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
Address addr=null;
addr=(Address)super.clone();
return addr;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr=new Address();
addr.setAddr("beijjj");
Student s1=new Student();
s1.setStuId(1);
s1.setStuName("caojie");
s1.setAddr(addr);
Student s2=(Student)s1.clone();
System.out.println(s1);
System.out.println(s2);
addr.setAddr("haha");
Address addr2=new Address();
addr2.setAddr("hehe");
s2.setAddr(addr2);
System.out.println(s1);
System.out.println(s2);
}
}
打印结果:
Student [stuId=1, stuName=caojie, addr=Address [addr=beijjj]]
Student [stuId=1, stuName=caojie, addr=Address [addr=beijjj]]
Student [stuId=1, stuName=caojie, addr=Address [addr=haha]]
Student [stuId=1, stuName=caojie, addr=Address [addr=hehe]]
public Object clone() {
Date d = null;
try {
d = (Date)super.clone();
if (cdate != null) {
d.cdate = (BaseCalendar.Date) cdate.clone();
}
} catch (CloneNotSupportedException e) {} // Won't happen
return d;
}