• java 对象克隆


    内容 为 其他博客复制

    Java克隆(Clone)的应用
     
     
    简介:
     
    Java克隆(Clone)是Java语言的特性之一,但在实际中应用比较少见。但有时候用克隆会更方便更有效率。
     
    对于克隆(Clone),Java有一些限制:
    1、被克隆的类必须自己实现Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。Cloneable 接口实际上是个标识接口,没有任何接口方法。
    2、实现Cloneable接口的类应该使用公共方法重写 Object.clone(它是受保护的)。某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的,也无法保证它将获得成功。
    3、在Java.lang.Object类中克隆方法是这么定义的:
    protected Object clone()
                    throws CloneNotSupportedException
    创建并返回此对象的一个副本。表明是一个受保护的方法,同一个包中可见。
    按照惯例,返回的对象应该通过调用 super.clone 获得。
     
     
    引题:
     
    举个例子说吧,现在有一个对象比如叫foo,你需要在创建当前对象的一个副本作为存根你能怎么做?
     
    假如你不用Clone,那么你可以先new一个对象foo1:Foo foo1=new Foo(),然后用foo给foo1对象set值,这样就得到foo的副本foo1;除此之外,别无选择。
     
    这样说,也许有人会觉得说的过于绝对了,不过事实如此啊。
     
    要产生一个副本,那副本要不要内存?----当然要了,那就对了!既然需要内存,(不克隆的情况下)你不new还有什么办法呢?请大家时刻铭记对象是Java运行时产生的,驻留在计算机内存中。
     
    常见错误:
    下面我澄清几个初学者容易犯迷糊的错误,同样的问题,产生foo对象的副本:
    1、Foo foo1=new Foo();
       foo1=foo;
       然后就想当然的认为副本foo1生成了!
     
    错误原因:foo1没错是申请了内存,但是执行foo1=foo后,foo1就不在指向刚申请的内存区域了,转而指向foo对象的内存区域,这时候,foo1、foo指向了同一内存区域。刚才new的操作制造一堆垃圾等着JVM回收。
     
    2、Foo foo1=foo;
    错误原因:还是两个变量都指向了同一块内存。
     
    3、有些老鸟更厉害一些:在Foo中定义一个返回自身的方法:
        public Foo getInstance(){
            return this;
        }
     
        然后,Foo foo1=foo.getInstance();
     
    错误原因:同上,主要还是没有重新开辟内存,this在对象里是什么?----就是对象自己的引用!那么getInstance()自然返回的就是对象自己,反正又是两个对象穿了一条裤子----***,哈哈。错得心服口服吧。为了节省篇幅,我在最后写个例子,留给那些对此有异议的人看。
     
    引入克隆
     
    看了这么多方法都不行,还很麻烦!干脆用克隆吧,简单明了。
     
    废话不说了,看例子:
    定义两个类CloneFooA、CloneFooB,然后写个测试类CloneDemo分别克隆这两个类的对象,然后打印测试结果到控制台。
     
     
    /**
     * Created by IntelliJ IDEA.
     * User: leizhimin
     * Date: 2007-9-20
     * Time: 19:40:44
     * 简单类克隆实现
     * 要实现克隆,必须实现Cloneable接口,这是一个标识接口,没有接口方法
     * 实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。
     * 按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。
     */
    public class CloneFooA implements Cloneable {
        private String strA;
        private int intA;
     
        public CloneFooA(String strA, int intA) {
            this.strA = strA;
            this.intA = intA;
        }
     
        public String getStrA() {
            return strA;
        }
     
        public void setStrA(String strA) {
            this.strA = strA;
        }
     
        public int getIntA() {
            return intA;
        }
     
        public void setIntA(int intA) {
            this.intA = intA;
        }
     
        /**
         * @return 创建并返回此对象的一个副本。
         * @throws CloneNotSupportedException
         */
        public Object clone() throws CloneNotSupportedException {
            //直接调用父类的clone()方法,返回克隆副本
            return super.clone();
        }
    }
     
     
    /**
     * Created by IntelliJ IDEA.
     * User: leizhimin
     * Date: 2007-9-20
     * Time: 19:59:55
     * 深度克隆对象,当类存在聚合关系的时候,克隆就必须考虑聚合对象的克隆
     */
    public class CloneFooB implements Cloneable {
        private CloneFooA fooA;
        private Double douB;
     
        public CloneFooB(Double douB) {
            this.douB = douB;
        }
     
        public CloneFooB(CloneFooA fooA, Double douB) {
            this.fooA = fooA;
            this.douB = douB;
        }
     
        public CloneFooA getFooA() {
            return fooA;
        }
     
        public void setFooA(CloneFooA fooA) {
            this.fooA = fooA;
        }
     
        public Double getDouB() {
            return douB;
        }
     
        public void setDouB(Double douB) {
            this.douB = douB;
        }
     
        /**
         * 克隆操作
         *
         * @return 自身对象的一个副本
         * @throws CloneNotSupportedException
         */
        public Object clone() throws CloneNotSupportedException {
            //先调用父类的克隆方法进行克隆操作
            CloneFooB cloneFooB = (CloneFooB) super.clone();
            //对于克隆后出的对象cloneFooB,如果其成员fooA为null,则不能调用clone(),否则出空指针异常
            if (this.fooA != null)
                cloneFooB.fooA = (CloneFooA) this.fooA.clone();
     
            return cloneFooB;
        }
    }
     
     
    /**
     * Created by IntelliJ IDEA.
     * User: leizhimin
     * Date: 2007-9-20
     * Time: 19:52:01
     * 测试类:分别克隆CloneFooA和CloneFooB类,并打印克隆前后的结果.
     */

    public class CloneDemo {
        public static void main(String args[]) throws CloneNotSupportedException {
            //CloneFooA克隆前
            CloneFooA fooA1 = new CloneFooA("FooA", 11);
            System.out.println("CloneFooA的对象克隆前对象fooA1值为: " + fooA1.getStrA() + "," + fooA1.getIntA());
            //CloneFooA克隆后
            CloneFooA fooA2 = (CloneFooA) fooA1.clone();
            System.out.println("CloneFooA的对象克隆后对象fooA2值为: " + fooA2.getStrA() + "," + fooA2.getIntA());
            //比较fooA1和fooA2内存地址
            if (fooA1 == fooA2) System.out.println("比较fooA1和fooA2内存地址:相等!");
            else System.out.println("比较fooA1和fooA2内存地址:不相等!");
     
            System.out.println("-------------------------");
     
            //CloneFooB克隆前
            CloneFooB fooB1 = new CloneFooB(fooA1, new Double("33"));
            System.out.println("CloneFooB的对象克隆前对象fooB1值为: " + fooB1.getFooA().getStrA() + "," + fooB1.getFooA().getIntA() + " | " + fooB1.getDouB());
            //CloneFooB克隆后
            CloneFooB fooB2 = (CloneFooB) fooB1.clone();
            System.out.println("CloneFooB的对象克隆前对象fooB2值为: " + fooB2.getFooA().getStrA() + "," + fooB2.getFooA().getIntA() + " | " + fooB2.getDouB());
     
            if (fooA1 == fooA2) System.out.println("比较fooB1和fooB2内存地址:相等!");
            else System.out.println("比较fooB1和fooB2内存地址:不相等!");
        }
    }
     
    运行结果:
     
    CloneFooA的对象克隆前对象fooA1值为: FooA,11
    CloneFooA的对象克隆后对象fooA2值为: FooA,11
    比较fooA1和fooA2内存地址:不相等!
    -------------------------
    CloneFooB的对象克隆前对象fooB1值为: FooA,11 | 33.0
    CloneFooB的对象克隆前对象fooB2值为: FooA,11 | 33.0
    比较fooB1和fooB2内存地址:不相等!
     
    Process finished with exit code 0
     
     
    反面教材:
     
    最后,我给出我上面提出到最后要给出的反面例子。
     
    随便写一个,在CloneFooA 的基础上做了少许改动,内容如下:
     
    public class CloneFooA implements Cloneable {
        private String strA;
        private int intA;
     
        public CloneFooA(String strA, int intA) {
            this.strA = strA;
            this.intA = intA;
        }
     
        public String getStrA() {
            return strA;
        }
     
        public void setStrA(String strA) {
            this.strA = strA;
        }
     
        public int getIntA() {
            return intA;
        }
     
        public void setIntA(int intA) {
            this.intA = intA;
        }
     
        /**
         * @return 创建并返回此对象的一个副本。
         * @throws CloneNotSupportedException
         */
        public Object clone() throws CloneNotSupportedException {
            //直接调用父类的clone()方法,返回克隆副本
            return super.clone();
        }
     
        /**
         * @return 返回运行时的对象
         */
        public CloneFooA getInstance(){
            return this;
        }
     
        public static void main(String args[]){
            CloneFooA fooA=new CloneFooA("aa",11);
            System.out.println(fooA.getStrA()+"  "+fooA.getIntA());
     
            CloneFooA fooA1=fooA.getInstance();
            System.out.println(fooA1.getStrA()+"  "+fooA1.getIntA());
            if(fooA==fooA1) System.out.println("fooA和fooA1内存地址相等!");
     
            System.out.println("-------------------------");
     
            //改变后fooA或者fooA1中任何一个,看看另外一个是否会改变
            fooA1.setStrA("bb");
            System.out.println(fooA.getStrA()+"  "+fooA.getIntA());
            System.out.println(fooA1.getStrA()+"  "+fooA1.getIntA());
     
            if(fooA==fooA1) System.out.println("fooA和fooA1内存地址相等,改变fooA1后,fooA的值也跟着变化了");
        }
    }
     
    深度可能方法二
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;

    public class MyUtil {

        private MyUtil() {
            throw new AssertionError();
        }

        @SuppressWarnings("unchecked")
        public static <T extends Serializable> T clone(T obj) throws Exception {
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bout);
            oos.writeObject(obj);

            ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bin);
            return (T) ois.readObject();

            // 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义
            // 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放
        }
    }
     
    import java.io.Serializable;

    class Person implements Serializable {
        private static final long serialVersionUID = -9102017020286042305L;

        private String name;    // 姓名
        private int age;        // 年龄
        private Car car;        // 座驾

        public Person(String name, int age, Car car) {
            this.name = name;
            this.age = age;
            this.car = car;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public Car getCar() {
            return car;
        }

        public void setCar(Car car) {
            this.car = car;
        }

        @Override
        public String toString() {
            return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
        }

    }

    class Car implements Serializable {
        private static final long serialVersionUID = -5713945027627603702L;

        private String brand;       // 品牌
        private int maxSpeed;       // 最高时速

        public Car(String brand, int maxSpeed) {
            this.brand = brand;
            this.maxSpeed = maxSpeed;
        }

        public String getBrand() {
            return brand;
        }

        public void setBrand(String brand) {
            this.brand = brand;
        }

        public int getMaxSpeed() {
            return maxSpeed;
        }

        public void setMaxSpeed(int maxSpeed) {
            this.maxSpeed = maxSpeed;
        }

        @Override
        public String toString() {
            return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";
        }

    }

    class CloneTest {

        public static void main(String[] args) {
            try {
                Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300));
                Person p2 = MyUtil.clone(p1);   // 深度克隆
                p2.getCar().setBrand("BYD");
                // 修改克隆的Person对象p2关联的汽车对象的品牌属性
                // 原来的Person对象p1关联的汽车不会受到任何影响
                // 因为在克隆Person对象时其关联的汽车对象也被克隆了
                System.out.println(p1);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
  • 相关阅读:
    golang json字符串合并操作
    goland 无法跳转 struct等
    golang 中mgo update报错: The dollar ($) prefixed field '$inc' in '$inc' is not valid for storage.
    解决windows下使用vscode没有函数提示的问题
    【转载,实测好用】gitlab结合sourcetree使用
    C++单继承、多继承情况下的虚函数表分析
    Linux 日志文件管理——限制大小
    C++ RCSP智能指针简单实现与应用
    Makefile模板(C++)
    Git关于pull,commit,push的总结
  • 原文地址:https://www.cnblogs.com/ckaifeng/p/7070449.html
Copyright © 2020-2023  润新知