在计算机内存中,每一个对象都有一个地址,这个地址指向对象在内存中存储的位置。当我们使用变量引用一个对象时,实际上是将该对象的地址赋值给变量。
在Java中生成一个对象 User user = new User(); 这个过程可以分三个步骤
- 创建User user的变量
- 创建User对象存放到内存中
- 将User存放在内存的地址赋值给user变量。可以将user当成一个指针,指向了User对象
浅拷贝是创建了一个新的变量,该变量指向了对象的地址,这两个变量共享了同一个内存地址。因此如果修改了复制对象中的属性和元素,原始对象中的对应属性和元素也会被修改
深拷贝是创建了一个新的变量,同时创建新的空间将原对象数据复制,新变量指向了新开辟出来的对象地址。如果修改了复制对象中的属性和元素,原始对象中的对应属性和元素不会受到影响。
扩展知识
浅拷贝常用方式
BeanUtils.copyProperties(Object source, Object target) spring工具包下
,通过反射实现
BeanUtils.copyProperties(Object dest, Object orig) apache.commons包下
,通过反射实现
BeanCopier beanCopier = BeanCopier.create(Class source, Class target, boolean useConverter);
beanCopier.copy(source, target, null); (spring包下,通过cglib实现)
深拷贝常用方式
JSON.parseObject(JSON.toJSONString(object), T.class); fastjson包,将对象转化为JSON字符串,然后在转成目标对象
SerializationUtils.clone() apache.commons.lang3包下,将对象先转化为流,在转化为目标对象。该对象必须实现Serializable接口,否则将抛出SerializationException异常。
封装的工具类
fastjson
public class ObjectTransformUtil {
/**
* 将给定对象转化为目标对象
*
* @param obj 需要转化的对象
* @param targetClazz 目标对象
* @return 目标对象
* @Author wangwei
* @Date 2021/4/2
*/
public static <S, T> T transfer(S obj, Class<T> targetClazz) {
return JSON.parseObject(JSON.toJSONString(obj), targetClazz);
}
/**
* 使用指定的日期格式化规则来转化对象
*
* @param obj 需要转化的对象
* @param targetClazz
* @param dataFormat
* @param <S>
* @param <T>
* @return
* @Author wangwei
* @Date 2021/4/2
*/
public static <S, T> T transferWithDataFormat(S obj, Class<T> targetClazz, String dataFormat) {
return JSON.parseObject(JSON.toJSONStringWithDateFormat(obj, dataFormat, SerializerFeature.WriteDateUseDateFormat), targetClazz);
}
/**
* 将集合转化为目标对象
*
* @param list 需要转化的对象
* @param targetClazz 目标对象
* @return 目标对象
* @Author wangwei
* @Date 2021/4/2
*/
public static <S, T> List<T> transferList(List<S> list, Class<T> targetClazz) {
if (list == null || list.isEmpty()) {
return null;
}
return JSON.parseArray(JSON.toJSONString(list), targetClazz);
}
}
BeanCopier
public class BeanCopierUtil {
/**
* BeanCopier的缓存,避免频繁创建,高效复用
*/
private static final ConcurrentHashMap<String, BeanCopier> BEAN_COPIER_MAP_CACHE = new ConcurrentHashMap<String, BeanCopier>();
/**
* BeanCopier的copyBean,高性能推荐使用,增加缓存
*
* @param source 源文件的
* @param target 目标文件
*/
public static void copyBean(Object source, Object target) {
String key = genKey(source.getClass(), target.getClass());
BeanCopier beanCopier;
if (BEAN_COPIER_MAP_CACHE.containsKey(key)) {
beanCopier = BEAN_COPIER_MAP_CACHE.get(key);
} else {
beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false);
BEAN_COPIER_MAP_CACHE.put(key, beanCopier);
}
beanCopier.copy(source, target, null);
}
/**
* 不同类型对象数据copylist
*
* @param sourceList
* @param targetClass
* @param <T>
* @return
*/
public static <T> List<T> copyListProperties(List<?> sourceList, Class<T> targetClass) throws Exception {
if (ObjectUtil.isNotEmpty(sourceList)) {
List<T> list = new ArrayList<T>(sourceList.size());
for (Object source : sourceList) {
T target = copyProperties(source, targetClass);
list.add(target);
}
return list;
}
return Lists.newArrayList();
}
/**
* 返回不同类型对象数据copy,使用此方法需注意不能覆盖默认的无参构造方法
*
* @param source
* @param targetClass
* @param <T>
* @return
*/
public static <T> T copyProperties(Object source, Class<T> targetClass) throws Exception {
T target = targetClass.newInstance();
copyBean(source, target);
return target;
}
/**
* @param srcClazz 源class
* @param tgtClazz 目标class
* @return string
*/
private static String genKey(Class<?> srcClazz, Class<?> tgtClazz) {
return srcClazz.getName() + tgtClazz.getName();
}
}