Skip to content

拷贝与引用

[[如何配置clone]] - 基本数据类型是在栈上存储的,其它均在堆上创建对象,栈上存储引用 - image-20231222163627747

拷贝对象

  • 需要将想要克隆的子类中将clone()方法重写为public(默认为protected)
  • clone的返回值是Object因此还需要进行显式类型转换
  • 要注意的是有时clone并不总是符合预期,如对ArrayList进行clone并不会对内部的元素进行复制
  • 直接赋值二进制数据,因此把引用复制了过来
  • 包装数据类型是例外,包装数据类型不可变,修改后会创建新对象,因此使用clone后不会再同步修改

  • Cloneable接口,用于验证一个对象是否具有克隆能力,如果一个类没有实现该接口,在使用clone方法时会报错

  • 由于会引发异常,因此需要trycatch
class Duplo implements Cloneable {
  private int n;
  Duplo(int n) { this.n = n; }
  @Override public Duplo clone() {    
    try {
        //对于大部分对象的处理
      return (Duplo)super.clone();
    } catch(CloneNotSupportedException e) {
      throw new RuntimeException(e);
    }
  }
  public int getValue() { return n; }
  public void setValue(int n) { this.n = n; }
  public void increment() { n++; }
  @Override public String toString() {
    return Integer.toString(n);
  }
}
  • 克隆组合对象
  • 除了克隆自身外要对所有引用对象进行克隆和重新绑定

    @Override public OceanReading clone() {
        OceanReading or = null;
        try {
            or =(OceanReading)super.clone();
        } catch(CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
        // 克隆并重新绑定引用
        or.depth = (DepthReading)or.depth.clone();
        or.temperature =(TemperatureReading)or.temperature.clone();
        return or;
    }
    

  • 深拷贝ArrayList

  • 先cone集合,之后对每一项进行clone和重新绑定到集合

  • 通过序列换进行深拷贝

  • 序列化再反序列化实现拷贝(但是这很慢)
  • 需要实现Serializable
    import java.io.*;
    
    public class DeepCopy {
    
        @SuppressWarnings("unchecked")
        public static <T extends Serializable> T deepCopy(T object) {
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
                    oos.writeObject(object);
                }
    
                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                try (ObjectInputStream ois = new ObjectInputStream(bais)) {
                    return (T) ois.readObject();
                }
            } catch (IOException | ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
    }
    

控制可克隆性

  • 一个类将clone重写为了public,那么所有的子类都可以clone了,因为无法降低基于基类的方法在子类中的可访问性
  • 不实现接口:不允许直接克隆但是可以继承该类并在子类中实现克隆
  • 或者在clone中重写并抛出异常禁止使用
  • 但是如果子类没有遵守调用super.clone这种限制会失效
  • 类设置为 final,实现完全禁止克隆(可以与构造器设置为 private 配合,实现限制对象的数目)

引用类型

  • 强引用:Strong Reference
  • 默认情况下,当我们在 Java 中创建一个对象并赋值给一个变量时,就是创建了一个强引用。只要强引用存在,垃圾回收器就不会回收被引用的对象

  • 软引用:Soft Reference

  • 软引用是一种内存敏感的引用类型。垃圾回收器在内存不足时会回收这些对象。软引用通常用于实现内存敏感的缓存。
  • SoftReference<String> softReference = new SoftReference<>(new String("Hello"));

  • 弱引用:

  • 弱引用比软引用更弱。垃圾回收器在下一次回收时,无论内存是否足够,都会回收只被弱引用指向的对象。弱引用适合用于实现没有阻止垃圾收集器回收对象的映射(例如,WeakHashMap)。
  • WeakReference<String> weakReference = new WeakReference<>(new String("Hello"));

  • 虚引用:

  • 虚引用是最弱的一种引用类型。设置虚引用的唯一目的是在这个对象被垃圾回收器回收时收到一个系统通知。虚引用对于确定对象何时被从内存中移除非常有用,它们常被用于实现高级的内存管理和监控。
  • PhantomReference<String> phantomReference = new PhantomReference<>(new String("Hello"), new ReferenceQueue<>());