Effective Java英文第三版讀書筆記(1) -- 科學(xué)地創(chuàng)建和銷毀對象

寫在前面

《Effective Java》原書國內(nèi)的翻譯只出版到第二版采桃,書籍的編寫日期距今已有十年之久。這期間昼弟,Java已經(jīng)更新?lián)Q代好幾次啤它,有些實踐經(jīng)驗已經(jīng)不再適用。去年底,作者結(jié)合Java7变骡、8离赫、9的最新特性,編著了第三版(參考https://blog.csdn.net/u014717036/article/details/80588806)塌碌。當前只有英文版本渊胸,可以在互聯(lián)網(wǎng)搜索到PDF原書。本讀書筆記都是基于原書的理解台妆。


以下是正文部分

如何科學(xué)地創(chuàng)建和銷毀對象(Creating and Destroying Objects)

實踐1 拋棄構(gòu)造函數(shù)翎猛,使用靜態(tài)工廠方法

什么是靜態(tài)工廠方法(static factory method)

簡單講,它就是一個返回當前對象實例的靜態(tài)方法接剩。示例如下:

public static Boolean valueOf(boolean b) { 
  return b ? Boolean.TRUE : Boolean.FALSE;
}

1.1 優(yōu)點

  1. 構(gòu)造函數(shù)都以類名命名切厘,區(qū)分度不高,而靜態(tài)工廠方法可以個性化搂漠,對用戶更加友好迂卢。
  2. 靜態(tài)工廠方法不是必須重新創(chuàng)建一個對象,例如上面 Boolean 的代碼中桐汤,返回的是早前已經(jīng)創(chuàng)建好的對象而克。這類似于設(shè)計模式中的享元模式(Flyweight pattern),典型的怔毛,相同內(nèi)容的String以及Enum就用了該模式员萍。
  3. 靜態(tài)工廠方法可以返回子類。Java8中拣度,取消了接口不能包含static方法的限制碎绎,因此在接口上實現(xiàn)這種靜態(tài)工廠方法類,簡化了文檔抗果,用戶也只需關(guān)注主類筋帖。
  4. 靜態(tài)工廠方法可以根據(jù)參數(shù)而返回不同的內(nèi)容。如下示例冤馏,根據(jù)參數(shù)返回不同的類日麸,屏蔽了一些內(nèi)部細節(jié)。用戶只需要知道返回的是EnumSet或其子類即可逮光,哪怕以后EnumSet進一步細分代箭,代碼也幾乎不需要重構(gòu)。
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
    Enum<?>[] universe = getUniverse(elementType);
    if (universe == null)
        throw new ClassCastException(elementType + " not an enum");
    if (universe.length <= 64)
        return new RegularEnumSet<>(elementType, universe);
    else
        return new JumboEnumSet<>(elementType, universe);
}
  1. 靜態(tài)工廠方法返回的對象類型涕刚,甚至可以在當前位置不存在嗡综。 這在SPI中用處較多。例如JDBC服務(wù)里面杜漠,java.sql.Driver接口是對外公開的一個加載驅(qū)動接口极景,但Jdk中并沒有相關(guān)實現(xiàn)察净,實際是由各sql廠商拿到接口后做的實現(xiàn)。

1.2 不足

  1. 沒有public或者protected構(gòu)造函數(shù)的類是無法被繼承的戴陡。
  2. 在接口中塞绿,靜態(tài)工廠方法不如構(gòu)造函數(shù)顯眼,使用者難以發(fā)現(xiàn)恤批。

1.3 最佳實踐

Date d = Date.from(instant);
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
StackWalker luke = StackWalker.getInstance(options);
Object newArray = Array.newInstance(classObject, arrayLen);
FileStore fs = Files.getFileStore(path);
BufferedReader br = Files.newBufferedReader(path);
List<Complaint> litany = Collections.list(legacyLitany);

實踐2 當構(gòu)造函數(shù)包含過多參數(shù)時,使用builder

在實際的業(yè)務(wù)開發(fā)中裹赴,某些類可能包含豐富多樣的屬性喜庞。例如一個網(wǎng)站用戶可能包含用戶名、密碼棋返、昵稱延都、頭像、手機睛竣、證件晰房、郵箱、ID射沟、公司名等等信息殊者,有些是必選參數(shù),有些是可選參數(shù)验夯。當前端送來一個用戶注冊請求時猖吴,則需要創(chuàng)建一個用戶對象。這樣挥转,可能根據(jù)參數(shù)和用戶類型海蔽,需要N個復(fù)雜的構(gòu)造函數(shù)。下面列出幾種解決方案:

2.1 Telescoping Constructor 模式

難讀绑谣、難用党窜。

public class Account {
  private final String name;
  private final String password;
  private final String phone;
  private final String email;

  public Account(String name, String password) {
    this(name, password, null);
  }

  public Account(String name, String password, String phone) {
    this(name, password, phone, null);
  }

  public Account(String name, String password, String phone, String email) {
    this.name = name;
    this.password = password;
    this.phone = phone;
    this.email = email;
  }
}

2.2 JavaBean 模式

把對象初始化拆分成了幾條語句,代碼層面上更加清晰借宵,閱讀順暢幌衣。但是相應(yīng)的缺點是這種操作是非原子性的,在并發(fā)編程中暇务,需要專門為此做保護泼掠。

public class Account {
  private String name;
  private String password;
  private String phone;
  private String email;

  public Account() {}

  public String getName() { return name; }
  public String getPassword() { return password; }
  public String getPhone() { return phone; }
  public String getEmail() { return email; }
  public void setName(String name) { this.name = name; }
  public void setPassword(String password) {  this.password = password; }
  public void setPhone(String phone) {  this.phone = phone; }
  public void setEmail(String email) {  this.email = email;
  }

2.3 Builder 模式

結(jié)合了前兩種方式的優(yōu)點,同時安全性和可繼承性得以保證垦细。但是這種方式也有缺陷择镇。首先,創(chuàng)建真正的對象前需要創(chuàng)建Builder對象括改,增大了系統(tǒng)開銷腻豌。在參數(shù)量少時,不宜過度使用該模式。

public class Account {
  private String name;
  private String password;
  private String phone;
  private String email;

  private Account(Builder builder) {
    this.name = builder.name;
    this.password = builder.password;
    this.phone = builder.phone;
    this.email = builder.email;
  }

  public static class Builder {
    private String name;
    private String password;
    private String phone = null;
    private String email = null;

    public Builder(String val1, String val2) {
      name = val1;
      password = val2;
    }

    public Builder phone(String val) {
      phone = val;
      return this;
    }

    public Builder email(String val) {
      email = val;
      return this;
    }

