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); } }