java深度复制和浅度复制

本文详细解释了Java中对象的深复制与浅复制的区别及应用场景,介绍了如何利用Serializable接口实现深度复制,探讨了序列化过程中静态变量的处理方法,同时提供了自定义序列化流程的具体示例。

在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<Shape> 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<Shape> sh = (List<Shape>) 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

### OpenCVSharp 中 Mat 对象深拷贝拷贝 #### 拷贝 在 OpenCVSharp 中,当使用 `=` 运算符来复制一个 `Mat` 对象时,默认情况下会创建该对象的一个拷贝。这意味着两个变量实际上指向同一块内存区域。如果修改其中一个变量的内容,另一个也会受到影响。 ```csharp using (var img1 = Cv2.ImRead("image.jpg")) { using (var img2 = img1) // 创建了一个拷贝 { // 修改 img1 将会影响 img2 img1.SetTo(Scalar.Red); // 显示图像 Cv2.ImShow("Original Image", img1); Cv2.ImShow("Shallow Copy", img2); Cv2.WaitKey(0); } } ``` #### 深拷贝 为了实现真正的数据隔离,即创建一个新的独立副本而不共享底层的数据缓冲区,可以使用 `.Clone()` 方法或 `.CopyTo()` 函数来进行深拷贝操作。这两种方式都会分配新的内存并复制原始图像的所有像素值到新位置上[^2]。 - **`.Clone()`**: 总是会在堆栈中开辟一块全新的连续存储空间用于保存完整的图像信息,并返回这个新实例。 ```csharp using (var img1 = Cv2.ImRead("image.jpg")) { var img2 = img1.Clone(); // 完整的深拷贝 // 即使现在改变 img1 的内容也不会影响到 img2 img1.SetTo(Scalar.Blue); // 展示两幅图的不同状态 Cv2.ImShow("Modified Original", img1); Cv2.ImShow("Deep Copied Version", img2); Cv2.WaitKey(0); } ``` - **`.CopyTo(Mat dst)`**: 此方法允许指定目标容器(dst),它可以根据目的端的具体情况决定是否重新分配内存。如果目标容器已经存在并且尺寸合适,则可以直接覆盖其内部数据;反之则需先调整大小再执行复制过程[^4]。 ```csharp using (var srcImg = Cv2.ImRead("source_image.jpg")) { using (var destImg = new Mat()) { srcImg.CopyTo(destImg); // 如果destImg为空或者尺寸不合适将会被重置为合适的尺寸 // 可以安全地对源图片做任何更改而不用担心会影响到目的地 srcImg.SetTo(Scalar.Green); Cv2.ImShow("Source After Modification", srcImg); Cv2.ImShow("Destination Unchanged By Source Changes", destImg); Cv2.WaitKey(0); } } ``` 综上所述,在处理多份相同但又希望保持各自独立性的图像资料时,应当优先考虑采用深拷贝的方式以避免不必要的副作用[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值