工作三年,小胖连 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 () 是深克隆还是浅克隆;以及深刻龙的几种实现方式。希望对你有帮助~


相关文章
|
5月前
|
移动开发 前端开发 JavaScript
童年回忆——开心消消乐(内含源码inscode一键运行)
童年回忆——开心消消乐(内含源码inscode一键运行)
|
5月前
|
机器人 程序员 C++
Scratch3.0——助力新进程序员理解程序(案例一十一、大象吃苹果)
Scratch3.0——助力新进程序员理解程序(案例一十一、大象吃苹果)
44 0
|
5月前
|
存储 缓存 Unix
同事跳槽阿里,临走甩给一份上千页的Linux源码笔记,真香
UNIX操作系统以简单、-致、优雅的设计著称,这种真正非凡的特性使得UNIX系统在超过1/4世纪的时间里影响了整个世界。而且,正是由于Linux的蓬勃发展,发源于UNIX的思想才依然活力依旧,并在可预见的未来其发展势头会一直持续下去。
|
算法
每日一题冲刺大厂提高组 第二十四天 跑路
大家好,我是泡泡,给大家带来每日一题的目的是为了更好的练习算法,我们的每日一题为了让大家练到各种各样的题目,熟悉各种题型,一年以后,蜕变成为一个不一样的自己!
106 0
工作三年,小胖连 Clone 源码都没读过?真的菜!(上)
哈喽,我是狗哥。这是 Java 源码剖析的第三篇。克隆这个知识点在工作中使用不多,很容易被人忽略。但是面试中的面试官就很常问,因此小伙伴们还是要了解下。
工作三年,小胖连 Clone 源码都没读过?真的菜!(上)
|
存储 算法 安全
工作三年,小胖连 HashMap 源码都没读过?真的菜!(下)
工作三年,小胖连 HashMap 源码都没读过?真的菜!
工作三年,小胖连 HashMap 源码都没读过?真的菜!(下)
|
算法 Java
工作三年,小胖连 HashMap 源码都没读过?真的菜!(上)
工作三年,小胖连 HashMap 源码都没读过?真的菜!
工作三年,小胖连 HashMap 源码都没读过?真的菜!(上)
工作三年,小胖连 String 源码都没读过?真的菜!(上)
工作三年,小胖连 String 源码都没读过?真的菜!
工作三年,小胖连 String 源码都没读过?真的菜!(上)
|
XML 缓存 JSON
工作三年,小胖连 String 源码都没读过?真的菜!(下)
工作三年,小胖连 String 源码都没读过?真的菜!
工作三年,小胖连 String 源码都没读过?真的菜!(下)
|
SQL 缓存 Java
在项目中随手把haseMap改成了currenHaseMap差点被公司给开除了。
在项目中随手把haseMap改成了currenHaseMap差点被公司给开除了。
134 0