使用序列化实现对象的拷贝

引言

       我们知道在Java中存在这个接口Cloneable,实现该接口的类都会具备被拷贝的能力,同时拷贝是在内存中进行,在性能方面比我们直接通过new生成对象来的快,特别是在大对象的生成上,使得性能的提升非常明显。然而我们知道拷贝分为深拷贝和浅拷贝之分,但是浅拷贝存在对象属性拷贝不彻底问题。

浅拷贝问题

package util.clone;

/**
 * 职员
 * @project Test
 * @date 2018年4月23日 下午2:34:52 
 * @author Huaxu-Charles
 */
public class Staff implements Cloneable{

	private String name;
	
	private Uniform uniform;

	public Staff(String name, Uniform uniform) {
		this.name = name;
		this.uniform = uniform;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Uniform getUniform() {
		return uniform;
	}

	public void setUniform(Uniform uniform) {
		this.uniform = uniform;
	}
	
	protected Staff clone() {
		Staff staff = null;
		try {
			staff = (Staff) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return staff;
	}

	@Override
	public String toString() {
		return "Staff [name=" + name + ", uniform=" + uniform.getType() + "]";
	}
}


package util.clone;

/**
 * 制服
 * @project Test
 * @date 2018年4月23日 下午2:33:14 
 * @author Huaxu-Charles
 */
public class Uniform {

	private String type;

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}
	
	public Uniform(String type) {
		this.type = type;
	}

	@Override
	public String toString() {
		return "Uniform [type=" + type + "]";
	}
}

      上面代码展示的就是,公司新员工入职这个时候需要订做新的制服,因为每个人身材大小不一样,但是据大数据程序猿分析,穿xxl的占据80%,所以就优先声明制服的默认大小为xxl。这个时候看输出结果:

public class Client {
	
	public static void main(String[] args) {
		Staff staff1 = new Staff("张三", new Uniform("XXL"));
		
		Staff staff2 = staff1.clone();
		staff2.setName("李四");
		
		Staff staff3 = staff1.clone();
		staff3.setName("王五");
		
		System.out.println(staff1);
		System.out.println(staff2);
		System.out.println(staff3);
	}
}
输出结果
Staff [name=张三, uniform=XXL]
Staff [name=李四, uniform=XXL]
Staff [name=王五, uniform=XXL]

   现在看起来是没问题,大概率通用一套。如果王五的需要的尺码是xxxl,这个时候我们只需要修改一下代码

public class Client {
	
	public static void main(String[] args) {
		Staff staff1 = new Staff("张三", new Uniform("XXL"));
		
		Staff staff2 = staff1.clone();
		staff2.setName("李四");
		
		Staff staff3 = staff1.clone();
		staff3.setName("王五");
		// 王五需要XXXL的
		staff3.getUniform().setType("XXXL");
		
		System.out.println(staff1);
		System.out.println(staff2);
		System.out.println(staff3);
	}
}
Staff [name=张三, uniform=XXXL]
Staff [name=李四, uniform=XXXL]
Staff [name=王五, uniform=XXXL]

       这个时候结果就不是那么满意,你王五需要xxxl的,但是张三李四需要的是xxl的啊!其实出现问题的关键在于clone()方法上我们知道该clone()方法是使用object的clone()方法,但是该方法存在缺陷,它并不会将对象的所有属性全部拷贝过来,而是有选择性的拷贝,基本规则整理如下:

基本类型 如果变量是基本数据类型,则拷贝其值,如int,float等
对象 如果变量是一个实例对象,则拷贝其地址引用,也就是说此时新对象与原来对象是公用该实例变量
String 字符串

若变量为String字符串,则拷贝其地址引用,但是在修改的时候,它会从字符串池中重新生成一个新的字符串,原来的保持不变

        经过介绍后我好像就知道怎么回事,那只需要该一点点代码,也就是这样!
protected Staff clone() {
		Staff staff = null;
		try {
			staff = (Staff) super.clone();
			// 重新建一个对象
			staff.setUniform(new Uniform(staff.getUniform().getType()));
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return staff;
	}

        所以:clone不利于直接使用,它只是java提供的一个简单的方法。

        这样问题虽然解决,但是如果工程很大,这样就会需要new大量的对象,根本还是深拷贝。而且每一个类都需要写一个这样的方法,不利于后期维护。

利用序列化实现对象的拷贝

输出结果
/**
 * 克隆工具类
 * @project Test
 * @date 2018年4月23日 下午3:00:16 
 * @author Huaxu-Charles
 */
public class CloneUtils {

	@SuppressWarnings("unchecked")
	public static<T extends Serializable> T clone(T obj){
		T cloneObj = null;
		try {
			// 写入字节流
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			ObjectOutputStream obs =new ObjectOutputStream(out);
			obs.writeObject(obj);
			obs.close();
			
			// 分配内存,写入原始对象,生成新对象
			ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
			ObjectInputStream ois = new ObjectInputStream(in);
			// 返回生成的对象
			cloneObj = (T) ois.readObject();
			ois.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		return cloneObj;
	}
}

      值得注意的是序列化的对象必须实现Serializable接口,至此这个问题完美解决,需要用此工具类的方法只需要实现Serializable接口。

输出结果
Staff [name=张三, uniform=XXL]
Staff [name=李四, uniform=XXL]
Staff [name=王五, uniform=XXXL]


猜你喜欢

转载自blog.csdn.net/weixin_39923425/article/details/80050281