1、原型模式應用場景
當遇到大量耗費勞動力的 get,set賦值場景時,如下:
public class SetGetParam {
public void setParam(UserDto userDto) {
User user = new User();
user.setAge(userDto.getAge());
//...
userDao.addUser(user);
}
}
原型模式(Prototype pattern)是指原型實例指定創建對象的種類,並且通過復制這些原型創建新的對象。原型模式主要適用於以下:
(1)類初始化消耗資源較多;
(2)使用new 生成一個對象需要非常繁瑣的過程(數據准備訪問權限等);
(3)構造函數比較復雜;
(4)在循環體中產生大量對象;
在spring中用到的原型模式有:scope="prototype" ,還有常用的JSON.parseObject()也是一種原型模式
2、淺克隆
創建具體需要克隆的類:
@Data
public class User {
private String name;
private Integer age;
private List<String> hobbies;
public UserDto clone() {
UserDto dto = new UserDto();
dto.setAge(this.age);
dto.setName(this.name);
dto.setHobbies(this.hobbies);
return dto;
}
}
創建Client:
public class Client {
private User user;
public Client(User user) {
this.user = user;
}
public UserDto startClone(User user) {
return user.clone();
}
}
測試克隆,對比復制過來的值是否有自己的地址,還是用的原來的地址
public class PrototypeTest {
public static void main(String[] args) {
//創建具體需要克隆的對象
User user = new User();
user.setName("皮膚黝黑的小白");
user.setHobbies(new ArrayList<>());
System.out.println(user);
//創建Client對象,准備開始克隆
Client client = new Client(user);
UserDto dto = client.startClone(user);
System.out.println(dto);
System.out.println(user.getHobbies() == dto.getHobbies());
System.out.println(user.getName() == dto.getName());
}
}
結果:
User(name=皮膚黝黑的小白, age=null, hobbies=[]) UserDto(name=皮膚黝黑的小白, age=null, hobbies=[]) true true
從測試結果可以看出:hobbies和name的內存地址是相同的,這說明我們並沒有重新創建對象,這就是淺克隆。
3、深克隆
采用序列化反序列化克隆,實現深克隆,
@Data
public class UserDeepClone implements Cloneable {
private String name;
private Integer age;
private List<String> hobbies;
@Override
protected Object clone() throws CloneNotSupportedException {
return this.deepClone();
}
public UserDto deepClone() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
UserDto dto = (UserDto) ois.readObject();
return dto;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public class DeepCloneTest {
public static void main(String[] args) {
DeepCloneTest deepCloneTest = new DeepCloneTest();
UserDeepClone user = new UserDeepClone();
user.setName("皮膚黝黑的小白");
user.setHobbies(new ArrayList<>());
System.out.println(user);
UserDto dto = null;
try {
dto = (UserDto) deepCloneTest.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println(dto);
System.out.println(user.getName() == dto.getName());
System.out.println(user.getHobbies() == dto.getHobbies());
}
}
4、克隆破壞單例
深克隆會破壞單例,其實防御方式很簡單,單例類不要實現Cloneable接口即可。
5、ArrayList中clone()方法的源碼
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}

