java细节


1、对象的clone

1.1 实现Cloneable接口

​ 继承Cloneable接口,覆盖public Object clone()方法。若类中还有其他类的引用,则其他类中也必须覆盖clone方法。

public class Point  implements Cloneable
{

    int x;
    int y;
    public Point()
    {

    }

    public Point(int x,int y)
    {
        this.x=x;
        this.y=y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    @Override
    public String toString() {
        return "Point [x=" + x + ", y=" + y + "]";
    }

    @Override  
        public Object clone() {  
            Point p = null;  
            try{  
                p = (Point)super.clone();  
              }catch(CloneNotSupportedException e) {  
                e.printStackTrace();  
            }  

            return p;  
        }
}
public class Address implements Cloneable {  

    private String add; 
    Point center;

    public Address()
    {
        this.add=null;
        this.center=null;
    }

    public Address(String ss,Point center)
    {
        this.add=ss;
        this.center=center;
    }


    public String getAdd() {  
        return add;  
    }  

    public void setAdd(String add) {  
        this.add = add;  
    }  

    @Override  
    public Object clone() {  
        Address addr = null;  
        try{  
            addr = (Address)super.clone();  
            addr.center=(Point)this.center.clone();
        }catch(CloneNotSupportedException e) {  
            e.printStackTrace();  
        }  

        return addr;  
    }

     public boolean test(Address o)
     {
         return this.add==o.add;
     }

    public boolean testCenter(Address o)
     {
         return this.center==o.center;
     }


    @Override
    public String toString() {
        return "Address [add=" + add + "]";
    }  
}  
public class AppAddress {

    public static void main(String[] args) {
        Address add=new Address("aaa",new Point(3,3));

        Address add2=(Address)add.clone();

        System.out.println(add.test(add2));    //true
        add2.setAdd("bbb");
        System.out.println(add.test(add2));    //false
        System.out.println(add.getAdd());    //aaa
        add2.center.x=9;
        System.out.println(add.testCenter(add2));    //false    
    }
}

​ 当add2=add.clone()后,add2引用add的地址。当add2改变自己的属性值时,将不再引用add的地址,而是重新指向一块地址,为深拷贝。


clone方式深拷贝小结:

​ 1.如果有一个非原生成员,如自定义对象的成员,那么就需要:

  • 该成员实现Cloneable接口并覆盖clone()方法,不要忘记提升为public可见。
  • 同时,修改被复制类的clone()方法,增加成员的克隆逻辑。

​ 2. 如果被复制对象不是直接继承Object,中间还有其它继承层次,每一层super类都需要实现Cloneable接口并覆盖clone()方法。

​ 与对象成员不同,继承关系中的clone不需要被复制类的clone()做多余的工作。

​ 一句话来说,如果实现完整的深拷贝,需要被复制对象的继承链、引用链上的每一个对象都实现克隆机制。

前面的实例还可以接受,如果有N个对象成员,有M层继承关系,就会很麻烦。

1.2 BeanUtils

​ BeanUtils是一个工具类,类中提供了cloneBean(Object object)方法。

自己实现BeanUtils(反射机制):

public class BeanUtils 
{
   public static Object copyObject(Object obj)
   {
       if(obj==null) return null;
       Class clz=obj.getClass();
       Object temp=null;
       try{
           temp=clz.newInstance();
           Field[] fields=clz.getDeclaredFields();
           for (Field field : fields) 
           {
               field.setAccessible(true);
               Object value=field.get(obj);
               if(check(field.getType()))
                 { 
                   field.set(temp, value);
                 }
               else
               {
                   Object t=copyObject(value);
                   field.set(temp, t);
               }

            }

       }catch(Exception e)
       {
           e.printStackTrace();
       }

       return temp;
   }

   private static boolean check(Class clz)
   {
       if(clz==Integer.class||clz==Integer.TYPE) return true;
       if(clz==Double.class||clz==Double.TYPE) return true;
       if(clz==Float.class||clz==Float.TYPE) return true;
       if(clz==Boolean.class||clz==Boolean.TYPE) return true;
       if(clz==Long.class||clz==Long.TYPE) return true;
       if(clz==String.class) return true;
       return false;
   }
}
public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
        // TODO Auto-generated method stub
        Address add=new Address("aaa",new Point(3,3));
        Address add2=(Address)BeanUtils.copyObject(add);
        System.out.println(add.testCenter(add2));    //false

        Address add3  =(Address)org.apache.commons.beanutils.BeanUtils.cloneBean(add);
        System.out.println(add.test(add3));        //false
        System.out.println(add.testCenter(add3));    //false
    }

1.3 序列化

public class Person implements Serializable {
    private String name;
    private Integer age;
    private Address address;
    public Person deepClone() {
        Person p2=null;
        Person p1=this;
        PipedOutputStream out=new PipedOutputStream();
        PipedInputStream in=new PipedInputStream();
        try {
            in.connect(out);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try(ObjectOutputStream bo=new ObjectOutputStream(out);
                ObjectInputStream bi=new ObjectInputStream(in);) {
            bo.writeObject(p1);
            p2=(Person) bi.readObject();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return p2;
    }
}

​ clone机制不是强类型的限制,比如实现了Cloneable并没有强制继承链上的对象也实现;也没有强制要求覆盖clone()方法。因此编码过程中比较容易忽略其中一个环节,对于复杂的项目排查就是困难了。

​ 要寻找可靠的,简单的方法,序列化就是一种途径。

  • 被复制对象的继承链、引用链上的每一个对象都实现java.io.Serializable接口。这个比较简单,不需要实现任何方法,serialVersionID的要求不强制,对深拷贝来说没毛病。
  • 实现自己的deepClone方法,将this写入流,再读出来。俗称:冷冻-解冻。

文章作者: kilig
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kilig !
 上一篇
内部类匿名类 内部类匿名类
1、内部类1.1 内部类是什么?​ 所谓内部类,就是在一个类的内部还有一个类的嵌套操作。 public class Test { private int m=6; class InnerTe
下一篇 
基于CNN+ATTENTION的漏洞检测系统 基于CNN+ATTENTION的漏洞检测系统
1、项目简介​ 在网络日益发展的今天,软件安全成为了从业人员必须要注意的一块。在国际上,软件漏洞静态检测是一种有效的方式去检测程序中静态存在的错误。在检测方法中,相较于二进制漏洞检测法,源代码漏洞检测法更简便更快速。​
2019-05-14
  目录