深拷贝和浅拷贝

一、数据在内存中的存储

1、什么是基本类型和引用类型?

(1)基本类型:就是值类型,变量所对应的内存区域中存储的是值。

(2)引用类型:就是地址类型,变量所对应的内存区域中存储的是地址,真正的数据存放在地址           对应的内存区域里。

2、在方法中声明的变量(局部变量)

每当程序调用方法时,系统都会为该方法建立一个方法栈,在方法中声明的变量,即局部变量就放在方法栈中,方法结束后,系统会释放方法栈,在方法中声明的变量随着栈的销毁而结束,这就是局部变量只能在方法中有效的原因。

在方法中声明的变量可以是基本类型变量,也可以是引用类型变量。

(1)当声明基本类型变量时,其变量名和值是放在栈中。

(2)当声明引用类型变量时,该变量会存储一个内存地址,该内存地址指向所引用的对象。引用           变量名是放在栈中,指向的对象是放在堆中。

3、在类中声明的变量(成员变量)

也叫全局变量是放在堆中的,不会随着某个方法执行结束而销毁。 

同样在类中声明的变量可以是基本类型变量,也可以是引用类型变量。

(1)当声明基本类型变量时,其变量名和值是放在堆中。

(2)当声明引用类型变量时,引用变量名和指向的对象都是放在堆中。

二、拷贝

所谓拷贝,就是赋值。把一个变量赋给另外一个变量,就是把变量的内容进行拷贝。把一个对象的值赋给另外一个对象,就是把一个对象拷贝一份。

1、基本类型变量的赋值

基本类型变量赋值时,赋的是数据,不存在深拷贝和浅拷贝的问题。

public class Test {
	public static void main(String[] args) {
		int x = 20;
		int y = x;
		System.out.println("此时x和y都是20:");
		System.out.println(x);
		System.out.println(y);
		
		y = 40;
		System.out.println("如果改变y的值,x的值不会改变:");
		System.out.println(x);
		System.out.println(y);
	}
}

2、引用类型变量的赋值(引用拷贝)

引用类型变量赋值时,赋的值是地址。

public class CopyTest {
	public static void main(String[] args) {
		int[] age1 = new int[]{11,22,33};
		int[] age2 = age1;
	
		System.out.println(age1);
		System.out.println(age2);
		
		System.out.println(age1[1]);
		System.out.println(age2[1]);
		
		age2[1] = 40;
		System.out.println(age1[1]);
		System.out.println(age2[1]);
	}
}

由输出结果可以看出,age1和age2的地址值是相同的,age1和age2只是引用而已,它们都指向了一个相同的对象int[]{11,22,33}这就叫做引用拷贝。创建一个指向对象的引用变量的拷贝,并没有创建出一个新的对象。

如果改变age2所引用的数据,age2[1] = 40时,age1[1]也变为40。原因就是age1和age2引用了同一块内存区域。

只是把age1的地址拷贝一份给了age2,并没有把age1的数据拷贝一份。拷贝的深度不够。

3.对象拷贝

创建一个新的对象,这个对象是对原始对象的精确拷贝。

深拷贝和浅拷贝都是对象拷贝。

实现对象拷贝的类,必须实现Cloneable接口,并覆写clone()方法。

java中的clone

clone即复制, 在Java中, clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和原对象同样大小的空间,在这个空间中创建一个新的对象。

在java中,有两种方式可以创建对象:

1. 使用new操作符创建一个对象

new操作符的本意是分配内存。程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把它的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。

2. 使用clone方法复制一个对象 

clone在第一步是和new相似的, 都是分配内存,调用clone方法时,分配的内存和原对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域, 填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。

public class Person implements Cloneable {
	private int age;
	private String name;
	
	public Person(int age,String name) {
		this.age = age;
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public Object clone() throws CloneNotSupportedException{
		return super.clone();
	}
}
	public static void main(String[] args) throws CloneNotSupportedException {
		Person p1 = new Person(23,"张三");
		Person p2 = p1;
		System.out.println(p1);
		System.out.println(p2);
	}

