zoukankan      html  css  js  c++  java
  • java中的深拷贝与浅拷贝

    Java中深拷贝与浅拷贝

    在谈论深拷贝、浅拷贝之前,首先要理解什么是值类型?什么是引用类型?这对于理解深拷贝、浅拷贝很关键。

    Java的世界,我们要习惯用引用去操作对象。在Java中,像数组、类Class、枚举EnumInteger包装类等等,就是典型的引用类型,所以操作时一般来说采用的也是引用传递的方式;

    但是Java中基础数据类型,如int这些基本类型,操作时一般采取的则是值传递的方式,所以有时候也称它为值类型。


    为了方便案例的演示,首先准备两个类,一个是教师类,一个是学生类,这里以一个老师只教一个学生为例,教师类中包含自己所教的学生。

    • 教师类,Teacher
    public class Teacher {
    	/**
    	 * 姓名
    	 */
    	private String name;
    	/**
    	 * 年龄
    	 */
    	private int age;
    	/**
    	 * 学生
    	 */
    	private Student stu;
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    	......
    }
    
    • 学生类,Student
    public class Student {
    	/**
    	 * 学号
    	 */
    	private long stuNo;
    	/**
    	 * 姓名
    	 */
    	private String stuName;
    
    	public long getStuNo() {
    		return stuNo;
    	}
    
    	public void setStuNo(long stuNo) {
    		this.stuNo = stuNo;
    	}
        ......
    }
    

    此时,这两个类的关系如下:

    image-20211213165802562

    浅拷贝

    浅拷贝,它的特性体现在这个“浅”字上面。

    • 浅拷贝代码实现:
    public class Teacher implements Cloneable{
    	/**
    	 * 姓名
    	 */
    	private String name;
    	/**
    	 * 年龄
    	 */
    	private int age;
    	/**
    	 * 学生
    	 */
    	private Student stu;
    
    	@Override
    	public Teacher clone() {
    		try {
    			return (Teacher) super.clone();
    		} catch (CloneNotSupportedException e) {
    			throw new AssertionError();
    		}
    	}
       	......
    }
    
    • 主程序
    public class ShallowMain {
    	public static void main(String[] args) {
    		Student student = new Student(2016021006, "李四");
    		Teacher teacher = new Teacher("鲁班", 24);
    		teacher.setStu(student);
    
    		// 克隆教师对象
    		Teacher teacher1 = teacher.clone();
    
    		System.out.println(teacher == teacher1);
    		// 原对象
    		System.out.println(teacher);
    		// 克隆对象
    		System.out.println(teacher1);
    
    		System.out.println("-------------------修改克隆对象的name以及年龄---------------------");
    		// 修改克隆对象的name以及年龄
    		teacher1.setName("鲁班大师");
    		teacher1.setAge(34);
    
    		// 原对象
    		System.out.println("原对象:" + teacher);
    		// 克隆对象
    		System.out.println("克隆对象:" + teacher1);
    
    		System.out.println("-------------------修改原对象的学生属性---------------------");
    		// 修改原对象的学生属性
    		student.setStuName("张三");
    		student.setStuNo(666666);
    
    		// 原对象
    		System.out.println("原对象:" + teacher);
    		// 克隆对象
    		System.out.println("克隆对象:" + teacher1);
    	}
    }
    

    打印出的结果:

    image-20211213172207396


    根据这里执行的结果得出结论:

    • teacher == teacher1打印出的结果是false,则说明是创建了一个新的对象

    • 修改克隆出的对象的name和age属性,发现并不会影响原对象的属性值

    • 修改克隆出的对象的学生属性,导致原对象的学生属性也被修改了

    浅拷贝中 值类型的字段会复制一份,而引用类型的字段拷贝的仅仅是引用地址,而该引用地址指向的实际对象空间其实只有一份。

    image-20211214100413129


    (1)引出的问题,String类型是属于引用类型,为什么它也和基本数据类型一样是复制值?后面再研究这个问题

    深拷贝

    深拷贝相对于浅拷贝,在复制基本数据类型时,也会将引用类型所指向的内存拷贝一份。如下图。

    image-20211214101427220

    • 深拷贝代码实现:

    深拷贝需要本例中的Student也实现Cloneable接口,这样Student也可以拷贝。

    • Student类
    public class Student implements Cloneable{
    	/**
    	 * 学号
    	 */
    	private long stuNo;
    	/**
    	 * 姓名
    	 */
    	private String stuName;
    
    	@Override
    	public Student clone() {
    		try {
    			return (Student) super.clone();
    		} catch (CloneNotSupportedException e) {
    			throw new AssertionError();
    		}
    	}
    	
    	......
    }
    
    • Teacher类
    public class Teacher implements Cloneable{
    	/**
    	 * 姓名
    	 */
    	private String name;
    	/**
    	 * 年龄
    	 */
    	private int age;
    	/**
    	 * 学生
    	 */
    	private Student stu;
    
    	@Override
    	public Teacher clone() {
    		try {
                //先克隆Teacher对象
    			Teacher teacher = (Teacher) super.clone();
                //克隆Student对象,复值给克隆出的teacher对象中的stu属性
    			teacher.stu = stu.clone();
    			return teacher;
    		} catch (CloneNotSupportedException e) {
    			throw new AssertionError();
    		}
    	}
    	
    	......
    }
    
    • 主程序类与浅拷贝主程序代码一致

    • 执行结果

    image-20211214102620298

    根据执行结果得出:克隆出的Teacher对象已经是相互独立的互不干扰。


    深拷贝的另一种实现方式:反序列化实现对象深拷贝

    • Teacher类
    public class Teacher implements Serializable{
    	/**
    	 * 姓名
    	 */
    	private String name;
    	/**
    	 * 年龄
    	 */
    	private int age;
    	/**
    	 * 学生
    	 */
    	private Student stu;
    
    	@Override
    	public Teacher clone() {
    		/*try {
    			Teacher teacher = (Teacher) super.clone();
    			teacher.stu = stu.clone();
    			return teacher;
    		} catch (CloneNotSupportedException e) {
    			throw new AssertionError();
    		}*/
    
    		try {
    			//将当前对象序列化到字节流
    			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    			ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
    			objectOutputStream.writeObject(this);
    			//反序列化创建对象
    			ObjectInputStream objectInputStream = new ObjectInputStream(
    					new ByteArrayInputStream(outputStream.toByteArray()));
    			return  (Teacher) objectInputStream.readObject();
    		} catch (IOException | ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    }
    
    • 反序列创建对象,需要被引用的子类可以被序列化。也就是Student类需要实现Serializable接口。
    public class Student implements Serializable {
    	/**
    	 * 学号
    	 */
    	private long stuNo;
    	/**
    	 * 姓名
    	 */
    	private String stuName;
    
    	/*@Override
    	public Student clone() {
    		try {
    			return (Student) super.clone();
    		} catch (CloneNotSupportedException e) {
    			throw new AssertionError();
    		}
    	}*/
    }
    
    • 主程序执行结果:

    image-20211214103954413

    String引用类型

    String常见的两种创建方式,示例代码:

    public class StringMain {
    	public static void main(String[] args) {
    		String str1 = new String("Hello");
    		String str2 = "Hello";
    		System.out.println(str1 == str2); //false
    	}
    }
    

    image-20211214111731292

    str2指向的是常量池中的Hello字符串

    str1指向的是在堆内存中创建的String对象,内容是Hello字符串。

    在java中String通过常量赋值认为是基本数据类型,通过new关键字创建的对象则是引用类型。

  • 相关阅读:
    mycat主要参数
    Linux上通过docker方式安装mysql
    Docker镜像拉取慢的解决方法
    mysql关联、子查询索引优化
    Mysql优化单表查询
    Mysql如何快速插入100万条记录?
    git连接到github
    git分支操作2
    git基本操作1
    git简介及安装(win10)
  • 原文地址:https://www.cnblogs.com/YpfBolg/p/15687056.html
Copyright © 2011-2022 走看看