java深度复制和浅度复制

在java中除了基本类型之外的一切对象皆是引用,那么就设计到了一个问题,当我们需要复制的时候就有了深度复制(deep copy)和浅度(shadow copy)复制两种了。
java中api的大部分复制都是前度的复制,例如Collections.ncopies()是只复制引用,并不复制对象。Object的clone()方法是复制对象的内存,这也存在一个问题,如果这个对象中有属性指向的是另一个对象,那么复制的这个对象和原对象中的属性指向的是同一个对象,所有也是浅复制。

在java中可以通过实现Serializable接口来实现深度复制。因为Serializable接口的序列化是序列化对象网,即对象的属性指向的对象也会被序列化,这样就可以实现深度复制了

只要将任何对象序列化到单一流中,就可以恢复出与我们写入是一样的对象网

方法如下:

 /** 
     * 深度复制,实参类必须实现Serializable接口 
     * @param o 
     * @return 
     * @throws IOException 
     * @throws ClassNotFoundException 
     */  
    public static Object deepCopy(Object o) throws IOException, ClassNotFoundException {  
//      //先序列化,写入到流里  
        ByteArrayOutputStream bo = new ByteArrayOutputStream();  
        ObjectOutputStream oo = new ObjectOutputStream(bo);  
        oo.writeObject(o);  
        //然后反序列化,从流里读取出来,即完成复制  
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());  
        ObjectInputStream oi = new ObjectInputStream(bi);  
        return oi.readObject();  
    }  

但是有一个问题是,序列化不会序列化静态的属性,这样会导致静态属性在返序列化的时候为空或者默认值,所有需要在类中添加静态的方法来自己序列化和反序列化静态属性
同样需要使用

public static void serializeStaticState(ObjectOutputStream os) throws IOException {
        //使用os来序列化静态属性
    }

public static void deserializeStaticState(ObjectInputStream os) throws IOException {
 //反序列化时使用os来实现
 }

具体的例子:

abstract class Shape implements Serializable{
    public static final int RED = 1,BLUE = 2,GREEN = 3;
    private int xPox,yPox,dimension;
    private static Random random = new Random(47);
    private static int counter = 0;
    public abstract void setColro(int newColor);
    public abstract int getColor();

    public Shape(int xVale,int yVale,int dim){
        xPox = xVale;
        yPox = yVale;
        dimension = dim;
    }

    @Override
    public String toString() {
        return getClass() +
                "color[" + getColor() + "] xPos[" + xPox +
                "] yPos[" + yPox + "] dim[" + dimension + "]\n";
    }

    public static Shape randomFactory(){
        int xVal = random.nextInt(100);
        int yVal = random.nextInt(100);
        int dim = random.nextInt(100);
        switch(counter++ % 3) {
            default:
            case 0: return new Circle(xVal, yVal, dim);
            case 1: return new Square(xVal, yVal, dim);
            case 2: return new Line(xVal, yVal, dim);
        }
    }
}

class Circle extends Shape{
    private static int color;
    public static void serializeStaticState(ObjectOutputStream os) throws IOException {
        os.writeInt(color);
    }

    public static void deserializeStaticState(ObjectInputStream os)
            throws IOException { color = os.readInt(); }
    public Circle(int xVal, int yVal, int dim) {
        super(xVal, yVal, dim);
    }
    @Override
    public void setColro(int newColor) {
        color = newColor;
    }

    @Override
    public int getColor() {
        return color;
    }
}
class Square extends Shape{
    private static int color;

    public static void serializeStaticState(ObjectOutputStream os) throws IOException {
        os.writeInt(color);
    }

    public static void deserializeStaticState(ObjectInputStream os)
            throws IOException { color = os.readInt(); }

    public Square(int xVale, int yVale, int dim) {
        super(xVale, yVale, dim);
    }

    @Override
    public void setColro(int newColor) {
        color = newColor;
    }

    @Override
    public int getColor() {
        return color;
    }
}
class Line extends Shape{
    private static int color;
    public Line(int xVale, int yVale, int dim) {
        super(xVale, yVale, dim);
    }
    public static void serializeStaticState(ObjectOutputStream os) throws IOException {
        os.writeInt(color);
    }

    public static void deserializeStaticState(ObjectInputStream os)
            throws IOException { color = os.readInt(); }

    @Override
    public void setColro(int newColor) {
        color = newColor;
    }

    @Override
    public int getColor() {
        return color;
    }
}
public class CADSate {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        List shapeList = new ArrayList<>();
        for(int i=0;i<10;i++){
            shapeList.add(Shape.randomFactory());
        }
        for (int i=0;i<10;i++){
            ((Shape)shapeList.get(i)).setColro(Shape.GREEN);
        }

        ObjectOutputStream out = new ObjectOutputStream(
                new FileOutputStream("F:\\workspace\\javaStudy\\src\\com\\thinkingInJava\\Serializable\\CAD.out"));

        Circle.serializeStaticState(out);
        Square.serializeStaticState(out);
        Line.serializeStaticState(out);
        out.writeObject(shapeList);

        System.out.println(shapeList);
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("F:\\workspace\\javaStudy\\src\\com\\thinkingInJava\\Serializable\\CAD.out"));

        Circle.deserializeStaticState(in);
        Square.deserializeStaticState(in);
        Line.deserializeStaticState(in);
        List sh = (List) in.readObject();
        System.out.println(sh);
    }
}

有时候我们需要序列化部分数据,例如在序列化一个login对象的时候,涉及到安全问题,我们不希望序列化password这个属性。所有我们可以使用关键字transient在login对象的类的password属性上添加transient时,就不会序列化这个字段。
或者使用接口Externalizable接口来实现序列化和反序列化,实现这个接口需要实现两个方法

   @Override
    public void writeExternal(ObjectOutput out) throws IOException {

    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

    }

通过这两个方法可以看出,这是希望我们自己序列化那些需要需要序列化的属性。


添加特别方法实现
如果不想用Externalizable接口,也不希望使用transient关键字的话,可以实现Serializable接口,并且添加(注意我说的是“添加”,而非“覆盖”或者“实现”)名为writeObject()和readObject()方法。这样一旦对象被序列化或者反序列化还原,就会自动的分别调用者两个方法

private void writeObject(ObjectOutputStream stream)throws IOException;
private void readObject(ObjectInputStream stream)throws IOException;

这两个方法并非接口所有的(因为接口的方法都自动是public的),也不是覆盖的父类的。在调用方法时,会检查所传递的Serializable对相,看看是否实现了它自己的writeObject()和readObject()方法,如果有就跳过正常的序列化过程,并且调用它的方法。
还有另一个技巧,在你的writeObject()内部,可以调用defaultWriteObject()来选择执行默认的writeObject()。在readObject()内部也是如此。
如果我们打算使用默认机制写入对象的非transient部分,那么必须调用defaultwriteobject()作为writeObject()中的第一个操作。同理read

你可能感兴趣的:(java基础)