Java基礎筆記(六)--Lambda表達式和異常

函數式編程與面向對象編程的區別: 函數式編程將程序代碼看作數學中的函數, 函數自己是另外一個函數的函數或返回值, 即高階函數.

Lambda 表達式

示例: 經過匿名類實現計算兩個int值的功能
public class HelloWorld {   public static Calculate calculate(char opt)   {     Calculate result;     if(opt == '+')     {       // 匿名類實現Calculate接口
      result = new Calculate() {       // 實現加法運算
      @Override         public int calculateInt(int a, int b) {           return a + b;         }       };     }else     {       result = new Calculate()       {         // 實現減法運算
        @Override         public int calculateInt(int a, int b)         {           return a -b;         }       };     }     return result;   }   public static void main(String[] args)   {     int n1 = 10;     int n2 = 5;     Calculate f1 = HelloWorld.calculate('+');     Calculate f2 = HelloWorld.calculate('-');     System.out.println(f1.calculateInt(n1, n2));     System.out.println(f2.calculateInt(n1, n2));   } }
上例中經過匿名類實現 calculateInt 方法. 如今經過 Lambda 表達式將該方法的 if-else 部分修改成:
if(opt == '+') {   // Lambda 表達式
  result = (int a, int b) ->   {     return a+b;   }; }else {   // Lambda 表達式
  result = (int a, int b) ->   {     return a - b;   }; }
Lambda 表達式是一個匿名函數 (方法) 代碼塊, 能夠做爲表達式、方法參數和方法返回值. 其標準語法形式爲:
(參數列表) -> {   // Lambda 表達式
}
函數式接口
Lambda 表達式實現的接口不是普通的接口, 是函數式接口, 這種接口只能有一個方法. 爲防止在函數式接口中聲明多個抽象方法, Java 8 提供了一個聲明函數式接口的註解 「@FunctionalInterface」.
Lambda 表達式是一個匿名方法的代碼塊, 它實現的是在函數接口中聲明的方法, 返回的是該接口的一個實例.

Lambda 表達式簡化形式

省略參數形式
Lambda 表達式能夠根據上下文環境推斷出參數類型. 上例中的 if-else 能夠修改成:
if(opt == '+') {   result = (a, b) ->   {     return a+b;   }; }else {   result = (a, b) ->   {     return a - b;   }; }
省略參數小括號
Lambda 表達式中參數只有一個時, 能夠省略參數小括號.
將接口 Calculable 中的 calculateInt 方法修改成:
int calculateInt(int a);
上例中的 if-else 能夠修改成:
if(opt == "square") {   result = a ->   {     return a * a;   }; }
省略 return 和大括號
Lambda 表達式體中只有一條語句時, 能夠省略 return 和大括號.
繼續上例中的 if-else 能夠修改成:
if(opt == "square") {   result = a -> a * a; }

做爲參數使用 Lambda 表達式

Lambda 表達式常見用途之一是做爲參數傳遞給方法. 這須要聲明參數類型爲函數式接口類型.
public class HelloWorld {   public void display(Calculate c, int a)   {     System.out.println(c.squareInt(a));   }   public static void main(String[] args)   {     int n = 12;     HelloWorld h = new HelloWorld();     // 傳入 Lambda 表達式做爲參數
    h.display(x -> x * x, n);   } } // 定義接口
interface Calculate {   // 計算兩個int的值
  int squareInt(int a); }

訪問變量

Lambda 表達式能夠訪問所在外層做用域內定義的變量, 包括成員變量和局部變量.
訪問成員變量
public class HelloWorld {   private int value = 10;   private static int staticValue = 5;   public static Calculate add()   {     Calculate result = (int a, int b) ->     {       // add是靜態方法, 不能訪問非靜態變量, 只能訪問靜態變量
      staticValue++;       int c = a + b + staticValue;       return c;     };     return result;   }   public Calculate sub()   {     Calculate result = (int a, int b) ->     {       staticValue++;       this.value++; //若是不與局部變量衝突, 能夠省略this
      int c = a - b - staticValue - this.value;       return c;     };     return result;   } } // 定義接口
interface Calculate {   int calculateInt(int a, int b); }
捕獲局部變量
Lambda 表達式訪問做用域外層的局部變量時, 會發生 「捕獲變量」 狀況. Lambda 表達式捕獲變量時, 會將變量當成 final 的, 不管該變量是否被 final 修飾.

方法引用

