美文网首页
JAVA设计模式:原型模式与拷贝

JAVA设计模式:原型模式与拷贝

作者: vczyh | 来源:发表于2020-02-18 14:46 被阅读0次

    深拷贝与浅拷贝

    浅拷贝:对象A进行赋值操作得到对象B,这就是浅拷贝,修改对象A的属性会影响到B的属性

    // 引用类型 sb1调用自身方法会影响到sb2,赋值操作就是对地址的复制,指向同一个实例
    StringBuilder sb1 = new StringBuilder("hello");
    StringBuilder sb2 = sb1;
    sb1.append(" world");
    System.out.println(sb1.toString());  // hello world
    System.out.println(sb2.toString());  // hello world
    

    深拷贝:深拷贝就是希望对象A和对象B的操作互不影响。

    如何实现深拷贝

    // 对User的对象进行深拷贝
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
    
        private String name;
        private Address address;
    
    }
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    class Address {
        String province;
        String city;
    }
    
    方法一:使用new
    // 被复制的对象
    Address address = new Address("hebei", "zhangjiakou");
    User user = new User("zhang", address);
    // 使用 new 深拷贝
    Address addressCopy = new Address(address.getProvince(), address.getCity());
    User userCopy = new User(user.getName(), addressCopy);
    

    当嵌套的对象越来越多,这种方法显得繁琐而且易出错

    方法二:使用clone()

    既然是复制,那么可以把User实例所在的内存区域拷贝一份,然后用新引用指向新区域,事实上Java也提供了这样的操作,即 Object.clone()

    进行拷贝的类需要实现Cloneable接口,这是个标记接口,没有任何方法,实现这个接口的类表示调用clone()合法。不实现Cloneable调用clone()会抛出CloneNotSupportedException

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User implements Cloneable{
    
        private String name;
        private Address address;
    
        @Override
        protected User clone() throws CloneNotSupportedException {
            return (User) super.clone();
        }
    }
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    class Address {
        String province;
        String city;
    }
    
    // 被复制的对象
    Address address = new Address("hebei", "zhangjiakou");
    User user = new User("zhang", address);
    // 使用 clone() 深拷贝
    User userCopy = user.clone();
    // 检查
    user.getAddress().setCity("handan");
    System.out.println(userCopy.getAddress().getCity()); // handan
    

    这依然是浅拷贝,因为user实例内存区域的address对象依然是个地址,所以需要对address进行拷贝。

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User implements Cloneable {
    
        private String name;
        private Address address;
        
        // change
        @Override
        protected User clone() throws CloneNotSupportedException {
            User user = (User) super.clone();
            Address address = user.getAddress().clone();
            user.setAddress(address);
            return user;
        }
    }
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    class Address implements Cloneable {
        String province;
        String city;
        
        // change
        @Override
        protected Address clone() throws CloneNotSupportedException {
            return (Address) super.clone();
        }
    }
    
    // 被复制的对象
    Address address = new Address("hebei", "zhangjiakou");
    User user = new User("zhang", address);
    // 使用 clone() 深拷贝
    User userCopy = user.clone();
    // 检查
    user.getAddress().setCity("handan");
    System.out.println(userCopy.getAddress().getCity()); // zhangjiakou
    

    这样在调用上比new优雅许多,但在clone()里面也需要注意嵌套调用,那么有没有更方便的方法呢。

    方法三:序列化

    首先是JAVA自带的序列化功能

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User implements Serializable {
    
        private String name;
        private Address address;
        // change
        public User deepClone() throws IOException, ClassNotFoundException {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (User) ois.readObject();
        }
    }
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    class Address implements Serializable {
        String province;
        String city;
    }
    
    // 被复制的对象
    Address address = new Address("hebei", "zhangjiakou");
    User user = new User("zhang", address);
    // 使用 Serialize 深拷贝 change
    User userCopy = user.deepClone();
    // 检查
    user.getAddress().setCity("handan");
    System.out.println(userCopy.getAddress().getCity()); // zhangjiakou
    

    使用JSON序列化也可以

    // 被复制的对象
    Address address = new Address("hebei", "zhangjiakou");
    User user = new User("zhang", address);
    // 使用 JSON序列化 深拷贝
    ObjectMapper mapper = new ObjectMapper();
    String json = mapper.writeValueAsString(user);
    User userCopy = mapper.readValue(json, User.class);
    // 检查
    user.getAddress().setCity("handan");
    System.out.println(userCopy.getAddress().getCity()); // zhangjiakou
    

    原型模式

    简单来说,原型模式就是通过一个方法获得一个实例的深拷贝,这里的深拷贝是通过clone(),具体代码就是上面的代码,原型模式很简单,主要是理解浅拷贝和深拷贝。

    原型模式在Spring中的应用

    // todo

    相关文章

      网友评论

          本文标题:JAVA设计模式:原型模式与拷贝

          本文链接:https://www.haomeiwen.com/subject/nfpgfhtx.html