Object源码分析(一)-- clone方法
2017-09-05 21:11
525 查看
Object是所有类的基类,当你没有显示extends一个父类时,编译期会自动为你加上一个Object类。
![](https://img-blog.csdn.net/20170905211311897?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzI5MjQzNDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
1.这是一个native方法,说明这个方法的实现不是在java中,而是由C/C++实现,并编译成.dll文件,由java调用。registerNatives主要是将C/C++的方法映射到java中的native方法,实现方法命名的解耦。了解即可,知道是注册,细节暂时不研究。
2.第二点来关注 clone方法。
1.clone方法是native方法,native方法的效率远高于非native方法,因此还是使用clone方法去做对象的拷贝而不是使用new的方法,copy。
2.此方法被protected修饰。这就意味着想要使用,必须继承它(废话,默认都是继承的)。然后重载它,如果想要使得其他类能使用这个类,需要设置成public。
3.返回值是一个Object对象,所以要强制转换才行。
测试clone方法,代码如下,
(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
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
相关文章推荐
- JAVA源码分析之---Object类(二)---hashCode,equals,clone方法的使用
- Object源码分析(二)-- hashcode方法
- 面试题分析:7JAVA中Object的clone方法详解-克隆-深克隆
- Java之类Object方法源码分析
- 方法object面试题分析:7JAVA中Object的clone方法详解-克隆-深克隆
- [Guava源码分析]Objects 和 ComparisonChain:帮助重写Object方法
- jQuery静态方法isPlainObject,isEmptyObject方法使用和源码分析
- Object源码研究2——getClass()方法分析
- Object分析--1、clone()方法
- Weka源码分析(1)逆向工程Eclipse插件ObjectAid和AmaterasUML的安装方法
- JAVA源码分析之---Object类(一)---registerNatives,getClass方法的使用
- jQuery源码分析之buildFragment方法和clone方法
- java的Arrays.sort(Object[] a)方法源码分析
- 关于Object.clone克隆方法的测试
- spring mvc框架源码分析(三)-执行请求路径所对应方法并返回
- 大项目源码分析方法总结
- Swift源码分析----swift-object-auditor(1)
- AFNetworking3.1.0源码分析(四)详解AFHTTPRequestSerializer 之初始化方法
- 分析java中clone()方法 (转载+修改)
- Google Test(GTest)使用方法和源码解析——预处理技术分析和应用