在其他文章中已经说明了场景,和一些对象克隆的实现方式。本篇文章主要是对Cloneable接口如何实现对象克隆展开。
首先需要声明下,对象有分为浅克隆和深克隆。
浅克隆是对要克隆的对象,其中的基本类型复制一份新的产生给对象。但是对于非基本类型的数据类型,仅仅是复制一份引用给新产生的对象。即基本类型是新产生的,非基本类型是复制一份内存引用。
实现步骤:
1.实现Cloneable接口
要clone方法,为什么还要实现Cloneable接口呢?我们可以看看,Cloneable其中是一个空方法,他个RandomAccess一样
都属于,标识接口。针对的是Object里的clone方法,进行重写。告诉Object而已。如果调用了super.clone()方法。没有实现Cloneable接口。那么会抛出异常CloneNotSupportedException异常。
2.重写clone方法。
JDK中的API文档也说明了,这是一个对象拷贝的过程,不是对象的初始化过程。他包含了一些原有对象的信息。
Object中的clone的返回值是native。native属于一般是由C语言实现,效率一般都高于java代码,所以这里我们没有选择new一个新的对象,而是选择对象拷贝。
Object中的clone修饰符是protected,我们要把它改成public。
实例代码如下:
bean:school,student。school中包含有student。main为执行方法。
student类如下:
package run;
public class student {
private String gg;
public String getGgg() {
return gg;
}
public void setGgg(String ggg) {
this.gg = ggg;
}
@Override
public String toString() {
return "student{" +
"gg='" + gg + '\'' +
'}';
}
}
school类代码如下:
package run;
public class school implements Cloneable{
private String name;
private String age;
private student st;
public student getSt() {
return st;
}
public void setSt(student st) {
this.st = st;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "school{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", st=" + st.toString() +
'}';
}
}
main的执行方法:
package run;
public class main {
public static void main(String args[]) throws CloneNotSupportedException {
student b1 = new student();
b1.setGgg("123");
school school1 = new school();
school1.setAge("11");
school1.setName("lsp");
school1.setSt(b1);
school school2 = (school) school1.clone();
school2.setAge("26");
school2.getSt().setGgg("456");
System.out.println(school1);
System.out.println(school2);
}
}
输出如下:
school{name='lsp', age='11', st=student{gg='456'}}
school{name='lsp', age='26', st=student{gg='456'}}
分析:通过输出可以看出,浅克隆对于基本类型数据,已经完成了克隆,但是对于非基本类型数据还是指向同一个内存地址。
深度克隆,其实就是对于浅度克隆的一种完善,浅度克隆只能完成基本类型的克隆,对于非基本类型无法克隆,只能复制内存地址到新的对象上。而深度克隆不同,深度克隆是完成了,基本数据类型和非基本数据类型,都可以克隆。
深度克隆:
要克隆的类和类中所有非基本类型对应的类
1.都要实现java.lang.Cloneable接口。
2.都要重写java.lang.Object.clone()方法。
3.在主类上添加子类的Clone方法。
实例代码如下:
bean:school,student。school中包含有student。main为执行方法。
student类和浅克隆的区别在于,实现了Cloneable接口。重写了clone方法。
package run;
public class student implements Cloneable{
private String gg;
public String getGgg() {
return gg;
}
public void setGgg(String ggg) {
this.gg = ggg;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "student{" +
"gg='" + gg + '\'' +
'}';
}
}
school类的clone方法是重点,它调用了子类的clone方法。所以才可以实现深度克隆。
package run;
public class school implements Cloneable{
private String name;
private String age;
private student st;
public student getSt() {
return st;
}
public void setSt(student st) {
this.st = st;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
protected school clone() throws CloneNotSupportedException {
school s = (school) super.clone();
s.st= (student) st.clone();
return s;
}
@Override
public String toString() {
return "school{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", st=" + st.toString() +
'}';
}
}
main方法执行没有改动:
package run;
public class main {
public static void main(String args[]) throws CloneNotSupportedException {
student b1 = new student();
b1.setGgg("123");
school school1 = new school();
school1.setAge("11");
school1.setName("lsp");
school1.setSt(b1);
school school2 = (school) school1.clone();
school2.setAge("26");
school2.getSt().setGgg("456");
System.out.println(school1);
System.out.println(school2);
}
}
输出如下:
school{name='lsp', age='11', st=student{gg='123'}}
school{name='lsp', age='26', st=student{gg='456'}}
分析:通过上述代码输出,可以看到引用数据类型,互不影响了。
补充:
1.重写,重写的返回值可以是原返回值,或者原返回值类型的子类。
2.java的基本类型,byte,short,int,long,char,float,double,boolean。但是非基本类型比如,integer,stringbuffere。这些非基本数据类型的对象。让他们实现Cloneable并重写clone非常的麻烦。所以大多数情况下,程序员还是会选择使用序列化的方式,完成对象克隆。
3.另外clone的写法。
默认生成如下:
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
优化生成:
@Override
protected Object clone() {
try {
return (student)super.clone();
}catch (CloneNotSupportedException e){
return null;
}
}