clone 方法 与 Cloneable 接口
Ojbect类中的clone方法签名如下,native 指示其是一个本地方法(非Java实现)。需要注意的是该方法的访问修饰符为 protected,说明该方法只能在本包下或子类中去调用
protected native Object clone() throws CloneNotSupportedException;
标记接口:没有任何方法和字段的空接口,其仅用于说明该类支持某种特性/功能。Cloneable 接口 即是一个标记接口,其只表明所标记类的对象是可拷贝的,否则对该类的对象调用 clone 方法将会抛 CloneNotSupportedException异常
下述代码示例即对Dept类对象进行拷贝操作,该类实现了Cloneable接口,故可正常调用clone方法
浅拷贝
Object类中的clone方法的访问修饰符为 protected,只能在包内及其子类调用。故在我们自己类中一般通过重载的方式将该类的访问权限改为 public 来方便我们在类外使用它。
如下代码所示,我们重载Dept的clone方法,也仅仅是扩大了访问权限,其依然是调用Object中的clone方法去实现
package org.example.Cloneable;
import lombok.Data;
import java.util.Date;
/**
* 部门类
*/
@Data
public class Dept implements Cloneable{
private String deptName; // 不可变对象属性
private Integer totalNum; // 不可变对象属性
private int deptIdx; // 非对象属性(基本类型属性)
private Date createTime; // 对象属性
private Fence fence; // 对象属性
public Dept(String deptName, Integer totalNum, int deptIdx, Date createTime, Fence fence) {
this.deptName = deptName;
this.totalNum = totalNum;
this.deptIdx = deptIdx;
this.createTime = createTime;
this.fence = fence;
}
@Override
public Object clone(){
Dept dept = null;
try{
dept = (Dept) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
return dept;
}
}
Fence 自定义类
package org.example.Cloneable;
import lombok.Data;
/**
* 地理围栏类
*/
@Data
public class Fence {
private Integer posX;
private Integer posY;
private Integer poxZ;
private String FenceName;
public Fence(Integer posX, Integer posY, Integer poxZ, String fenceName) {
this.posX = posX;
this.posY = posY;
this.poxZ = poxZ;
this.FenceName = fenceName;
}
}
测试类
package org.example.Cloneable;
import org.junit.Test;
import java.util.Date;
public class CloneTest {
@Test
public void cloneDept(){
Dept dept = new Dept("1部门", 1, 1,
new Date(), new Fence(1, 1, 1, "name"));
Dept copy = (Dept) dept.clone();
System.out.println("dept: "+dept.toString());
System.out.println("copy: "+copy.toString());
copy.setDeptName("2222");
copy.setTotalNum(2);
copy.getFence().setFenceName("name2");
System.out.println("====================");
System.out.println("dept: "+dept.toString());
System.out.println("copy: "+copy.toString());
}
}
运行结果:
从运行结果中可看出:基本数据类型 AND 不可变对象属性 String、Integer 是不会发生数据更改的,但对于自定义对象就会被copy的对象影响值,也就是意义上的没产生独立的拷贝对象,这就是所谓的浅拷贝。
深拷贝
有了上文对浅拷贝的介绍,相信大家已经对其缺陷有了一定的认识,其对对象的拷贝程度非常“浅”,那么有没有办法完全完全的拷贝一个对象,使得其所有属性均和原对象完全不相干呢?答案就是深拷贝。其通过“层层clone”的方式实现对其需要对象中的所有对象属性进行克隆
以上文为例,我们首先对Fence类实现Cloneable接口重载clone方法,由于该类中除了不可变对象属性外无其他的对象属性,故只需调用Object类中的clone方法即可,无需“层层clone”:
package org.example.Cloneable;
import lombok.Data;
/**
* 地理围栏类
*/
@Data
public class Fence implements Cloneable{
private Integer posX;
private Integer posY;
private Integer poxZ;
private String FenceName;
public Fence(Integer posX, Integer posY, Integer poxZ, String fenceName) {
this.posX = posX;
this.posY = posY;
this.poxZ = poxZ;
this.FenceName = fenceName;
}
@Override
public Object clone() {
Fence fence = null;
try {
fence = (Fence) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
return fence;
}
}
对于Dept类而言,其内部除了不可变对象属性外,有createTime和fence对象属性,所以需要进行“层层clone”,分别对这两个对象进行clone,重载后clone方法如下所示:
package org.example.Cloneable;
import lombok.Data;
import java.util.Date;
/**
* 部门类
*/
@Data
public class Dept implements Cloneable{
private String deptName; // 不可变对象属性
private Integer totalNum; // 不可变对象属性
private int deptIdx; // 非对象属性(基本类型属性)
private Date createTime; // 对象属性
private Fence fence; // 对象属性
public Dept(String deptName, Integer totalNum, int deptIdx, Date createTime, Fence fence) {
this.deptName = deptName;
this.totalNum = totalNum;
this.deptIdx = deptIdx;
this.createTime = createTime;
this.fence = fence;
}
@Override
public Object clone(){
Dept dept = null;
try{
dept = (Dept) super.clone();
if( dept.getFence() != null ) {
Fence fence = (Fence) dept.getFence().clone();
dept.setFence(fence);
}
} catch (CloneNotSupportedException e) {
return null;
}
return dept;
}
}
运行结果:可以看出copy对象对原对象结果无影响