Java習慣用法

來源:互聯網
上載者:User

標籤:

http://www.admin10000.com/document/6157.html
  • 實現:
    • equals()
    • hashCode()
    • compareTo()
    • clone()
  • 應用:
    • StringBuilder/StringBuffer
    • Random.nextInt(int)
    • Iterator.remove()
    • StringBuilder.reverse()
    • Thread/Runnable
    • try-finally
  • 輸入/輸出:
    • 從輸入資料流裡讀取位元組資料
    • 從輸入資料流裡讀取塊資料
    • 從檔案裡讀取文本
    • 向檔案裡寫文本
  • 預防性檢測:
    • 數值
    • 對象
    • 數組索引
    • 數組區間
  • 數組:
    • 填充元素
    • 複製一個範圍內的數組元素
    • 調整數組大小
  • 封裝
    • 個位元組封裝成一個int
    • 分解成4個位元組

  實現equals()
class Person {  String name;  int birthYear;  byte[] raw;  public boolean equals(Object obj) {    if (!obj instanceof Person)      return false;    Person other = (Person)obj;    return name.equals(other.name)        && birthYear == other.birthYear        && Arrays.equals(raw, other.raw);  }  public int hashCode() { ... }}
  • 參數必須是Object類型,不能是外圍類。
  • foo.equals(null) 必須返回false,不能拋NullPointerException。(注意,null instanceof 任意類 總是返回false,因此上面的代碼可以運行。)
  • 基本類型域(比如,int)的比較使用 == ,基本類型數組域的比較使用Arrays.equals()。
  • 覆蓋equals()時,記得要相應地覆蓋 hashCode(),與 equals() 保持一致。
  • 參考: java.lang.Object.equals(Object)。

  實現hashCode()
class Person {  String a;  Object b;  byte c;  int[] d;  public int hashCode() {    return a.hashCode() + b.hashCode() + c + Arrays.hashCode(d);  }  public boolean equals(Object o) { ... }}
  • 當x和y兩個對象具有x.equals(y) == true ,你必須要確保x.hashCode() == y.hashCode()。
  • 根據逆反命題,如果x.hashCode() != y.hashCode(),那麼x.equals(y) == false 必定成立。
  • 你不需要保證,當x.equals(y) == false時,x.hashCode() != y.hashCode()。但是,如果你可以儘可能地使它成立的話,這會提高雜湊表的效能。
  • hashCode()最簡單的合法實現就是簡單地return 0;雖然這個實現是正確的,但是這會導致HashMap這些資料結構運行得很慢。
  • 參考:java.lang.Object.hashCode()。

  實現compareTo()
class Person implements Comparable<Person> {  String firstName;  String lastName;  int birthdate;  // Compare by firstName, break ties by lastName, finally break ties by birthdate  public int compareTo(Person other) {    if (firstName.compareTo(other.firstName) != 0)      return firstName.compareTo(other.firstName);    else if (lastName.compareTo(other.lastName) != 0)      return lastName.compareTo(other.lastName);    else if (birthdate < other.birthdate)      return -1;    else if (birthdate > other.birthdate)      return 1;    else      return 0;  }}
  • 總是實現泛型版本 Comparable 而不是實現原始類型 Comparable 。因為這樣可以節省代碼量和減少不必要的麻煩。
  • 只關心返回結果的加號或減號(負/零/正),它們的大小不重要。
  • Comparator.compare()的實現與這個類似。
  • 參考:java.lang.Comparable。

  實現clone()
class Values implements Cloneable {  String abc;  double foo;  int[] bars;  Date hired;  public Values clone() {    try {      Values result = (Values)super.clone();      result.bars = result.bars.clone();      result.hired = result.hired.clone();      return result;    } catch (CloneNotSupportedException e) {  // Impossible      throw new AssertionError(e);    }  }}
  • 使用 super.clone() 讓Object類負責建立新的對象。
  • 基本類型域都已經被正確地複製了。同樣,我們不需要去複製String和BigInteger等不可變類型。
  • 手動對所有的非基本類型域(對象和數組)進行深度複製(deep copy)。
  • 實現了Cloneable的類,clone()方法永遠不要拋CloneNotSupportedException。因此,需要捕獲這個異常並忽略它,或者使用不受檢異常(unchecked exception)封裝它。
  • 不使用Object.clone()方法而是手動地實現clone()方法是可以的也是合法的。
  • 參考:java.lang.Object.clone()、java.lang.Cloneable()。

