要么读书,要么旅行,身体和心灵总有一个要在路上。——罗马假日
上篇我们讲了hashCode和equals方法,首先我们先回顾一下
- hashCode是根据一定的规则和对象相关的信息映射成一个数值,这个数值成为散列值。他是由native关键字修饰的,native关键字主要是Java平台与本地C代码进行交互的API,即为Java Native Interface(JNI)。
- equals关键字是用来比较对象是否相等的。如果使用Object的equals方法,实际上比较的是两个对象的内存地址是否相同,这与我们的初衷往往不一样。我们常见的类型,如String,Integer,Long等,JDK都为我们重写了equals方法,所以我们可以直接使用equals方法来实现对象数值的比较。如果是我们自定义的对象,就要手动重写equals方法。
如有不明白,可以看我另外一篇文章哈。传送门来了,Java中的Object类 (上篇)。
今天我讲clone方法,他的作用就是快速创建一个已有对象的副本,克隆后的对象类型与被克隆对象的类型相同。
咱先简单使用:
public class Person implements Cloneable {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
public class Test {
public static void main(String[] args){
Person person1=new Person();
person1.setId("1");
person1.setName("张三");
System.out.println("修改前的person1:"+person1.getId()+","+person1.getName());
try {
Person person2=person1.clone();
System.out.println("修改前的person2:"+person2.getId()+","+person2.getName());
person2.setId("2");
person2.setName("李四");
System.out.println("修改后的person2:"+person2.getId()+","+person2.getName());
System.out.println("修改后的person1:"+person1.getId()+","+person1.getName());
}catch (Exception e){
e.printStackTrace();
}
}
}
我们来看一下,这些代码到底干了啥呢。
首先Person类重写了clone方法,同时也抛出了cloneNotSupportedException这个异常,也就是说这个这个类不支持cloneable接口,就会抛出异常,这也就解释了Person实现了Cloneable接口。
其次我们看一下clone方法里面,也就是调用了父类Object的clone方法。
最后我们看一下测试类Test,先定义了一个person1对象,并为其赋值,id为1,name为张三,这第一行输出没啥问题。然后调用Person类的clone方法,实现赋值的功能,产生了person2对象,第二行输出也没问题。再修改了person2对象,id为2,name为李四,第三行也就输出了修改后的值。最后输出了person1对象的值。
这整个过程看下来,clone在这个过程中实现了深克隆,也就是person1和person2是两个完全不同的对象,他们互不干扰。
咱来试试复杂一点的,看代码咯
public class Clothes {
private String color;
private String size;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public Clothes(String color, String size) {
this.color = color;
this.size = size;
}
}
public class Person implements Cloneable {
private String name;
private Clothes clothes;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Clothes getClothes() {
return clothes;
}
public void setClothes(Clothes clothes) {
this.clothes = clothes;
}
public Person(String name, Clothes clothes) {
this.name = name;
this.clothes = clothes;
}
@Override
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
public class Test {
public static void main(String[] args) {
Person person1 = new Person("张三", new Clothes("红", "m号"));
System.out.println("修改前的person1:name("+person1.getName()+"),clothes("+person1.getClothes().getColor()+","+person1.getClothes().getSize()+")");
try {
Person person2 = (Person) person1.clone();
System.out.println("修改前的person2:name("+person2.getName()+"),clothes("+person2.getClothes().getColor()+","+person2.getClothes().getSize()+")");
person2.setName("李四");
person2.getClothes().setColor("蓝");
person2.getClothes().setSize("l号");
System.out.println("修改后的person2:name("+person2.getName()+"),clothes("+person2.getClothes().getColor()+","+person2.getClothes().getSize()+")");
System.out.println("修改后的person1:name("+person1.getName()+"),clothes("+person1.getClothes().getColor()+","+person1.getClothes().getSize()+")");
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
这几个类描述了一个person类和clothes类,学生类中包括成员变量name和clothes。person类中重写了clone()方法,而clothes类并没有重写clone方法。
我们看一下运行结果,person2是由person1克隆过来的,所以输出语句的第一行和第二行,属性值是一样的。然后修改了person2的name值和clothes值,结果person1的name值没有改变,还是原来的张三,而clothes值并变成了蓝,l号。
这说明其实调用Object类的clone方法,是在内存上开辟一块和原始对象一样的空间,然后原样拷贝原始对象的内容,对于基本的数据类型来说,是没有对象的(就像刚才的示例一样,在修改完person2的name和clothes后,person1的name并没有改变,还是原来的张三)。但对于非基础类型来说,他保存的只是对象的引用(就像刚才的示例,在修改完person2的name和clothes后,person1的clothes发生了变化)。
解决方法:
将非基础类型也重写clone方法,实现引用克隆。至于代码,就在上面Clothes类里面加上重写的代码就ok啦,我就不写了哦。