一、浅拷贝
浅拷贝是指拷贝对象时仅仅拷贝对象本身和对象中的基本数据类型,而不拷贝对象中的引用类型。换句话说,对于对象中的引用类型,浅拷贝只是拷贝了引用,而没有拷贝引用的对象。因此,原对象和拷贝后的对象共享同一个引用对象。
下面是一个浅拷贝的示例:
public class ShallowCopyExample { private int value; private String refValue; public ShallowCopyExample(int value, String refValue) { this.value = value; this.refValue = refValue; } // getter 和 setter 省略 @Override public String toString() { return "ShallowCopyExample{" + "value=" + value + ", refValue='" + refValue + '\'' + '}'; } public static void main(String[] args) { ShallowCopyExample original = new ShallowCopyExample(1, "Original"); ShallowCopyExample copied = new ShallowCopyExample(original.getValue(), original.getRefValue()); System.out.println("Original: " + original); System.out.println("Copied: " + copied); copied.setRefValue("Copied"); System.out.println("After modification:"); System.out.println("Original: " + original); System.out.println("Copied: " + copied); }
}
在上面的示例中,我们创建了一个ShallowCopyExample类,它包含一个基本数据类型value和一个引用类型refValue。在main方法中,我们创建了一个原始对象original,并通过浅拷贝创建了一个拷贝对象copied。当我们修改copied对象的refValue属性时,可以发现原始对象original的refValue属性也被修改了,这是因为它们共享同一个引用对象。
二、深拷贝
深拷贝是指拷贝对象时不仅拷贝对象本身和基本数据类型,还递归拷贝对象中的引用类型。也就是说,对于对象中的引用类型,深拷贝会创建一个新的对象,并将原对象中的引用指向新创建的对象。因此,原对象和拷贝后的对象不共享任何引用对象。
在Java中,实现深拷贝通常有两种方法:
1. 使用序列化和反序列化;
通过实现Serializable接口,我们可以将对象转换为字节流,然后再从字节流中恢复出一个新的对象。这个过程会创建对象所有属性的新副本,包括引用类型属性指向的对象。
2. 使用拷贝构造函数和覆写clone方法。
这种方法需要手动编写代码来递归地复制对象的所有属性。需要注意的是,如果对象包含循环引用,这种方法可能会导致无限递归。
对于特定的数据类型,如集合或数组,Java也提供了相关的拷贝方法,如ArrayList的addAll()方法或数组的copyOf()方法。但是这些方法通常只适用于浅拷贝。
三、 深拷贝的工具类
在Java中,实现深拷贝的工具类有很多,包括一些第三方库如Apache Commons Lang和Google Guava,以及Java标准库中的clone()方法和序列化机制。下面是一些常见的深拷贝工具类和方法:
1. Apache Commons Lang的SerializationUtils
Apache Commons Lang库提供了一个名为SerializationUtils的工具类,它可以通过序列化和反序列化来实现对象的深拷贝。使用这种方法的一个优点是它可以处理复杂的对象图,包括循环引用。
import org.apache.commons.lang3.SerializationUtils; // ... MyObject original = new MyObject();
// 设置original对象的属性... MyObject copied = SerializationUtils.clone(original);
请注意,这种方法要求对象及其所有嵌套对象都实现了Serializable接口。
2. Java的clone()方法和自定义克隆
Java的Object类提供了一个受保护的clone()方法,用于创建并返回此对象的一个拷贝。如果类实现了Cloneable接口,就可以调用此方法。但是,默认的clone()方法实现的是浅拷贝,所以需要在类中覆写此方法来实现深拷贝。
public class MyObject implements Cloneable { private MyNestedObject nestedObject; @Override protected Object clone() throws CloneNotSupportedException { MyObject obj = (MyObject) super.clone(); obj.nestedObject = (MyNestedObject) this.nestedObject.clone(); return obj; } // MyNestedObject类也需要实现clone()方法来进行深拷贝...
}
3. 使用Java序列化进行深拷贝
另一种利用Java序列化实现深拷贝的方法是手动序列化对象到字节流,然后从字节流中反序列化出一个新的对象。这种方法类似于SerializationUtils,但它是手动实现的。
java
import java.io.*; public class DeepCopyUtil { public static <T> T deepCopy(T object) { try { // 将对象写入到字节流中 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(object); oos.flush(); oos.close(); // 从字节流中恢复对象 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); @SuppressWarnings("unchecked") T copy = (T) ois.readObject(); ois.close(); return copy; } catch (IOException | ClassNotFoundException e) { throw new RuntimeException(e); } }
}
4. 使用第三方库如Google Guava
Google Guava库也提供了一些用于深拷贝的工具,但它不像Apache Commons Lang那样直接提供一个通用的深拷贝方法。相反,Guava提供了一些实用程序类和方法,可以帮助你更容易地实现深拷贝逻辑。
在选择深拷贝工具类时,需要考虑对象的复杂性、性能需求、是否支持循环引用以及是否需要额外的库依赖等因素。如果对象的结构相对简单,并且没有循环引用,那么自定义的clone()方法可能就足够了。对于更复杂的场景,使用Apache Commons Lang或Java序列化可能是更好的选择。
三、 常见面试汇总
面试题目1:Object类的clone()方法实现的是深拷贝还是浅拷贝?
Object类的clone()方法实现的是浅拷贝。它只会复制对象的非引用类型属性和引用类型属性的引用,而不会复制引用的对象本身。因此,如果对象包含引用类型属性,那么这些属性在拷贝后仍然指向原始对象中的对象。
要实现深拷贝,通常需要在具体的类中覆写clone()方法,并递归地复制所有引用类型属性指向的对象。