zoukankan      html  css  js  c++  java
  • Java中把一个对象复制给另外一个对象引发的思考

      Spring生态在Java项目中被广泛应用,从架构到技术应用再到常用的基本功能,Spring给我们的开发带来了很大的便利。今天翻到项目中导出报表功能的时候,发现经常复制对象的方法:

      BeanUtils.copyProperties;

      把源对象的属性值赋值给目标对象,Spring和Apache和其他的一些框架都给我们提供了对象属性的拷贝方法:

      org.springframework.beans.BeanUtils.copyProperties(Object source, Object target)

      org.apache.commons.beanutils.BeanUtils.copyProperties(Object dest, Object orig)

      使用的时候注意这两个方法的参数顺序,源和目标是相反的。这两个方法底层都用到了反射,心血来潮之下我自己造了一个对象拷贝的轮子,代码如下:

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.HashMap;
    
    public class ObjConvertor {
        private static  HashMap<String,HashMap<String,Method>> mapSetMethods=new  HashMap<String,HashMap<String,Method>>();
        private static HashMap<String,HashMap<String,Method>> mapGetMethods=new  HashMap<String,HashMap<String,Method>>();
        
        static
        {
            
        }
        
        public static boolean Convert(Object src,Object dst) {
            HashMap<String,Method> mapSrc=new HashMap<String,Method>();
            HashMap<String,Method> mapDst=new HashMap<String,Method>();
            if(mapGetMethods.containsKey(src.getClass().getTypeName()))
                mapSrc=mapGetMethods.get(src.getClass().getTypeName());
            else
            {
                Method srcMethods[]=src.getClass().getMethods();
                mapGetMethods.put(src.getClass().getTypeName(),mapSrc);
                for(Method method:srcMethods)
                {
                    String name=method.getName();
                    if(name.startsWith("get"))
                    {
                        String prop=name.substring(3).toLowerCase();
                        mapSrc.put(prop, method);
                    }
                }
            }
            if(mapSetMethods.containsKey(dst.getClass().getTypeName()))
                mapDst=mapSetMethods.get(dst.getClass().getTypeName());
            else
            {
                Method dstMethods[]=dst.getClass().getMethods();
                mapSetMethods.put(dst.getClass().getTypeName(),mapDst);
                for(Method method:dstMethods)
                {
                    String name=method.getName();
                    if(name.startsWith("set"))
                    {
                        String prop=name.substring(3).toLowerCase();
                        mapDst.put(prop, method);
                    }
                }
            }
            for(String prop:mapSrc.keySet())
            {
                if(mapDst.containsKey(prop)){
                    Method setMethod=mapDst.get(prop);
                    Method getMethod=mapSrc.get(prop);
                    try {
                        Object data=getMethod.invoke(src);
                        if(data==null)
                            continue;
                        if(data instanceof java.sql.Time)
                        {
                            if(setMethod.getParameterTypes()[0]==String.class)
                                data=data.toString();
                        }
                        else if(data instanceof java.sql.Date)
                        {
                            if(setMethod.getParameterTypes()[0]==String.class)
                                data=new SimpleDateFormat("yyyy-MM-dd").format(data);
                        }
                        else if(data instanceof java.util.Date)
                        {
                            if(setMethod.getParameterTypes()[0]==String.class)
                                data=new SimpleDateFormat("yyyy-MM-dd").format((java.util.Date)data);
                        }
                        else if(data instanceof java.sql.Timestamp)
                        {
                            if(setMethod.getParameterTypes()[0]==String.class)
                                data=data.toString();
                        }
                        if(setMethod.getParameterTypes()[0]==java.sql.Time.class)
                        {
                            if(data instanceof String)
                                data=java.sql.Time.valueOf((String)data);
                        }
                        else if(setMethod.getParameterTypes()[0]==java.sql.Date.class)
                        {
                            if(data instanceof String)
                                data=java.sql.Date.valueOf((String)data);
                        }
                        else if(setMethod.getParameterTypes()[0]==java.util.Date.class)
                        {
                            if(data instanceof String)
                            {
                                data=(new SimpleDateFormat("yyyy-MM-dd")).parse((String)data);
                            }
                        }
                        else if(setMethod.getParameterTypes()[0]==java.sql.Timestamp.class)
                        {
                            if(data instanceof String)
                                data=java.sql.Timestamp.valueOf((String)data);
                        }
                        if(data==null)
                            continue;
                        setMethod.invoke(dst, data);
                    } catch (IllegalAccessException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        return false;
                    } catch (IllegalArgumentException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        return false;
                    } catch (InvocationTargetException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        return false;
                    } catch (ParseException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        return false;
                    }
                }
            }
            return true;
        }
    }

      对象拷贝的功能至此完结,如果项目中需要一些特殊格式的数据,可以考虑自己造轮子,这个就看业务需求了。

      接下来谈谈对象拷贝,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝,比如Spring和Apache的copyProperties和上面自己的拷贝方法。反过来,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

      通过对比不难发现:深拷贝相当于创建了一个新的对象,只是这个对象的所有内容,都和被拷贝的对象一模一样,即两者的修改是隔离的,相互之间没有影响;而浅拷贝也是创建了一个对象,但是这个对象的某些内容(比如A)依然是被拷贝对象的,即通过这两个对象中任意一个修改A,两个对象的A都会受到影响。

      Spring和Apache的copyProperties都属于浅拷贝,在日常开发中基本可以满足使用了。需要注意的是,在Java开发手册中提到,不建议用Apache的BeanUtils工具类,因为其中有很多的检验、类型转换、日志打印等,实现复杂度高,性能比较差。

      那么如果要实现高性能且安全的深度拷贝呢?深拷贝就是被拷贝对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量,那些引用其他对象的变量将指向被拷贝过的新对象,而不再试原有的那些被引用的对象,就是说,深拷贝把要拷贝的对象所引用的对象都复制了一遍。我觉得最靠谱的深拷贝还是要用序列化后写流的方法,这种方法需要实现Serializable接口,多层拷贝时,引用类型都要实现Serializable接口。代码如下:

    import java.io.*;
    import java.util.Date;
    
    public class CopyDemo {
        class User implements Serializable {
            private int id;
            private String username;// 用户姓名
            private String sex;// 性别
            private Date birthday;// 生日
            private String address;// 地址
            private Person person; //引用类型
    
            public User myColon(){
                User copy=null;
                try {
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ObjectOutputStream oos = new ObjectOutputStream(baos);
                    oos.writeObject(this);
                    //将流序列化成对象
                    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                    ObjectInputStream ois = new ObjectInputStream(bais);
                    copy = (User) ois.readObject();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                return copy;
            }
    
            //省略get-set方法代码
        }
    
        class Person implements Serializable {
            private int id;
            private String userName ;
            private int age ;
            private String mobilePhone ;
            public  Person(){}
            public Person(int id,String userName, int age, String mobilePhone) {
                this.id = id;
                this.userName = userName;
                this.age = age;
                this.mobilePhone = mobilePhone;
            }
            //省略get-set方法
        }
    }

      在Java语言里深拷贝一个对象,先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。通过代码可以更明显的看到这个过程。把对象写到流里的过程是串行化(Serilization)过程,而把对象从流中读出来是并行化(Deserialization)过程。需要注意的是,需要拷贝的对象可能包含多层引用类型,多层拷贝不仅要将拷贝对象实现序列化接口,引用对象也同样的要实现序列化接口。

         


  • 相关阅读:
    一个很好的国外的算法网站
    Windows 2008 R2 强制删除Cluster
    .net 4.5 新特性 async await 一般处理程序实例
    基于RSA的加密/解密示例C#代码
    解决WCF 调用方未由服务器进行身份验证或消息包含无效或过期的安全上下文令牌
    SQL Server查看所有表大小,所占空间
    关于Latch
    关闭SQL Server 数据库所有使用连接
    MysqliDb 库的一些使用简单技巧(php)
    Linux 常用命令
  • 原文地址:https://www.cnblogs.com/JohanChan/p/14435823.html
Copyright © 2011-2022 走看看