CV程序员都要掌握的对象拷贝

前言

CV程序员:熟练使用ctrl+c和ctrl+v或者command+c和command+v的程序员,CV程序员分两种,第一种见多识广,游走在各类搜索引擎、博客以及项目中,搜寻着本身想要的代码,第一种是把本身的代码从一个类复制到另外一个类。java

我以为若是是做为一个刚起步的程序员来讲,CV别人的代码挺好的,由于至少他具有了搜索以及本身解决问题的能力。这种能力和学习方式是很是重要的,由于不少问题都须要本身去解决一遍才会慢慢积累经验,向更高的水平进阶,可是要注意的是懂得回顾和反思,虽然已经把代码CV过来了,可是过后须要思考代码为何要这么写,而不是解决了就丢一旁。日积跬步,终有一天你的代码也会被别人用来CV。而若是是CV本身的代码就要注意了,由于你在写着冗余的代码,须要思考为何有这么多重复的代码,是否能够作一些抽象设计或者运用一些设计模式来避免代码的冗余。程序员

今天要讲的是CV程序员都要掌握的对象拷贝,主要是从如下三个方面开始讲解:设计模式

克隆什么?markdown

怎么克隆?ide

为何要克隆?学习

深拷贝和浅拷贝区别是什么?测试

克隆什么?

要讲克隆,先弄清楚第一个问题:克隆什么东西?这还用回答吗,固然是克隆对象。可是到底克隆的是什么类型的对象?this

咱们知道在 Java中基本数据类型和引用数据类型之分,基本数据类型与引用数据类型最本质的区别是:搜索引擎

  • 基本数据类型直接包含值,在被建立时,在栈上给其划分一块内存,将数值直接存储在栈上。
  • 引用数据类型被建立时,首先要在栈上给其引用(句柄)分配一块内存,而对象的具体信息都存储在堆内存上,而后由栈上面的引用指向堆中对象的地址,能够理解为栈上面保存的是堆中对象的地址。

而咱们所说的克隆,能够分这两种类型的对象来讨论。spa

偷偷引入一个知识点:

装箱:将基本数据类型转换为引用数据类型。好比int转化为Integer。

拆箱:将引用数据类型转换为基本数据类型。好比Integer转化为int。

怎么克隆?

弄清楚了要克隆什么,那么咱们来看看怎么克隆。仍是分为克隆基本类型对象和引用类型对象:

基本类型对象克隆

下面是一个基本类型的克隆示例:

public static void main(String[] args) {
    int a = 1;
    // 用"="克隆
    int b = a;
    System.out.println("a=" + a);
    System.out.println("b=" + b);
    // 测试是否修改了a的值
    b = 2;
    System.out.println("修改了b的值后,a=" + a);
    System.out.println("修改了b的值后,b=" + b);

}
复制代码

运行结果

对于基本类型来讲,直接用“=”就能完成克隆,而且这样克隆出来的对象是彻底独立于原来的对象的。

引用类型对象

引用类型对象的克隆有两种方法

  • 用”=“进行克隆
  • 用Object类的clone方法进行克隆

下面先定义一个类:

public class School implements Cloneable {
    /** * 校名 */
    String name;
    /** * 校长 */
    String president;

    public School(String name, String president) {
        this.name = name;
        this.president = president;
    }

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

    public void setPresident(String president) {
        this.president = president;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "School:[name=" + name + "," +
                "president=" + president + "]";
    }
}
复制代码

用”=“进行克隆

示例

public static void main(String[] args) throws CloneNotSupportedException {
    School school = new School("家里蹲", "crazyhzm");
    // 用"="克隆
    School schoolDuplicate = school;
    System.out.println("school的hashcode=" + school.hashCode());
    System.out.println("schoolDuplicate的hashcode=" + schoolDuplicate.hashCode());
    System.out.println("school的值=" + school.toString());
    System.out.println("schoolDuplicate的值=" + schoolDuplicate.toString());
    // 修改 schoolDuplicate的值
    schoolDuplicate.setPresident("加点代码调调味");
    System.out.println("修改schoolDuplicate的值后,school的值=" + school.toString());
    System.out.println("修改schoolDuplicate的值后,schoolDuplicate的值=" + schoolDuplicate.toString());
}
复制代码

运行结果

从运行结果看,用“=”进行克隆,其实只是克隆了原对象的内存地址,schoolDuplicate和school对象都指向了同一块内存区域,

用Object类的Clone方法进行克隆

示例

public static void main(String[] args) throws CloneNotSupportedException {
    School school = new School("家里蹲", "crazyhzm");
    // 用"clone"克隆
    School schoolDuplicateForClone = (School) school.clone();
    System.out.println("school的hashcode=" + school.hashCode());
    System.out.println("schoolDuplicateForClone的hashcode=" + schoolDuplicateForClone.hashCode());
    System.out.println("school的值=" + school.toString());
    System.out.println("schoolDuplicateForClone的值=" + schoolDuplicateForClone.toString());

    // 修改 schoolDuplicateForClone的值
    schoolDuplicateForClone.setPresident("点个赞");
    System.out.println("修改schoolDuplicateForClone的值后,school的值=" + school.toString());
    System.out.println("修改schoolDuplicateForClone的值后,schoolDuplicateForClone的值=" + schoolDuplicateForClone.toString());

}
复制代码

