工作三年,小胖连 Clone 源码都没读过?真的菜!(下)

简介: 工作三年,小胖连 Clone 源码都没读过?真的菜!

5. 深克隆还有哪些实现方式?


深克隆的实现方式很多,总的来说有以下几种:


  • 所有对象都实现克隆方法。
  • 通过构造方法实现深克隆。
  • 使用 JDK 自带的字节流。
  • 使用第三方工具实现,比如:Apache Commons Lang。
  • 使用 JSON 工具类实现,比如:Gson,FastJSON 等等。


1、有对象都实现克隆方法


/**
 * Project Name:review_java <br/>
 * Package Name:com.nasus.clone <br/>
 * Date:2021/1/31 20:57 <br/>
 *
 * @author <a href="turodog@foxmail.com">chenzy</a><br/>
 */
public class DeepCloneOneExample {
    static class User implements Cloneable {
        public User() {
        }
        public User(Integer age, String name, Address address) {
            this.age = age;
            this.name = name;
            this.address = address;
        }
        // 年龄
        private Integer age;
        // 名称
        private String name;
        // 地址
        private Address address;
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Address getAddress() {
            return address;
        }
        public void setAddress(Address address) {
            this.address = address;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            User user = (User) super.clone();
            // 引用类型克隆赋值
            user.setAddress((Address) this.address.clone());
            return user;
        }
    }
    static class Address implements Cloneable {
        public Address() {
        }
        public Address(String province, String city) {
            this.province = province;
            this.city = city;
        }
        // 省份
        private String province;
        // 城市
        private String city;
        public String getProvince() {
            return province;
        }
        public void setProvince(String province) {
            this.province = province;
        }
        public String getCity() {
            return city;
        }
        public void setCity(String city) {
            this.city = city;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return (Address) super.clone();
        }
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        // 创建被赋值对象
        Address address = new Address("广东", "广州");
        User userOne = new User(22, "clone", address);
        // 克隆 userOne 对象
        User userTwo = (User) userOne.clone();
        // 修改原型对象
        userOne.getAddress().setCity("清远");
        // 输出 p1 和 p2 地址信息
        System.out.println("userOne:" + userOne.getAddress().getCity() +
                ",userTwo:" + userTwo.getAddress().getCity());
    }
}


运行结果:


userOne:清远,userTwo:广州


可以看到,修改了原型对象的引用对象并没有改变克隆对象的引用对象。说明两者引用对象已经不是同一个引用对象了,所以是深克隆。


2、通过构造方法实现深克隆


《Effective Java》 中「推荐使用构造器(Copy Constructor)来实现深克隆,如果构造器的参数为基本数据类型或字符串类型则直接赋值,如果是对象类型,则需要重新 new 一个对象」,实现代码如下:


/**
 * Project Name:review_java <br/>
 * Package Name:com.nasus.clone <br/>
 * Date:2021/1/31 21:16 <br/>
 *
 * @author <a href="turodog@foxmail.com">chenzy</a><br/>
 */
public class DeepCloneSecondExample {
    static class User {
        public User() {
        }
        public User(Integer age, String name, Address address) {
            this.age = age;
            this.name = name;
            this.address = address;
        }
        // 年龄
        private Integer age;
        // 名称
        private String name;
        // 地址
        private Address address;
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Address getAddress() {
            return address;
        }
        public void setAddress(Address address) {
            this.address = address;
        }
    }
    static class Address {
        public Address() {
        }
        public Address(String province, String city) {
            this.province = province;
            this.city = city;
        }
        // 省份
        private String province;
        // 城市
        private String city;
        public String getProvince() {
            return province;
        }
        public void setProvince(String province) {
            this.province = province;
        }
        public String getCity() {
            return city;
        }
        public void setCity(String city) {
            this.city = city;
        }
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        // 创建对象
        Address address = new Address("广东", "广州");
        User userOne = new User(22, "clone", address);
        // 调用构造函数克隆对象
        User userTwo = new User(userOne.getAge(), userOne.getName(),
                new Address(userOne.getAddress().getProvince(), userOne.getAddress().getCity()));
        // 修改原型对象
        userOne.getAddress().setCity("清远");
        // 输出 userOne 和 userOne 地址信息
        System.out.println("userOne:" + userOne.getAddress().getCity() +
                ",userTwo:" + userTwo.getAddress().getCity());
    }
}


运行结果:


userOne:清远,userTwo:广州


3、使用 JDK 自带的字节流


使用 JDK 自带字节流实现,先将要原型对象写入到内存中的字节流,再从这个字节流中读出刚刚存储的信息,作为一个新对象返回,此时这个新对象和原型对象就不存在任何地址上的共享,从而实现深克隆,代码如下:


