吐槽,Java 設計的槽點

今天不灌水,直接上乾貨!但願下面的講解,能與你產生一些共鳴。java

 

1. 求長度各有千秋

 

你是否曾經在面試的時候,常常被問到:數組有沒有 length() 方法?字符串有沒有 length() 方法? 集合有沒有 length() 方法?面試

 

面對這個問題,那麼不得不吐槽一下,Java 中獲取長度的方式,設計着實有點亂,對剛入門的程序猿而言,那絕對是一臉的懵逼。編程

 

String[] array = {"abc", "def"};
String str = "abcedf";
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("def");

System.out.println("數組的長度: " + array.length);
System.out.println("字符串的長度: " + str.length());
System.out.println("集合的長度: " + list.size());

  

正式科普一下,但願可以銘記你心中。數組求長度用 length 屬性;字符串求長度用 length() 方法;集合求長度用 size() 方法。數組

 

2. 字符串截取有深意

 

對於程序猿來講,編程規範可以養成良好的編程習慣,提升代碼質量,減小溝通成本。阿里 Java 開發手冊編程規約中記載,【強制】方法名、參數名、成員變量、局部變量都統一使用 lowerCamelCase 風格,必須聽從駝峯形式。this

 

看到這裏,不得不提 String 中的 substring 方法,你是否是常常把「substring」寫成「subString」。本次這個命名不是吐槽的重點。主要想分享以下代碼片斷。編碼

 

public class StringInterview {
   public static void main(String[] args) {
       String str = "......abcdefgh.......";
       String subStr = str.substring(1,3);
       str = null;
       System.out.println(subStr);
   }
}

  

 

假如上述這段程序在 Java 1.6 中運行,代碼中雖然強制使 str 引用爲空,本意是釋放 str 佔用的空間,可是這個時候,GC 是沒法回收這個大的 char 數組的,由於還在被 subStr 字符串內部引用着,雖然 subStr 只截取這個大數組的一小部分。當 str 是一個很是大字符串的時候,這種浪費是很是明顯的,甚至會帶來內存泄露問題。spa

 

深刻 Java 1.6 中 substring 的設計一探究竟。設計

public String substring(int beginIndex, int endIndex) {

  if (beginIndex < 0) {
      。。 。。 。。
  }

  if (endIndex > count) {
       。。 。。 。。
  }

  if (beginIndex > endIndex) {
       。。 。。 。。
  }

  return ((beginIndex == 0) && (endIndex == count)) ? this :
   new String(offset + beginIndex, endIndex - beginIndex, value);
}

  

上述方法調用的構造方法blog

String(int offset, int count, char value[]) {
  this.value = value;
  this.offset = offset;
  this.count = count;
}

  

到此你應該撥雲見日豁然開朗。當咱們調用字符串 str 的 substring 獲得字符串 subStr,其實這個操做,無非就是調整了一下 subStr 的 offset 和 count ,用到的內容仍是 str 以前的 value 字符數組,並無從新建立新的專屬於 subStr 的內容字符數組。若是 subStr 的生命週期要長於 str 或者手動設置 str 爲null,當垃圾回收進行後,str 被回收掉,subStr 沒有回收掉,那麼內存佔用依舊存在,由於 subStr 持有 str 字符數組的引用。生命週期

 

正式科普一下,這個問題出如今 Java 1.6,而且 Java 1.7 中已經修復。雖然已經修復,並不表明咱們就不須要了解,若是你正在求職路上,稍微瞭解一下,說不定會加分。

 

3. 一條 if 語句引起不滿

 

先給各位拋一段 Java LinkedList 類的代碼片斷,一塊兒吐槽吐槽。

 

public E getFirst() {
  final Node<E> f = first;
  if (f == null)
       throw new NoSuchElementException();
   return f.item;
}

  

JDK 中 if 語句後只有一條語句,大部分都是這麼實現的。原則上,if 語句若是後面跟着只有一句話,是能夠不加的。可是在咱們實際開發中,有些現象卻會讓你匪夷所思,不信你看看下面的代碼片斷。

 

片斷一:

if (f == null)
   //拋出異常,或者加一條打印語句,加上此句註釋邏輯就變了
   throw new NoSuchElementException();

  

片斷二:

public class Interview {
   public static void main(String[] args) {
       Class c = Interview.class;
       try {
           Object o = c.newInstance();
           if (o instanceof Interview)
               Interview tt = (Interview) o; //爲何會報錯?請各位解釋緣由
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}

  

正式科普一下,看似一個簡單的編碼規範,背後隱藏了多少坑啊,因此爲了良好的編程習慣,建議仍是統一加上大括號爲好,良好的編碼習慣是真重要啊。

 

4. 時間實現也找茬

 

Tiago Fernandez 作過一次投票,選舉最爛的 Java API,排第二的就是日期 API(Date 和Calender)。一言不合就拋代碼,以下片斷是計算兩個日期之間的天數。

public static void main(String[] args) {
   Calendar begin = Calendar.getInstance();
   begin.set(1990, Calendar.JUNE, 17);
   Calendar end = Calendar.getInstance();
   System.out.println(alculatedDays(begin, end));
   System.out.println(alculatedDays(begin, end)); // 爲何顯示 0?
}

public static long alculatedDays(Calendar begin, Calendar end) {
   long days = 0;
   while (begin.before(end)) {
       begin.add(Calendar.DAY_OF_MONTH, 1);
       days++;
   }

   return days;
}

 

alculatedDays 方法,若是連續計算兩個 Date 實例的話,第二次會取得 0,由於 Calendar 狀態是可變的,考慮到重複計算的場合,最好複製一個新的 Calendar,改造以下

public static long alculatedDays(Calendar begin, Calendar end) {
   Calendar calendar = (Calendar) begin.clone(); // 複製
   long days = 0;
   while (calendar.before(end)) {
       calendar.add(Calendar.DAY_OF_MONTH, 1);
       days++;
   }

   return days;
}

  

不過萬物都在向前進化,由於因爲原來老舊的日期 API 一直被人詬病,因此 JDK 1.8 中對日期的改動是特別大的,基本上是引入了一套全新易用的 API,各位有時間能夠體驗一下。

 

好了,吐槽中見真諦,今天就講這麼多吧。但願你能 get 到一點點共鳴,若是你比較感興趣,就多多分享給身邊的朋友吧。

相關文章
相關標籤/搜索