java clone 对象_java对象clone

java克隆

为什么需要克隆

我们在很多时候需要使用一个对象去记录另外一个对象的当前状态,对象中可能会有很多属性,如果我们一个一个去设置,不仅不方便,而且效率很低,我们看一个初学者可能遇到的问题

class Person{

String name;

int age;

public Person() {}

public Person(String name, int age) {

super();

this.name = name;

this.age = age;

}

@Override

public String toString() {

return "Person [name=" + name + ", age=" + age + "]";

}

}

@Test

public void test2() {

Person p1=new Person("tom",20);

Person p2=p1;

System.out.println(p1);

System.out.println(p2);

System.out.println("---------");

p1.age=30;

System.out.println(p1);

System.out.println(p2);

}

输出:

Person [name=tom, age=20]

Person [name=tom, age=20]

---------

Person [name=tom, age=30]

Person [name=tom, age=30]

也许有的人认为Person p2=p1这样的方式就可以克隆一个对象,这种想法是错误的,这种使用等号赋值的方式只是将p1的地址赋值给了p2对象,那么p1和p2都指向了堆中的同一个对象,所以,修改p1那么p2也就变了

8127ab5b7c7ea924213f3819e399c85c.png

如果一个一个属性的去手动设置不仅麻烦,而且属性可能也有属性,修改起来不容易

public void test2() {

Person p1=new Person("tom",20);

Person p2=new Person();

p2.age=p1.age;

p2.name=p1.name;

}

这里Person的属性层级很简单,修改起来看起来很简单,但是如果Person多了一个Address类型的属性,那么手动修改就必须要去new一个Address并赋值属性,假如属性的嵌套层级深了,就很难了

所以我们可以使用Object提供的clone方法来克隆对象,由于clone方法是用protected关键字修饰的,我们如果想在类外使用就需要重写父类的方法,Object的clone方法是一个native关键字修饰的方法,即调用其他语言的方法,效率很高,值得注意的一点是要克隆对象的类必须实现Cloneable接口,这个接口是一个标记接口,里面并没有定义任何方法,如果没事实现Cloneable接口使用clone方法,会抛出CloneNotSupportedException异常

浅克隆

如果原型对象的属性是值类型,那么复制一份给克隆对象,如果属性是引用类型,那么把属性的引用赋值给克隆对象

9643872d51bd595410954f399815a78c.png

class Person implements Cloneable{//必须实现Cloneable接口

String name;

int age;

public Person() {}

public Person(String name, int age) {

super();

this.name = name;

this.age = age;

}

@Override

public String toString() {

return "Person [name=" + name + ", age=" + age + "]";

}

@Override

protected Object clone(){

Person p=null;

try {

p=(Person)super.clone();

}catch(CloneNotSupportedException e) {//实现了Cloneable接口这个异常就不可能发生

e.printStackTrace();

}

return p;

}

}

@Test

public void test2() {

Person p1=new Person("tom",20);

Person p2=(Person)p1.clone();

System.out.println(p1);

System.out.println(p2);

System.out.println("---------");

p1.age=30;

System.out.println(p1);

System.out.println(p2);

}

输出:

Person [name=tom, age=20]

Person [name=tom, age=20]

---------

Person [name=tom, age=30]

Person [name=tom, age=20]

这种Object提供的clone在类中的属性全是值类型的时候不会出现问题,但是如果类属性有引用类型就会出现问题

class Person implements Cloneable{

String name;

int age;

Address address;

public Person() {}

public Person(String name, int age,Address addr) {

super();

this.name = name;

this.age = age;

this.address=addr;

}

@Override

public String toString() {

return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";

}

@Override

protected Object clone(){

Person p=null;

try {

p=(Person)super.clone();

}catch(CloneNotSupportedException e) {

e.printStackTrace();

}

return p;

}

}

class Address{

String addr;

public Address(String addr) {

super();

this.addr = addr;

}

@Override

public String toString() {

return "Address [addr=" + addr + "]";

}

}

@Test

public void test2() {

Address addr=new Address("成都市郫都区");

Person p1=new Person("tom",20,addr);

Person p2=(Person)p1.clone();

System.out.println(p1);

System.out.println(p2);

System.out.println("---------");

p1.address.addr="成都市金牛区";

System.out.println(p1);

System.out.println(p2);

}

输出:

Person [name=tom, age=20, address=Address [addr=成都市郫都区]]

Person [name=tom, age=20, address=Address [addr=成都市郫都区]]

---------

Person [name=tom, age=20, address=Address [addr=成都市金牛区]]

Person [name=tom, age=20, address=Address [addr=成都市金牛区]]

修改p1的address根据p1克隆出来的p2的address也改变了,这就是所说的当克隆对象的属性是引用类型时,只会复制它的引用,而这种情况一般是我们不希望的,所以需要使用深克隆

深克隆

b7a393290572374a5d86b87a82914646.png

1.克隆对象所有的引用类型都重写clone方法

这里所说的引用类型重写clone方法,是指克隆对象本身属性是引用类型的必须重写clone方法且引用类型中的引用类型也必要要重写,且必须在clone方法中显式条用

