Object类的clone方法

Object类的clone方法

Object是所有类的基类,当你没有显示extends一个父类时,编译期会自动为你加上一个Object类。
在这里插入图片描述

  1. 这是一个native方法,说明这个方法的实现不是在java中,而是由C/C++实现,并编译成.dll文件,由java调用。registerNatives主要是将C/C++的方法映射到java中的native方法,实现方法命名的解耦。了解即可,知道是注册,细节暂时不研究。
private static native void registerNatives();
    static {
        registerNatives();
    }

  1. 第二点来关注 clone方法。
    protected native Object clone() throws CloneNotSupportedException;
    通过源代码可以发现几点:
    clone方法是native方法,native方法的效率远高于非native方法,因此还是使用clone方法去做对象的拷贝而不是使用new的方法,copy。
    此方法被protected修饰。这就意味着想要使用,必须继承它(废话,默认都是继承的)。然后重载它,如果想要使得其他类能使用这个类,需要设置成public。
    返回值是一个Object对象,所以要强制转换才行。
public class TestReen{
	
	public static void main(String[] args) throws Exception{
		
		TestReen tReen = new TestReen();
		
		TestReen copy = (TestReen)tReen.clone();
	}
	
}

但是运行时,发现抛异常了。

Exception in thread "main" java.lang.CloneNotSupportedException: com.test.count.TestReen
	at java.lang.Object.clone(Native Method)
	at com.test.count.TestReen.main(TestReen.java:11)

进入clone方法,看注释发现如下信息:表明此类不支持Cloneable接口的话就会报错。

@exception  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.

去看看Cloneable接口,空的,

public interface Cloneable {
}

总结其注释的话,差不多三点:
(1)此类实现了Cloneable接口,以指示Object的clone()方法可以合法地对该类实例进行按字段复制;
(2)如果在没有实现Cloneable接口的实例上调用Object的clone()方法,则会导致抛出CloneNotSupporteddException;
(3)按照惯例,实现此接口的类应该使用公共方法重写Object的clone()方法,Object的clone()方法是一个受保护的方法;
因此想实现clone的话,除了继承Object类外,还需要实现Cloneable接口;
创建并返回此对象的一个副本。对于任何对象x,表达式:
(1)x.clone() != x为true
(2)x.clone().getClass() == x.getClass()为true
(3)x.clone().equals(x)一般情况下为true,但这并不是必须要满足的要求

public class TestReen implements Cloneable{
	
	private int id;
	
	private static int i = 1;
	
	public TestReen() {
		System.out.println("i = " + i);
		System.out.println("执行了构造函数"+i);
		i++;
	}
 
	public int getId() {
		return id;
	}
 
	public void setId(int id) {
		this.id = id;
	}
 
	public static void main(String[] args) throws Exception{
		
		TestReen t1 = new TestReen();
		t1.setId(1);
		
		TestReen t2 = (TestReen)t1.clone();
		
		TestReen t3 = new TestReen();
		
		System.out.println("t1 id: "+ t1.getId());
		System.out.println("t2 id: "+ t2.getId());
		System.out.println("t1 == t2 ? " + (t1 == t2));
		System.out.println("t1Class == t2Class ? " + (t1.getClass() == t2.getClass()));
		System.out.println("t1.equals(t2) ? " + t1.equals(t2));
		
	}
}

结果如下,有几个发现:

  1. 构造函数除了new执行以外,clone并没有调用到构造函数,也就是clone方法是不走构造方法的。

  2. t1 和 t2 是不等的,说明指向了不同的堆地址空间,是两个对象。

  3. getClass是相同的,getClass是什么?是获取这个实例的类型类,有点拗口,其实就是TestReen类型,可见clone出来的类型还是一样的。

i = 1
执行了构造函数1
i = 2
执行了构造函数2
t1 id: 1
t2 id: 1
t1 == t2 ? false
t1Class == t2Class ? true
t1.equals(t2) ? false

国际惯例所有博客都会去关注的一点,浅克隆(shallow clone)和 深克隆(deep clone)。
浅克隆:Object的clone提供的就是浅克隆,由下面的例子可以看见,只克隆了自身对象和对象内实例变量的地址引用,它内部的实例变量还是指向原先的堆内存区域。

深克隆:克隆所有的对象,包括自身以及自身内部对象。
浅克隆测试代码:
TestReen中有一个实例变量SonReen。

