函数式编程与面向对象编程的区别: 函数式编程将程序代码看作数学中的函数, 函数自己是另外一个函数的函数或返回值, 即高阶函数.
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("业务逻辑异常");