函數式編程與面向對象編程的區別: 函數式編程將程序代碼看作數學中的函數, 函數自己是另外一個函數的函數或返回值, 即高階函數.
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 方法修改成:
上例中的 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 類. 異常類繼承層次如圖:
異常基類 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("業務邏輯異常");