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("业务逻辑异常");

猜你喜欢

转载自www.cnblogs.com/fxyy/p/11487719.html