    public Account build() {
      return new Account(this);
    }
  }
}
//調(diào)用方式
Account account = new Account.Builder("Amy", "123456").phone("15199998888").email("a@163.com").build();

實踐3 使用私有構(gòu)造函數(shù)或枚舉類型來強制單例

無狀態(tài)的對象通常采用單例模式吝梅。通常虱疏,有兩種方式來實現(xiàn)單例。這兩種方式都是通過私有構(gòu)造函數(shù)+公有的靜態(tài)實例成員實現(xiàn)的苏携。

3.1 單例實現(xiàn)A

沒有公有構(gòu)造函數(shù)確保了只有 INSTANCE 在初始化時調(diào)用私有構(gòu)造函數(shù)一次做瞪,之后,不能再創(chuàng)建該對象的任何實例右冻。該方式的一個缺點是可能遭受反射攻擊 装蓬,參考:AccessibleObject.setAccessible

// Singleton with public final field
public class Elvis { 
  public static final Elvis INSTANCE = new Elvis();
  private Elvis() { ... }
  public void leaveTheBuilding() { ... } 
}

3.2 單例實現(xiàn)B

使用了靜態(tài)工廠方法纱扭,通過getInstance去獲取實例牍帚。相對來說,該方式更加明晰乳蛾。并且暗赶,如果以后需要改造為非單例,對于用戶代碼沒有影響肃叶。

// Singleton with public final field
public class Elvis { 
  private static final Elvis INSTANCE = new Elvis();
  private Elvis() { ... }
  public static Elvis getInstance() { return INSTANCE; }
  public void leaveTheBuilding() { ... } 
}

3.3 Enum實現(xiàn)單例

雖然看起來不太自然蹂随,但常常是實現(xiàn)單例的最佳方式。

// Enum singleton - the preferred approach 
public enum Elvis { 
  INSTANCE;
  public void leaveTheBuilding() { ... } 
}

實踐4 使用私有構(gòu)造函數(shù)來限制實例化

有時被环,我們編寫的對象只包含一組靜態(tài)的方法和變量糙及,這樣的對象是無需實例化的。但是在Java中筛欢,編譯器始終會采用默認構(gòu)造函數(shù)的策略浸锨。為了避免這種情況有以下2種方法:

4.1 抽象類

引入abstract關(guān)鍵字,使得類類型為抽象類是無法實例化的版姑。但是這種方式有個缺點是如果有其他類繼承該抽象類柱搜,則繼承類是可以實例化的。并且抽象類容易迷惑用戶剥险,用戶會認為需要繼承這個類聪蘸,而不是直接使用。

4.2 添加私有構(gòu)造函數(shù)

編譯器只在沒有顯式構(gòu)造函數(shù)時為類添加默認構(gòu)造函數(shù)。只要我們在類中顯式添加一個私有構(gòu)造函數(shù),則該類就沒法實例化了砂竖。示例如下,AssertionError并不是必須的娜遵,它只是確保沒有在類內(nèi)部誤調(diào)用。
該方式也有缺點:無法繼承壤短。由于子類的構(gòu)造函數(shù)總會(隱式或顯式地)調(diào)用父類的構(gòu)造函數(shù)设拟,當父類構(gòu)造函數(shù)為private時慨仿,將無法完成該動作。

// Noninstantiable utility class
public class UtilityClass { 
  // Suppress default constructor for noninstantiability 
  private UtilityClass() { throw new AssertionError(); } 
  ... // Remainder omitted 
}

實踐5 使用依賴注入代替硬編碼資源

類與類之間通常都存在依賴關(guān)系纳胧。下面是兩種如何添加這種依賴關(guān)系的反面示例镰吆。這兩種方式下,對于多線程跑慕,多實例及參數(shù)化資源都沒法很好支持万皿。

// Inappropriate use of static utility - inflexible & untestable! 
public class SpellChecker { 
  private static final Lexicon dictionary = ...;
  private SpellChecker() {} // Noninstantiable
  public static boolean isValid(String word) { ... } 
  public static List<String> suggestions(String typo) { ... } 
}

// Inappropriate use of singleton - inflexible & untestable! 
public class SpellChecker { 
  private final Lexicon dictionary = ...;
  private SpellChecker(...) {} 
  public static INSTANCE = new SpellChecker(...);
  public boolean isValid(String word) { ... } 
  public List<String> suggestions(String typo) { ... }
 }

一種較好的解決方案是,在類的構(gòu)造函數(shù)中傳入相關(guān)資源核行。這就是依賴注入:在對象創(chuàng)建時注入相寇。該方式在靜態(tài)工廠方法,Builder模式同樣適用钮科。當工程過大時,某個類可能依賴成百上千資源婆赠,這時就需要注入框架來幫忙了绵脯,例如Dagger, Guice, Spring等。

// Dependency injection provides flexibility and testability 
public class SpellChecker { 
  private final Lexicon dictionary;
  public SpellChecker(Lexicon dictionary) { 
    this.dictionary = Objects.requireNonNull(dictionary); 
  }
  public boolean isValid(String word) { ... } 
  public List<String> suggestions(String typo) { ... } 
}

實踐6 不要創(chuàng)建不必要的實例

對于不可修改的對象休里,采用共享模式而不是每次新建蛆挫。有助于提升程序的性能。

6.1 示例1

String對象的新建妙黍,此處字符串值是固定不變的悴侵。

// 每次調(diào)用都將創(chuàng)建一個新的對象,浪費
String s = new String("bikini");
// 享元模式
String s = "bikini";

6.2 示例2

一個正則匹配模式的例子拭嫁。

static boolean isRomanNumeral(String s) { 
  return s.matches("^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$"); 
}

此處正則表達式^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$是固定不變的可免,但上述實現(xiàn)中,每次都會用它新建一個Pattern對象做粤,因此可以把 Pattern 抽取出來浇借。原書作者實測性能提升6倍多。

On my machine, the original versiontakes 1.1 μs on an 8-character input string, while the improved version takes 0.17 μs, which is 6.5 times faster.

public class RomanNumerals {
  private static final Pattern ROMAN = Pattern.compile( "^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
  static boolean isRomanNumeral(String s) { 
    return ROMAN.matcher(s).matches(); 
  }
}

此處有個可能的爭議是怕品,如果isRomanNumeral方法從未被調(diào)用到妇垢,那么ROMAN的初始化是浪費的。原書作者認為:雖然可以通過懶加載的方式來進一步避免該問題肉康,但是增加了代碼的復(fù)雜性闯估,且性能實際提升價值不大。

6.3 基礎(chǔ)類型的使用

對于基礎(chǔ)類型吼和,應(yīng)盡量使用 int, long 而不是 Integer, Long涨薪。后者可能觸發(fā)不必要的大量對象創(chuàng)建。

private static long sum() {
  // 創(chuàng)建大量對象
  Long sum = 0L; 
  // 創(chuàng)建1個對象
  long sum = 0L;
  for (long i = 0; i <= Integer.MAX_VALUE; i++) sum += i;
  return sum; 
}

實踐7 解決過時引用問題

Java的自動垃圾回收機制纹安,使得程序員可能產(chǎn)生幻覺:不需要進行內(nèi)存管理尤辱。然而事實并非如此砂豌。如下就是一個內(nèi)存管理不當?shù)氖纠谶@個場景下光督,隨著棧的增長阳距,elements 可能擴張到很大,但是元素pop()size減小结借,但是elements 并未聯(lián)動減小筐摘,那些沒有被垃圾回收掉的比size標號大的對象成為了過時引用(obsolete reference,意思是再也不會用到的引用)船老。

// Can you spot the "memory leak"?
public class Stack {
  private static final int DEFAULT_INITIAL_CAPACITY = 16;
  private Object[] elements;
  private int size = 0;