/**
 * Project Name:review_java <br/>
 * Package Name:com.nasus.clone <br/>
 * Date:2021/1/31 21:25 <br/>
 *
 * @author <a href="turodog@foxmail.com">chenzy</a><br/>
 */
public class DeepCloneThirdExample {
    static class User implements Serializable{
        public User() {
        }
        public User(Integer age, String name, Address address) {
            this.age = age;
            this.name = name;
            this.address = address;
        }
        // 年龄
        private Integer age;
        // 名称
        private String name;
        // 地址
        private Address address;
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Address getAddress() {
            return address;
        }
        public void setAddress(Address address) {
            this.address = address;
        }
    }
    static class Address implements Serializable {
        public Address() {
        }
        public Address(String province, String city) {
            this.province = province;
            this.city = city;
        }
        // 省份
        private String province;
        // 城市
        private String city;
        public String getProvince() {
            return province;
        }
        public void setProvince(String province) {
            this.province = province;
        }
        public String getCity() {
            return city;
        }
        public void setCity(String city) {
            this.city = city;
        }
    }
    /**
     * 通过字节流实现克隆
     */
    static class StreamClone {
        public static <T extends Serializable> T clone(User user) {
            T cloneObj = null;
            try {
                // 写入字节流
                ByteArrayOutputStream bo = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bo);
                oos.writeObject(user);
                oos.close();
                // 分配内存,写入原始对象,生成新对象
                // 获取上面的输出字节流
                ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
                ObjectInputStream oi = new ObjectInputStream(bi);
                // 返回生成的新对象
                cloneObj = (T) oi.readObject();
                oi.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return cloneObj;
        }
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        // 创建对象
        Address address = new Address("广东", "广州");
        User userOne = new User(22, "clone", address);
        // 调用构造函数克隆对象
        User userTwo = StreamClone.clone(userOne);
        // 修改原型对象
        userOne.getAddress().setCity("清远");
        // 输出 userOne 和 userOne 地址信息
        System.out.println("userOne:" + userOne.getAddress().getCity() +
                ",userTwo:" + userTwo.getAddress().getCity());
    }
}


运行结果:


userOne:清远,userTwo:广州


结果还是一样的,但这里要注意下。由于是通过字节流序列化实现的深克隆,所以每个对象必须能被序列化。也即必须实现 Serializable 接口。


4、通过第三方工具实现深克隆


比如:Apache Commons Lang,实现代码如下:


/**
 * Project Name:review_java <br/>
 * Package Name:com.nasus.clone <br/>
 * Date:2021/1/31 21:41 <br/>
 *
 * @author <a href="turodog@foxmail.com">chenzy</a><br/>
 */
public class DeepCloneFourthExample {
    static class User implements Serializable {
        public User() {
        }
        public User(Integer age, String name, Address address) {
            this.age = age;
            this.name = name;
            this.address = address;
        }
        // 年龄
        private Integer age;
        // 名称
        private String name;
        // 地址
        private Address address;
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Address getAddress() {
            return address;
        }
        public void setAddress(Address address) {
            this.address = address;
        }
    }
    static class Address implements Serializable {
        public Address() {
        }
        public Address(String province, String city) {
            this.province = province;
            this.city = city;
        }
        // 省份
        private String province;
        // 城市
        private String city;
        public String getProvince() {
            return province;
        }
        public void setProvince(String province) {
            this.province = province;
        }
        public String getCity() {
            return city;
        }
        public void setCity(String city) {
            this.city = city;
        }
    }
    public static void main(String[] args) {
        // 创建对象
        Address address = new Address("广东", "广州");
        User userOne = new User(22, "clone", address);
        // 调用 apache.commons.lang 克隆对象
        User userTwo = SerializationUtils.clone(userOne);
        // 修改原型对象
        userOne.getAddress().setCity("清远");
        // 输出 userOne 和 userOne 地址信息
        System.out.println("userOne:" + userOne.getAddress().getCity() +
                ",userTwo:" + userTwo.getAddress().getCity());
    }
}


输出结果:


userOne:清远,userTwo:广州


实际上,这种工作中我自己是用的比较多的一种方法。因为方便,它跟第三种有点像。其实底层是一样的,还是李用字节流实现。


5、使用 JSON 工具类实现


比如 Gson 或者 FastJson 等等,下面以 Gson 为例,实现代码如下:


/**
 * Project Name:review_java <br/>
 * Package Name:com.nasus.clone <br/>
 * Date:2021/1/31 21:58 <br/>
 *
 * @author <a href="turodog@foxmail.com">chenzy</a><br/>
 */
