zoukankan      html  css  js  c++  java
  • Java对象和集合的拷贝/克隆/复制

    昨天同事遇到了一个奇怪的问题,他需要将一个JavaBean拷贝一份,然后对新创建的Bean进行操作。但是他对新的Bean操作后,会影响旧的Bean的值。当听到这个问题的时候,我第一反应就是他的拷贝方法有问题,只是将aBean的内容复制给了bBean,但是在内存中指向的是同一个地址。这里就引出了两个关键词,浅拷贝深拷贝

    浅拷贝(浅克隆)

    被复制对象的所有变量值都和原来的对象的值相同,但是复制后的对象的引用仍然指向原来的对象。简单来说,就是对A进行拷贝生成B,只是将A的值复制给了B,内存中指向的是同一地址,影响就是改变B的同时,A的值也会改变。

    深拷贝(深克隆)

    被复制对象的所有变量值都和原来的对象的值相同,除了变量的值改变,也会创建新的指向变量的地址。源对象A和复制后的B,虽然内容是一样的,但是各自指向的地址不同,所以改变相互不受影响。

    那问题已经知道,Java的Bean对象要怎么进行深拷贝呢。

    一、Java对象克隆

    Java中对象的深克隆

    ①利用Object类的clone()方法。
    ②在派生类中重写基类的clone方法,并声明为public。
    ③在派生类的clone()方法中,调用super.clone()。
    ④在派生类中实现Cloneable接口。

    这是一个Student的类,重写了clone()方法。

    public class Student implements Cloneable {
    	private String name;
    	private int age;
    
    	public Student(String name, int age) {
    		super();
    		this.name = name;
    		this.age = age;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	
    	public Object clone() {
    		Object o = null;
    		try {
    			o = (Student)super.clone();
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		
    		return o;
    	}
    	@Override
    	public String toString() {
    		return "Student [name=" + name + ", age=" + age + "]";
    	}
    }
    

    测试clone

    public static void main(String[] args) { 
    	Student s1=new Student("张三",18);
    	System.out.println("修改前s1值:" + s1.toString());
    	
    	Student s2=(Student)s1.clone(); 
    	s2.setName("李四");
    	s2.setAge(20);
    	
    	//修改学生2后,不影响学生1的值。
    	System.out.println("修改后s1值:" + s1.toString());
    	System.out.println("s2的值:" + s2.toString());
    }
    

    控制台输出的结果如下:

    修改前s1值:Student [name=张三, age=18]
    修改后s1值:Student [name=张三, age=18]
    s2的值:Student [name=李四, age=20]
    

    上面的例子对象中的属性是基本类型,但是如果包含非基本类型,结果怎样呢,上代码,对Student类进行修改,加入Course类。
    Student类

    public class Student implements Cloneable {
    	
    	private String name;
    	private int age;
    	private Course course;
    	
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	public Course getCourse() {
    		return course;
    	}
    	public void setCourse(Course course) {
    		this.course = course;
    	}
    	
    	public Student(String name, int age, Course course) {
    		super();
    		this.name = name;
    		this.age = age;
    		this.course = course;
    	}
    	
    	public Student() {
    	}
    	
    	public Object clone() {
    		Object o = null;
    		try {
    			o = (Student)super.clone();
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		
    		return o;
    	}
    	@Override
    	public String toString() {
    		return "Student [name=" + name + ", age=" + age + ", course=" + course + "]";
    	}
    	
    }
    

    Course类

    
    public class Course {
    	
    	private String name;
    	private int value;
    	
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getValue() {
    		return value;
    	}
    	public void setValue(int value) {
    		this.value = value;
    	}
    	
    	public Course(String name, int value) {
    		super();
    		this.name = name;
    		this.value = value;
    	}
    	public Course() {
    		
    	}
    	@Override
    	public String toString() {
    		return "Course [name=" + name + ", value=" + value + "]";
    	}
    	
    }
    

    测试克隆

    public static void main(String[] args) { 
    	
    	Student s1 = new Student();
    	s1.setName("张三");
    	s1.setAge(24);
    	
    	Course c = new Course();
    	c.setName("语文");
    	c.setValue(80);
    	
    	s1.setCourse(c);
    	System.out.println("修改前s1值:" + s1.toString());
    	
    	Student s2 = (Student)s1.clone();
    	s2.setName("李四");
    	s2.setAge(20);
    	
    	Course c2 = s2.getCourse();
    	c2.setName("数学");
    	c2.setValue(90);
    	
    	//修改学生2的Course后,影响学生1的Course。
    	System.out.println("修改后s1值:" + s1.toString());
    	System.out.println("s2的值:" + s2.toString());
    }
    

    控制台输出结果

    修改前s1值:Student [name=张三, age=24, course=Course [name=语文, value=80]]
    修改后s1值:Student [name=张三, age=24, course=Course [name=数学, value=90]]
    s2的值:Student [name=李四, age=20, course=Course [name=数学, value=90]]
    
    上面的例子说明调用clone方法,拷贝原始对象中的内容,对于基本数据类型,这样的操作是没有问题的,但是对于非基本类型,它们保存的仅仅是对象的引用。

    为了解决上面的问题,我们需要使用深度克隆方案。修改之后的Student类和Course类如下
    Student类

    public class Student implements Cloneable {
    	
    	private String name;
    	private int age;
    	private Course course;
    	
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	public Course getCourse() {
    		return course;
    	}
    	public void setCourse(Course course) {
    		this.course = course;
    	}
    	
    	public Student(String name, int age, Course course) {
    		super();
    		this.name = name;
    		this.age = age;
    		this.course = course;
    	}
    	
    	public Student() {
    	}
    	
    	public Student clone() {
    		Student s = null;
    		try {
    			s = (Student)super.clone();
    			s.course = course.clone();
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		
    		return s;
    	}
    	@Override
    	public String toString() {
    		return "Student [name=" + name + ", age=" + age + ", course=" + course + "]";
    	}
    	
    }
    

    Course类

    
    public class Course implements Cloneable{
    	
    	private String name;
    	private int value;
    	
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getValue() {
    		return value;
    	}
    	public void setValue(int value) {
    		this.value = value;
    	}
    	
    	public Course(String name, int value) {
    		super();
    		this.name = name;
    		this.value = value;
    	}
    	public Course() {
    		
    	}
    	
    	public Course clone() {
    		Course c = null;
    		try {
    			c = (Course)super.clone();
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		
    		return c;
    	}
    	@Override
    	public String toString() {
    		return "Course [name=" + name + ", value=" + value + "]";
    	}
    	
    }
    

    测试方法使用之前的方法,下面的运行后的结果。

    修改前s1值:Student [name=张三, age=24, course=Course [name=语文, value=80]]
    修改后s1值:Student [name=张三, age=24, course=Course [name=语文, value=80]]
    s2的值:Student [name=李四, age=20, course=Course [name=数学, value=90]]
    

    根据结果可知,通过深度克隆后,clone后的对象非基本类的变量修改,不会对原对象造成影响。

    对于集合的Clone操作也一样,集中的属性如果是基本数据类型的话,循环赋值,或者使用addAll()方法都不会对原集合造成影响。若集合中存放了非基本数据类型的话,如上述中的Student对象,就必须对Student对象添加重写clone()方法。

  • 相关阅读:
    转 sql 时间转换格式 convert(varchar(10),字段名,转换格式)
    C#页面添加提交数据后跳出小弹窗的功能
    解决粘包问题
    粘包问题
    模拟ssh远程执行命令
    基于TCP协议的socket套接字编程
    Linux和git使用
    osi七层协议
    TCP协议的三次握手和四次挥手
    C/S 和 B/S架构
  • 原文地址:https://www.cnblogs.com/ghq120/p/11603961.html
Copyright © 2011-2022 走看看