 如果把对象p1直接赋给对象p2,那这只是引用拷贝,它们的地址值是相同的并没有创建出一个新的对象。

如果改变p2所引用的数据,p1所引用的数据也会跟着改变。

	public static void main(String[] args) throws CloneNotSupportedException {
		Person p1 = new Person(23,"张三");
		Person p2 = (Person) p1.clone();
		System.out.println(p1);
		System.out.println(p2);
	}

 使用clone()方法后,对象p1和对象p2的地址是不相同的,说明创建了新的对象,因此实现了对象拷贝。

        System.out.println(p1.getName());
		System.out.println(p2.getName());
		
		p2.setName("李四");
		System.out.println(p1.getName());
		System.out.println(p2.getName());

 如果改变p2所引用的数据,p1所引用的数据不会跟着改变

4.浅拷贝

拷贝出来的对象仍然保留原有对象的所有引用

特点:只要任意一个拷贝对象(或原有对象)中的引用发生改变,所有的对象都会受到影响。

public class Student implements Cloneable {
	private int age;
	private String name;
	private Person person;
	
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Person getPerson() {
		return person;
	}
	public void setPerson(Person person) {
		this.person = person;
	}
	
	public Object clone() throws CloneNotSupportedException{
		return super.clone();
	}
}
public static void main(String[] args) throws CloneNotSupportedException {
		Person person = new Person(23,"张三");
		
		Student student1 = new Student();
		student1.setAge(20);
		student1.setName("jack");
		student1.setPerson(person);
		
		Student student2 = (Student) student1.clone();
		System.out.println(student2.getAge());
		System.out.println(student2.getName());
		System.out.println(student2.getPerson().getAge());
		System.out.println(student2.getPerson().getName());
		
		person.setName("李四");
		System.out.println(student1.getPerson().getName());
		System.out.println(student2.getPerson().getName());
	}

 

 对student1进行对象拷贝,创建了新的对象student2。student1中包含指向Person(23,"张三")的引用,student2中也包含了指向Person(23,"张三")的引用。当改变person的Name为"李四"之后,student1和student2中person的Name也改变为"李四"。

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。“里面的对象”会在原来的对象和它的拷贝对象之间共享。

浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。

两个引用student1和student2指向不同的两个对象,它们中的两个person引用指向的是同一个对象。

 

5.深拷贝

拷贝出来的对象产生了所有引用的新的对象

特点:修改任意一个对象,不会对其他对象产生影响。

public class Student implements Cloneable {
	private int age;
	private String name;
	private Person person;
	
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Person getPerson() {
		return person;
	}
	public void setPerson(Person person) {
		this.person = person;
	}
	
	public Object clone() throws CloneNotSupportedException{
		//浅拷贝时:return super.clone();
		
		//深拷贝:
		Student student = (Student) super.clone();
		student.setPerson((Person) student.getPerson().clone());
		return student;
	}
}
	public static void main(String[] args) throws CloneNotSupportedException {
		Person person = new Person(23,"张三");
		
		Student student1 = new Student();
		student1.setAge(20);
		student1.setName("jack");
		student1.setPerson(person);
		
		Student student2 = (Student) student1.clone();
		System.out.println(student2.getAge());
		System.out.println(student2.getName());
		System.out.println(student2.getPerson().getAge());
		System.out.println(student2.getPerson().getName());
		
		person.setName("李四");
		System.out.println(student1.getPerson().getName());
		System.out.println(student2.getPerson().getName());
	}

从运行结果看,与浅拷贝的案例不一样的是,当改变person的Name为"李四"之后,student1中person的Name改变为"李四",但是student2中person的Name没有改变,仍然是拷贝过来的“张三”。

深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

深拷贝把要复制的对象所引用的对象都复制了一遍。

两个引用student1和student2指向不同的两个对象,它们中的两个person引用指向的也是两个对象。但对person对象的修改只能影响student1对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Night-Monkey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值