Java核心技術卷一第六章讀書筆記

6.1 接口

  • 接口:主要用來描述類具有什麼功能,而並不需要給出每個功能的具體實現
  • lambda表達式:一種可以在將來某個時間點執行的代碼塊的簡介方法
  • 在Java程序設計語言中,接口不是類,而是對類的一組需求描述,這些類要遵循接口描述的統一格式進行定義(例如,Arrays裏的sort方法,如果要對不是數組的對象進行排序,就需要實現Comparable接口)
  • 接口中的方法自動的設置爲public,所以不需要提供關鍵字public,同時在實現接口方法時,需要將方法設置爲public
  • 接口不能含有實例域,現在的接口可以包含具體方法,但是需要設置爲默認方法
  • 爲了讓一個類實現接口,需要以下步驟(關鍵字:implement
    • 將類聲明爲實現給定的接口
    • 對接口中所有的方法進行定義
  • * Arrays.sort()方法使用了兩種排序方法(快速排序和優化的歸併排序):對於基本類型使用快速排序,對於對象類型,使用歸併排序,因爲快速排序不是穩定的,歸併排序是穩定的
  • 接口不是類,尤其不能使用new運算符實例化一個接口:

x = new Comparable()

然而,儘管不能構造接口的對象,卻kennel聲明接口的變量:

Comparable x;

接口變量必需引用實現類接口的類對象;

  • 接口可以繼承其他接口
  • 與接口中方法自動的設置爲public一樣,接口中的域將被自動設置爲public static final
  • 儘管每一個類只能擁有一個超類,但可以實現多個接口。這就爲實現類的行爲提供類靈活性
  • Java的設計者選擇類不支持多繼承,其主要原因是多繼承會讓語言變得非常複雜(C++),效率也會降低(Eiffel)
  • 在Java SE8中,允許在接口中增加靜態方法。理論上講,沒有任何理由認爲這是不合法的。只是這有違於將接口作爲抽象規範的初衷。通常的做法是將靜態方法放在伴隨類中。在標準庫中,你會看到成對出現的接口和使用工具類,如Collections/Collection
  • 默認方法
    • 使用默認方法可以不用實現接口的一個方法,減少程序員的工作
    • 實現與舊代碼的兼容
  • 默認方法衝突:
    • 超類有限。如果超類提供了一個方法,同名且有相同參數類型的默認方法會被忽略。
    • 接口衝突。這個時候必需處理衝突,在子類中決定調用哪個接口的方法。

      person.super.getname()

6.2 接口示例

  • 回調(callback)是一種常見的程序設計模式,可以指出某個特定事件發生時應該採取的動作
  • 接口是實現回調非常好的一種方式,例如ActionLister接口
  • clone方法是Object的一個protected方法。
  • 對於每一個類,需要確定:
    • 默認的clone方法是否滿足要求
    • 是否可以在可變的子對象上調用clone來修補默認的clone方法
    • 是否不該使用clone
    • 實現Cloneable接口。重新定義clone方法,並指定public訪問修飾符
  • Cloneable接口是Java提供的一組標記接口之一。建議在程序中不要使用標記接口。即使clone的默認(淺拷貝)可以滿足要求,還是需要實現Cloneable接口,將clone重新定義爲public,再調用super.clone
  • 克隆沒有想象中的那麼常用,標準庫裏只有不到5%的類實現了clone
  • 所有數組類型都有一個public的clone方法,而不是protected

6.3 lambda表達式

  • 是一個難點,建議編寫代碼練習
  • lambda表達式是一個可傳遞的代碼塊,可以在以後執行一次或多次(在Java中傳遞代碼塊並不容易,不能直接傳遞代碼塊。Java是一種面嚮對象語言,所以必需構造一個對象,這個對象的類需要有一個方法能包含所需要的代碼)
  • lambda表達式的語法:
    • 參數,箭頭(->)以及一個表達式。
      (String first, String second)
          -> first.length() - second.length()
      
    • 如果一句代碼無法完成計算,放在{}中:
      (String first, String second) ->
      {
          if(first.length() < second.length()) return -1;
          else return 0;
      }
      
    • 即使lambda表達式沒有參數,也需要提供空括號,就像無參數方法一樣:
      () -> {for(int i = 100;i >= 0;i--) System.out.println(i);}
      
    • 如果可以推導出一個lambda表達式的參數類型,則可以忽略其類型
      Comparator<String> comp = (first, second) ->
          first.length() - second.length()
      
    • 如果方法只有一個參數,而且這個參數的類型可以推導出,那麼還可以省略小括號:
      ActionListener listener = event ->
          System.out.println("The time is " + new Date());
      
  • 特別重要lambda表達式是一個函數,可以賦值給一個只有一個抽象方法的函數式接口
  • 對於只有一個抽象方法的接口,需要這種接口的對象愛心難過是,就可以提供一個lambda表達式。這種接口稱作:函數式接口
  • 最好把一個lambda表達式看作一個函數,而不是一個對象,另外要接受lambda表達式可以傳遞到函數接口
  • 爲此,Java設計者還是決定保持我們熟悉的接口概念,沒有爲Java語言增加函數類型
  • 方法引用:(當已經有lambda表達式的函數時):
    Timer t = new Timer(1000, System.out::println);
    
  • 類似於lambda表達式,方法引用不能獨立存在,總是會轉化爲函數式接口的實例
  • 構造器引用於方法引用很相似,只不過方法名爲new
    Stream<Person> stream - names.stream().map(Person::new)
    
  • 關於代碼塊以及自由變量值偶一個術語:閉包(closure)。如果有人吹噓他們的語言有閉包,現在你也可以自信的說Java也有閉包。在Java中,lambda表達式就是閉包
  • lambda表達式捕獲的變量,必需是最終變量。只能引用值不會改變的變量。在lambda表達式中聲明一個與局部變量同名的參數或局部變量是不合法的:
    Path first = Paths.get("/usr/bin");
    Comparator<String> comp = 
        (first, second) -> first.length() - second.length();
        //Error
    
  • 處理lambda表達式:把一個函數式接口對象作爲參數,要選擇合適的函數式接口,也可以自己編寫函數式接口
  • 如果自己設計函數式接口,可以用@FunctionalInterface註解來標記這個接口

6.4 內部類

  • 使用內部類的原因有一下三點:
    • 內部類方法可以訪問該類定義所在作用域的數據,包括私有的數據
    • 內部類可以對同一個包中的其他類隱藏起來
    • 當想要定一個毀掉函數且不想板鞋大量代碼時,使用匿名(anonymous)內部類比較便捷
  • 內部類既可以訪問自身的數據域,也可以訪問創建它的外圍類的數據域
  • 內部類的對象總有一個隱式引用,指向創建它的外部類的對象。這個引用在內部類中定義是不可見的。
  • 只有內部類可以是私有類,而常規類只可以具有包可見性,或公有可見性
  • 內部類中聲明的所有靜態域都必須是final
  • 內部類不能有static方法
  • 局部內部類:可以在一個方法裏定義一個類,對外部世界完全隱藏起來
  • 靜態內部類:把一個類隱藏在另一個類的內部,不需要引用外圍類的對象。只有內部類可以聲明爲static。且內部類對象只能在靜態方法中構造
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章