欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

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

前言

面试的时候被问到了Java8新特性,忘记了很多,好好整理了关于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流操作

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

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));
		}
	}

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

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);
	}

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

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

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

本文地址:https://blog.csdn.net/qq_49313444/article/details/107382112