深拷贝与浅拷贝的区别
浅拷贝只是拷贝了源对象的地址,所以当源对象发生改变时,拷贝的对象的值也会对应发生改变。
深拷贝则是拷贝了源对象的所有值,而不是地址,所以深拷贝对象中的值不会随着源对象中的值的改变而改变。
发生的场景
当如果要拷贝一个A对象,而A对象中又有一个B对象,那么如果对A拷贝的时候,重新拷贝出来一个A1对象并且重新分配内存地址,但是对于A中的B对象,仅仅只是把A1中拷贝出来的B1对象的引用指向原来的B对象而已,并没有把拷贝的B1对象也重新进行分配一个新的内存地址。这就是浅拷贝。
而深拷贝就是在第1的基础上,不仅重新给A1对象分配了新的内存地址,而且还给A1中的B1也重新进行分配了新的内存地址,而不只是仅仅把原本的B的引用给B1。这就是深拷贝。
最基本的实现实现
如果要深拷贝一个对象,那么这个对象必须要实现 Cloneable 接口,实现 重写clone()方法,并且在 clone 方法内部,把该对象引用的其他对象也要 clone 一份,这就要求这个被引用的对象必须也要实现Cloneable 接口并且实现 clone 方法
利用反射机制实现
public static void copy(Object source, Object dest) throws Exception {
Class destClz = dest.getClass();
// 获取目标的所有成员
Field[] destFields = destClz.getDeclaredFields();
Object value;
for (Field field : destFields) { // 遍历所有的成员,并赋值
// 获取value值
value = getVal(field.getName(), source);
field.setAccessible(true);
field.set(dest, value);
}
}
private static Object getVal(String name, Object obj) throws Exception {
try {
// 优先获取obj中同名的成员变量
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException e) {
// 表示没有同名的变量
}
// 获取对应的 getXxx() 或者 isXxx() 方法
name = name.substring(0, 1).toUpperCase() + name.substring(1);
String methodName = "get" + name;
String methodName2 = "is" + name;
Method[] methods = obj.getClass().getMethods();
for (Method method : methods) {
// 只获取无参的方法
if (method.getParameterCount() > 0) {
continue;
}
if (method.getName().equals(methodName)
|| method.getName().equals(methodName2)) {
return method.invoke(obj);
}
}
return null;
}