Java 8 以後增長了雙冒號 「::」 運算符, 該運算符用於 「方法引用」 , 注意不是調用方法. 「方法引用」 雖然沒有直接使用 Lambda 表達式, 但也與 Lambda 表達式有關, 與函數式接口有關.
方法引用分爲: 靜態方法的方法引用和實例方法的方法引用. 語法形式以下:
類型名:: 靜態方法 // 靜態方法的方法引用
類型名:: 實例方法 // 實例方法的方法引用
被引用方法的參數列表和返回值類型, 必須與函數式接口方法的參數列表和返回值類型一致.
public class LambdaDemo {   // 聲明被引用的靜態方法
  public static int add(int a, int b)   {     return a + b;   }   // 聲明被引用的實例方法
  public int sub(int a, int b)   {     return a - b;   }   // 聲明使用函數式接口實例爲參數的方法
  public static void display(Calculable c, int n1, int n2)   {     System.out.println(c.calculateInt(n1, n2));   }   public static void main(String[] args)   {     int n1 = 10;     int n2 = 5;     // 引用靜態方法
    display(LambdaDemo::add, n1, n2);     LambdaDemo ld = new LambdaDemo();     // 引用實例方法
    display(ld::sub, n1, n2);   } } interface Calculable {   int calculateInt(int a, int b); }
方法引用就是使用其餘類的方法代替了 Lambda 表達式, 使引用的方法起到 Lambda 表達式的做用.

異常處理

Java 中異常封裝成爲類 Exception, 此外, 還有 Throwable 和 Error 類. 異常類繼承層次如圖:
Alt text
異常基類 Throwable 有幾個經常使用方法:
String getMessage(): 得到發生異常的詳細信息.
void printStackTrace(): 打印異常堆棧跟蹤信息.
String toString(): 得到異常對象的描述.
Throwable 有兩個子類 Error 和 Exception.
Error
Error 是程序沒法恢復的嚴重錯誤, 只能讓程序終止.
Exception
Exception 是程序能夠恢復的異常. 該類能夠分爲: 受檢查異常和運行時異常.
受檢查異常
編譯器會檢查這類異常是否進行了處理, 即要麼捕獲 (try-catch 語句), 要麼拋出 (經過在方法後聲明 throws), 不然會發生變異錯誤.
運行時異常
編譯器不檢查這類異常是否進行了處理. 但因爲沒有進行異常處理, 一旦運行時異常發生就會致使程序終止.
對運行時異常不採用拋出或捕獲處理方式, 而是應該提早預判, 防止發生這種異常.

捕獲異常

當前方法有能力解決時, 則捕獲異常進行處理; 沒有能力解決, 則拋給上層調用方法處理. 上層調用方法也無力解決時, 繼續拋給它的上層調用方法. 若是全部方法都沒有處理該異常, JVM 會終止程序運行.
try-catch 語句
語法格式:
try {   // 可能發生異常的語句
}catch(Throwable e) {   // 異常處理
}
try 代碼塊中包含可能發生異常的代碼語句. 每一個 try 代碼塊能夠伴隨一個或多個 catch 代碼塊, 用於處理 try 代碼塊中可能發生的異常.
多個異常類之間存在父子關係時, 捕獲異常順序與 catch 代碼塊的順序有關. 通常先捕獲子類, 後捕獲父類, 不然子類捕獲不到.
多重捕獲
Java 7 推出了多重捕獲 (multi-catch) 技術, 在 catch 中多重捕獲異經常使用 「|」 運算符鏈接.
try {   ... }catch(IOException | ParseException e) {   ... }

釋放資源

有時在 try-catch 語句中會佔用一些非 Java 資源. 爲了確保這些資源能夠釋放, 可使用 finally 代碼塊或 Java 7 以後提供自動資源管理技術.
finally 代碼塊
try-catch 語句後面還能夠跟一個 finally 代碼塊:
try {   ... }catch(Throwable e) {   ... }fianlly {   ... }
不管是否發生異常, finally 代碼塊都會執行.
自動資源管理
Java 7 以後提供了自動資源管理技術. 自動資源管理是在 try 語句上的擴展, 語法以下:
try(聲明或初始化資源語句) {   ... }catch(Throwable e) {   ... }
在 try 語句後面追加聲明或初始化資源語句, 能夠有多條語句, 多條語句間使用分號 「;」 分隔.

throws 與聲明方法拋出異常

方法後面聲明拋出異常使用 「throws」 關鍵字. 多個異常間使用逗號 (,) 分隔.

自定義異常類

實現自定義異常類須要繼承 Exception 類或其子類. 若是自定義運行時異常類須要繼承 RuntimeException 類或其子類.
自定義異常類主要是提供兩個構造方法:
public class MyException extends Exception {   public MyException{}   public MyException(String message)   {     super(message);   } }
throw 與顯式拋出異常
throws 用於方法後聲明拋出異常, throw 關鍵字用來人工引起異常.
throw new Exception("業務邏輯異常");
相關文章
相關標籤/搜索