class Person implements Cloneable{

String name;

int age;

Address address;//这个属性是引用类型,必须实现Cloneable接口重写clone方法

public Person() {}

public Person(String name, int age,Address addr) {

super();

this.name = name;

this.age = age;

this.address=addr;

}

@Override

public String toString() {

return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";

}

@Override

protected Object clone(){

Person p=null;

try {

p=(Person)super.clone();

}catch(CloneNotSupportedException e) {

e.printStackTrace();

}

//显式调用克隆引用数据类型

p.address=(Address)address.clone();

return p;

}

}

class Address implements Cloneable{

String addr;

public Address(String addr) {

super();

this.addr = addr;

}

@Override

public String toString() {

return "Address [addr=" + addr + "]";

}

@Override

protected Object clone() {

Address addr=null;

try {

addr=(Address)super.clone();

}catch(CloneNotSupportedException e) {

e.printStackTrace();

}

return addr;

}

}

@Test

public void test2() {

Address addr=new Address("成都市郫都区");

Person p1=new Person("tom",20,addr);

Person p2=(Person)p1.clone();

System.out.println(p1);

System.out.println(p2);

System.out.println("---------");

p1.address.addr="成都市金牛区";

System.out.println(p1);

System.out.println(p2);

}

输出:

Person [name=tom, age=20, address=Address [addr=成都市郫都区]]

Person [name=tom, age=20, address=Address [addr=成都市郫都区]]

---------

Person [name=tom, age=20, address=Address [addr=成都市金牛区]]

Person [name=tom, age=20, address=Address [addr=成都市金牛区]]

使用序列化方法

需要克隆的对象类必须实现Serializable接口,这个接口也是一个标记接口,接口中没有任何方法,这个方法的实现类表示可以将这个类的对象写入到IO流中,那么久可以把对象在网络中发送或保存到本地磁盘

class Person implements Cloneable,Serializable{

/**

*

*/

private static final long serialVersionUID = 8990580911834489134L;

String name;

int age;

Address address;

public Person() {}

public Person(String name, int age,Address addr) {

super();

this.name = name;

this.age = age;

this.address=addr;

}

@Override

public String toString() {

return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";

}

@Override

protected Object clone(){

Person p=null;

try {

//将对象写入流中

ByteArrayOutputStream baos=new ByteArrayOutputStream();

ObjectOutputStream oos=new ObjectOutputStream(baos);

oos.writeObject(this);

//将对象从流中读取出来

ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());

ObjectInputStream ois=new ObjectInputStream(bais);

p=(Person)ois.readObject();

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

return p;

}

}

class Address implements Serializable{

/**

*

*/

private static final long serialVersionUID = 328854588872604721L;

String addr;

public Address(String addr) {

super();

this.addr = addr;

}

@Override

public String toString() {

return "Address [addr=" + addr + "]";

}

}

@Test

public void test2() {

Address addr=new Address("成都市郫都区");

Person p1=new Person("tom",20,addr);

Person p2=(Person)p1.clone();

System.out.println(p1);

System.out.println(p2);

System.out.println("---------");

p1.address.addr="成都市金牛区";

System.out.println(p1);

System.out.println(p2);

}

输出:

Person [name=tom, age=20, address=Address [addr=成都市郫都区]]

Person [name=tom, age=20, address=Address [addr=成都市郫都区]]

---------

Person [name=tom, age=20, address=Address [addr=成都市金牛区]]

Person [name=tom, age=20, address=Address [addr=成都市郫都区]]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的clone()方法是Object类中定义的方法,它用于创建并返回当前对象一个副本。这个副本就是一个新的对象,它与原始对象具有相同的属性和方法。通常情况下,我们需要在一个类中实现clone()方法来支持对象的克隆。 克隆对象是在Java中处理对象的一种常见方式。通过克隆,我们可以在不修改原始对象的情况下创建该对象一个副本。这在某些情况下是非常有用的,例如在多线程环境下,我们需要多个线程同时访问同一个对象,但是又不希望它们之间相互干扰。 在Java中,要使用clone()方法来实现对象的克隆,我们需要满足两个条件: 1. 实现Cloneable接口:这个接口是一个标记接口,它没有任何方法,只是用来标记一个类可以被克隆。 2. 重写clone()方法:这个方法是Object类中的一个protected方法,需要在我们的类中进行重写。在重写这个方法时,我们需要调用super.clone()方法来创建一个新的对象,并将原始对象中的属性复制到这个新对象中。 下面是一个示例代码,演示了如何在Java中实现对象的克隆: ``` public class MyClass implements Cloneable { private int value; public MyClass(int value) { this.value = value; } public int getValue() { return value; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } ``` 在这个示例代码中,我们实现了一个MyClass类,并重写了clone()方法。在这个方法中,我们调用了super.clone()方法来创建一个新的对象,并返回这个新对象。由于我们的类实现了Cloneable接口,因此它可以被克隆。 使用这个类进行克隆的代码如下: ``` MyClass obj1 = new MyClass(10); MyClass obj2 = (MyClass) obj1.clone(); System.out.println(obj1.getValue()); // 输出10 System.out.println(obj2.getValue()); // 输出10 ``` 在这个代码中,我们创建了一个MyClass对象obj1,并将其克隆为obj2。由于这两个对象具有相同的属性和方法,因此它们的输出结果也是相同的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值