说到clone,工作中可能使用到的不多,所以今天就给大家讲一下。
clone方法是Object类的一个方法可能大家都知道,但是可能很多人都没有注意到clone方法的修饰符,我们来看一下源码
public class Object {
* @throws CloneNotSupportedException if the object's class does not
* support the {@code Cloneable} interface. Subclasses
* that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
protected native Object clone() throws CloneNotSupportedException;
}
得出以下三个结论:
- clone是一个native方法,而不是java方法,所以可以通过这个修饰符得出结论,clone方法并不会像java一样new一个新对象出来,而是创建一个新的对象,将原对象的数据复制了一份
- clone方法的修饰符是protected,所以任意对象想要调用clone方法,必须自己实现clone方法
- clone方法会抛出CloneNotSupportedException异常。通过注释,我们可以知道只有实现了Cloneable接口的类才能调用clone方法,否则会抛出CloneNotSupportedException异常
接下来我们详细讲一下clone方法的复制过程
我们先来看一下下面这个测试用例:
public class MyTest {
public static void main(String[] args) throws CloneNotSupportedException {
User user = new User(new Hobby());
User user2 = (User)user.clone();
System.out.println(user == user2);
System.out.println(user.getHobby() == user2.getHobby());
}
}
class User implements Cloneable{
private Hobby hobby;
public User(Hobby hobby) {
super();
this.hobby = hobby;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Hobby getHobby() {
return hobby;
}
}
class Hobby{
public Hobby() {
super();
}
}
- 因为user != user2,所以我们可以知道,clone方法并不是创建一个新的变量user2,用于指向原对象user的内存地址,而是开辟了一块新的内存,创建了一个新的对象
- 因为user.getHobby() == user2.getHobby(),所以我们可以知道,Object类的clone方法对于复制User类的hobby属性,没有new一个新的对象,而是直接引用了原对象hobby属性的地址
得出结论,Object类的clone方法,对于成员变量引用的其他对象不会创建一个新的对象,只是引用原对象的地址,术语上叫浅层复制。而如果我们想将引用对象也复制一份,而不是单纯的引用地址,就需要深层复制。
那么怎样去实现深层复制呢?可以去重写User的clone方法,使得在复制User对象的时候也将Hobby对象复制一份
class User implements Cloneable{
private Hobby hobby;
public User(Hobby hobby) {
super();
this.hobby = hobby;
}
@Override
protected Object clone() throws CloneNotSupportedException {
User user = (User)super.clone();
user.hobby = (Hobby)hobby.clone();
return user;
}
public Hobby getHobby() {
return hobby;
}
}
class Hobby implements Cloneable{
public Hobby() {
super();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
我们在调用User的clone方法的时候,会先去调用Hobby的clone方法,这样就能保证复制出来的User对象的Hobby属性是也是一个新的的对象。
看起来重写了User的clone方法就实现了我们想要的深层复制,但是如果Hobby类的成员变量也有引用,引用一个Music对象怎么办呢?似乎只能再重写Hobby的clone方法。
这样就陷入一个无限循环,所以clone方法理论上是不存在绝对的深层复制的。
那么如果说,我就想实现一个绝对的深层复制,怎么办呢?
有时候思路不能局限于一个方法,如果想从clone方法上找解决方案,多半是不现实的了。
开阔一下思维就能想到可以使用序列化来实现深层复制,序列化和反序列化生成的对象以及对象的属性一定都是新的对象,而不是对原对象地址的引用,下面给出针对上面User类的复制代码(类必须实现Serializable接口)
class User implements Serializable {
private static final long serialVersionUID = 1L;
private Hobby hobby;
public Object deepClone() throws IOException, OptionalDataException, ClassNotFoundException {// 将对象写到流里
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (oi.readObject());
}
public User(Hobby hobby) {
super();
this.hobby = hobby;
}
public Hobby getHobby() {
return hobby;
}
}
class Hobby implements Serializable {
private static final long serialVersionUID = 1L;
public Hobby() {
super();
}
}
关于clone方法、浅层复制以及深层复制的分享到这里就结束了,希望对大家有所帮助。
扫码关注,不迷路