目录
浅拷贝
目标:通过调用object的clone方法将对象student1的内容拷贝到student2中去
省流总结:浅拷贝的常见问题:
1:Object的clone方法是protected修饰需要在克隆类中重写
2:编译时异常/受查异常
3:向下转型时需要强制类型转换
4:克隆类需要设置标记接口
浅拷贝过程和问题解析:
首先我们有一个Student对象并给他实例化
package csda克隆代码示范;
class Student{
public int age;
public Student(int age) {
this.age = age;
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student(30);//待拷贝内容
}
}
因为Java中所有的类都会默认继承Object父类,所以我们最简单直接的想法是直接通过student1调用Object的clone方法并用student2接收
不幸的是,student1无法调用clone方法——————————。。。。。。。。。。。。
为什么呢?
我们查看来查看object的clone方法的原码(双击shift键搜索object然后回车,在左侧Structure中找打clone方法)
clone方法如下:
真相大白!!!!
Object中clone方法是用protected修饰的!!!所以无法通过不同包的非子类来调用;
同时我们发现clone方法确实会返回一个满足我们拷贝需求的对象,并且是Object类型的(后面要用)。
那怎么解决这个问题呢?
答案是在我们的Student类中重写一个clone方法并在这个方法中用super调用Object类的clone方法、
快捷方法:右键+generate+Override Methdos+clone
结果:
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
同时,student1中可以点出clone方法
但是,这是我们发现clone方法还是错的。。。。。。。
这里的错误是受查异常/编译时异常,解决方法:鼠标放在clone上,同时alt+回车
选择第一个选项。
令人悲伤的:
红线变长了。。。。。
又一个错误出现。
我们回忆一下,刚刚clone方法的原码。。。。。
Object的clone方法会返回一个Object类型的对象给student中重写的clone方法,然后我们用student2来给这个对象实例化。
总结一下:我们将父类的Object类交给了子类Student。这是一个什么过程?——向下转型
向下转型需要将我们的父类强制类型转换成子类类型:
Student studen2 = (Student)student1.clone();
然后运行。你以为这就结束了?
。。。。。。。。。。。。。。。。
别急,还有最后一个问题需要解决(真的是最后一个)
上面的报错是:不支持克隆
我们需要在Student类继承一个克隆接口
class Student implements Cloneable{
}
这个接口是一个空接口也叫标记接口,证明当前类是可以被克隆的。
浅拷贝完整代码
package csda克隆代码示范;
class Student implements Cloneable{
public int age;//需要克隆的成员变量
//构造函数
public Student(int age) {
this.age = age;
}
@Override//重写的clone方法
protected Object clone() throws CloneNotSupportedException {
return super.clone();//调用Object的clone方法
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student(30);
Student student2 = (Student)student1.clone();
System.out.println(student1.age);
System.out.println(student2.age);
}
}
结果:
浅拷贝与深拷贝区别详解
我们现在写一个Number类,给Student类中设置一个Number类型的成员变量,并在Student的构造方法中给其实例化。
接下来在Test类中先输出student1和student2的值,然后修改student2的m的num值,发现student1中的m的num值也随之改变
package csda克隆代码示范;
//
class Number{
int num;
}
class Student implements Cloneable{
public int age;
public Number m;//在原来的代码的基础上添加一个Number类型的成员变量,名称为m
public Student(int age) {
this.age = age;
this.m = new Number();//实例化
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student(30);
Student student2 = (Student)student1.clone();
System.out.println(student1.m.num);
System.out.println(student2.m.num);
student1.m.num = 1;//更改student2的m的num的值
System.out.println(student1.m.num);
System.out.println(student2.m.num);
}
}
结果:
为什么我们更改了student2的m的num值,student1的m的num值也改变了?
浅拷贝图解:
当我们在student类中实例化一个m类后:
我们的super.clone克隆了student1中的内容,所以我们在student2中也实例化了一个Number类型的m对象,它们都引用了类Number,所以我们在通过student2更改m的值之后,student1的m的num值也会改变。
用上述代码我们在克隆一个对象(这个对象里还有一个对象)时,对象里的对象并不满足拷贝,这种情况我们就称作浅拷贝。
那怎么改成深拷贝呢?
实现深拷贝
1:在克隆对象里的对象对应的类重写克隆方法并添加标记接口(本文中时Number类)
class Number implements Cloneable{
int num;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2:设置一个过渡的类接收clone返回的克隆对象并返回tmp
protected Object clone() throws CloneNotSupportedException {
Student tmp = (Student)super.clone();
tmp.m = (Number)this.m.clone();
return tmp;
}
深拷贝完整代码:
package csda克隆代码示范;
class Number implements Cloneable{
int num;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Student implements Cloneable{
public int age;
public Number m;
public Student(int age) {
this.age = age;
this.m = new Number();
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student tmp = (Student)super.clone();
tmp.m = (Number)this.m.clone();
return tmp;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student(30);
Student student2 = (Student)student1.clone();
System.out.println(student1.m.num);
System.out.println(student2.m.num);
student2.m.num = 1;
System.out.println(student1.m.num);
System.out.println(student2.m.num);
}
}