运行结果

为何要克隆?

了解了克隆什么以及怎么克隆后,咱们到底为何要使用克隆?

从JDK1.0版本开始,Java语言就提供了克隆机制。要知道咱们为何要克隆,先来了解一下克隆这种行为有什么方法替代,对于CV程序员来讲,若是不能 复制黏贴,那就知道本身把代码一行行本身敲上去。那么对于对象的克隆来讲,无非就是本身new一个新的对象,而后将其中的属性一个一个赋值过去,若是遇到引用数据类型,还须要继续new,继续赋值。当想要建立的新对象的属性值与原来的对象的属性值保持一致时,这种手动new,再一个一个赋值的行为就看似有点繁琐了,这种时候咱们就能够用到克隆。从上面怎么克隆能够看到会出现两种不同的结果,在克隆中,须要搞清楚什么是深拷贝和浅拷贝。

深拷贝和浅拷贝区别是什么?

在这里姑且把克隆改成拷贝,它存在深拷贝和浅拷贝,咱们知道一个对象的属性中可能存在着另外一个对象的引用,而这个对象是引用数据类型的。而浅克隆后的对象中非基本对象和原对象指向同一块内存,所以对这些非基本对象的修改会同时更改克隆先后的对象。深克隆能够实现彻底的克隆,能够用反射的方式或序列化的方式实现。具体是怎么样的,我修改一下上述的示例来介绍:

浅拷贝

下面仍是School类的定义,可是新增了一个Grade属性。

public class School implements Cloneable {
    /** * 校名 */
    String name;
    /** * 校长 */
    String president;

    Grade grade;

    public School(String name, String president) {
        this.name = name;
        this.president = president;
    }

    public School(String name, String president, Grade grade) {
        this.name = name;
        this.president = president;
        this.grade = grade;
    }

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

    public void setPresident(String president) {
        this.president = president;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", president='" + president + '\'' +
                ", grade=" + grade.toString() +
                '}';
    }
}
复制代码

Grade类定义:

public class Grade {
    /** * 年级名称 */
    String name;

    public Grade(String name) {
        this.name = name;
    }

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

    @Override
    public String toString() {
        return "Grade{" +
                "name='" + name + '\'' +
                '}';
    }
}
复制代码

示例代码

public static void main(String[] args) throws CloneNotSupportedException {
    Grade grade = new Grade("一年级");
    School school = new School("家里蹲", "crazyhzm", grade);
    // 用"clone"克隆
    School schoolDuplicateForClone = (School) school.clone();
    System.out.println("school的hashcode=" + school.hashCode());
    System.out.println("school.grade的hashcode=" + school.grade.hashCode());
    System.out.println("schoolDuplicateForClone的hashcode=" + schoolDuplicateForClone.hashCode());
    System.out.println("schoolDuplicateForClone.grade的hashcode=" + schoolDuplicateForClone.grade.hashCode());
    System.out.println("school的值=" + school.toString());
    System.out.println("schoolDuplicateForClone的值=" + schoolDuplicateForClone.toString());

    // 修改 schoolDuplicateForClone的值
    schoolDuplicateForClone.setPresident("点个赞");
    schoolDuplicateForClone.grade.setName("二年级");
    System.out.println("修改schoolDuplicateForClone的值后,school的值=" + school.toString());
    System.out.println("修改schoolDuplicateForClone的值后,schoolDuplicateForClone的值=" + schoolDuplicateForClone.toString());

}
复制代码

运行结果

从运行结果能够看到,School的拷贝对象修改了年级名称后,被拷贝对象的值也被修改了,而且能够从他们的hashcode也能看出来,在拷贝的时候都指向了同一个内存区域,这就是所谓的浅拷贝。

深拷贝

深拷贝咱们稍微修改一下上述的School以及Grade代码

school类

修改了clone方法。

public class School implements Cloneable {
    /** * 校名 */
    String name;
    /** * 校长 */
    String president;

    Grade grade;

    public School(String name, String president) {
        this.name = name;
        this.president = president;
    }

    public School(String name, String president, Grade grade) {
        this.name = name;
        this.president = president;
        this.grade = grade;
    }

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

    public void setPresident(String president) {
        this.president = president;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        School school = (School) super.clone();
        school.grade = (Grade) grade.clone();
        return school;
    }

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", president='" + president + '\'' +
                ", grade=" + grade.toString() +
                '}';
    }
}
复制代码

Grade类

Grade实现了Cloneable接口。

public class Grade implements Cloneable{
    /** * 年级名称 */
    String name;

    public Grade(String name) {
        this.name = name;
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Grade{" +
                "name='" + name + '\'' +
                '}';
    }
}
复制代码

仍是执行上述示例代码,运行后结果为:

能够看到school中grade属性也被拷贝,修改年级名称后,原对象的数据并无随之改变,这就是深拷贝。

内推区域

点击【加点代码调调味】我的主页的最新沸点,有内推信息,欢迎咨询。

送福利区域

扫描下方二维码关注公众号【加点代码调调味】 点击菜单栏获取免费49篇的《Dubbo源码解析》系列文章

相关文章
相关标签/搜索