定义:Clonable接口叫做克隆接口,接口中设有Object.clone()方法,可以对其进行重写达到保留字段副本克隆的目的。
底层源码注释:
该接口专门用于克隆,所以该接口继承java.lang 只需要重写其中的clone()方法。
翻译:
类实现了可克隆接口,以向Object.clone()方法表明,该方法为该类的实例创建字段对字段副本是合法的。
在未实现Cloneable接口的实例上调用Object的clone方法会导致引发异常CloneNotSupportedException。
按照惯例,实现此接口的类应使用公共方法重写Object.clone(受保护)。有关重写此方法的详细信息,请参见Object.clone()。
请注意,此接口不包含clone方法。因此,不可能仅仅因为对象实现了这个接口而克隆对象。即使以反射方式调用clone方法,也不能保证它会成功
适用范围:
1、自己重写的话可用于Page分页封装类的参数克隆。
2、Java自带的包例如lang、util、array等都有自带对数据的克隆方法的重写。
作用:
克隆的数据应该是暂时放在缓存中,这个是副本思想,总的目的来说:1、保持数据的读写方便搞笑2、防止数据错误,读写不一致,也就是事务中的脏读或者幻读概念,以上是我个人解读,不一定正确hhhhh,
使用示例:
Page工具类clone()的重写:
@Override
public Page clone() {
try{
return (Page)super.clone();
}catch(CloneNotSupportedException e) {
final Page page = new Page();
page.now = now;
page.size = size;
page.end = end;
page.inputPage = inputPage;
page.count = count;
return page;
}
}
Java自带的ObjectUtil包的clone重写:
public static <T> T clone(final T obj) {
if (obj instanceof Cloneable) {
final Object result;
if (obj.getClass().isArray()) {
final Class<?> componentType = obj.getClass().getComponentType();
if (componentType.isPrimitive()) {
int length = Array.getLength(obj);
result = Array.newInstance(componentType, length);
while (length-- > 0) {
Array.set(result, length, Array.get(obj, length));
}
} else {
result = ((Object[]) obj).clone();
}
} else {
try {
final Method clone = obj.getClass().getMethod("clone");
result = clone.invoke(obj);
} catch (final NoSuchMethodException e) {
throw new CloneFailedException("Cloneable type "
+ obj.getClass().getName()
+ " has no clone method", e);
} catch (final IllegalAccessException e) {
throw new CloneFailedException("Cannot clone Cloneable type "
+ obj.getClass().getName(), e);
} catch (final InvocationTargetException e) {
throw new CloneFailedException("Exception cloning Cloneable type "
+ obj.getClass().getName(), e.getCause());
}
}
@SuppressWarnings("unchecked") // OK because input is of type T
final T checked = (T) result;
return checked;
}
return null;
}
简单拷贝类Person示例
public class CloneDemo {
public static void main(String[] args) {
Person p1 = new Person();
p1.setName("哈哈哈哈哈");
Person p2 = p1.clone();
System.out.println("p1.name = " + p1.getName());
System.out.println("p2.name = " + p2.getName());
System.out.println("p1 = p2 ? " + (p1 == p2));
}
}
class Person implements Cloneable {//谁被克隆,谁要继承Clonable,不然会报错,上面有解释过
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 实现克隆Person对象,这是经常的实现方法,类似与模板
@Override
protected Person clone() {
Person p = null;
try {
p = (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
实现思路:
把需要备份的数据处理过后,例如类型转化过后,判断格式,判断非空等等处理之后调用clone(),进行备份。
此时,改变克隆对象的值不会影响到原对象的值。
如果当需要克隆的对象具有引用类型的时候呢?(引用类型是指在一个类中,引用其他类,作为对象属性。private Person p1)
深拷贝与浅拷贝
引入概念前言
当类中有引用时,将引用类拷贝一份后,发现修改拷贝对象的值还是会影响原对象的值,这是为什么呢?
从拷贝原理上来看,因为拷贝原对象的引用类属性,是拷贝的其属性值(哈希地址)在堆中指向相同的空间,当修改属性值的时候,指向这个空间的栈中的对象被调用的时候,获取到的是修改后的值。
这种修改其中一个值,会影响到另一个的拷贝叫做浅拷贝。
深拷贝:修改其中一个值,不会影响到另一个的拷贝。
深拷贝实现思路:如果要引用类型的拷贝值不指向同一个空间地址的话,那么,将引用类型也拷贝一份,再去修改值,就可以啦。
使用以上例子来说
class Person implements Cloneable {
private String name;
private Student grade;
public Wallet getGrade() {
return grade;
}
public void setGrade(Student Grade) {
this.grade = grade;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Person clone() {
Person p = null;
try {
p = (Person) super.clone();
grade = this.grade.clone();//此处是将引用类型的属性值进行克隆,这样不会指向原属性值同一个地址,达到深拷贝
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}