他是java中的特殊函数:
protected native Object clone() throws CloneNotSupportedException;
protect: 表示clone()
的访问权限是保护的;由于他是protected的,再加之它位于java.lang
包中,而此包是jdk提供的包,不能修改的,所以我们一般不会将自己的运用程序的代码放在此目录下,所以clone()函数一般是不可见的遵守protected访问修饰符的规则可参考:protected的访问控制。所以最原始的clone()函数(即由Object声明)一般只会出现在下面两种情况:
clone()
函数;throws CloneNotSupportedException:
表示此clone函数可以抛出CloneNotSupportedException异常;所以,虽然Object类中定义了clone方法,但是,如果没有在类的定义中重写它,或者将其放在其他函数的实现中,则无法使用clone()的功能, 所以接下来,主要讲清如何让自定义的类可以使用clone()的功能,对clone()进行重写和将其放在其他成员函数中是类似 的
clone()
方法,则必须对clone()
方法重写。clone()
的类必须实现Cloneable
接口,同时还要捕捉CloneNotSupportedException
实现克隆重写的基本结构(很实用):
public Text implements Cloneable{
//Text的其他结构代码
//下面是重写 clone()函数的代码,实现的是最基本的浅克隆即实现最外层的数据克隆
public Text clone(){
Text clone = null; //clone为克隆对象的引用
try {
clone = (Name)super.clone(); //浅克隆返回克隆对象
}catch(CloneNotSupportedException e){
System.err.println(e.toString);
}//try.....至此是捕获异常,及处理异常,必不可少,此部分也是基本框架
/*
如果要进行更深层此的克隆,则可以从此出开始处理,实现深克隆
*/
return clone;
}
}
在上面的基本代码框架中,实现的是浅克隆。 实际上,所谓浅克隆实现的结果就是通过super.clone()新建一个Text类的匿名对象( 显然这个对象所在的空间和调用super.clone()函数的实例的堆空间不同的),由于clone函数的操作语言不是java,应该是C语言,所以clone()可以获得实例本身的地址,通过类本身的信息(类的结束位置,及变量的数据类型)及指针偏于,完全将实例本身的空间的数据,赋值给匿名对象所在的空间。
等等
那为什么不是通过super.clone()的调用,而内部实现的操作是对象本身通过实例名.成员名
来进行复制呢?原因是由于类的结构并不是唯一的,固定的,不同的类之间千差万别,所以super.clone()根本无法对类的结构进行统一的描述,加之clone并不是最好的解决问题的办法或者说在需要时也可以通过其他方法实现同样的目的,所有,java开发者也不会把精力用在clone的问题上。
回过头来
最终用clone引用指向这个匿名变量,这就是所谓的浅克隆!!
然在内存是上,浅克隆的引用与原实例的引用是完全不同的,但是,但是,他们之间有时是相互影响的,例如对克隆的操作,可能会影响到原实例的值。
而所谓深克隆,就是克隆之后对两者之间的操作永远不会互相影响!!
package text;
class A implements Cloneable {
int a;
int b;
public static void main(String[] args){
A aA = new A(1, 2); //实例a
A bA = aA.clone(); //由a克隆浅克隆而来的b
//输出aA和bA
aA.showA();
aA.showA();
//修改aA的值
aA.setA(3, 4);
//输出
aA.showA();
bA.showA();
}
A(int a, int b){//构造函数
this.a = a;
this.b = b;
}
void setA(int a, int b){
this.a = a;
this.b = b;
}
void showA(){
System.out.print("a = " + a);
System.out.println("\tb = " + b);
}
public A clone(){//重写克隆
A clone = null;
try {
clone = (A)super.clone();
}catch(CloneNotSupportedException e){
System.err.println(e.toString);
}
return clone;
}
}
//输出结果:
a = 1 b = 2
a = 1 b = 2
a = 3 b = 4
a = 1 b = 2
//所以此时浅拷贝相当于深拷贝
package text;
class B{//类B中只有基本数据成员
private int a = 1;
private int b = 1;
void setB(int a, int b){
this.a = a;
this.b = b;
}
void showB(){
System.print("in class B part : " + a "\t" + b);
}
class A implements Cloneable {
String str;
B bB;
public static void main(String[] args){
A aA = new A(); //实例a
A bA = aA.clone(); //由a克隆浅克隆而来的b
//输出aA和bA
aA.showA();
aA.showA();
//修改aA的值
aA.setA("jjf_1, 2, 2);
//输出
aA.showA();
bA.showA();
}
A(){//构造函数
str = "jjf";
bB = new B()
}
void setA(string str, int a, int b){
this.str = str;
bB.setB(a, b);
}
void showA(){
System.out.print("str = " + str + "\t")
System.out.println(bB.showB);
}
public A clone(){//重写克隆
A clone = null;
try {
clone = (A)super.clone();
}catch(CloneNotSupportedException e){
System.err.println(e.toString);
}
return clone;
}
}
//输出结果为
jjf in class B part : 1 1
jjf in class B part : 1 1
jjf_1 in class B part : 2 2
jjf in class B part : 1 1
//由结果可以看出,此时浅拷贝相当于深拷贝
package text;
class B{//类B中只有基本数据成员
private String str;
B(String str){
thsi.str = str;
}
void setB(String str){
this.str = str;
}
void showB(){
System.print("in class B part : " + s);
}
class A implements Cloneable {
String str;
B bB;
public static void main(String[] args){
A aA = new A(jjf, zxm); //实例a
A bA = aA.clone(); //由a克隆浅克隆而来的b
//输出aA和bA
aA.showA();
aA.showA();
//修改aA的值
aA.setA("jjf_1, zxm_1);
//输出
aA.showA();
bA.showA();
}
A(String str1, String str2){//构造函数
str = str1;
bB = new B(str2);
}
void setA(string str1, String str2){
str = str1;
bB.setB(str2);
}
void showA(){
System.out.print("str = " + str + "\t")
System.out.println(bB.showB);
}
public A clone(){//重写克隆
A clone = null;
try {
clone = (A)super.clone();
}catch(CloneNotSupportedException e){
System.err.println(e.toString);
}
return clone;
}
}
//输出结果为:
jjf in class B part : zxm
jjf in class B part : zxm
jjf_1 in class B part : zxm_1
jjf in class B part : zxm_1
由输出结果可以看到bA.bB.str
因为通过aA.setA()
函数调用bB.setB()
函数,改变了aA.bB.str
,同时也使bA.bB.str
的值改变,而bA.str
的值并没有因为aA.setA()
,而改变,其还是jjf
,而和aA.str
的jjf_1
不同。这就是上面所说的情况。然而为什么会造成这种情况?
回顾一下文章的中间所说的:浅克隆的结果。。。
我们可以发现原来啊:实例aA
与克隆实例bA
中有自己的数据成员空间:str(String类型)
和 bB(class B
类型),这不很明显吗?他们都是引用类型,他们并不是String
对象(jjf)本身和类(new B())
本身,str
的值只是"jjf
"在堆中的地址,bB
的值只是new B()
对象的地址。
所以虽然aA
与bA
自有空间,不过他们自己数据成员的值指向的是同一堆空间:
所以:
str
存放的是引用,在改变str
的值其实是让其指向其他空间,所以,改的是地址,不同两个实例中str
的值不一样表示指向的是不同的地址,改地址的内容由字符串本身决定或者由bB
对象本身决定。aA.bB
和bA.bB
都是引用变量,bA
实例由aA
实例克隆而来,所以aA.bB
的值与bA.bB
的值一样,即他们都指向同一堆空间,即这两个引用公用同一空间,setA(jjf_1, zxm_1)
语句,jjf_1
是用来改str的值的,而zxm_1
是用来改bB.str
的值的,所以,最终结果是aA.str
指向的是jjf_1
的字符串,而aA.bB.str
指向的是zxm_1
字符串,由于aA.bB
与bA.bB
是一样的,所以bA.bB.str
也指向zxm_1
字符串,zxm
则成为无引用指向的垃圾值!要决解例题3的问题,实现深克隆:只需在在浅克隆的基础上加一条语句:
clone.bB = new B("jjf");
public A clone(){//重写克隆
A clone = null;
try {
clone = (A)super.clone();
}catch(CloneNotSupportedException e){
System.err.println(e.toString);
}
clone.bB = new B("jjf"); //深克隆
return clone;
}
即:深克隆是建立在浅克隆的基础上的。
至此:深浅克隆及堆地址的关系细述完毕
下面是主要的问题:
java对于所有数组都自定义了clone()函数,即,数组对象是自带clone()的,此外字符串,基本数据机基本数据类型一般不会有clone()函数,如果实现该函数,将十分多余的愚蠢,而实际上,数据复制,并非只能clone(),