Java基础:对象的克隆(复制)

这篇具有很好参考价值的文章主要介绍了Java基础:对象的克隆(复制)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

假如想复制一个简单变量。很简单:

int apples = 5;
int pears = apples;

不仅int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。

但是如果你复制的是一个对象,情况就复杂了。

假设说我是一个beginner,我会这样写:

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;  
          
        System.out.println("学生1:" + stu1.getNumber());  
        System.out.println("学生2:" + stu2.getNumber());  
    }  
} 

结果:

学生1:12345  

学生2:12345  


这里自定义学生类,该类只有number字段。

我们新建了一个学生实例,然后将该值赋值给stu2实例。(Student stu2 = stu1;)

再看看打印结果,作为一个新手,拍了拍胸腹,对象复制不过如此,

难道真的是这样吗?

我们试着改变stu2实例的number字段,再打印结果看看:

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

为什么改变学生2的学号,学生1的学号也发生变化?

原因出在(stu2 = stu1) 这一句。该语句是将stu1的引用赋值给stu2,

这样,stu1和stu2指向内存堆中同一个对象。如图:

java对象复制到另一个对象,Java,数据结构


那么,怎样才能达到复制一个对象呢?

是否记得万类之王Object。它有11个方法,有两个protected的方法,其中一个为clone方法。

在Java中所有的类都是继承自Java语言包中的Object类的,查看它的源码,发现里面有一个访问限定符为protected的方法clone():

/*
Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
The general intent is that, for any object x, the expression:
1) x.clone() != x will be true
2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
3) x.clone().equals(x) will be true, this is not an absolute requirement.
*/
protected native Object clone() throws CloneNotSupportedException;

仔细一看,它还是一个native方法,大家都知道native方法是非Java语言实现的代码,供Java程序调用的,因为Java程序运行在JVM虚拟机上,要想访问比较底层与操作系统相关的就没办法,只能由靠近操作系统的语言来实现。

  1. 第一次声明保证克隆对象将有单独的内存地址分配。
  2. 第二次声明表明,原始和克隆的对象应该具有相同的类类型,但它不是强制性的。
  3. 第三声明表明,原始和克隆的对象应该是平等的equals()方法使用,但它不是强制性的。

因为每个类直接或间接的父类都是Object,因此它们都含有clone()方法,但是因为该方法是protected,所以都不能在类外进行访问。

要想对一个对象进行复制,就需对clone方法覆盖。


为什么要克隆?

为什么需要克隆对象?直接new一个对象不行吗?

答案是:克隆的对象可能包含一些已修改过的属性,而new出来的对象的属性都是初始化时的值,所以当需要一个新的对象来保存当前对象的“状态”就靠clone方法。

那把这个对象的临时属性一个个赋值给新new的对象不也行嘛?可以是可以,但是一来麻烦不说,二来,通过上面的源码都发现clone是一个native方法,就是快,在底层实现。

提个醒,我们常见的Object a=new Object();Object b;b=a;这种形式的代码复制的是引用,即对象在内存中的地址,a和b对象仍指向同一个对象。

而通过clone方法赋值的对象跟原来的对象是同时独立存在的。


如何实现克隆

介绍下两种不同的克隆方法,浅克隆(ShallowClone)深克隆(DeepClone)

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

一般步骤是(浅克隆):

1. 被复制的类需实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常), 该接口为标记接口(不含任何方法)

2. 覆盖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()); // 学生1:12345  
        System.out.println("学生2:" + stu2.getNumber()); // 学生2:12345  
          
        stu2.setNumber(54321);  
      
        System.out.println("学生1:" + stu1.getNumber()); // 学生1:12345  
        System.out.println("学生2:" + stu2.getNumber()); // 学生2:54321
    }  
} 

如果还不相信这两个对象不是同一个对象,可以看看这一句:

System.out.println(stu1 == stu2); // false  

上面被称为浅克隆。

还有一种复杂的深度复制:

我们在学生类里再加一个Address类。

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());
    }
}

结果:

学生1:123,地址:杭州市  

学生2:123,地址:杭州市

乍一看没问题,真的是这样吗?在main方法中改变addr实例的地址。

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方法,完整代码如下:

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());
    }
}

结果:文章来源地址https://www.toymoban.com/news/detail-698232.html

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

