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


相关推荐

  • 数位DP模板详解

    数位DP模板详解已经很长时间没有做过关于数位 DP 的题目了 现在来写一下自己对于数位 DP 的理解 一般这种题目都是问在区间 l r 内满足某种条件的数有多少 显然我们可以转换为求 0 x 中满足该条件的数有多少 然后利用前缀和思想 直接用 0 r 中满足某种条件的数的个数减去 0 l 1 中满足某种条件的个数即可 这个就不细说了 下面看一下板子 inta N llf N s 第一维一般是当前枚举到的位数 第二位表示状态 具体问题具体分析 lldp intpos 当前枚举到的位 s 代表状态 b

    2025年6月15日
    0
  • java位运算

    java位运算

    2021年9月29日
    42
  • C语言数组初始化的三种方法[通俗易懂]

    C语言数组初始化的三种方法[通俗易懂]C语言中,数组初始化的方式主要有三种:1、声明时,使用{0}初始化;2、使用memset;3、用for循环赋值。那么,这三种方法的原理以及效率如何呢?请看下面的测试代码:#defineARRAY_SIZE_MAX(1*1024*1024)voidfunction1(){chararray[ARRAY_SIZE_MAX]={0};…

    2022年10月8日
    0
  • js解压gzip数据_gzip 压缩

    js解压gzip数据_gzip 压缩微信小程序开发交流qq群173683895承接微信小程序开发。扫码加微信。GZIP压缩,GZIP解压需要用到pako.js文件:下载地址:https://download.csdn.net/download/qq_35713752/10627338使用方法:JS压缩和解压:varpako=require(‘../../utils/pako.min.js…

    2022年9月7日
    0
  • VUE学习笔记——es6对象合并 数组转对象

    VUE学习笔记——es6对象合并 数组转对象constarr=[{date:”2018-11-18″,name:”demo1″},{date:”2018-11-19″,name:”demo2″}];consttarget={};arr.forEach(a=>{constsource=JSON.parse(`{“${a.date}”:”${a.na…

    2022年9月4日
    3
  • stm32直流电机控制—PID算法篇

    stm32直流电机控制—PID算法篇stm32直流电机控制—PID算法篇提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加例如:第一章Python机器学习入门之pandas的使用**加粗样式*提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录stm32直流电机控制—PID算法篇前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结前言提示:这里可以添加本文要记录的大概内容:例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了

    2022年5月7日
    46

发表回复

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

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