Java——Cloneable接口与深浅拷贝

一、Cloneable接口的作用


cloneable其实就是一个标记接口,只有实现这个接口后,然后在类中重写Object中的clone方法,然后通过类调用clone方法才能克隆成功,如果不实现这个接口,则会抛出CloneNotSupportedException(克隆不被支持)异常。
Cloneable接口源码:

public interface Cloneable {
}

Object中clone方法源码:

protected native Object clone() throws CloneNotSupportedException;

native关键字

  • 一个native方法就是一个Java调用非Java代码的接口。一个native方法是指该方法的实现由非Java语言实现,比如用C或C++实现。
  • 在定义一个native方法时,并不提供实现体(比较像定义一个Java Interface),因为其实现体是由非Java语言在外面实现的。

二、深拷贝和浅拷贝


java中创建对象的方式:

  • new: 通过new关键字在堆中为对象开辟空间,在执行new时,首先会看所要创建的对象的类型,知道了类型,才能知道需 要给这个对象分配多大的内存区域,分配内存后,调用对象的构造函数,填充对象中各个变量的值,将对象初始化,然后通过构造方法返回对象的地址;
  • clone: clone也是首先分配内存,这里分配的内存与调用clone方法对象的内存相同,然后将源对象中各个变量的值,填充到新的对象中,填充完成后,clone方法返回一个新的地址,这个新地址的对象与源对象相同,只是地址不同。
  • 另外还有输入输出流,反射构造对象等

1、浅拷贝

学生类:

public class Student{
	private String name;   //姓名
	private int age;       //年龄
	private StringBuffer sex;  //性别
	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 StringBuffer getSex() {
		return sex;
	}
	public void setSex(StringBuffer sex) {
		this.sex = sex;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", sex=" + sex + "]";
	}
}

学校类,类中重写clone方法

public  class School implements Cloneable{
	private String schoolName;   //学校名称
	private int stuNums;         //学校人数
	private Student stu;         //一个学生
	public String getSchoolName() {
		return schoolName;
	}
	public void setSchoolName(String schoolName) {
		this.schoolName = schoolName;
	}
	public int getStuNums() {
		return stuNums;
	}
	public void setStuNums(int stuNums) {
		this.stuNums = stuNums;
	}
	public Student getStu() {
		return stu;
	}
	public void setStu(Student stu) {
		this.stu = stu;
	}
	@Override
	protected School clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return (School)super.clone();;
	}
	@Override
	public String toString() {
		return "School [schoolName=" + schoolName + ", stuNums=" + stuNums + ", stu=" + stu + "]";
	}
}

main类测试:

public static void main(String[] args) throws CloneNotSupportedException {
	School s1 = new School();       
	s1.setSchoolName("实验小学");
	s1.setStuNums(100);
	Student stu1 = new Student();
	stu1.setAge(20);
	stu1.setName("zhangsan");
	stu1.setSex(new StringBuffer("男"));
	s1.setStu(stu1);
	System.out.println("s1: "+s1+" s1的hashcode:"+s1.hashCode()+"  s1中stu1的hashcode:"+s1.getStu().hashCode());
	School s2 = s1.clone();  //调用重写的clone方法,clone出一个新的school---s2
	System.out.println("s2: "+s2+" s2的hashcode:"+s2.hashCode()+" s2中stu1的hashcode:"+s2.getStu().hashCode());
}

在这里插入图片描述
可以看出s1与s2的hashcode不同,也就是说clone方法并不是把s1的引用赋予s2,而是在堆中重新开辟了一块空间,将s1复制过去,将新的地址返回给s2。
但是s1中stu的hashcode与s2中stu的hashcode相同,也就是这两个指向了同一个对象,修改s2中的stu会造成s1中stu数据的改变。但是修改s2中的基本数据类型与Stirng类型时,不会造成s1中数据的改变,基本数据类型例如int,在clone的时候会重新开辟一个四个字节的大小的空间,将其赋值。而String则由于String变量的唯一性,如果在s2中改变了String类型的值,则会生成一个新的String对象,对之前的没有影响。 这就是浅拷贝。

2、深拷贝

下面是其中一种方法,另外使用序列化将student变成流,输入再输出也可以。

public class Student implements Cloneable{
	
	private String name;
	private int age;
	private StringBuffer sex;
	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 StringBuffer getSex() {
		return sex;
	}
	public void setSex(StringBuffer sex) {
		this.sex = sex;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", sex=" + sex + "]";
	}
	@Override
	protected Student clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return (Student)super.clone();
	}
}

然后,在school的clone方法中将school中的stu对象手动clone一下。

	@Override
	protected School clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		School s = null;
		s = (School)super.clone();
		s.stu = stu.clone();
		return s;
	}

再次执行main方法查看结果:

public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
	School s1 = new School();       
	s1.setSchoolName("实验小学");
	s1.setStuNums(100);
	Student stu1 = new Student();
	stu1.setAge(20);
	stu1.setName("zhangsan");
	stu1.setSex(new StringBuffer("男"));
	s1.setStu(stu1);
	System.out.println("s1: "+s1+" s1的hashcode:"+s1.hashCode()+"  s1中stu1的hashcode:"+s1.getStu().hashCode());
	School s2 = s1.clone();  //调用重写的clone方法,clone出一个新的school---s2
	System.out.println("s2: "+s2+" s2的hashcode:"+s2.hashCode()+" s2中stu1的hashcode:"+s2.getStu().hashCode());
	
	//修改s2中的值,看看是否会对s1中的值造成影响
		 s2.setSchoolName("希望小学");
		 s2.setStuNums(200);
		 Student stu2 = s2.getStu();
		 stu2.setAge(30);
		 stu2.setName("lisi");
		 stu2.setSex(stu2.getSex().append("6666666"));
		 s2.setStu(stu2);
		 
		 //再次打印两个school,查看结果
		 System.out.println("-------------------------------------------------------------------------");
		 System.out.println("s1: "+s1+" hashcode:"+s1.hashCode()+"  s1中stu1的hashcode:"+s1.getStu().hashCode());
		 System.out.println("s2: "+s2+" hashcode:"+s2.hashCode()+" s2中stu1的hashcode:"+s2.getStu().hashCode());
}
}

在这里插入图片描述
这里可以看到两个stu的hashcode已经不同了,说明这已经是两个对象了,但是在s2中修改sex的值,为什么还会影响到s1呢?

原因在于sex的类型是Stringbuffer,在clone的时候将StringBuffer对象的地址传递了过去,而StringBuffer类型没有实现cloneable接口,也没有重写clone方法。

这种情况应该怎么解决呢?

  1. 只实现浅度clone
  2. stu2.setSex(new StringBuffer(“newString”)); 在设置stu2的sex时创建一个新的StringBuffer对象。

注: 转载自https://blog.csdn.net/qq_37113604/article/details/81168224

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值