  使用StringBuilder或StringBuffer
// join(["a", "b", "c"]) -> "a and b and c"String join(List<String> strs) {  StringBuilder sb = new StringBuilder();  boolean first = true;  for (String s : strs) {    if (first) first = false;    else sb.append(" and ");    sb.append(s);  }  return sb.toString();}
  • 不要像這樣使用重複的字串串連:s += item ,因為它的時間效率是O(n^2)。
  • 使用StringBuilder或者StringBuffer時,可以使用append()方法添加文本和使用toString()方法去擷取串連起來的整個文本。
  • 優先使用StringBuilder,因為它更快。StringBuffer的所有方法都是同步的,而你通常不需要同步的方法。
  • 參考java.lang.StringBuilder、java.lang.StringBuffer。

  產生一個範圍內的隨機整數
Random rand = new Random();// Between 1 and 6, inclusiveint diceRoll() {  return rand.nextInt(6) + 1;}
  • 總是使用Java API方法去產生一個整數範圍內的隨機數。
  • 不要試圖去使用 Math.abs(rand.nextInt()) % n 這些不確定的用法,因為它的結果是有偏差的。此外,它的結果值有可能是負數,比如當rand.nextInt() == Integer.MIN_VALUE時就會如此。
  • 參考:java.util.Random.nextInt(int)。

  使用Iterator.remove()
void filter(List<String> list) {  for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {    String item = iter.next();    if (...)      iter.remove();  }}
  • remove()方法作用在next()方法最近返回的條目上。每個條目只能使用一次remove()方法。
  • 參考:java.util.Iterator.remove()。

  返轉字串
String reverse(String s) {  return new StringBuilder(s).reverse().toString();}
  • 這個方法可能應該加入Java標準庫。
  • 參考:java.lang.StringBuilder.reverse()。

  啟動一條線程

  下面的三個例子使用了不同的方式完成了同樣的事情。

  實現Runnnable的方式:

void startAThread0() {  new Thread(new MyRunnable()).start();}class MyRunnable implements Runnable {  public void run() {    ...  }}

  繼承Thread的方式:

void startAThread1() {  new MyThread().start();}class MyThread extends Thread {  public void run() {    ...  }}

  匿名繼承Thread的方式:

void startAThread2() {  new Thread() {    public void run() {      ...    }  }.start();}
  • 不要直接調用run()方法。總是調用Thread.start()方法,這個方法會建立一條新的線程並使建立的線程調用run()。
  • 參考:java.lang.Thread, java.lang.Runnable。

  使用try-finally

  I/O流例子:

void writeStuff() throws IOException {  OutputStream out = new FileOutputStream(...);  try {    out.write(...);  } finally {    out.close();  }}

  鎖例子:

void doWithLock(Lock lock) {  lock.acquire();  try {    ...  } finally {    lock.release();  }}
  • 如果try之前的語句運行失敗並且拋出異常,那麼finally語句塊就不會執行。但無論怎樣,在這個例子裡不用擔心資源的釋放。
  • 如果try語句塊裡面的語句拋出異常,那麼程式的運行就會跳到finally語句塊裡執行儘可能多的語句,然後跳出這個方法(除非這個方法還有另一個外圍的finally語句塊)。

  從輸入資料流裡讀取位元組資料
InputStream in = (...);try {  while (true) {    int b = in.read();    if (b == -1)      break;    (... process b ...)  }} finally {  in.close();}
  • read()方法要麼返回下一次從流裡讀取的位元組數(0到255,包括0和255),要麼在達到流的末端時返回-1。
  • 參考:java.io.InputStream.read()。

  從輸入資料流裡讀取塊資料
InputStream in = (...);try {  byte[] buf = new byte[100];  while (true) {    int n = in.read(buf);    if (n == -1)      break;    (... process buf with offset=0 and length=n ...)  }} finally {  in.close();}
  • 要記住的是,read()方法不一定會填滿整個buf,所以你必須在處理邏輯中考慮返回的長度。
  • 參考: java.io.InputStream.read(byte[])、java.io.InputStream.read(byte[], int, int)。

  從檔案裡讀取文本
BufferedReader in = new BufferedReader(    new InputStreamReader(new FileInputStream(...), "UTF-8"));try {  while (true) {    String line = in.readLine();    if (line == null)      break;    (... process line ...)  }} finally {  in.close();}
  • BufferedReader對象的建立顯得很冗長。這是因為Java把位元組和字元當成兩個不同的概念來看待(這與C語言不同)。
  • 你可以使用任何類型的InputStream來代替FileInputStream,比如socket。
  • 當達到流的末端時,BufferedReader.readLine()會返回null。
  • 要一次讀取一個字元,使用Reader.read()方法。
  • 你可以使用其他的字元編碼而不使用UTF-8,但最好不要這樣做。
  • 參考:java.io.BufferedReader、java.io.InputStreamReader。