public class TestReen implements Cloneable{
	private int id;
	private String name;
	private SonReen sonReen;
	public TestReen(int id, String name, SonReen sonReen) {
		this.id = id;
		this.name = name;
		this.sonReen = sonReen;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public SonReen getSonReen() {
		return sonReen;
	}
	public void setSonReen(SonReen sonReen) {
		this.sonReen = sonReen;
	}
	public static void main(String[] args) throws Exception{
		
		SonReen sonReen = new SonReen(1, "张三");
		TestReen t1 = new TestReen(1, "李四", sonReen);
		TestReen t3 = new TestReen(2, "李四1", sonReen);
		TestReen t4 = new TestReen(3, "李四", sonReen);
		TestReen t2 = (TestReen)t1.clone(); 
		System.out.println("t1 ? "+ (t1 == t2));
		System.out.println("sonReen: ? "+ (t1.getSonReen() == t2.getSonReen()));
		System.out.println("t1.name == t3.name: ? " + (t1.getName() == t3.getName()));
		System.out.println("t1.name == t4.name: ? " + (t1.getName() == t4.getName()));
		System.out.println("T name: ? " + (t1.getName() == t2.getName()));
		System.out.println("S name: ? " + (t1.getSonReen().getSonName() == t2.getSonReen().getSonName()));
	}
}
class SonReen{
	private int sonId;
	private String sonName;
	public SonReen(int sonId, String sonName) {
		super();
		this.sonId = sonId;
		this.sonName = sonName;
	}
	public int getSonId() {
		return sonId;
	}
	public void setSonId(int sonId) {
		this.sonId = sonId;
	}
	public String getSonName() {
		return sonName;
	}
	public void setSonName(String sonName) {
		this.sonName = sonName;
	}
}

得到的结果如下:可以发现几点:

首先TestReen 不是同一个对象,但是它内部的实例变量sonReen 确实同一个对象。内部的String变量的行为和new出来的t1和t4行为一致,也是相等的。

t1 ? false
sonReen: ? true
t1.name == t3.name: ? false
t1.name == t4.name: ? true
T name: ? true
S name: ? true

深克隆:方法有两种:
1.先对对象进行序列化操作,然后立马反序列化出。

2.先调用super.clone()方法克隆出一个新对象来,然后在子类的clone()方法中手动给克隆出来的非基本数据类型(引用类型)赋值。(这个是网上别人的观点,稍后会去研究各个集合内的克隆原理,也是一个知识点)

序列化例子:

public class TestReen implements Cloneable,Serializable{
	private int id;
	private String name;
	private SonReen sonReen;
	public TestReen(int id, String name, SonReen sonReen) {
		this.id = id;
		this.name = name;
		this.sonReen = sonReen;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public SonReen getSonReen() {
		return sonReen;
	}
	public void setSonReen(SonReen sonReen) {
		this.sonReen = sonReen;
	}
	public Object deepClone() throws Exception{
		
		ByteArrayOutputStream bo = new ByteArrayOutputStream();
		ObjectOutputStream out = new ObjectOutputStream(bo);
		out.writeObject(this);
		
		ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
		ObjectInputStream oi = new ObjectInputStream(bi);
		return oi.readObject();
		
	}
	public static void main(String[] args) throws Exception{
		
		SonReen sonReen = new SonReen(1, "张三");
		TestReen t1 = new TestReen(1, "李四", sonReen);
		TestReen t2 = (TestReen)t1.deepClone(); 
		System.out.println("t1 ? "+ (t1 == t2));
		System.out.println("sonReen: ? "+ (t1.getSonReen() == t2.getSonReen()));
		System.out.println("T name: ? " + (t1.getName() == t2.getName()));
		System.out.println("S name: ? " + (t1.getSonReen().getSonName() == t2.getSonReen().getSonName()));
		
		SonReen sonReen1 = new SonReen(1, "王五");
		t2.setSonReen(sonReen1);
		System.out.println("son : "+ t1.getSonReen().getSonName());
		System.out.println("son1 : "+ t2.getSonReen().getSonName());
		System.out.println("son == son1 : ? "+ (t1.getSonReen() ==  t2.getSonReen()));
		
	}
}
class SonReen implements Serializable{
	private int sonId;
	private String sonName;
	public SonReen(int sonId, String sonName) {
		super();
		this.sonId = sonId;
		this.sonName = sonName;
	}
	public int getSonId() {
		return sonId;
	}
	public void setSonId(int sonId) {
		this.sonId = sonId;
	}
	public String getSonName() {
		return sonName;
	}
	public void setSonName(String sonName) {
		this.sonName = sonName;
	}
}

如下结果: 发现t1 和 t2 内的sonReen比较时 已经是false了,说明克隆的同时将对象内部的实例对象也跟着一起复制了,并且下面对 t2 内部son 的名字改变也没有影响 t1的值。

t1 ? false
sonReen: ? false
T name: ? false
S name: ? false
son : 张三
son1 : 王五
son == son1 : ? false

》》》博主长期更新学习心得,推荐点赞关注!!!
》》》若有错误之处,请在评论区留言,谢谢!!!

  • 11
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值