  public Stack() {
    elements = new Object[DEFAULT_INITIAL_CAPACITY];
  }

  public void push(Object e) {
    ensureCapacity();
    elements[size++] = e;
  }

  public Object pop() {
    if (size == 0) throw new EmptyStackException();
    return elements[--size];
  }
  /**
   * * Ensure space for at least one more element, roughly * doubling the capacity each time the
   * array needs to grow.
   */
  private void ensureCapacity() {
    if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1);
  }
}

7.1 解決方案

相對來說咖熟,具有垃圾回收機制的編程語言的內(nèi)存管理問題潛伏的更深,不易察覺柳畔,最終影響程序的性能馍管。修復(fù)示例程序中這種類型的問題很簡單:將引用設(shè)置為null。這樣不僅使得垃圾能夠盡快回收掉薪韩,并且使得隨后的引用變得更安全确沸,錯誤引用將觸發(fā)NullPointerException

public Object pop() {
  if (size == 0) throw new EmptyStackException();
  Object result = elements[--size];
  elements[size] = null; // Eliminate obsolete reference
  return result;
}

需要注意的是俘陷,不要過度使用這個方法罗捎,它會使編程變得復(fù)雜繁瑣。通常只有在程序員自行管理內(nèi)存的時候拉盾,才需要這個手段桨菜。對于其他情況,在Java中捉偏,對象的生命周期通常是在一定范圍內(nèi)倒得,例如 {}中,跳出范圍告私,則自動銷毀屎暇。因此,將對象定義在最小化使用范圍中是一種較好的編程習(xí)慣驻粟。

7.2 高發(fā)場景:

  • 緩存根悼,原書作者建議使用WeakHashMap來解決。
  • 監(jiān)聽與回調(diào)蜀撑,如果向API注冊了回調(diào)函數(shù)而忘記去注冊會有問題挤巡。同樣,原書作者建議使用WeakHashMap來解決酷麦。

實踐8 避免使用finalizercleaner

(實際開發(fā)中未使用矿卑,指導(dǎo)意義不大,暫未閱讀)

實踐9 try-with-resources優(yōu)于try-finally

對于資源沃饶,在Java程序中使用完之后需要進行關(guān)閉動作母廷。例如文件流轻黑、socket連接等等。如果我們忽視了琴昆,則可能給程序帶來不良后果氓鄙,盡管這些資源有finalizer來收尾。

9.1 try-finally方式

