public class CloneUtil {
private static final Logger log = LoggerFactory.getLogger(CloneUtil.class);
/**
* 无需进行复制的特殊类型数组
*/
@SuppressWarnings("rawtypes")
static Class[] needlessCloneClasses = new Class[]{String.class,Boolean.class,Character.class,Byte.class,Short.class,
Integer.class,Long.class,Float.class,Double.class,Void.class,Object.class,Class.class
};
/**
* 判断该类型对象是否无需复制
* @param c 指定类型
* @return 如果不需要复制则返回真,否则返回假
*/
@SuppressWarnings("rawtypes")
private static boolean isNeedlessClone(Class c){
if(c.isPrimitive()){//基本类型
return true;
}
for(Class tmp:needlessCloneClasses){//是否在无需复制类型数组里
if(c.equals(tmp)){
return true;
}
}
return false;
}
/**
* 尝试创建新对象
* @param value 原始对象
* @return 新的对象
* @throws IllegalAccessException
*/
private static Object createObject(Object value) throws IllegalAccessException{
try {
return value.getClass().newInstance();
} catch (InstantiationException e) {
return null;
} catch (IllegalAccessException e) {
throw e;
}
}
/**
* 复制对象数据
* @param value 原始对象
* @param level 复制深度。
* 小于0为无限深度,即将深入到最基本类型和Object类级别的数据复制;
* 大于0则按照其值复制到指定深度的数据;
* 等于0则直接返回对象本身而不进行任何复制行为。
* @return 返回复制后的对象
* @throws IllegalAccessException
* @throws InstantiationException
* @throws InvocationTargetException
* @throws NoSuchMethodException
*/
@SuppressWarnings({ "rawtypes", "unchecked"})
private static Object clone(Object value,int level) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException{
if(value==null){
return null;
}
if(level==0){
return value;
}
Class c = value.getClass();
if(isNeedlessClone(c)){
return value;
}
level--;
if(value instanceof Collection){
//复制新的集合
Collection tmp = (Collection)c.newInstance();
for(Object v:(Collection)value){
//深度复制
tmp.add(clone(v,level));
}
value = tmp;
}
else if(c.isArray()){
//复制新的Array,首先判断是否为基本数据类型
if(c.equals(int[].class)){
int[] old = (int[])value;
value = (int[])Arrays.copyOf(old, old.length);
}
else if(c.equals(short[].class)){
short[] old = (short[])value;
value = (short[]) Arrays.copyOf(old, old.length);
}
else if(c.equals(char[].class)){
char[] old = (char[])value;
value = (char[])Arrays.copyOf(old, old.length);
}
else if(c.equals(float[].class)){
float[] old = (float[])value;
value = (float[])Arrays.copyOf(old, old.length);
}
else if(c.equals(double[].class)){
double[] old = (double[])value;
value = (double[])Arrays.copyOf(old, old.length);
}
else if(c.equals(long[].class)){
long[] old = (long[])value;
value = (long[])Arrays.copyOf(old, old.length);
}
else if(c.equals(boolean[].class)){
boolean[] old = (boolean[])value;
value = (boolean[])Arrays.copyOf(old, old.length);
}
else if(c.equals(byte[].class)){
byte[] old = (byte[])value;
value = (byte[])Arrays.copyOf(old, old.length);
}
else {
Object[] old = (Object[])value;
Object[] tmp = (Object[])Arrays.copyOf(old, old.length, old.getClass());
for(int i = 0;i<old.length;i++){
tmp[i] = clone(old[i],level);
}
value = tmp;
}
}
else if(value instanceof Map){
//复制新的MAP
Map tmp = (Map)c.newInstance();
Map org = (Map)value;
for(Object key:org.keySet()){
//深度复制
tmp.put(key, clone(org.get(key),level));
}
value = tmp;
}
else {
Object tmp = createObject(value);
//无法创建新实例则返回对象本身,没有克隆
if(tmp==null){
return value;
}
Set<Field> fields = new HashSet<Field>();
while(c!=null&&!c.equals(Object.class)){
fields.addAll(Arrays.asList(c.getDeclaredFields()));
c = c.getSuperclass();
}
for(Field field:fields){
//仅复制非final字段
if(!Modifier.isFinal(field.getModifiers())){
field.setAccessible(true);
//深度复制
field.set(tmp, clone(field.get(value),level));
}
}
value = tmp;
}
return value;
}
/**
* 浅表复制对象
* @param value 原始对象
* @return 复制后的对象,只复制一层
* @throws IllegalAccessException
* @throws InstantiationException
* @throws InvocationTargetException
* @throws NoSuchMethodException
*/
public static Object shallowClone(Object value){
Object retObject = null;
try {
retObject = clone(value,1);
} catch (IllegalAccessException e) {
log.error("shallowClone IllegalAccessException",e);
} catch (InstantiationException e) {
log.error("shallowClone InstantiationException",e);
} catch (InvocationTargetException e) {
log.error("shallowClone InvocationTargetException",e);
} catch (NoSuchMethodException e) {
log.error("shallowClone NoSuchMethodException",e);
}
return retObject;
}
/**
* 深度复制对象
* @param value 原始对象
* @return 复制后的对象
* @throws IllegalAccessException
* @throws InstantiationException
* @throws InvocationTargetException
* @throws NoSuchMethodException
*/
public static Object deepClone(Object value){
Object retObject = null;
try {
retObject = clone(value,-1);
} catch (IllegalAccessException e) {
log.error("deepClone IllegalAccessException",e);
} catch (InstantiationException e) {
log.error("deepClone InstantiationException",e);
} catch (InvocationTargetException e) {
log.error("deepClone InvocationTargetException",e);
} catch (NoSuchMethodException e) {
log.error("deepClone NoSuchMethodException",e);
}
return retObject;
}
/**
* 自定义复制对象
* @param value 原始对象
* @return 复制后的对象,只复制一层
* @throws IllegalAccessException
* @throws InstantiationException
* @throws InvocationTargetException
* @throws NoSuchMethodException
*/
public static Object customClone(Object value,int level){
Object retObject = null;
try {
retObject = clone(value,level);
} catch (IllegalAccessException e) {
log.error("customClone IllegalAccessException",e);
} catch (InstantiationException e) {
log.error("customClone InstantiationException",e);
} catch (InvocationTargetException e) {
log.error("customClone InvocationTargetException",e);
} catch (NoSuchMethodException e) {
log.error("customClone NoSuchMethodException",e);
}
return retObject;
}
/***
* @Description: 深度克隆;将对象序列化后写在输出流里,因为写在流里面的对象是一份拷贝,原对象仍然在JVM里;
* 然后再把输出流转换为输入流,把对象反序列化后写出来!这样就实现了对象的深度克隆,克隆后的两个对象完全独立开来,
* 要进行深度克隆的对象都要实现Serializable接口!互不影响!
* @Param: [obj] 原始对象
* @return: T 复制后的对象
*/
public static <T extends Serializable> T clone(T obj) throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();
// 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义
// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放
}
}
浅拷贝
如果一个类中有指针对象,那么在拷贝这个类的对象的时候,默认的拷贝方式是只拷贝指针本身,而不重新构建并拷贝指针所指内容。这就叫做浅拷贝Shallow Copy。Java的 Object.clone()
的机制是浅拷贝
深拷贝
如果拷贝的方式是不仅仅拷贝指针,而且把指针所指的内容也新建一份,那就叫深拷贝Deep Copy。
在有些业务场景下,我们需要两个完全相同却彼此无关的java对象。比如使用原型模式、多线程编程等。对此,java提供了深拷贝的概念。通过深度拷贝可以从源对象完美复制出一个相同却与源对象彼此独立的目标对象。这里的相同是指两个对象的状态和动作相同,彼此独立是指改变其中一个对象的状态不会影响到另外一个对象。实现深拷贝常用的实现方式有2种:Serializable,Cloneable。工作中遇到的大多是Serializable方式,这种方式代码量小,不容易出错。
public class CloneUtils {
public static <T extends Serializable> T clone(T obj) {
T cloneObj = null;
try {
//写入字节流
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream obs = new ObjectOutputStream(out);
obs.writeObject(obj);
obs.close();
//分配内存,写入原始对象,生成新对象
ByteArrayInputStream is = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream os = new ObjectInputStream(is);
cloneObj = (T) os.readObject();
os.close();
} catch (Exception e) {
e.printStackTrace();
}
return cloneObj;
}
}
BeanUtils.copyProperties()
Spring包下的org.springframework.beans.BeanUtils.copyProperties();
public static void copyProperties(Object source, Object target) throws BeansException {
copyProperties(source, target, null, (String[]) null);
}
Apeche包下的org.apache.commons.beanutils.BeanUtils.copyProperties();
public static void copyProperties(Object dest, Object orig)
throws IllegalAccessException, InvocationTargetException {
BeanUtilsBean.getInstance().copyProperties(dest, orig);
}
通过源码可以发现,Spring包下的原类在前,目标类在后;而Apeche包下的恰恰相反。
三.总结
1.看了下源码和网上讨论,Spring的BeanUtils.copyProperties()会忽略null的属性,只拷贝有值的属性,相比Apache包下的不容易出现一些错误。
source对象和target对象相应属性的名称和类型必须都一样才可以成功拷贝属性值
source对象和target对象的属性增加set、get方法。比如给name、age属性增加set、get方法
2.无论Spring的还是Apache的,cglib包下也有一个对象拷贝工具,Spring和Apache是使用反射机制实现的,cglib是使用动态代理实现的。它们都会牺牲一定的效率。
异常兼容
User user= userService.getList(feeId, phone, productType, today);
if (Objects.isNull(user)) {
user = new User();
}
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
忽略某个属性拷贝
BeanUtils.copyProperties(kafkaNoticeVO,kafkaNotice,new String[]{"actiontime","dbtime"})
————————————————
版权声明:本文为CSDN博主「@航空母舰」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hudeyong926/article/details/114675276