java中clone_java copyproperties

java中clone_java copyproperties1.克隆的用处在日常编码中我们经常需要产生某个对象的副本,这里的副本并不是指向同一个对象的不同引用,而是与当前对象状态一模一样的另一个新的对象。如果使用单纯的引用赋值,会发生什么效果呢?我们可以观察下面的代码:packagecom.coderap.foundation.clone;classAddress{publicStringprovince;publicStringcity;…


Jetbrains全系列IDE稳定放心使用

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.net/182124.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • oracle数据库索引增加和删除[通俗易懂]

    oracle数据库索引增加和删除[通俗易懂]1.增加索引createindex索引名on表名(列名)tablespace数据库名pctfree10–initrans2maxtrans255storage(initial64Knext1Mminextents1maxextentsunlimited)nologging;2.删除索引dropindex索引名;3.查找索引…

    2022年9月5日
  • c语言中putchar的用法举例_putchar和getchar

    c语言中putchar的用法举例_putchar和getcharC语言中getchar()和putchar()的用法getchar()和putchar()是一对字符输入/输出函数.getchar()不带任何参数,他从输入序列中返回下一个字符。例如,下面的语句读取下一个字符输入,并把该字符的值赋给变量ch:ch=getcha();putchar()函数打印它的参数。例如,下面的语句把之前赋给ch的值作为字符打印出来:putchar(ch);由于这两个函数只处理字符,所以他们通常比scanf()和printf()函数更快更便捷。而且,ge

    2022年10月18日
  • insert into select 和 insert into values区别「建议收藏」

    insert into select 和 insert into values区别「建议收藏」INSERTINTOSELECT语句:从一个表复制数据,然后把数据插入到一个已存在的表中。将一个table1的数据的部分字段复制到table2中,或者将整个table1复制到table2中,这时候我们就要使用SELECTINTO 和 INSERTINTOSELECT 表复制语句了。1.INSERTINTOSELECT语句语句形式为:InsertintoTable2(field1,…

    2022年7月15日
  • 微信小程序php后端搭建_微信小程序访问服务器

    微信小程序php后端搭建_微信小程序访问服务器    在去年的时候,参加过学校的一个创业活动,在团队决定做一个关于图书的微信小程序以后,我负责做微信小程序的后台程序,因为初次接触,决定选用较为简单的PHP作为后台语言,实现了微信小程序的一些基本功能,例如对微信前段来的图片文字进行存储,或者接收到前端传来的关键字对服务器里的数据进行查询并返回前端,利用前端传来图书的ISBN码调用第三方豆瓣图书接口进行图书信息查询,最后还有比较难实现的微信支付功…

    2022年9月17日
  • 用bat批量重命名图片_bat批量提取文件名

    用bat批量重命名图片_bat批量提取文件名@echooffsetlocalenabledelayedexpansionsetcount=100for/f”delims=”%%iin(‘dir/b*.jpg,*.png,*.bmp,*.jpeg,*.gif’)docall:Rename”%%~i”pauseexit:Renameset/acount+=1if/i”%~1″==”!count:~1!%~x1″goto:eofifexist”!count:~1!%~x1″gotoRena

    2022年9月3日
  • 关于Oracle的length函数问题

    关于Oracle的length函数问题提莫的Oracle的length函数不按常理出牌,length(”)返回的是null而不是0

    2022年5月8日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号