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); } 复制代码
运行结果
对于基本类型来讲,直接用“=”就能完成克隆,而且这样克隆出来的对象是彻底独立于原来的对象的。
引用类型对象的克隆有两种方法
下面先定义一个类:
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对象都指向了同一块内存区域,
示例
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属性也被拷贝,修改年级名称后,原对象的数据并无随之改变,这就是深拷贝。