您的位置:首页 > 移动开发 > Objective-C

Object源码分析(一)-- clone方法

2017-09-05 21:11 525 查看
Object是所有类的基类,当你没有显示extends一个父类时,编译期会自动为你加上一个Object类。



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

private static native void registerNatives();
static {
registerNatives();
}


2.第二点来关注 clone方法。

protected native Object clone() throws CloneNotSupportedException;
通过源代码可以发现几点:

1.clone方法是native方法,native方法的效率远高于非native方法,因此还是使用clone方法去做对象的拷贝而不是使用new的方法,copy。

2.此方法被protected修饰。这就意味着想要使用,必须继承它(废话,默认都是继承的)。然后重载它,如果想要使得其他类能使用这个类,需要设置成public。

3.返回值是一个Object对象,所以要强制转换才行。

测试clone方法,代码如下,

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()方法中手动给克隆出来的非基本数据类
4000
型(引用类型)赋值。(这个是网上别人的观点,稍后会去研究各个集合内的克隆原理,也是一个知识点)

序列化例子:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: