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("業務邏輯異常");


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM