最近由於要開發中間件。可是寫了好幾個版本,都感受不怎麼好。比起一些大廠的實現,感受在代碼架構的設計,java語言的基礎知識上面仍是掌握的不夠牢靠。因而打算重讀一些比較經典的書籍。《Effective Java》就是其中一本。java
主要講解了對象的建立和銷燬。多用工廠和建造者模式,儘可能避免冗長的構造方法。其中對於單例的建立,提到了有關在單例序列化/反序列化的時候兩個個陷阱:架構
public class User implements Serializable {
private static final long serialVersionUID = -672817526808710807L;
private static final User user = new User();
private String age;
private String name;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private User() {
this.age = "3";
this.name = "java";
}
public static User getInstance() {
return user;
}
private Object readResolve() {
return user;
}
}
public class SerializableDemo {
@SuppressWarnings("resource")
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("/Users/tiny/Desktop/test.txt"));
out.writeObject(User.getInstance());
FileInputStream fis = new FileInputStream(new File("/Users/xujianxing/Desktop/test.txt"));
ObjectInputStream ois = new ObjectInputStream(fis);
User u = (User) ois.readObject();
System.out.println(u.getName());
if(User.getInstance()==u){
System.out.println("equals");
}
else{
System.out.println("not equals");
}
}
}複製代碼
輸出not equals。
將User對象,稍稍改造:優化
package demo;
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = -672817526808710807L;
private static final User user = new User();
private String age;
private String name;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private User() {
this.age = "3";
this.name = "java";
}
public static User getInstance() {
return user;
}
private Object readResolve() {
return user;
}
}
package demo;
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = -672817526808710807L;
private static final User user = new User();
private String age;
private String name;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private User() {
this.age = "3";
this.name = "java";
}
public static User getInstance() {
return user;
}
private Object readResolve() {
return user;
}
}複製代碼
增長一個readResolve
方法。ui
AccessibleObject.setAccessible
強制調用私有構造器。就像這樣:public class SerializableDemo {
@SuppressWarnings("resource")
public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException, IllegalArgumentException {
Class<?> clazz = User.getInstance().getClass();
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor.getName());
constructor.setAccessible(true); // AccessibleObject
System.out.println(constructor.newInstance(null));
System.out.println(User.getInstance());
}
}
}複製代碼
若是要抵禦這種攻擊。能夠修改構造器,但在構造器中拋出異常不是一個好的實踐。而且會破壞final域。this
在某些狀況下,能夠利用建造者模式來大幅優化業務代碼,使得業務代碼看起來也十分優雅,而且具備很強的擴展性。好比有一個攝影師系統。攝影師的積分規則以下:spa
若是直接在業務系統裏面計算代碼,一但業務改變,那麼業務中的代碼也會改變,這樣就違背了「對擴展開放,對修改關閉"的原則。使用建造者模式能夠減小對業務系統的入侵。設計
public class PointRule {
//積分變更記錄
private Multimap<String, String> myMultimap =null;
private int changeTotal;
public int getChangeTotal() {
return changeTotal;
}
public void setChangeTotal(int changeTotal) {
this.changeTotal = changeTotal;
}
private PointRule(int changeTotal,Multimap<String, String> record) {
this.changeTotal = changeTotal;
this.record=record;
}
private static final int PLUS_NEW_ORDER = 10;
private static final int PLUS_ADD_SHEET = 1;
private static final int PLUS_COMMENT_FIVE = 5;
private static final int PLUS_COMMENT_FOUR = 10;
private static final int PLUS_UPLOAD_CURRENT_DAY = 10;
private static final int PLUS_UPLOAD_SECOND_DAY = 5;
private static final int PLUS_BID_FAIL = 2;
private static final int PLUS_UPLOAD_WORK = 1;
private static final int REDUCE_UPLOAD_DELAY = -5;
public static class Builder {
private int total = 0;
Multimap<String, String> myMultimap = ArrayListMultimap.create();
public static Builder getInstance() {
return new Builder();
}
public Builder plusNewOrder(String orderId) {
total += PLUS_NEW_ORDER;
this.record.put("orderId", "plusNewOrder");
return this;
}
public Builder plusAddSheet(String orderId) {
total += PLUS_ADD_SHEET;
this.record.put("orderId", "plusAddSheet");
return this;
}
public Builder plusCommentFive(String orderId) {
total += PLUS_COMMENT_FIVE;
this.record.put("orderId", "plusCommentFive");
return this;
}
public Builder plusCommentFour() {
total += PLUS_COMMENT_FOUR;
this.record.put("orderId", "plusCommentFour");
return this;
}
public Builder plusUploadCurrentDay() {
total += PLUS_UPLOAD_CURRENT_DAY;
this.record.put("orderId", "plusUploadCurrentDay");
return this;
}
public Builder plusUploadSecondDay() {
total += PLUS_UPLOAD_SECOND_DAY;
this.record.put("orderId", "plusUploadSecondDay");
return this;
}
public Builder plusBidFail(String orderId) {
total += PLUS_BID_FAIL;
this.record.put("orderId", "plusBidFail");
return this;
}
public Builder plusUploadWork(String orderId) {
total += PLUS_UPLOAD_WORK;
this.record.put("orderId", "plusUploadWork");
return this;
}
public Builder reduceUploadDelay(String orderId) {
total += REDUCE_UPLOAD_DELAY;
this.record.put("orderId", "reduceUploadDelay");
return this;
}
public PointRule build() {
PointRule pointRule = new PointRule(this.total,this.record);
return pointRule;
}
}
}複製代碼
假設有一個Service:code
public class PhotographerService {
public void userConfirmSheetOrder(){
//用戶收片的邏輯。
PointRule.Builder buidler=PointRule.Builde.getInstance();
if(當天收片){
buidler.plusCurrentDay();
}
if(修了10張片){
(for int i=0; i<10;i++){
buidler.plusAddSheet();
}
//其餘的業務邏輯
最後獲得此次要改變的分數:
PointRule rule=buidler.builder();
//若是要將積分變更的記錄入庫。那麼能夠循環PointRule 的map對象。
}複製代碼
對於積分變更記錄,可使用迭代器模式。當讓這個例子只是一個舉例說明。比起直接在service裏面算積分,是否是會好不少呢?中間件