java中clone_java copyproperties

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

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 常用电平转换芯片_硬件电路设计教程

    常用电平转换芯片_硬件电路设计教程在设计数字电路的时候,经常会遇到控制电压不一致,尤其是ARM与一些芯片的电平不一致,比如ARM是5V供电,芯片是3.3V,或者反过来。虽然有的芯片两种电压兼容,不如STM32系列的ARM在3.3V供电的情况的下仍可以兼容5V输入,但是为了安全起见,一般都会使用电平转换芯片。电平转换芯片有两个电源分别为VCCA,对应A1-A8输入;VCCB,对应B1-B8输入.OE使能低…

    2022年8月10日
    8
  • 多进程 python_python多进程编程

    多进程 python_python多进程编程进程前置知识点进程:一个程序运行起来后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单元。并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行

    2022年7月29日
    7
  • 弗洛伊德算法怎么理解_弗洛伊德算法思想

    弗洛伊德算法怎么理解_弗洛伊德算法思想这个方法中,其中每一个顶点到另一个顶点最多就是两步。所以就是找到两个顶点的最近距离packagea;importjava.lang.reflect.Array;importjava.util.Arrays;publicclassFloydDemo{publicstaticvoidmain(String[]args){char[]di…

    2025年8月12日
    3
  • pycharm如何设置背景为自己的图片_扣扣空间背景图片怎么设置

    pycharm如何设置背景为自己的图片_扣扣空间背景图片怎么设置pycharm的背景很单调,喜欢花里胡哨的朋友们看这里啦~操作步骤可以设置自己看着舒服的透明度和喜欢的图片学会了吗赶紧去试试吧!

    2022年8月26日
    7
  • jquery弹窗插件dialog_jquery进度条插件

    jquery弹窗插件dialog_jquery进度条插件143行js顶部进度条最小插件-nanobar.js源码解析

    2022年4月20日
    69
  • 网络编程学习笔记一:Socket编程

    网络编程学习笔记一:Socket编程“一切皆Socket!”话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket。——有感于实际编程和开源项目研究。我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠socket?那什么是socket?socket的类型有哪些?还有

    2022年7月13日
    15

发表回复

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

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