到了这里,关于Java基础:对象的克隆(复制)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java对象克隆详解

    概述: 当我们new一个对象时,其中的属性就会被初始化, 那么想要保存刚开始初始化的值就靠clone方法来实现, 平时我们最常见的是一个对象的引用指向另一个对象,并不是创建了两个对象. 克隆肯定是创建了两个对象 克隆分为浅克隆(ShallowClone)和深克隆(DeepClone)。 在 Java 语言中,

    2024年02月09日
    浏览(29)
  • Java的对象克隆

    本节我们会讨论 Cloneable 接口,这个接口指示一个类提供了一个安全的 clone() 方法。 Object 类提供的 clone() 方法是 “浅拷贝”,并没有克隆对象中引用的其他对象,原对象和克隆的对象仍然会共享一些信息。深拷贝指的是:在对象中存在其他对象的引用的情况下,会同时克隆

    2023年04月19日
    浏览(27)
  • git命令使用 将git仓库克隆到另一个仓库,并保留原来的提交记录

    君子拙于不知己,而信于知己。——司马迁   清屏:clear 查看当前面板的路径:pwd 查看当前面板的文件:ls 创建文件夹:mkdir 文件夹名 创建文件:touch 文件名 删除文件夹:rm -rf 文件夹名 删除文件:rm -f 文件名 移动文件/文件夹:mv 需移动文件/文件名 目标文件夹/(../上级)  

    2024年02月12日
    浏览(52)
  • Three.js教程:对象克隆、复制

    推荐:将 NSDT场景编辑器 加入你的3D工具链 其他系列工具: NSDT简石数字孪生 Threejs大多数对象都有克隆 .clone() 和复制 .copy() 两个方法,点模型 Points 、线模型 Line 、网格网格模型 Mesh 一样具有这两个方法。 A.copy(B) 表示B属性的值赋值给A对应属性。 N = M.clone() 表示返回一个和

    2024年02月08日
    浏览(47)
  • linux怎么复制文件到另一个文件夹

    1、linux怎么复制文件到另一个文件夹 2、Linux怎么复制文件进入文件系统中? 3、在Linux系统中,要将文件复制到另一个目录中,为防止意外覆盖相同文件名... 4、linux下怎样复制文件并且重命名文件? 可以使用cp命令来实现文件复制。例如,如果要将文件filetxt复制到文件夹folder2中,

    2024年02月10日
    浏览(50)
  • 从一个服务器复制东西到另一个服务器的命令

    您可以使用scp命令从一个服务器复制文件或目录到另一个服务器。以下是基本的scp命令格式: 其中,source是要复制的文件或目录的路径,destination是复制的目标路径,可以是本地路径或远程服务器路径。如果destination是远程服务器路径,则需要在路径前加上用户名和服务器地

    2024年02月14日
    浏览(48)
  • 将一个 PostgreSQL 数据库复制到另一个数据库中

    以管理员身份进入cmd窗口,输入如下命令 语法: 示例:

    2024年02月22日
    浏览(44)
  • Java 学习路线:基础知识、数据类型、条件语句、函数、循环、异常处理、数据结构、面向对象编程、包、文件和 API

    Java 是一种由 Sun Microsystems 于 1995 年首次发布的编程语言和计算平台。Java 是一种通用的、基于类的、面向对象的编程语言,旨在减少实现依赖性。它是一个应用程序开发的计算平台。Java 快速、安全、可靠,因此在笔记本电脑、数据中心、游戏机、科学超级计算机、手机等领

    2024年03月24日
    浏览(91)
  • 从一个word里面复制表格到另一个word时,表格变形的问题

    复制过来保留源格式,检查段落、页边距里面的格式都和原始word一致后,仍然表格变形。 这时点页边距-自定义页边距-文档网格 看字符数是不是一致的

    2024年03月10日
    浏览(69)
  • java面试基础 -- 深克隆 & 浅克隆

            说到java的克隆你还记得多少? 一说到克隆你可能就会想起来那个接口, 没错, 他就是 Cloneable         Cloneable 是java里面内置的很常用的接口, 我们说 Object类中也有一个clone方法:          但是要想合法调用 clone 方法, 必须要先实现 Clonable 接口, 否则就会抛出 C

    2024年02月12日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包