一種常見的方式是try-finally來確保資源能夠在正常/異常情況下也正確關(guān)閉业舍。但是當一段try-finally要使用多個資源時抖拦,嵌套后的代碼看起來會非常復(fù)雜。另外舷暮,該方式還有一個問題時态罪,如果IO設(shè)備故障,那么 read 以及 close操作都會拋出異常下面,但是因為close在后复颈,所以之前的異常被沖掉,調(diào)試時只看到最后一個異常沥割,增大調(diào)試難度券膀。

// try-finally is ugly when used with more than one resource!
static void copy(String src, String dst) throws IOException {
  InputStream in = new FileInputStream(src);
  try {
    OutputStream out = new FileOutputStream(dst);
    try {
      byte[] buf = new byte[BUFFER_SIZE];
      int n;
      while ((n = in.read(buf)) >= 0) out.write(buf, 0, n);
    } finally {
      out.close();
    }
  } finally {
    in.close();
  }
}

9.1 try-with-resources方式

Java7引入的try-with-resources,規(guī)定了資源對象必須實現(xiàn)AutoCloseable接口驯遇,這個接口中僅包含了一個方法:void close()

public class MyFile implements AutoCloseable{
  @Override
  public void close() throws Exception {...}
}

try-with-resources的調(diào)用語法如下蓄髓。在這種語法下叉庐,當 read 以及 close操作都拋出異常時,close的異常被抑制掉会喝,以確保程序員看到想要的那個異常陡叠。并且,整個代碼也更加簡潔肢执。

static String firstLineOfFile(String path) throws IOException {
  try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
  }
}

static void copy(String src, String dst) throws IOException {
  try (InputStream in = new FileInputStream(src);
      OutputStream out = new FileOutputStream(dst)) {
    byte[] buf = new byte[BUFFER_SIZE];
    int n;
    while ((n = in.read(buf)) >= 0) out.write(buf, 0, n);
  }
}

(完)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末枉阵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子预茄,更是在濱河造成了極大的恐慌兴溜,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耻陕,死亡現(xiàn)場離奇詭異拙徽,居然都是意外死亡,警方通過查閱死者的電腦和手機诗宣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門膘怕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人召庞,你說我怎么就攤上這事岛心±雌疲” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵忘古,是天一觀的道長徘禁。 經(jīng)常有香客問我,道長存皂,這世上最難降的妖魔是什么晌坤? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮旦袋,結(jié)果婚禮上骤菠,老公的妹妹穿的比我還像新娘。我一直安慰自己疤孕,他們只是感情好商乎,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著祭阀,像睡著了一般鹉戚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上专控,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天抹凳,我揣著相機與錄音,去河邊找鬼伦腐。 笑死赢底,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的柏蘑。 我是一名探鬼主播幸冻,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼咳焚!你這毒婦竟也來了洽损?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤革半,失蹤者是張志新(化名)和其女友劉穎碑定,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體又官,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡不傅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了赏胚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片访娶。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖觉阅,靈堂內(nèi)的尸體忽然破棺而出崖疤,到底是詐尸還是另有隱情秘车,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布劫哼,位于F島的核電站叮趴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏权烧。R本人自食惡果不足惜眯亦,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望般码。 院中可真熱鬧妻率,春花似錦、人聲如沸板祝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽券时。三九已至孤里,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間橘洞,已是汗流浹背捌袜。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留炸枣,地道東北人琢蛤。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像抛虏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子套才,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法迂猴,類相關(guān)的語法,內(nèi)部類的語法背伴,繼承相關(guān)的語法沸毁,異常的語法,線程的語...
    子非魚_t_閱讀 31,622評論 18 399
  • 第3章 基本概念 3.1 語法 3.2 關(guān)鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類型 5種簡單數(shù)據(jù)類型:Unde...
    RickCole閱讀 5,122評論 0 21
  • 【Day17】今日閱讀《西藏生死書》第11傻寂、12章 道別 道別這個詞是我們在日常的生活中會經(jīng)常應(yīng)用到的息尺,校園畢業(yè)走...
    微運動小王子閱讀 392評論 1 1
  • 多情 無情 皆因你
    長馬閱讀 114評論 0 2
  • 有的時候,瞬會摸天谷后頸疾掰。 這里是大貓最脆弱的地方搂誉,一旦咬中,他們便無法動彈静檬。 對于沒有鬃毛的天谷來說炭懊,更是并级。 天...
    我是要成為足球王的男人閱讀 2,016評論 0 0