  向檔案裡寫文本
PrintWriter out = new PrintWriter(    new OutputStreamWriter(new FileOutputStream(...), "UTF-8"));try {  out.print("Hello ");  out.print(42);  out.println(" world!");} finally {  out.close();}
  • Printwriter對象的建立顯得很冗長。這是因為Java把位元組和字元當成兩個不同的概念來看待(這與C語言不同)。
  • 就像System.out,你可以使用print()和println()列印多種類型的值。
  • 你可以使用其他的字元編碼而不使用UTF-8,但最好不要這樣做。
  • 參考:java.io.PrintWriter、java.io.OutputStreamWriter。

  預防性檢測(Defensive checking)數值
int factorial(int n) {  if (n < 0)    throw new IllegalArgumentException("Undefined");  else if (n >= 13)    throw new ArithmeticException("Result overflow");  else if (n == 0)    return 1;  else    return n * factorial(n - 1);}
  • 不要認為輸入的數值都是正數、足夠小的數等等。要顯式地檢測這些條件。
  • 一個設計良好的函數應該對所有可能性的輸入值都能夠正確地執行。要確保所有的情況都考慮到了並且不會產生錯誤的輸出(比如溢出)。

  預防性檢測對象
int findIndex(List<String> list, String target) {  if (list == null || target == null)    throw new NullPointerException();  ...}
  • 不要認為對象參數不會為空白(null)。要顯式地檢測這個條件。

  預防性檢測數組索引
void frob(byte[] b, int index) {  if (b == null)    throw new NullPointerException();  if (index < 0 || index >= b.length)    throw new IndexOutOfBoundsException();  ...}
  • 不要認為所以給的數組索引不會越界。要顯式地檢測它。

  預防性檢測數組區間
void frob(byte[] b, int off, int len) {  if (b == null)    throw new NullPointerException();  if (off < 0 || off > b.length    || len < 0 || b.length - off < len)    throw new IndexOutOfBoundsException();  ...}
  • 不要認為所給的數組區間(比如,從off開始,讀取len個元素)是不會越界。要顯式地檢測它。

  填充數組元素

  使用迴圈:

// Fill each element of array ‘a‘ with 123byte[] a = (...);for (int i = 0; i < a.length; i++)  a[i] = 123;

(優先)使用標準庫的方法:

Arrays.fill(a, (byte)123);
  • 參考:java.util.Arrays.fill(T[], T)。
  • 參考:java.util.Arrays.fill(T[], int, int, T)。

  複製一個範圍內的數組元素

  使用迴圈:

// Copy 8 elements from array ‘a‘ starting at offset 3// to array ‘b‘ starting at offset 6,// assuming ‘a‘ and ‘b‘ are distinct arraysbyte[] a = (...);byte[] b = (...);for (int i = 0; i < 8; i++)  b[6 + i] = a[3 + i];

  (優先)使用標準庫的方法:

System.arraycopy(a, 3, b, 6, 8);
  • 參考:java.lang.System.arraycopy(Object, int, Object, int, int)。

  調整數組大小

  使用迴圈(擴大規模):

// Make array ‘a‘ larger to newLenbyte[] a = (...);byte[] b = new byte[newLen];for (int i = 0; i < a.length; i++)  // Goes up to length of A  b[i] = a[i];a = b;

  使用迴圈(減小規模):

// Make array ‘a‘ smaller to newLenbyte[] a = (...);byte[] b = new byte[newLen];for (int i = 0; i < b.length; i++)  // Goes up to length of B  b[i] = a[i];a = b;

  (優先)使用標準庫的方法:

a = Arrays.copyOf(a, newLen);
  • 參考:java.util.Arrays.copyOf(T[], int)。
  • 參考:java.util.Arrays.copyOfRange(T[], int, int)。

  把4個位元組封裝(packing)成一個int
int packBigEndian(byte[] b) {  return (b[0] & 0xFF) << 24       | (b[1] & 0xFF) << 16       | (b[2] & 0xFF) <<  8       | (b[3] & 0xFF) <<  0;}int packLittleEndian(byte[] b) {  return (b[0] & 0xFF) <<  0       | (b[1] & 0xFF) <<  8       | (b[2] & 0xFF) << 16       | (b[3] & 0xFF) << 24;}

  把int分解(Unpacking)成4個位元組
byte[] unpackBigEndian(int x) {  return new byte[] {    (byte)(x >>> 24),    (byte)(x >>> 16),    (byte)(x >>>  8),    (byte)(x >>>  0)  };}byte[] unpackLittleEndian(int x) {  return new byte[] {    (byte)(x >>>  0),    (byte)(x >>>  8),    (byte)(x >>> 16),    (byte)(x >>> 24)  };}
  • 總是使用無符號右移操作符(>>>)對位進行封裝(packing),不要使用算術右移操作符(>>)。

Java習慣用法

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.