Java8新特性实在太好用了!!

   日期:2020-07-18     浏览:106    评论:0    
核心提示:前言面试的时候被问到了Java8新特性,忘记了很多,好好整理了关于Java8新特性的知识,如果有写得不对的地方还请大佬指正,让我们畅游在知识的海洋中吧1. Lambda表达式Lambda 表达式是一种匿名函数(对 Java 而言这并不完全正确,但现在姑且这么认为),简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和名字。我们总是通过匿名类给方法传递函数功能,以下是旧版的事件监听代码:someObject.addMouseListener(new MouseAdapter() {

前言

面试的时候被问到了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);
	}

还有很多方法就不多列举了 喜欢的可以自行查找哦

创作不易,如果本篇文章能帮助到你,请给予支持,赠人玫瑰,手有余香,虫虫蟹蟹观众姥爷了

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服