Java中的clone()和Cloneable接口实例
作者:lylodlig 发布时间:2022-09-27 15:31:05
clone()和Cloneable接口
clone顾名思义就是克隆,即,复制一个相等的对象,但是不同的引用地址。
我们知道拿到一个对象的地址,只要提供相应的方法就可以修改这个对象,但是如果我们想要得到这个对象去修改它,又想保留这个对象原来的属性,这是就可以使用clone(),它会复制一个内容相同的对象而具有不同内存地址。
Cloneable接口,就是我们要使用clone()必须实现的接口,不然会抛出异常。
public class Bean implements Cloneable {
private String a;
public Bean(String a) {
this.a = a;
}
public String getA() {
return a;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public boolean equals(Object o) {
if(o instanceof Bean){
Bean bean = (Bean) o;
return bean.getA().equals(a);
}
return false;
}
}
在Cloneable 接口中并没有给我们定义任何方法
这里需要重写clone()方法
protected native Object clone() throws CloneNotSupportedException;
protected Object clone() throws CloneNotSupportedException {
if (!(this instanceof Cloneable)) {
throw new CloneNotSupportedException("Class " + getClass().getName() +
" doesn't implement Cloneable");
}
return internalClone();
}
它是Object类里面的native方法,它是protected的,根据需要可以写为public,可以看到如果不实现Cloneable接口将会抛出CloneNotSupportedException 异常。
测试一下
try {
Bean a = new Bean("lzy");
Bean b = a;
Bean c = (Bean) a.clone();
Log.i(TAG, "onCreate: " + (a == b)); //true
Log.i(TAG, "onCreate: " + (a.equals(b))); //true
Log.i(TAG, "onCreate: " + (a == c)); //false
Log.i(TAG, "onCreate: " + (a.equals(c))); //true
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
可以看到克隆出来的类的地址是不同的,而内容是相同的。
下面修改一下,在Bean加一个成员变量ChildBean
public class ChildBean implements Cloneable {
private String c;
public String getC() {
return c;
}
public ChildBean(String c) {
this.c = c;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public boolean equals(Object o) {
if (o instanceof ChildBean) {
ChildBean bean = (ChildBean) o;
return bean.getC().equals(c);
}
return false;
}
}
public class Bean implements Cloneable {
private String a;
private ChildBean childBean;
public Bean(String a, ChildBean childBean) {
this.a = a;
this.childBean = childBean;
}
public String getA() {
return a;
}
public ChildBean getChildBean() {
return childBean;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public boolean equals(Object o) {
if (o instanceof Bean) {
Bean bean = (Bean) o;
return bean.getA().equals(a);
}
return false;
}
}
Bean a = new Bean("lzy", new ChildBean("child"));
Bean b = a;
Bean c = (Bean) a.clone();
Log.i(TAG, "onCreate: " + (a.getChildBean() == b.getChildBean())); //true
Log.i(TAG, "onCreate: " + (a.getChildBean().equals(b.getChildBean()))); //true
Log.i(TAG, "onCreate: " + (a.getChildBean() == c.getChildBean())); //true
Log.i(TAG, "onCreate: " + (a.getChildBean().equals(c.getChildBean()))); //true
测试发现有一个结果不是我们所预期的,这意味着并没有真正克隆ChildBean,只是克隆的它的内存地址,导致两个具有相同的内存地址,这也就是浅克隆,此时我们需要的是深克隆,需要按照下面方法修改,重写clone()方法
@Override
public Object clone() throws CloneNotSupportedException {
Bean bean = (Bean) super.clone();
bean.childBean = (ChildBean) bean.childBean.clone();
return bean;
}
但是这样做如果有很多层的类,那每一层都需要去重写,显得很麻烦。所以我们可以用下面的工具类来实现
public class BeanUtil {
public static <T> T cloneTo(T src) throws RuntimeException {
ByteArrayOutputStream memoryBuffer = new ByteArrayOutputStream();
ObjectOutputStream out = null;
ObjectInputStream in = null;
T dist = null;
try {
out = new ObjectOutputStream(memoryBuffer);
out.writeObject(src);
out.flush();
in = new ObjectInputStream(new ByteArrayInputStream(memoryBuffer.toByteArray()));
dist = (T) in.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (out != null)
try {
out.close();
out = null;
} catch (IOException e) {
throw new RuntimeException(e);
}
if (in != null)
try {
in.close();
in = null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return dist;
}
}
Bean a = new Bean("lzy", new ChildBean("child"));
Bean b = BeanUtil.cloneTo(a);
Log.i(TAG, "onCreate: " + (a.getChildBean() == b.getChildBean())); //false
Log.i(TAG, "onCreate: " + (a.getChildBean().equals(b.getChildBean()))); //true
这样就可以很轻松的得到我们预期的结果,但是记得每一个类都要去 实现Serializable接口。
Cloneable和clone()的总结
1.Cloneable 的用途
Cloneable和Serializable一样都是标记型接口,它们内部都没有方法和属性,implements Cloneable表示该对象能被克隆,能使用Object.clone()方法。如果没有implements Cloneable的类调用Object.clone()方法就会抛出CloneNotSupportedException。
2.克隆的分类
(1)浅克隆(shallow clone),浅拷贝是指拷贝对象时仅仅拷贝对象本身和对象中的基本变量,而不拷贝对象包含的引用指向的对象。
(2)深克隆(deep clone),深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。
举例区别一下:对象A1中包含对B1的引用,B1中包含对C1的引用。浅拷贝A1得到A2,A2中依然包含对B1的引用,B1中依然包含对C1的引用。深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2中包含对C2(C1的copy)的引用。
3.克隆的举例
要让一个对象进行克隆,其实就是两个步骤:
1.让该类实现java.lang.Cloneable接口;
2.重写(override)Object类的clone()方法。
public class Wife implements Cloneable {
private int id;
private String name;
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 Wife(int id,String name) {
this.id = id;
this.name = name;
}
@Override
public int hashCode() {//myeclipse自动生成的
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {//myeclipse自动生成的
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Wife other = (Wife) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* @param args
* @throws CloneNotSupportedException
*/
public static void main(String[] args) throws CloneNotSupportedException {
Wife wife = new Wife(1,"wang");
Wife wife2 = null;
wife2 = (Wife) wife.clone();
System.out.println("class same="+(wife.getClass()==wife2.getClass()));//true
System.out.println("object same="+(wife==wife2));//false
System.out.println("object equals="+(wife.equals(wife2)));//true
}
}
4.浅克隆的举例
public class Husband implements Cloneable {
private int id;
private Wife wife;
public Wife getWife() {
return wife;
}
public void setWife(Wife wife) {
this.wife = wife;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Husband(int id) {
this.id = id;
}
@Override
public int hashCode() {//myeclipse自动生成的
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public boolean equals(Object obj) {//myeclipse自动生成的
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Husband other = (Husband) obj;
if (id != other.id)
return false;
return true;
}
/**
* @param args
* @throws CloneNotSupportedException
*/
public static void main(String[] args) throws CloneNotSupportedException {
Wife wife = new Wife(1,"jin");
Husband husband = new Husband(1);
Husband husband2 = null;
husband.setWife(wife);
husband2 = (Husband) husband.clone();
System.out.println("husband class same="+(husband.getClass()==husband2.getClass()));//true
System.out.println("husband object same="+(husband==husband2));//false
System.out.println("husband object equals="+(husband.equals(husband)));//true
System.out.println("wife class same="+(husband.getWife().getClass()==husband2.getWife().getClass()));//true
System.out.println("wife object same="+(husband.getWife()==husband2.getWife()));//true
System.out.println("wife object equals="+(husband.getWife().equals(husband.getWife())));//true
}
}
5.深克隆的举例
如果要深克隆,需要重写(override)Object类的clone()方法,并且在方法内部调用持有对象的clone()方法;注意如下代码的clone()方法
public class Husband implements Cloneable {
private int id;
private Wife wife;
public Wife getWife() {
return wife;
}
public void setWife(Wife wife) {
this.wife = wife;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Husband(int id) {
this.id = id;
}
@Override
public int hashCode() {//myeclipse自动生成的
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Husband husband = (Husband) super.clone();
husband.wife = (Wife) husband.getWife().clone();
return husband;
}
@Override
public boolean equals(Object obj) {//myeclipse自动生成的
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Husband other = (Husband) obj;
if (id != other.id)
return false;
return true;
}
/**
* @param args
* @throws CloneNotSupportedException
*/
public static void main(String[] args) throws CloneNotSupportedException {
Wife wife = new Wife(1,"jin");
Husband husband = new Husband(1);
Husband husband2 = null;
husband.setWife(wife);
husband2 = (Husband) husband.clone();
System.out.println("husband class same="+(husband.getClass()==husband2.getClass()));//true
System.out.println("husband object same="+(husband==husband2));//false
System.out.println("husband object equals="+(husband.equals(husband)));//true
System.out.println("wife class same="+(husband.getWife().getClass()==husband2.getWife().getClass()));//true
System.out.println("wife object same="+(husband.getWife()==husband2.getWife()));//false
System.out.println("wife object equals="+(husband.getWife().equals(husband.getWife())));//true
}
}
但是也有不足之处,如果Husband内有N个对象属性,突然改变了类的结构,还要重新修改clone()方法。
解决办法:可以使用Serializable运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法。
来源:https://blog.csdn.net/lylodyf/article/details/52763720


猜你喜欢
- 前言本文主要介绍如何在 Android App 里集成支付宝和微信支付的功能,文中将实现的步骤一步步介绍的非常详细,对同样遇到这个问题的朋友
- java通过IP解析地理位置在项目开发中,需要在登录日志或者操作日志中记录客户端ip所在的地理位置。目前根据ip定位地理位置的第三方api有
- 前言RSA加密算法是一种非对称加密算法,简单来说,就是加密时使用一个钥匙,解密时使用另一个钥匙。因为加密的钥匙是公开的,所又称公钥,解密的钥
- Android虚拟键盘的弹起会遮挡住部分ui,虽然通过在清单文件中设置,可以随着虚拟键盘的弹出,布局往上推,但是面对登陆界面时,并没有太大的
- 对象持久化是指将内存中的对象保存到可永久保存的存储设备中(如磁盘)的一种技术。本文介绍的是除数据库之外的几种对象持久化方式。具体如下:保存成
- spring boot 使用POI读取Excel文件Excel文件目录Excel模板文件存了resourse目录下,如下图:<depe
- 最近在做一个每天定点从FTP自动下载节目.xml并更新到数据库的功能。首先想到用 FileSystemWatcher来监控下载到某个目录中的
- 将字母全部转换为大写或小写,在C#编程中是一个非常常见的功能。在开发过程中,经常需要验证用户登录,用户在输入用户名时可能不区分大小写,如果我
- Oracle公司如期发布了Java 8正式版!没有让广大javaer失望。对于一个人来说,18岁是人生的转折点,从稚嫩走向成熟,法律意味着你
- 字符串每隔4位加空格今天弄了个银行卡识别功能,回显的时候想要将银行卡号每四位加一个空格,这样核对卡号会方便很多,这里记录一下1.正则表达式实
- 这两天因为要做一个随机的地图生成系统,所以一直在研究随机迷宫生成算法,好吧,算是有一点小小的成果。随机迷宫生成我自己的理解简而言之分为以下几
- 前提最近发现各个频道推荐了很多ULID相关文章,这里对ULID的规范文件进行解读,并且基于Java语言自行实现ULID,通过此实现过程展示U
- 在上面的例子中多次使用到了Thread类的join方法。我想大家可能已经猜出来join方法的功能是什么了。对,join方法的功能就是使异步执
- AntPathMatcher前言(1)SpringMVC的路径匹配规则是依照Ant的来的,实际上不只是SpringMVC,整个Spring框
- 一,程序执行慢导致的原因就是查询数据库慢.,导致返回值慢,那这个要怎么解决呢?1,优化数据库查询如这个文章 C#导出数据到excel如何提升
- 一、简介我们在线程编程的时候往往会涉及到线程的通信,通过信号的接受来进行线程是否阻塞的操作。AutoResetEvent 允许线程通过发信号
- spring-boot-maven-plugin:打包时排除provided依赖spring-boot-maven-plugin 插件提供s
- java 中死锁问题的实例详解先看代码在做解释public class DeadLock implements Runnable{ &nbs
- Map集合Map集合存储的是键值对Map集合的实现类:HashTable、LinkedHashMap、HashMap、TreeMapHash
- Freemarker 3种循环模式1. map循环举例:<#if vo.urlMap??>