记得以前写过一篇,比这个详细,然后不见了
1.浅拷贝
浅拷贝是将对象的栈上的属性直接拷贝一份给新对象,基本类型是没有问题的,但引用类型会拷贝一个地址引用,本质使用的还是堆上的同一个对象,修改时会同时发生变化。浅拷贝需要实现 Cloneable接口,不然无法调用clone方法,返回的是Object对象,可在重写中修改返回类型
public class User implements Cloneable{
// 基本类型
private String name;
private String email;
// 引用类型
private Date birthday;
// 可重写,也可不写
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
User prototype = new User("oldHowl","old@qq.com",new Date());
User shallowUser = (User) prototype.clone();
prototype.setName("newHowl");
prototype.setEmail("new@qq.com");
prototype.getBirthday().setMonth(10);
System.out.println("prototype: " + prototype);
System.out.println("shallowUser" + shallowUser);
}
}
// 修改原对象的基本类型的属性是不会改变克隆之后的对象属性
// 修改引用类型,公用一个堆上的引用对象,那么克隆对象也会被修改,解决方法是使用深拷贝,月份变成了10月
prototype: User{name='newHowl', email='new@qq.com', birthday=Tue Nov 02 14:29:35}
shallowUser: User{name='oldHowl', email='old@qq.com', birthday=Tue Nov 02 14:29:35}
2. 深拷贝
对具有引用类型属性的对象进行copy,引用对象需要不是直接复制一个引用地址了,而是新建一个引用对象,这个需要手动重写clone方法
public class User implements Cloneable {
// 必须重写
@Override
protected Object clone() throws CloneNotSupportedException {
// 对基本属性进行拷贝
User deepClone = (User) super.clone();
// 引用类型进行深拷贝
deepClone.setBirthday((Date) deepClone.getBirthday().clone());
return deepClone;
}
public static void main(String[] args) throws CloneNotSupportedException {
User prototype = new User("oldHowl","old@qq.com",new Date());
User shallowUser = (User) prototype.clone();
prototype.setName("newHowl");
prototype.setEmail("new@qq.com");
prototype.getBirthday().setMonth(10);
System.out.println("prototype: " + prototype);
System.out.println("shallowUser" + shallowUser);
}
}
// 引用类型的月份没有改变了,证明引用对象也是一个新的对象
prototype: User{name='newHowl', email='new@qq.com', birthday=Tue Nov 02 14:51:14}
shallowUserUser{name='oldHowl', email='old@qq.com', birthday=Wed Jun 02 14:51:14}
3. 拷贝工具类
-
设置各种getter/setter手动复制(没人用吧)
-
Apache BeanUtils(阿里巴巴规范不建议使用)
-
Spring BeanUtils(性能比Apache高)
3.1 Spring BeanUtils
// 是浅拷贝,是浅拷贝
// 注意Boolean类型生成的方法是isBoolean,要手动改写
// 基于内省+反射,借助getter/setter拷贝
// 只检查可访问性 和 属性名是否对应
User prototype = new User("Howl", "xxx@qq.com", new Date());
User shallowUser = new User();
// 使用简单
BeanUtils.copyProperties(prototype, shallowUser);
3.2 BeanUtils.copyProperties的源码
public static void copyProperties(Object source, Object target) throws BeansException {
// 源对象,目标对象,需转化类型,需要忽视的对象
copyProperties(source, target, (Class)null, (String[])null);
}
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable String... ignoreProperties) throws BeansException {
// 断言,前两个不为空
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
// 变量提升
// 看看目标对象能否被转化,继承关系,接口关系可转化
Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target not assignable to Editable class");
}
actualEditable = editable;
}
// 内省,属性描述器
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
// 看看是否有需要忽略的对象,传参过来的
List<String> ignore = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;
// 辅助来遍历属性描述器
PropertyDescriptor[] var7 = targetPds;
int var8 = targetPds.length;
// 遍历,var是jdk10的功能,下面主要判断源和目标对象的类型是否对应
for(int var9 = 0; var9 < var8; ++var9) {
PropertyDescriptor targetPd = var7[var9];
Method writeMethod = targetPd.getWriteMethod();
// 判断方法
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
// 源对象的属性描述器
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
// 源对象可读方法
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null) {
// 源对象方法的返回值类型
ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
// 目标方法的返回值类型
ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
boolean isAssignable = !sourceResolvableType.hasUnresolvableGenerics() && !targetResolvableType.hasUnresolvableGenerics() ? targetResolvableType.isAssignableFrom(sourceResolvableType) : ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType());
// 获取源对象的get方法然后使用获得 value
// 将获取的 value使用目标对象的set方式写入
if (isAssignable) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
// 读私有属性,设置可访问
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
// 写私有属性,设置可访问
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
} catch (Throwable var18) {
throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var18);
}
}
}
}
}
}
}