• Java 克隆


    1.为什么要克隆?

    新new一个不好吗?new一个的状态是初始值,如果改变了某个属性值,则需要通过相同的改变操作使得new出来的对象和现对象值相同。克隆可直接复制当前对象的任何值。并且初始化可能费时间比较多。克隆有浅克隆和深克隆。


    2.浅克隆

    需要重写Cloneable接口中的clone()方法。

    package my_test;
    
    public class TestCopy {
        public static void main(String[] args) throws Exception{
            Monkey m1=new Monkey();
            m1.setId(66);
            Monkey m2=(Monkey) m1.clone();
            Monkey m3=(Monkey)m2.clone();
            m2.setId(99);
            Monkey m4=m1;
            System.out.println("m1.id="+m1.getId()+" getClass()="+m1.getClass());
            System.out.println("m2.id="+m2.getId()+" getClass()="+m2.getClass());
            System.out.println("m3.id="+m3.getId()+" getClass()="+m3.getClass());
            System.out.println("m3.id="+m3.getId()+" getClass()="+m4.getClass());
            System.out.println(m1==m2);
            System.out.println(m1==m4);
            System.out.println(m1);
            System.out.println(m2);
            System.out.println(m3);
            System.out.println(m4);
        }
    }
    
    class Monkey implements Cloneable {
        private int id;
        public void setId(int id) {
            this.id = id;
        }
        public int getId() {
            return id;
        }
        public Object clone() throws CloneNotSupportedException{
            return super.clone();
        }
    }
    View Code

    输出:

    m1.id=66 getClass()=class my_test.Monkey
    m2.id=99 getClass()=class my_test.Monkey
    m3.id=66 getClass()=class my_test.Monkey
    m3.id=66 getClass()=class my_test.Monkey
    false
    true
    my_test.Monkey@15db9742
    my_test.Monkey@6d06d69c
    my_test.Monkey@7852e922
    my_test.Monkey@15db9742

    m2是通过m1克隆的,m3是通过m2克隆的,m4和m1指向同一个堆内存,再修改m2的值,通过返回toString()方法可知。m1、m2、m3地址各不相同,m1和m4地址相同,修改m2的值,不影响m1和m3,getClass()返回的类型相同。表明通过clone()方法克隆出的对象是与原对象是独立的,开辟了新的堆内存。

    在Monkey类里添加引用类型Peach进行测试

    package my_test;
    
    public class TestCopy {
        public static void main(String[] args) throws Exception{
            Peach peach=new Peach();
            peach.setName("猕猴桃");
            Monkey m1=new Monkey();
            m1.setId(66);
            m1.setP(peach);
            Monkey m2=(Monkey)m1.clone();
            System.out.println("m1.id="+m1.getId()+"  m1.p="+m1.getP()+"  p.name="+m1.getP().getName());
            System.out.println("m2.id="+m2.getId()+"  m2.p="+m2.getP()+"  p.name="+m2.getP().getName());
            m1.setId(99);
            peach.setName("水蜜桃");
            System.out.println("m1.id="+m1.getId()+"  m1.p="+m1.getP()+"  p.name="+m1.getP().getName());
            System.out.println("m2.id="+m2.getId()+"  m2.p="+m2.getP()+"  p.name="+m2.getP().getName());
        }
    }
    
    class Monkey implements Cloneable {
        private int id;
        private Peach p;
        public void setId(int id) {
            this.id = id;
        }
        public int getId() {
            return id;
        }
        public Object clone() throws CloneNotSupportedException{
            return super.clone();
        }
        public void setP(Peach p) {
            this.p=p;
        }
        public Peach getP() {
            return p;
        }
    }
    
    class Peach{
        private String name;
        public void setName(String name) {
            this.name = name;
        }
        public String getName() {
            return name;
        }
    }

    输出:

    m1.id=66 m1.p=my_test.Peach@15db9742 p.name=猕猴桃
    m2.id=66 m2.p=my_test.Peach@15db9742 p.name=猕猴桃
    m1.id=99 m1.p=my_test.Peach@15db9742 p.name=水蜜桃
    m2.id=66 m2.p=my_test.Peach@15db9742 p.name=水蜜桃

    m2是m1的克隆对象,m1和m2相互独立,但是 类中的引用类peach 是同一片内存,所以peach内容一改,m1和m2中的peach都改。

    通过Clone()方法实现的浅克隆

    在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;

    如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址

    简单来说,在浅克隆中,当对象被复制时 只 复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。即复制不完全,不够深刻。


    3.深克隆

    要完完全全深深刻刻地全部复制,也可通过Clone()方法在浅复制中嵌套浅复制。

    package my_test;
    
    public class TestCopy {
        public static void main(String[] args) throws Exception{
            Peach peach=new Peach();
            peach.setName("猕猴桃");
            Monkey m1=new Monkey();
            m1.setId(66);
            m1.setP(peach);
            Monkey m2=(Monkey)m1.clone();
            System.out.println("m1.id="+m1.getId()+"  m1.p="+m1.getP()+"  p.name="+m1.getP().getName());
            System.out.println("m2.id="+m2.getId()+"  m2.p="+m2.getP()+"  p.name="+m2.getP().getName());
            m1.setId(99);
            peach.setName("水蜜桃");
            System.out.println("m1.id="+m1.getId()+"  m1.p="+m1.getP()+"  p.name="+m1.getP().getName());
            System.out.println("m2.id="+m2.getId()+"  m2.p="+m2.getP()+"  p.name="+m2.getP().getName());
        }
    }
    
    class Monkey implements Cloneable {
        private int id;
        private Peach p;
        public void setId(int id) {
            this.id = id;
        }
        public int getId() {
            return id;
        }
        public void setP(Peach p) {
            this.p=p;
        }
        public Peach getP() {
            return p;
        }
        public Object clone() throws CloneNotSupportedException{//修改猴子类中的Clone()方法
            Monkey monkey=null;
            try {
                monkey=(Monkey)super.clone();//先实现一下浅复制
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            monkey.p=(Peach)p.Clone();//深复制 = 浅复制 套 浅复制
            return monkey;
        }
    }
    
    class Peach implements Cloneable{//桃子类也实现来实现Clone()方法
        private String name;
        public void setName(String name) {
            this.name = name;
        }
        public String getName() {
            return name;
        }
        public Object Clone() {
            Peach peach=null;
            try {
                peach = (Peach)super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return peach;
        }
    }

    输出:

    m1.id=66 m1.p=my_test.Peach@15db9742 p.name=猕猴桃
    m2.id=66 m2.p=my_test.Peach@6d06d69c p.name=猕猴桃
    m1.id=99 m1.p=my_test.Peach@15db9742 p.name=水蜜桃
    m2.id=66 m2.p=my_test.Peach@6d06d69c p.name=猕猴桃

    此时改了m1中的p,不影响m2中的p,实现了完全复制。类中类内存也相互独立。

    简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。

    用Clone()实现克隆的问题所在:如果多层引用类型,岂不是一层套一层套到天亮,为此有其他方法(序列化)可以实现深克隆。

    package my_test;
    
    import java.io.*;
    
    public class TestCopy {
        public static void main(String[] args) throws Exception{
            Peach peach=new Peach();
            peach.setName("猕猴桃");
            Monkey m1=new Monkey();
            Monkey m2=null;
            m1.setId(66);
            m1.setP(peach);
            try {
                m2=(Monkey)m1.deepClone();
            } catch (Exception e) {
                e.printStackTrace();
            }
            
            System.out.println("1  m1.id="+m1.getId()+"  m1.p="+m1.getP()+"  p.name="+m1.getP().getName());
            System.out.println("2  m2.id="+m2.getId()+"  m2.p="+m2.getP()+"  p.name="+m2.getP().getName());
            
            m1.setId(99);
            peach.setName("水蜜桃");
            System.out.println("3  m1.id="+m1.getId()+"  m1.p="+m1.getP()+"  p.name="+m1.getP().getName());
            System.out.println("4  m2.id="+m2.getId()+"  m2.p="+m2.getP()+"  p.name="+m2.getP().getName());
        }
    }
    
    class Monkey implements Serializable {
        private int id;
        private Peach p;
        public void setId(int id) {
            this.id = id;
        }
        public int getId() {
            return this.id;
        }
        public void setP(Peach p) {
            this.p=p;
        }
        public Peach getP() {
            return this.p;
        }
        public Object deepClone() throws IOException,ClassNotFoundException,OptionalDataException {
            //将对象写入流中
            ByteArrayOutputStream bao=new ByteArrayOutputStream();
            ObjectOutputStream oos=new ObjectOutputStream(bao);
            oos.writeObject(this);
            
            //将对象从流中取出
            ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());
            ObjectInputStream ois=new ObjectInputStream(bis);
            return (ois.readObject());
        }
    }
    
    class Peach implements Serializable{//类中的成员类也需要 实现这个接口
        private String name;
        public void setName(String name) {
            this.name = name;
        }
        public String getName() {
            return name;
        }
    }

    输出:

    m1.id=66 m1.p=my_test.Peach@55f96302 p.name=猕猴桃
    m2.id=66 m2.p=my_test.Peach@214c265e p.name=猕猴桃
    m1.id=99 m1.p=my_test.Peach@55f96302 p.name=水蜜桃
    m2.id=66 m2.p=my_test.Peach@214c265e p.name=猕猴桃

    通过序列化实现深克隆,成员的引用类型太多则不需要像Clone()那样实现太多方法。

    实现步骤:

    1. 需要克隆的类无论是主类还是成员类都要实现Serializable接口
    2. 写一个deepClone()方法搞定一切,代码照抄,异常抛出照抄

    参考:https://www.cnblogs.com/Qian123/p/5710533.html#_label0

  • 相关阅读:
    Socket.IO 客户端 API IO
    约束,在ios8 没问题,在ios7崩溃的问题,UItextField
    在Viewdidload里面给self.View加动画无效的问题
    改变UITextField PlaceHolder的字体和颜色,不影响正常输入
    iOS第三方键盘高度获取不准确问题
    tableViewCell的分割线短一截的问题
    iOS开发中模拟器归档成功,但是真机归档失败的问题
    C语言——数组、字符串
    C语言——函数
    C语言——流程控制
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/11742401.html
Copyright © 2020-2023  润新知