深入浅出| java中的clone方法
每天进步一丢丢,连接梦与想
我们还年轻,但这不是你浪费青春的理由
克隆和复制
clone,有人称之为克隆,有人称之为复制,其实都是同一个东西
本文称之为”克隆”,毕竟人家方法名叫”clone”
为什要用克隆
想一想,为什么需要克隆?为什么不重新new一个?道理很简单,目的是想要两个相同的对象,重新new一个还得自己重新赋值,太麻烦
如何克隆一个对象?
如果是个初学者,可能会这么写
public class Student {
String name;
public Student(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args){
Student stu1 = new Student("小明");
Student stu2 = stu1;
}
}
这确实是做了克隆,但只是克隆了引用变量
来验证一下
System.out.println("stu1:"+stu1.getName()+"
stu2:"+stu2.getName());
System.out.println("stu1 == stu2 : "+(stu1 == stu2));
//改名字
stu1.setName("小张");
System.out.println("改名后 stu1:"+stu1.getName()+"
stu2:"+stu2.getName());
//输出
stu1:小明 stu2:小明
stu1 == stu2 : true
改名后 stu1:小张 stu2:小张
修改了stu1的名字后,stu2的名字也随着改变
可以看出,两个引用stu1
和stu2
指向同一个对象
如图
你需要的是这样的克隆?
回想一下,平时真正需要的是两个不同对象
Object类中的clone
先来看下clone的源码,在Object类中
/*
Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
The general intent is that, for any object x, the expression:
1) x.clone() != x will be true
2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
3) x.clone().equals(x) will be true, this is not an absolute requirement.
*/
protected native Object clone() throws CloneNotSupportedException;
仔细看,它是个native方法,native方法是由非java语言实现的(因为java本身无法直接对操作底层进行访问和操作,需要通过其他语言实现)
注释主要说明了3点:
克隆对象和原对象不是同一个对象,占用不同的内存地址
克隆对象和原对象应该具有相同的类型,但它不是强制性的
克隆对象和原对象使用equals()方法比较应该是相等的,但它不是强制性的
因为每个类的基类都是Object,所以都有clone方法,但是它是protected,所以不能在类外访问
克隆一个对象,需要对clone重写
如何实现克隆
在说实现前,得区分下浅克隆和深克隆
浅克隆:原对象和克隆对象不同,但对象内的成员引用相同
深克隆:原对象和克隆对象不同,且对象内的成员引用也不同
不同:不是同一个对象,所占内存地址不同
成员引用:类中为引用类型的成员
以图说明,更形象些
男孩比喻为一个类,电脑比喻为类中的成员引用
一个男孩拥有一台电脑,通过浅克隆后,成了两个男孩,但他们共享一台电脑
一个男孩拥有一台电脑,通过深克隆后,成了两个男孩,他们拥有各自的电脑
浅克隆
//学生类
public class Student implements Cloneable{
private String name;
private Integer age;
private Bag bag;
public Student(String name,Integer age,Bag bag) {
this.name = name;
this.age = age;
this.bag = bag;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Bag getBag() {
return bag;
}
public void setBag(Bag bag) {
this.bag = bag;
}
@Override
public Student clone(){
Student stu = null;
try{
stu = (Student)super.clone();
} catch (CloneNotSupportedException e){
e.printStackTrace();
}
return stu;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", bag=" + bag.getName() +
'}';
}
}
//背包类
public class Bag {
private String name;
public Bag(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//测试类
public class Test {
public static void main(String[] args){
Student stu1 = new Student("小明",25,new Bag("小明的背包"));
Student stu2 = stu1.clone();
System.out.println("两对象是否相等");
System.out.println("stu1 == stu2 "+(stu1 == stu2));
System.out.println("stu1 "+stu1.toString());
System.out.println("stu2 "+stu2.toString());
System.out.println("对象内引用成员是否相等");
System.out.println("stu1.name == stu2.name "+ (stu1.getName() == stu2.getName()));
System.out.println("stu1.age == stu2.age "+(stu1.getAge() == stu2.getAge()));
System.out.println("stu1.bag == stu2.bag "+(stu1.getBag() == stu2.getBag()));
}
}
//输出
两对象是否是同一对象
stu1 == stu2 false
stu1 Student{name='小明', age=25, bag=小明的背包}
stu2 Student{name='小明', age=25, bag=小明的背包}
对象内引用成员是否相等
stu1.name == stu2.name true
stu1.age == stu2.age true
stu1.bag == stu2.bag true
可看出,原对象和克隆对象不是同一对象
,克隆对象内的值与原对象相同;对象内引用成员相等,说明只做了引用克隆,不同引用指向同一对象
//改变stu1类中成员的值
stu1.setName("小张");
stu1.setAge(18);
stu1.getBag().setName("小张的背包");
System.out.println("stu1 "+stu1.toString());
System.out.println("stu2 "+stu2.toString());
//输出
stu1 Student{name='小张', age=18, bag=小张的背包}
stu2 Student{name='小明', age=25, bag=小张的背包}
stu1改变bag的名称,stu2中bag会同时改变,因为两个bag指向的是同一个对象
但name,age成员为何没有跟着改变?因为它们的类型分别是String和Integer,String,Integer是不可变类,不可改变原值,对它赋值就等同于让它指向另一个新对象,其余的七种基本数据类型的包装类也一样
。
深克隆
有两种实现方法
多层实现Cloneable类
利用序列化和反序列化
1.多层实现Cloneable类
让上述的Bag类也实现Cloneable类,并重写clone方法
public class Bag implements Cloneable{
private String name;
public Bag(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Bag clone(){
Bag bag= null;
try{
bag= (Bag )super.clone();
} catch (CloneNotSupportedException e){
e.printStackTrace();
}
return bag;
}
}
且在Student类的clone方法中执行Bag的clone方法
@Override
public Student clone(){
Student stu = null;
try{
//浅克隆
stu = (Student)super.clone();
} catch (CloneNotSupportedException e){
e.printStackTrace();
}
//深克隆
stu.bag = (Bag)bag.clone();
return stu;
}
这样便可实现深克隆,但这种方法很麻烦,若Bag类中还含有成员引用,则又需要再让它实现Cloneable接口重写clone方法,这样代码会显得很臃肿,且繁琐。
还是第二种方法简单易用,来瞧一瞧
2.利用序列化和反序列化实现深克隆
public class Student implements Serializable {
private String name;
private Integer age;
private Bag bag;
...
public Student myClone(){
Student stu = null;
try {
//将对象序列化到流里
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(this);
//将流反序列化成对象
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
ObjectInputStream ois = new ObjectInputStream(is);
stu = (Student) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return stu;
}
}
需要注意的是成员引用也需要实现Serializable接口
public class Bag implements Serializable {...
这种方法是利用序列化对象后可将其拷贝到流里,而原对象仍在jvm中,然后从流中将其反序列化成另一个对象到jvm中,从而实现深克隆
总结
克隆可分为浅克隆和深克隆,实际应用中一般使用深克隆
深克隆有两种实现方法
实现Cloneable接口
利用序列化和反序列化(简单方便)
扩展
Cloneable接口和Serializable接口的代码非常简单,它们都是空接口,这种空接口也称为标识接口,标识接口中没有任何方法的定义,其作用是告诉JRE这些接口的实现类是否具有某个功能,如是否支持克隆、是否支持序列化等。
深入浅出| java中的clone方法的更多相关文章
- 详解Java中的clone方法
详解Java中的clone方法 参考:http://blog.csdn.net/zhangjg_blog/article/details/18369201/ 所谓的复制对象,首先要分配一个和源对象同样 ...
- 深入浅出Java中的clone克隆方法,写得太棒了!
作者:张纪刚 blog.csdn.net/zhangjg_blog/article/details/18369201/ Java中对象的创建 clone 顾名思义就是 复制 , 在Java语言中, c ...
- 详解Java中的clone方法:原型模式
转:http://developer.51cto.com/art/201506/478985.htm clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的 ...
- 转:Java中的Clone()方法详解
Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...
- Java中的clone方法-理解浅拷贝和深拷贝
最近学到Java虚拟机的相关知识,更加能理解clone方法的机制了 java中的我们常常需要复制的类型有三种: 1:8种基本类型,如int,long,float等: 2:复合数据类型(数组): 3:对 ...
- 详解Java中的clone方法 -- 原型模式
转自: http://blog.csdn.net/zhangjg_blog/article/details/18369201 Java中对象的创建 clone顾名思义就是复制, 在Java语言中, ...
- java中的clone方法
Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...
- JAVA中的clone方法剖析
原文出自:http://blog.csdn.net/shootyou/article/details/3945221 java中也有这么一个概念,它可以让我们很方便的"制造"出一个 ...
- Java中有关clone方法的用法
一.clone在数组基本数据类型中的使用 public class Main { public static void main(String[] args) { int[] arr= {7,8,9} ...
随机推荐
- koa2入门--01.ES6简单复习、koa2安装以及例子
1.ES6简单复习 /*let 和 const: let用于定义一个块作用域的变量,const 定义一个常量 */ let a = 'test'; const b = 2; /*对象的属性和方法的简写 ...
- CountDownLatch 部分加载和同时并发业务。
按顺序部分加载: import java.util.concurrent.CountDownLatch; /** * @Title: ThreadCountDownTest.java * @Descr ...
- JDBC 数据源
概述 JNDI 数据源配置的相关内容已经在 JNDI 资源文档中详细介绍过.但从 Tomcat 用户的反馈意见来看,有些配置的细节问题非常棘手. 针对常用的数据库,我们已经给 Tomcat 用户提供了 ...
- JavaScript中浅拷贝和深拷贝的区别
JavaScript数据类型 基础数据类型:保存在栈内存中的简单数据段 ,有undefined,boolean,number,string,null 引用数据类型:Array,object,Funct ...
- 【一起学源码-微服务】Nexflix Eureka 源码十二:EurekaServer集群模式源码分析
前言 前情回顾 上一讲看了Eureka 注册中心的自我保护机制,以及里面提到的bug问题. 哈哈 转眼间都2020年了,这个系列的文章从12.17 一直写到现在,也是不容易哈,每天持续不断学习,输出博 ...
- 【退役记】CSP2019 退役记
Day -1 机房自习,因为一些奇怪原因心不在焉 我可能太在意csp了 晚上有点扛不住去七楼阳台思考人生,得到了一些惊人的结论想下来由于某种原因继续跑到七楼思考人生 然后晚自习下课仰天大笑出门去,我辈 ...
- $AT2292\ Division\ into\ Two$ $dp$
正解:$dp$ 解题报告: 传送门$QwQ$ 不妨令$A\geq B$,于是先$sort$然后预处理判下如果有三个元素两两差都小于$B$的就直接$GG$了. 然后考虑对集合$X$进行$dp$,剩下的数 ...
- Java中日期格式化YYYY-DD的坑
写这篇博文是记录下跨年的bug.去年隔壁组的小伙伴就是计算两个日期之间间隔的天数,因为跨年的原因计算有误. 当时测试组的小姐姐也没有模拟出来这种场景,导致上生产环境直接影响线上的数据. 今天逛技术论论 ...
- [gitHub实践] 实践记录
[gitHub实践] 实践记录 版权2019.4.19更新 Q1:本地仓库和远程仓库连接提示输入用户名密码 本地仓库和远程仓库连接有两种方式 本地初始化建立一个仓库,远程也建立了一个仓库 本地建立仓库 ...
- selenium爬取驾考宝典题目
要求 [x] Python3+ [x] Chrome驱动并已配置环境变量 [x] Selenium ## 研究页面 发现驾考宝典的科目四页面URL都是以 https://www.jiakaobaodi ...