文章目录
1. Lambda表达式1.1 Lambda表达式语法1.2 Lambda表达式示例1.3 说明:函数式接口2. 方法的默认实现和静态方法3. 方法引用3.1 方法引用示例4. 注解(Annotation)5. 类型推测6. 新增Optional类7. 新增Stream类8. 调用JavaScript9. Base641. Lambda表达式
Lambda
表达式是一个匿名函数(指的是没有函数名的函数),它基于数学中的λ
演算得名,直接对应于其中的Lambda
抽象。
1.1 Lambda表达式语法
Lambda
表达式允许把函数作为一个方法的参数。Lambda
表达式的基本语法如下所示:
(parameters) -> expression// 或者(parameters) -> {expression }
1.2 Lambda表达式示例
Arrays.asList(1, 3, 5, 7, 9).forEach(item -> System.out.println(item));
运行结果:
以上这种写法中,i
的类型由编译器推测出来的,当然,也可以显式地指定类型,如下例所示:
Arrays.asList(1, 3, 5, 7, 9).forEach((Integer item) -> System.out.println(item));
在Java8
以前,Java
语言通过匿名函数的方法来代替Lambda
表达式。
对于列表的排序,如果列表里面存放的是自定义的类,那么通常需要指定自定义的排序方法,传统的写法如下所示:
Test.java
package com.tian;import java.util.Arrays;import parator;/*** @author CodeJiao*/class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}}public class Test {public static void main(String[] args) {Person[] peoples = {new Person("Jack", 20), new Person("Rose", 18)};// 自定义排序方法, 通过年龄进行排序Arrays.sort(peoples, new Comparator<Person>() {@Overridepublic int compare(Person person1, Person person2) {// 根据年龄升序排序return person1.getAge() - person2.getAge();}});for (Person people : peoples) {System.out.println(people);}}}
运行结果:
采用Lambda
表达式后,写法如下所示:
// 自定义排序方法, 通过年龄进行排序Arrays.sort(peoples, (person1, person2) -> {// 根据年龄升序排序return person1.getAge() - person2.getAge();});
或者
// 自定义排序方法, 通过年龄进行排序Arrays.sort(peoples, (Person person1, Person person2) -> {// 根据年龄升序排序return person1.getAge() - person2.getAge();});
1.3 说明:函数式接口
Lambda
表达式是通过函数式接口(只有一个方法的普通接口)来实现的。函数式接口可以被隐式地转换为Lambda
表达式。为了与普通的接口区分开(普通接口中可能会有多个方法),JDK1.8
新增加了一种特殊的注解@FunctionalInterface
。下面给出一个函数式接口的定义:
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface FunctionalInterface {}
2. 方法的默认实现和静态方法
JDK1.8
通过使用关键字default
可以给接口中的方法添加默认实现
此外,接口中还可以定义静态方法,示例代码如下所示:
public interface Test {void method1();default void method2() {System.out.println("我是接口的默认实现方法");}static void method3() {System.out.println("我是接口中的静态方法");}}
那么,为什么要引入接口中方法的默认实现呢?
这样做的最重要的一个目的就是为了实现接口升级。在原有的设计中,如果想要升级接口,例如给接口中添加一个新的方法,那么会导致所有实现这个接口的类都需要被修改,这给Java
语言已有的一些框架进行升级带来了很大的麻烦。如果接口能支持默认方法的实现,那么可以给这些类库的升级带来许多便利。例如,为了支持Lambda
表达式,Collection
中引入了foreach
方法,可以通过这个语法增加默认的实现,从而降低了对这个接口进行升级的代价,不需要所有实现这个接口的类进行修改。
3. 方法引用
方法引用指的是可以直接引用Java
类或对象的方法。它可以被看成是一种更加简洁易懂的Lambda
表达式,使用方法引用后,上例中的排序代码就可以使用下面更加简洁的方式来编写:
原来:
// 自定义排序方法, 通过年龄进行排序Arrays.sort(peoples, (person1, person2) -> {// 根据年龄升序排序return person1.getAge() - person2.getAge();});
现在:
// 自定义排序方法, 通过年龄进行排序Arrays.sort(peoples, paring(Person::getAge));
方法引用共有下面4种形式:
引用构造方法:ClassName::new
引用类静态方法:ClassName::methodName
引用特定类的任意对象方法:ClassName::methodName
引用某个对象的方法:instanceName::methodName
3.1 方法引用示例
Test.java
package com.tian;import java.util.Arrays;import parator;import java.util.function.Supplier;/*** @author CodeJiao*/class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public static Person getInstance(final Supplier<Person> supplier) {return supplier.get();}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public static int compareByAge(Person a, Person b) {return b.age - a.age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}}class CompareProvider {public int compareByAge(Person a, Person b) {return a.getAge() - b.getAge();}}public class Test {public static void main(String[] args) {// 引用构造方法Person p1 = Person.getInstance(Person::new);p1.setAge(22);System.out.println("测试引用构造方法:" + p1.getAge());Person[] peoples = {new Person("Jack", 20), new Person("Rose", 18)};// 引用特定类的任意对象方法Arrays.sort(peoples, paring(Person::getAge));System.out.println("测试引用特定类的任意对象方法:");for (Person people : peoples) {System.out.println(people);}// 引用静态方法Arrays.sort(peoples, Person::compareByAge);System.out.println("测试引用静态方法:");for (Person people : peoples) {System.out.println(people);}// 引用某个对象的方法Arrays.sort(peoples, new CompareProvider()::compareByAge);System.out.println("测试引用某个对象的方法:");for (Person people : peoples) {System.out.println(people);}}}
运行结果:
4. 注解(Annotation)
1)JDK1.5
中引入了注解机制,注解为开发人员在代码中添加信息提供了一种形式化的方法,它使得开发人员可以在某个时刻方便地使用这些数据(通过解析注解来使用这些数据)。注解的语法比较简单,除了@
符号的使用以外,它基本上与Java
语言的语法一致,Java
语言内置了三种注解方式,它们分别是@Override
(表示当前方法是覆盖父类的方法)、@Deprecated
(表示当前元素是不赞成使用的)、@SuppressWarnings
(表示关闭一些不当的编译器警告信息)。需要注意的是,它们都定义在java.lang
包中。2)JDK1.8
对注解进行了扩展。使得注解被使用的范围更广,例如可以给局部变量、泛型、方法异常等提供注解。5. 类型推测
JDK1.8加强了类型推测机制,这种机制可以使得代码更为简洁,假如有如下类的定义。
在调用的时候,可以使用下面的代码:
在 Java 7的时候,这种写法将会产生编译错误,Java 7中的正确写法如下所示:
同理,在调用cons方法的时候的写法为:
在Java 7
的时候需要显式地指定类型:List.cons(5,List.<Integer>nil());
6. 新增Optional类
在使用Java
语言进行编程的时候,经常需要使用大量的代码来处理空指针异常,而这种操作往往会降低程序的可读性。JDK1.8
引入了Optional
类来处理空指针的情况,从而增强了代码的可读性,下面给出一个简单的例子:
示例代码:
package com.tian;import java.util.Optional;public class Test1 {public static void main(String[] args) {Optional<String> s1 = Optional.of("Hello");// 判断是否有值if (s1.isPresent()) {System.out.println(s1.get());}Optional<String> s2 = Optional.ofNullable(null);// 判断是否有值if (s2.isPresent()) {System.out.println(s2.get());}}}
运行结果:
7. 新增Stream类
JDK1.8
新增加了Stream
类,从而把函数式编程的风格引入到Java
语言中,Stream
的API
提供了非常强大的功能,使用Stream
后,可以写出更加强大、更加简洁的代码(例如可以代替循环控制语句)。示例代码如下所示:
package com.tian;import java.util.*;import java.util.stream.Collectors;class Student {private String name;private Integer age;public Student(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return name;}public Integer getAge() {return age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}}public class Test2 {public static void main(String[] args) {ArrayList<Student> students = new ArrayList<>();// 初始化studentsstudents.add(new Student("张有余", 10));students.add(new Student("雷磊", 13));students.add(new Student("侯家领", 10));students.add(new Student("徐曦", 15));System.out.println("找出年龄为10的第一个学生");Optional<Student> s = students.stream().filter(student ->student.getAge() == 10).findFirst();if (s.isPresent()) {System.out.println(s.toString());}System.out.println("找出年龄为10的所有学生");List<Student> listStu = students.stream().filter(student -> student.getAge() == 10).collect(Collectors.toList());listStu.forEach(stu -> {System.out.println(stu.toString());});System.out.println("对学生按照年龄分组");Map<Integer, List<Student>> map = students.stream().collect(Collectors.groupingBy(Student::getAge));Iterator<Map.Entry<Integer, List<Student>>> iterator = map.entrySet().iterator();while (iterator.hasNext()) {Map.Entry<Integer, List<Student>> entry = iterator.next();Integer age = entry.getKey();System.out.println("age: " + age);List<Student> group = entry.getValue();for (Student student : group) {System.out.println(student.getName() + " ");}}}}
运行结果:
此外,Stream
类还提供了parallel
、map
、reduce
等方法,这些方法用于增加对原生类并发处理的能力,有兴趣的读者可以参考Java
官方文档学习。
8. 调用JavaScript
JDK1.8
增加API
使得通过Java
程序来调用JavaScript
代码,使用示例如下所示:
package com.tian;import javax.script.ScriptEngine;import javax.script.ScriptEngineManager;import javax.script.ScriptException;public class Test4 {public static void main(String[] args) throws ScriptException {ScriptEngineManager manager = new ScriptEngineManager();ScriptEngine engine = manager.getEngineByName("JavaScript");// 显示引擎名称System.out.println(engine.getClass().getName());// 调用JavaScript代码System.out.println(engine.eval("function f(){return 'Hello World'};" +"f();"));}}
运行结果:
9. Base64
Base64
编码是一种常见的字符编码,可用来作为电子邮件或Web Service
附件的传输编码。JDK1.8
把Base64
编码添加到了标准类库中。示例代码如下所示:
package com.tian;import java.nio.charset.StandardCharsets;import java.util.Base64;public class Test3 {public static void main(String[] args) {String str = "Hello World";String encodeStr = Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8));System.out.println(encodeStr);String decodeSr = new String(Base64.getDecoder().decode(encodeStr));System.out.println(decodeSr);}}
运行结果:
Java8 新特性 -- Lambda表达式:函数式接口 方法的默认实现和静态方法 方法引用 注解 类型推测 Optional类 Stream类 调用JavaScript Base64