深拷贝和浅拷贝

在前面,我们在java创建对象的四种方式中提到过通过clone来创建对象,点我前往

这一篇,我们将详细说说拷贝。

拷贝分为两种,浅拷贝和深拷贝。

浅拷贝:赋值原来的对象,但是不复制对对象的引用。对于基本数据类型,克隆前后互不影响,即创建了一个新的对象。对于引用类型,克隆前后相互影响

深克隆:不仅复制了对象,还赋值了对象的引用,即克隆前后完全是两个不同的对象,两者互不影响。

下面,我们就这两种情况进行代码的实现,相信看完以后,你对深浅克隆会有更加直观的认识。

首先创建两个类,Message和User (其中Message将作为User的引用类):

package day01;

public class Message {
	private String id;
	private String email;
	public Message(String id,String email) {
		this.id=id;
		this.email=email;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	@Override
	public String toString() {
		return "Message [id=" + id + ", email=" + email + "]";
	}
	
}
package day01;

public class User implements Cloneable{
	private String username;
	private String password;
	private Message message;
	public User() {}
	public User(String username,String password,Message message) {
		this.username=username;
		this.password=password;
		this.message=message;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	
	public Message getMessage() {
		return message;
	}
	public void setMessage(Message message) {
		this.message = message;
	}
	@Override
	public String toString() {
		return "User [username=" + username + ", password=" + password + ", message=" + message + "]";
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Object object=null;
		object= super.clone();
		return object;
	}
	public void Login(String username) {
		System.out.println(username+"的密码是"+password+",他的邮箱是"+message.getEmail());
	}
}

测试类:

package day01;
/*
 * 浅克隆
 */
public class Test{
	public static void main(String[] args) throws CloneNotSupportedException{
		Message message = new Message("20190910","2130054941@qq.com");
		User user1=new User("gangan","123",message);
		User user2=(User) user1.clone();
		//改变基本类型变量值
		user2.setPassword("456");
		//改变引用类型变量值
		user2.getMessage().setEmail("1982905232@qq.com");
		System.out.println(user1);
		System.out.println(user2);
	}
}

查看测试结果:

User [username=gangan, password=123, message=Message [id=20190910, email=1982905232@qq.com]]
User [username=gangan, password=456, message=Message [id=20190910, email=1982905232@qq.com]]

可以看出:对于成员变量是基本数据类型(包括String),克隆后会新创建一个对象,它的值的改变不会影响克隆前的那个对象

对于成员变量是引用类型,克隆后也会创建新的对象,但是和克隆前的对象共享同一个引用,它的值的改变会影响前者。

深克隆(基于前面):

修改Message(同时也需要实现cloneable接口):

package day01;

public class Message implements Cloneable{
	private String id;
	private String email;
	public Message(String id,String email) {
		this.id=id;
		this.email=email;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	@Override
	public String toString() {
		return "Message [id=" + id + ", email=" + email + "]";
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

修改User(重写clone):

	@Override
	protected Object clone() throws CloneNotSupportedException {
		User user=(User)super.clone();;
		user.message=(Message)message.clone();
		return user;
	}

测试类同上,直接上测试结果:

User [username=gangan, password=123, message=Message [id=20190910, email=2130054941@qq.com]]
User [username=gangan, password=456, message=Message [id=20190910, email=1982905232@qq.com]]

可以看出:克隆前后都完全互不影响,新建的对象不再引用原有的对象。同时我们也看出,深克隆需要对每个引用对象都进行克隆。如果对象是数组,还需要遍历数组,创建实例,再重新赋值。

所以,深克隆还提供了另外一种方式:序列化与反序列化

关于序列化和反序列化,前面的文章已经讲解并实现了。这里就不再赘述了,但是这里强调一下ArrayList的序列化:

ArrayList底层是个element数组,由关键字transient修饰,我们都知道transient和private修饰的变量都不会被序列化,原因在于序列化的对对象的操作,而这两个变量是类的变量。同时还存在一个效率问题。

如果ArrayList扩容后仅仅使用了一个位置,其余位置都将会是null,这个时候对数组序列化意味着将会对大量的null进行序列化,这是一种很大的浪费。所以,尽管ArrayList底层实现了Serializable接口,但是仍然不能直接序列化,而是采用自定义序列化,具体方式可以参考前面的序列化和反序列化。

这里再多啰嗦几句,序列化是如何实现的?

为什么有了Serializeable就能序列化?其实每次需要被序列化时都会检查是否实现了这个关键字,如果没有就会抛出相应的异常!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值