前言
面试的时候被问到了Java8新特性,忘记了很多,好好整理了关于Java8新特性的知识
,如果有写得不对的地方还请大佬指正,让我们畅游在知识的海洋中吧
1. Lambda表达式
Lambda 表达式是一种匿名函数
(对 Java 而言这并不完全正确,但现在姑且这么认为),简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和名字。
我们总是通过匿名类给方法传递函数功能,以下是旧版的事件监听代码:
someObject.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
//Event listener implementation goes here...
}
});
在上面的例子里,为了给 Mouse 监听器添加自定义代码,我们定义了一个匿名内部类 MouseAdapter 并创建了它的对象,通过这种方式,我们将一些函数功能传给 addMouseListener 方法。
在 Java 里将普通的方法或函数像参数一样传值并不简单,为此,Java 8 增加了一个语言级的新特性,名为 Lambda 表达式。
1.1 函数式接口
要注意的是想使用Lambda表达式就必须使用函数式接口,如果使用函数式接口,那么该接口之中就只能有一个抽象方法
Java中的lambda无法单独出现,它需要一个函数式接口来盛放,lambda表达式方法体其实就是函数接口的实现.
举例:
package com.yztcedu.lambdademo_01;
@FunctionalInterface //添加此注解后,接口中只能有一个抽象方法。
public interface A {
void call();
}
1.2 lambda语法
包含三部分:
1、一个括号内用逗号分隔的形式参数,参数是函数式接口里面方法的参数
2、一个箭头符号:->
3、方法体,可以是表达式和代码块
(parameters) -> expression 或者 (parameters) -> { statements; }
Runnable runnable = () -> System.out.println("这个是用拉姆达实现的线程");
如果不使用Lambda表达式情况是这样的:
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("这个是用内部类实现的线程");
}
};
1.3 方法引用
其实是lambda表达式的一种简化写法。所引用的方法其实是lambda表达式的方法体实现,语法也很简单,左边是容器(可以是类名,实例名),中间是"::",右边是相应的方法名。如下所示:
如果是静态方法,则是ClassName::methodName。如 Object ::equals
如果是实例方法,则是Instance::methodName。如Object obj=new Object();obj::equals;
构造函数.则是ClassName::new
Runnable runnable = Demo2::run;
public static void run(){
System.out.println("方法引用的代码...");
}
1.4 默认方法—接口改进
简单说,就是接口可以有实现方法,而且不需要实现类去实现其方法。只需在方法名前面加个default关键字即可。
package com.yztcedu.lambdademo_01;
@FunctionalInterface
public interface A {
void call();
default void fun() {
System.out.println("我是接口的默认方法1中的代码");
}
default void fun2() {
System.out.println("我是接口的默认方法2中的代码");
}
}
为什么要有这个特性?首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类
2. Stream 数据流
在JAVA8之前的传统编程方式,如果我们需要操作一个集合数据,需要使用集合提供的API,通过一个循环去获取集合的元素,这种访问数据的方式会使代码显得臃肿,JAVA8新引入的Stream类,用于重新封装集合数据,通过使用流式Stream代替常用集合数组、list和map的遍历操作可以极大的提高效率
可以形象地理解Stream的操作是对一组粗糙的工艺品原型(即对应的 Stream 数据源)进行加工成颜色统一的工艺品(即最终得到的结果),第一步筛选出合适的原型(即对应Stream的 filter 的方法),第二步将这些筛选出来的原型工艺品上色(对应Stream的map方法),第三步取下这些上好色的工艺品(即对应Stream的 collect(toList())方法)。在取下工艺品之前进行的操作都是中间操作,可以有多个或者0个中间操作,但每个Stream数据源只能有一次终止操作,否则程序会报错。
实际项目操作代码:
class Employee {
private Long empno; //员工号
private String ename; //员工姓名
private Integer salary; //薪水
private Integer deptno; //所属部门号
//此处省略get/set方法、构造方法以及toString方法
}
Employee e1 = new Employee(7369L, "SMITH", 800, 20);
Employee e2 = new Employee(7499L, "ALLEN", 1600, 30);
Employee e3 = new Employee(7521L, "WARD", 1250, 30);
Employee e4 = new Employee(7782L, "CLARK", 2450, 10);
Employee e5 = new Employee(7876L, "ADAMS", 1100, 20);
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5);
forEach方法
forEach方法用于迭代stream流中的每一个元素
employees.stream().forEach(System.out::println);
执行结果:
Employee{empno=7369, ename=‘SMITH’, salary=800, deptno=20}
Employee{empno=7499, ename=‘ALLEN’, salary=1600, deptno=30}
Employee{empno=7521, ename=‘WARD’, salary=1250, deptno=30}
Employee{empno=7782, ename=‘CLARK’, salary=2450, deptno=10}
Employee{empno=7876, ename=‘ADAMS’, salary=1100, deptno=20}
map方法
//map方法用于根据自定义的规则对stream流中的数据做一对一的映射
//获取所有员工的姓名
List<String> enames = employees.stream().map(employee -> employee.getEname()).collect(Collectors.toList());
enames.stream().forEach(System.out::println);
执行结果:
SMITH
ALLEN
WARD
CLARK
ADAMS
从Java 8开始,使用ArrayList的新API加上lambda表达式,我们可以这样写:
list.forEach(e -> System.out.println(e));
而这里的lambda表达式的内容其实只不过就是把参数传给了println()方法,而没有做任何别的事情,所以可以进一步简写为:
list.forEach(System.out::println);
仅此而已。
System.out是一个PrintStream实例的引用;System.out::println 是对一个实例方法的引用
该引用同时指定了对实例(System.out)的引用以及对方法(PrintStream::println)的引用
System.out::println 不是 System.out.println 的等价物;前者是一个方法引用表达式,而后者不能单独作为一个表达式,而必须在后面跟上由圆括号包围的参数列表来构成方法调用表达式。
System.out::println 可以看作 lambda表达式 e -> System.out.println(e) 的缩写形式。
常用的stream流操作
1.collect(toList()) 终止操作
由Stream中的值生成一个List列表,也可用collect(toSet())生成一个Set集合。
例:取 Stream 中每个字符串并放入一个新的列表
@Test
public void collectToList() {
String[] testStrings = { "java", "react", "angular", "vue" };
List<String> list = Stream.of(testStrings).collect(Collectors.toList());
for (int i = 0, length = list.size(); i < length; i++) {
System.out.println(list.get(i));
}
}
2.map 中间操作
将一种类型的值映射为另一种类型的值,可以将 Stream 中的每个值都映射为一个新的值,最终转换为一个新的 Stream 流。
例:把 Stream 中每个字符串都转换为大写的形式,
@Test
public void mapTest() {
String[] testStrings = { "java", "react", "angular", "vue" };
List<String> list = Stream.of(testStrings).map(test -> test.toUpperCase()).collect(Collectors.toList());
list.forEach(test -> System.out.println(test));
}
3.filter 中间操作
遍历并筛选出满足条件的元素形成一个新的 Stream 流。
例:筛选出以 j 字母开头的元素个数,此例中的count方法也是终止操作,是为了计算出 Stream 中的元素个数
@Test
public void filterTest() {
List<String> list = Arrays.asList("java", "react", "angular", "javascript", "vue");
long count = list.stream().filter(p -> p.startsWith("j")).count();
System.out.println(count);
}
还有很多方法
就不多列举了 喜欢的可以自行查找哦
创作不易,如果本篇文章能帮助到你,请给予支持,赠人玫瑰,手有余香,虫虫蟹蟹观众姥爷了