什么是拷贝
什么是拷贝,通俗来讲就是我们常说的复制粘贴,也就是大家用的最多的开发模式,CV开发模式,ctrl+C,ctrl+V开发模式,为什么大家都爱这种开发模式呢,因为开发模式效率极高,如果没有CV功能,哪怕有现成的代码,你也得一行一行抄写,花上的时间也是非常多的,而CV只需要几秒钟的时间,同样的功能,减少了上千倍的时间。
什么是分深拷贝与浅拷贝
浅拷贝
浅拷贝就是我们复制出来的对象与之前的对象是在内存地址对应的还是同一个内存地址,当原对象被更改后,拷贝的对象内容也随之更改。
如上图所示,当对象A浅拷贝出一个对象B后,A的list被修改后,不需要修改对象B,B中的list也会随之修改。实例代码如下
**Person类**
public class Person implements Cloneable{ // 需要实现 Cloneable接口 不然clone方法会报错
List<Integer> list;
public Person(List<Integer> list) {
this.list = list;
}
public List<Integer> getList() {
return list;
}
public void setList(List<Integer> list) {
this.list = list;
}
// clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试类
List<Integer> list =new ArrayList<>();
list.add(1);
list.add(2);
Person person =new Person(list);
Person clone = (Person)person.clone();
// 输出 person 与clone 的地址值比较
System.out.println(person.getList()==clone.getList());
// 先输出clone的list值,再修改person的list值
System.out.println("cloneList= " + clone.getList());
list.add(3);
person.setList(list);
// 修改后的值
System.out.println("cloneList= " + clone.getList());
System.out.println("person = " + person.getList());
结果
true //person 与clone 的地址值比较
cloneList= [1, 2] // person修改前clone list的值
cloneList= [1, 2, 3] // 修改后clone list的值
person = [1, 2, 3] // person的值
深拷贝
严格来说深拷贝才是真正意义上的复制粘贴,我们在网页上复制的东西在本地修改后并不会影响到原有的,同理我们在原有的基础上修改也不会影响到复制后的,原体与拷贝体除了刚开始的值相同外,是两个独立的个体。
深拷贝示意图
如上图所示,当对象A深拷贝出一个对象B后,A的list被修改后,对象B中的list不会随之被修改。要深拷贝一个对象,JDK中常用的方法是序列化为数据流,此方法的前提是对象以及对象中包含的子对象都要继承Serializable接口。实例代码如下
Person类
public class Person implements Cloneable , Serializable { //实现序列化serializable接口
List<Integer> list;
public Person(List<Integer> list) {
this.list = list;
}
public List<Integer> getList() {
return list;
}
public void setList(List<Integer> list) {
this.list = list;
}
@Override
protected Object clone() throws CloneNotSupportedException {
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Person clone = (Person)ois.readObject();
return clone;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
测试类
List<Integer> list =new ArrayList<>();
list.add(1);
list.add(2);
Person person =new Person(list);
Person clone = (Person)person.clone();
// 输出 person 与clone 的地址值比较
System.out.println(person.getList()==clone.getList());
// 先输出clone的list值,再修改person的list值
System.out.println("cloneList= " + clone.getList());
list.add(3);
person.setList(list);
// 修改后的值
System.out.println("cloneList= " + clone.getList());
System.out.println("person = " + person.getList());
结果
false//person 与clone 的地址值比较
cloneList= [1, 2] // person修改前clone list的值
cloneList= [1, 2] // 修改后clone list的值
person = [1, 2, 3] // person的值
原型模式
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式,是GOF的设计模式之一。
原型模式使用场景
原型模式的目的是 降低实例对象个数 , 减少构造函数的调用次数 ;
- 类初始化所需要的资源过多 : 类初始化时如果消耗过多的资源占用大量内存 , 为了节省开销 ;
- 初始化繁琐耗时 : 类对象创建时经过大量的计算 , 或与本地资源 ( 数据库 , 文件 ) 频繁交互 , 每次创建消耗大量的 CPU 与 时间资源 ;
- 构造函数复杂 : 类中定义的构造函数复杂,例如有几十上百个成员属性 ;
- 实例对象数量庞大 : 如果在内存中循环创建了很多该实例对象 , 就可以使用原型模式复用不用的对象 , 用于创建新对象 ;
java中实现原型模式的步骤
1.原型对象实现Cloneable接口
2.原型对象及其成员对象都实现Serializable接口
3.重写clone()方法,采用序列化为数据流进行深拷贝。
Spring中原型模式的使用
在经典框架Spring中也有着原型模式的使用,scope=“prototype”,多实例对象获取的bean就是通过原型模式
// 判断是不是单例.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 判断是不是原型对象
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
// 创建原型之前进行回调
beforePrototypeCreation(beanName);
// 进入原型对象的创建
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}