[计院]Java对象克隆(拷贝)

115 阅读3分钟
标记接口Clonable
浅拷贝
深拷贝

一、标记接口Clonable

声明如下
public interface Clonable{
}
可以注意到这个接口内没有定义任何方法。
如果一个类实现(implements )了Clonable接口,意味着这个类内重写了Object的clone方法。
如果类内重写了Object的clone方法却没有实现Clonable接口,编译器会抛出CloneNoSupportedException异常。
在Object类中的clone方法是protected方法,关于这个复杂的权限问题请看:
Java中protected方法访问权限的问题
子类必须重写父类方法,将clone方法设为public 或 protected 方法,子类的实例对象才有可能在其他类中调用重写后的clone方法,
否则不能在其他类中用 子类的实例对象 调用 父类的protected方法(除非大家都在一个包内)。
子类重写时还能修改clone的返回类型。

二、浅拷贝

浅拷贝有两种层次:
①使用 = 号进行拷贝:被拷贝的对象并没有多一份,还是原来那一份,只是多了个新的对象变量去引用它。
class Person{
    private String name = "first";
    private Student student;
    public void changeName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
}
class Man{
    private int id = 7;
}
public class Test{
    public static void main(String[] args){
        Person oldP = new Person();
        Person newP = oldP;
    } 
}
这里的newP 和 oldP 指向内存空间的同一个对象,newP对自己指向的对象进行修改操作等于对oldP指向的对象进行修改。
public class Test{
    public static void main(String[] args){
        Person oldP = new Person();
        Person newP = oldP;
        newP.changeName("Second");
        System.out.println(oldP.getName());
        
    } 
最后一条输出语句将会输出Second,说明newP做的操作是在oldP指向的对象上进行的,二者指向同一对象。
②使用object类中的clone方法:进行更深一点的拷贝,但是还不够深。
如第一部分所言,必须实现Clonable接口,但在重写Object类的clone方法时 调用的是父类用protected保护的clone方法,并没有定义新花样。
class Person implements Clonable{
    private String name = "first";
    private Student student;
    public void changeName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    @Override
    public Person clone() throws CloneNoSupportException{
        return (Employee)super.clone();
    }
    
    /**新增两个方法*/
    public void setId(int id){
        this.student.id = id;
    }
    public void getId(){
        return this.student.id;
}
class Man{
    private int id = 7;
}
public class Test{
    public static void main(String[] args){
        Person oldP = new Person();
        Person newP = oldP.clone();  //实现Clonable的目的是为了此时oldP对象可以在Test类中调用clone方法(此时oldP的clone方法内再调用Object类中用protected保护的clone方法)
    } 
}
oldP和newP引用的对象关系如下图:
接着使用newP进行修改操作:
public class Test{
    public static void main(String[] args){
        Person oldP = new Person();
        Person newP = oldP.clone();  //实现Clonable的目的是为了此时oldP对象可以在Test类中调用clone方法(此时oldP的clone方法内再调用Object类中用protected保护的clone方法)
        newP.changeName("Second");
        System.out.println(oldP.getName());  //输出:First
        newP.setId(20);
        System.out.println(oldP.getId());    //输出:20
    } 
}
输出结果表明,实际情况是图中所示的情况。Object类中的clone方法没有拷贝对象中定义的其他类型对象,话不多说,看图就成。

三、深拷贝

直接上图,深拷贝就是要旧的和新的 除了值相等外,其他没有瓜葛。
class Person implements Clonable{
    private String name = "first";
    private Student student;
    public void changeName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    @Override
    public Person clone() throws CloneNoSupportException{
        Person newOne = (Person) super.clone();
        newOne.student = (Student) student.clone();
        return newOne;
        
    }
    
    /**新增两个方法*/
    public void setId(int id){
        this.student.id = id;
    }
    public void getId(){
        return this.student.id;
}
class Man{
    private int id = 7;
}
public class Test{
    public static void main(String[] args){
        Person oldP = new Person();
        Person newP = oldP.clone();  //实现Clonable的目的是为了此时oldP对象可以在Test类中调用clone方法(此时oldP的clone方法内再调用Object类中用protected保护的clone方法)
    } 
} 
执行以下修改操作
public class Test{
    public static void main(String[] args){
        Person oldP = new Person();
        Person newP = oldP.clone();  //实现Clonable的目的是为了此时oldP对象可以在Test类中调用clone方法(此时oldP的clone方法内再调用Object类中用protected保护的clone方法)
        newP.changeName("Second");
        System.out.println(oldP.getName());  //输出:First
        newP.setId(20);
        System.out.println(oldP.getId());    //输出:7
    } 
}
newP 和 oldP 对象各自修改各自的,互不影响。