java中clone_java copyproperties

2022-11-04 15:12:22 浏览数 (1)

1. 克隆的用处

在日常编码中我们经常需要产生某个对象的副本,这里的副本并不是指向同一个对象的不同引用,而是与当前对象状态一模一样的另一个新的对象。如果使用单纯的引用赋值,会发生什么效果呢?

我们可以观察下面的代码:

package com.coderap.foundation.clone;

class Address {

public String province;

public String city;

public Address(String province, String city) {

this.province = province;

this.city = city;

}

@Override

public String toString() {

return “Address{”

“province='” province ‘”

“, city='” city ‘”

‘}’;

}

}

class Person {

public String name;

public Integer age;

public Address address;

public Person(String name, Integer age) {

this.name = name;

this.age = age;

}

}

public class CloneTest {

public static void main(String[] args) {

Person person = new Person(“Tom”, 20);

person.address = new Address(“CA”, “Los Angeles”);

System.out.println(“before: ” person.name);

System.out.println(“before: ” person.age);

System.out.println(“before: ” person.address);

Person newPerson = (Person) person;

person.name = “Jack”;

person.age = 22;

newPerson.address.province = “CA”;

newPerson.address.city = “Nevada”;

System.out.println(“after: ” person.name);

System.out.println(“after: ” person.age);

System.out.println(“after: ” person.address);

System.out.println(“person equal newPerson ? ” person.equals(newPerson));

}

}

在上面的代码中我们单纯地将一个新的引用指向一个已有的对象,然后使用新的引用对对象进行操作,可以发现,所有的更改在两个引用上都体现出来了:

before: Tom

before: 20

before: Address{province=’CA’, city=’Los Angeles’}

after: Jack

after: 22

after: Address{province=’CA’, city=’Nevada’}

person equal newPerson ? true

在Java中,两个引用同时指向相同的对象时,这两个引用是指向的同一块内存,所以使用任何一个引用对内存的操作都将直接反映到另一个引用上,单纯的引用赋值是不能够克隆对象的。为了解决克隆问题,Java提供了Cloneable接口和clone()方法。

2. Cloneable 接口和 clone 方法

Cloneable接口是一个标记接口,其中没有任何内容,定义如下:

package java.lang;

public interface Cloneable {}

clone()方法是在Object类中定义的:

protected native Object clone() throws CloneNotSupportedException;

clone()方法是被protected修饰的受保护的方法,类只有实现了Cloneable接口,才可以在该类的实例上调用clone()方法,否则会抛出CloneNotSupportException异常。

Object的clone()方法创建并返回此对象的一个副本。对于任何对象o,clone()方法有以下的规则:

o.clone() != o为true;

o.clone().getClass() == o.getClass()为true;

o.clone().equals(o)一般情况下为true,但这并不是必须要满足的要求。

Object中默认的实现是一个浅克隆,但是该方法是有缺陷的,如果需要实现深层次克隆的话,必须对类中可变域生成新的实例。

2.1. 浅克隆

浅克隆并不会把对象所有属性全部克隆一份,而是有选择性的克隆,克隆规则如下:

基本类型。如果变量是基本类型,则克隆其值,比如int、float、long等。

String字符串,对于字符串的克隆比较特殊,克隆的是引用地址,但是在修改的时候,它会从字符串池(String Pool)中重新生成新的字符串,原有的字符串对象保持不变,此处可以认为String是个基本类型。

对象。如果变量时一个实例对象,则克隆地址引用,也就是说此时新克隆出的对象与原有对象共享该实例变量,不受访问权限的限制。这中克隆操作是非常危险的,意味着不同的对象之间对某些引用对象是共有的,相互修改将受到影响。

注1:基本数据类型在克隆时是进行的原值克隆。

如下面的代码,我们只是简单的在Person类中实现了Cloneable接口并且重写了clone()方法,同时进行克隆操作:

package com.coderap.foundation.clone;

class Address {

public String province;

public String city;

public Address(String province, String city) {

this.province = province;

this.city = city;

}

@Override

public String toString() {

return “Address{”

“province='” province ‘”

“, city='” city ‘”

‘}’;

}

}

class Person implements Cloneable {

public String name;

public Integer age;

public Address address;

public Person() {

System.out.println(“Person() execute”);

}

public Person(String name, Integer age) {

System.out.println(“Person(String name, Integer age) execute”);

this.name = name;

this.age = age;

}

@Override

protected Object clone() throws CloneNotSupportedException {

return super.clone();

}

}

public class CloneTest {

public static void main(String[] args) throws CloneNotSupportedException {

Person person = new Person(“Tom”, 20);

person.address = new Address(“CA”, “Los Angeles”);

System.out.println(“before: ” person.name);

System.out.println(“before: ” person.age);

System.out.println(“before: ” person.address);

Person newPerson = (Person)person.clone();

newPerson.name = “Jack”;

newPerson.age = 22;

newPerson.address.province = “CA”;

newPerson.address.city = “Nevada”;

System.out.println(“after: ” person.name);

System.out.println(“after: ” person.age);

System.out.println(“after: ” person.address);

System.out.println(“person != newPerson ? ” String.valueOf(person != newPerson));

System.out.println(“person getClass equal newPerson getClass ? ” person.getClass().equals(newPerson.getClass()));

System.out.println(“person equal newPerson ? ” person.equals(newPerson));

}

}

运行上面的代码,可以得到打印信息如下:

Person(String name, Integer age) execute

before: Tom

before: 20

before: Address{province=’CA’, city=’Los Angeles’}

after: Tom

after: 20

after: Address{province=’CA’, city=’Nevada’}

person != newPerson ? true

person getClass equal newPerson getClass ? true

person equal newPerson ? false

我们可以得出以下结果:

克隆一个对象不会重复调用对应类的构造方法;

上述最后的三条的判断的结果是遵循了clone()方法三条规则的;

基本类型和String类型的数据都是独立的,并不会收到新对象的影响,但是引用类型的对象会受到新对象的影响。

需要注意的是,在修改城市信息时,如果我们直接指定newPerson.address = new Address(“CA”, “Nevada”)其实是不会影响到原来的person对象的,因为虽然newPerson和person的address指向的同一个Address对象,但使用newPerson.address = new Address(“CA”, “Nevada”)会给newPerson对象生成一个新的Address对象,并将newPerson的address引用指向这个新的对象,所以并不会影响到原有的person对象的address对象属性。

Java中实现了Cloneable接口的类有很多,如ArrayList、Calendar、Date、HashMap、Hashtable、HashSet、LinkedList等等。我们在使用这些类时并不需要考虑浅克隆带来的影响。

2.2. 深克隆

深克隆操作应该将除自身对象以外的所有对象,包括自身所包含的所有对象实例都进行克隆。

其实Object的clone()方法提供的是一种浅克隆的机制,如果想要实现对对象的深克隆,有两种办法:

先对对象进行序列化,紧接着马上反序列化出;

先调用super.clone()方法克隆出一个新对象来,然后在子类的clone()方法中手动给克隆出来的非基本数据类型(引用类型)赋值,比如ArrayList的clone()方法:

/**

* Returns a shallow copy of this ArrayList instance. (The

* elements themselves are not copied.)

*

* @return a clone of this ArrayList instance

*/

public Object clone() {

try {

ArrayList> v = (ArrayList>) super.clone();

v.elementData = Arrays.copyOf(elementData, size);

v.modCount = 0;

return v;

} catch (CloneNotSupportedException e) {

// this shouldn’t happen, since we are Cloneable

throw new InternalError(e);

}

}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/182124.html原文链接:https://javaforall.cn

0 人点赞