Java内容的复制三种方法转载

原创
小哥 3年前 (2022-10-28) 阅读数 162 #JAVA
文章标签 javajava教程

1、概述
在实际的编程过程中,我们经常会遇到这样的情况:有一个对象A,在某个时刻A一些有效的值已经包含在 需要一笔钱A完全相同的新物体B此后,在B任何更改都不会影响A价值在于,也就是,A与B是两个独立的物体,B的初始值A确定了对象。例如,以下过程显示了这种情况:

class Student {
private int number;

public int getNumber() {
return number;
}

public void setNumber(int number) {
this.number = number;
}

}
public class Test {
public static void main(String args[]) {
Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = stu1;
stu1.setNumber(54321);
System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
}
}

结果:

学生1:54321
学生2:54321

为什么要换学生?2学生号,学生1学号也变了吗?
原因出在(stu2 = stu1) 这句话。这一声明的目的是stu1将引用分配给。stu2,
这样,stu1和stu2指向内存堆中的同一对象。图:

那么,如何才能制作一个干净的对象副本呢?在……里面 Java在语言中,这一需求不能通过简单的赋值语句来满足。有很多方法可以满足这种需求,
(1)将A对象的值分别传递。set方法加入B对象中;
(2)通过重写java.lang.Object类中的方法clone();
(3)通过org.apache.commons中的工具类BeanUtils和PropertyUtils对象复制;
(4对象的复制是通过序列化实现的。

2、将A对象的值分别传递。set方法加入B对象中

为了逐个赋值,为了演示简单性,此示例设置了一个属性:

Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = new Student();
stu2.setNumber(stu1.getNumber());

我们发现,属性逐个赋值非常方便,但当属性较多时,需要始终get、set这很麻烦。

3、重写java.lang.Object类中的方法clone()

首先,介绍了两种不同的克隆方法,浅层克隆(ShallowClone)和深克隆(DeepClone)。

在Java在语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括。int、double、byte、boolean、char简单数据类型,如类、接口、数组和其他复杂类型。浅克隆和深克隆的主要区别在于是否支持复制引用类型的成员变量,下面将对两者进行详细描述。

3.1 浅克隆

一般步骤:

需要实现复制的类。Clonenable接口(如果未实现,则调用clone方法将引发CloneNotSupportedException异常), 该接口是标记接口。(不包含任何方法)

覆盖clone()方法,则设置访问修饰符。public。方法调用super.clone()方法获取所需的复制对象。(native对于本地方法)

class Student implements Cloneable{
private int number;

public int getNumber() {
return number;
}

public void setNumber(int number) {
this.number = number;
}

@Override
public Object clone() {
Student stu = null;
try{
stu = (Student)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
}
public class Test {
public static void main(String args[]) {
Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = (Student)stu1.clone();

System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());

stu2.setNumber(54321);

System.out.println("学生1:" + stu1.getNumber());
System.out.println("学生2:" + stu2.getNumber());
}
}

结果:
学生1:12345
学生2:12345
学生1:12345
学生2:54321

在浅层克隆中,如果Prototype对象的成员变量是值类型,则会复制到克隆对象;如果Prototype对象的成员变量是引用类型,则会将被引用对象的地址复制到克隆对象,即Prototype对象和克隆对象的成员变量指向相同的内存地址。

简单地说,在浅克隆中,当复制对象时,只复制其自身的成员变量和其中包含的值类型,而不复制引用类型的成员对象。

在Java语言,通过覆盖Object类的clone()方法可以实现浅层克隆。

3.2 深克隆

package abc;

class Address {
private String add;

public String getAdd() {
return add;
}

public void setAdd(String add) {
this.add = add;
}
}

class Student implements Cloneable{
private int number;

private Address addr;

public Address getAddr() {
return addr;
}

public void setAddr(Address addr) {
this.addr = addr;
}

public int getNumber() {
return number;
}

public void setNumber(int number) {
this.number = number;
}

@Override
public Object clone() {
Student stu = null;
try{
stu = (Student)super.clone();   //浅复制
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
}
public class Test {

public static void main(String args[]) {

Address addr = new Address();
addr.setAdd("杭州市");
Student stu1 = new Student();
stu1.setNumber(123);
stu1.setAddr(addr);

Student stu2 = (Student)stu1.clone();

System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());

addr.setAdd("西湖区");

System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
}
}

结果:
学生1:123,地址:杭州市
学生2:123,地址:杭州市
学生1:123,地址:西湖区
学生2:123,地址:西湖区

为什么两个学生的地址都变了?

原因是浅层复制仅复制addr对变量的引用并不会真正打开另一块空间,即复制值并返回对新对象的引用。

以实现对象的真实副本,而不是纯粹的参考副本。我们需要Address类可以复制和修改。clone方法,完整的代码如下所示:

package abc;

class Address implements Cloneable {
private String add;

public String getAdd() {
return add;
}

public void setAdd(String add) {
this.add = add;
}

@Override
public Object clone() {
Address addr = null;
try{
addr = (Address)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return addr;
}
}

class Student implements Cloneable{
private int number;

private Address addr;

public Address getAddr() {
return addr;
}

public void setAddr(Address addr) {
this.addr = addr;
}

public int getNumber() {
return number;
}

public void setNumber(int number) {
this.number = number;
}

@Override
public Object clone() {
Student stu = null;
try{
stu = (Student)super.clone();   //浅复制
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
stu.addr = (Address)addr.clone();   //深度复制
return stu;
}
}
public class Test {

public static void main(String args[]) {

Address addr = new Address();
addr.setAdd("杭州市");
Student stu1 = new Student();
stu1.setNumber(123);
stu1.setAddr(addr);

Student stu2 = (Student)stu1.clone();

System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());

addr.setAdd("西湖区");

System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
}
}

结果:
学生1:123,地址:杭州市
学生2:123,地址:杭州市
学生1:123,地址:西湖区
学生2:123,地址:杭州市

在深度克隆中,无论Prototype对象的成员变量是值类型还是引用类型,都会将副本复制到克隆对象,并且深度克隆还会将原型对象的所有引用对象复制到克隆对象。

简单地说,在深度克隆中,除了对象本身之外,对象中包含的所有成员变量都将被复制。

在Java语言,如果需要实现深度克隆,您可以重写Object类的clone()方法实现,也可以序列化。(Serialization)和其他方式来实现。

(如果引用类型也包含许多引用类型,或者内部引用类型的类包含引用类型,请使用它。clone这种方法会很麻烦。在这一点上,我们可以序列化对象的深度克隆。)

4、工具类BeanUtils和PropertyUtils进行对象复制

Student stu1 = new Student();
stu1.setNumber(12345);
Student stu2 = new Student();
BeanUtils.copyProperties(stu2,stu1);

这种编写方法,无论多少种属性,只需要一行代码,非常方便!除BeanUtils还有一个名字PropertyUtils的工具类,它还提供了copyProperties()方法、角色和BeanUtils同名的方法很相似,主要的区别是BeanUtils提供类型转换功能,即发现两个JavaBean如果同名属性的类型不同,则会在支持的数据类型范围内进行转换,而PropertyUtils不支持此功能,但速度会更快。在实际开发中,BeanUtils使用更普遍,犯错的风险也更低。

5,通过序列化复制对象。

序列化是将对象写入流的过程,其中对象是原始对象的副本,而原始对象仍然存在于内存中。通过序列化实现的复制不仅可以复制对象本身,还可以复制它所引用的成员对象,因此可以通过将对象序列化为流并将其从流中读出来实现深度克隆。需要注意的是,必须实现可序列化的对象类。Serializable接口,否则无法实现序列化操作。


版权声明:本文是CSDN博主「chun_soft“原文,跟上。CC 4.0 by-sa版权协议,转载请附上原始来源链接和本声明。
原始链接:https://blog.csdn.net/ztchun/article/details/79110096

版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除

热门