public class DeepCloneFifthExample {
    static class User {
        public User() {
        }
        public User(Integer age, String name, Address address) {
            this.age = age;
            this.name = name;
            this.address = address;
        }
        // 年龄
        private Integer age;
        // 名称
        private String name;
        // 地址
        private Address address;
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Address getAddress() {
            return address;
        }
        public void setAddress(Address address) {
            this.address = address;
        }
    }
    static class Address {
        public Address() {
        }
        public Address(String province, String city) {
            this.province = province;
            this.city = city;
        }
        // 省份
        private String province;
        // 城市
        private String city;
        public String getProvince() {
            return province;
        }
        public void setProvince(String province) {
            this.province = province;
        }
        public String getCity() {
            return city;
        }
        public void setCity(String city) {
            this.city = city;
        }
    }
    public static void main(String[] args) {
        // 创建对象
        Address address = new Address("广东", "广州");
        User userOne = new User(22, "clone", address);
        // 调用 Gson 克隆对象
        Gson gson = new Gson();
        User userTwo = gson.fromJson(gson.toJson(userOne), User.class);
        // 修改原型对象
        userOne.getAddress().setCity("清远");
        // 输出 userOne 和 userTwo 地址信息
        System.out.println("userOne:" + userOne.getAddress().getCity() +
                ",userTwo:" + userTwo.getAddress().getCity());
    }
}


运行结果:


userOne:清远,userTwo:广州


这种方法会先把对象转化成字符串,再从字符串转化成新的对象,因为新对象是从字符串转化而来的,因此不会和原型对象有任何的关联,所以实现了深克隆。


6. 总结


本文介绍了深浅克隆的概念、区别;怎么实现客隆;克隆有啥约定俗成的规则;


Arrays.copy () 是深克隆还是浅克隆;以及深刻龙的几种实现方式。希望对你有帮助~


相关文章
|
8月前
|
移动开发 前端开发 JavaScript
童年回忆——开心消消乐(内含源码inscode一键运行)
童年回忆——开心消消乐(内含源码inscode一键运行)
|
8月前
|
前端开发 JavaScript Java
童年回忆——捕鱼达人(内含源码inscode一键运行)
童年回忆——捕鱼达人(内含源码inscode一键运行)
|
监控 Java 应用服务中间件
工作三年,小胖问我 SpringBoot 是怎么启动的?真的离谱!
现在的 Java 项目基本都是直接上 SpringBoot,因此在面试中,面试官也会经常问 SpringBoot 相关的问题。比如:SpringBoot 与 Spring 的区别?它的特性?它的启动过程?
工作三年,小胖问我 SpringBoot 是怎么启动的?真的离谱!
工作三年,小胖连 Clone 源码都没读过?真的菜!(上)
哈喽,我是狗哥。这是 Java 源码剖析的第三篇。克隆这个知识点在工作中使用不多,很容易被人忽略。但是面试中的面试官就很常问,因此小伙伴们还是要了解下。
工作三年,小胖连 Clone 源码都没读过?真的菜!(上)
|
缓存 运维 前端开发
火爆!GitHub 标星 144k 的前后端学习路线,2021 年最新整理,看完后不再迷茫不再徘徊
火爆!GitHub 标星 144k 的前后端学习路线,2021 年最新整理,看完后不再迷茫不再徘徊
5255 3
火爆!GitHub 标星 144k 的前后端学习路线,2021 年最新整理,看完后不再迷茫不再徘徊
|
安全 Java 程序员
Gitee 关闭部分开源仓库;大厂一半以上程序员愿意降薪跳槽;Deno 1.22 发布 | 思否周刊
Gitee 关闭部分开源仓库;大厂一半以上程序员愿意降薪跳槽;Deno 1.22 发布 | 思否周刊
236 0
|
算法 Java
工作三年,小胖连 HashMap 源码都没读过?真的菜!(上)
工作三年,小胖连 HashMap 源码都没读过?真的菜!
工作三年,小胖连 HashMap 源码都没读过?真的菜!(上)
|
存储 算法 安全
工作三年,小胖连 HashMap 源码都没读过?真的菜!(下)
工作三年,小胖连 HashMap 源码都没读过?真的菜!
工作三年,小胖连 HashMap 源码都没读过?真的菜!(下)
工作三年,小胖连 String 源码都没读过?真的菜!(上)
工作三年,小胖连 String 源码都没读过?真的菜!
工作三年,小胖连 String 源码都没读过?真的菜!(上)
|
XML 缓存 JSON
工作三年,小胖连 String 源码都没读过?真的菜!(下)
工作三年,小胖连 String 源码都没读过?真的菜!
工作三年,小胖连 String 源码都没读过?真的菜!(下)