一文带你深入理解【Java基础】· Java8的其他新特性(上)
创始人
2024-06-02 00:48:59
0

写在前面


        Hello大家好, 我是【麟-小白】,一位软件工程专业的学生,喜好计算机知识。希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误或不足之处,请多多指正!谢谢大家!!!

        如果小哥哥小姐姐们对我的文章感兴趣,请不要吝啬你们的小手,多多点赞加关注呀!❤❤❤ 爱你们!!!


目录

写在前面

前言:

1. Lambda表达式

1.1 为什么使用 Lambda 表达式

1.2 类型推断

1.3 代码演示

 2. 函数式(Functional)接口

2.1 什么是函数式(Functional)接口

2.2 如何理解函数式接口

2.3 函数式接口举例

2.4 自定义函数式接口

2.5 作为参数传递 Lambda 表达式

2.6 Java 内置四大核心函数式接口

2.7 其他接口

2.8 代码演示

3. 方法引用与构造器引用

3.1 方法引用(Method References)

3.2 构造器引用

3.3 数组引用

3.4 代码演示

结语


【往期回顾】

一文带你深入理解【Java基础】· Java反射机制(下)

一文带你深入理解【Java基础】· Java反射机制(上)

一文带你深入理解【Java基础】· 网络编程(下)

一文带你深入理解【Java基础】· 网络编程(上)

一文带你深入理解【Java基础】· IO流(下)

一文带你深入理解【Java基础】· IO流(中)

一文带你深入理解【Java基础】· IO流(上)

一文带你深入理解【Java基础】· 泛型


前言:

Java8新特性简介 Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。Java 8 是oracle公司于2014年3月发布,可以看成是自Java 5 以来最具革命性的版本。Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。
  • 速度更快
  • 代码更少(增加了新的语法:Lambda 表达式)
  • 强大的 Stream API
  • 便于并行
  • 最大化减少空指针异常:Optional
  • Nashorn引擎,允许在JVM上运行JS应用

 并行流与串行流

  • 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。
  • Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。

1. Lambda表达式


1.1 为什么使用 Lambda 表达式

Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
  • 从匿名类到 Lambda 的转换举例1

  • 从匿名类到Lambda 的转换举例2 

Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为“->” ,该操作符被称为Lambda 操作符箭头操作符。它将Lambda 分为两个部分:

  • 左侧:指定了Lambda 表达式需要的参数列表
  • 右侧:指定了Lambda ,是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能。


1.2 类型推断

上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

1.3 代码演示

public class LambdaTest {@Testpublic void test1(){Runnable r1 = new Runnable() {@Overridepublic void run() {System.out.println("我爱北京天安门");}};r1.run();System.out.println("***********************");Runnable r2 = () -> System.out.println("我爱北京故宫");r2.run();}@Testpublic void test2(){Comparator com1 = new Comparator() {@Overridepublic int compare(Integer o1, Integer o2) {return Integer.compare(o1,o2);}};int compare1 = com1.compare(12,21);System.out.println(compare1);System.out.println("***********************");//Lambda表达式的写法Comparator com2 = (o1,o2) -> Integer.compare(o1,o2);int compare2 = com2.compare(32,21);System.out.println(compare2);System.out.println("***********************");//方法引用Comparator com3 = Integer :: compare;int compare3 = com3.compare(32,21);System.out.println(compare3);}
}
/*** Lambda表达式的使用** 1.举例: (o1,o2) -> Integer.compare(o1,o2);* 2.格式:*      -> :lambda操作符 或 箭头操作符*      ->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表)*      ->右边:lambda体 (其实就是重写的抽象方法的方法体)** 3. Lambda表达式的使用:(分为6种情况介绍)**    总结:*    ->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略*    ->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字** 4.Lambda表达式的本质:作为函数式接口的实例** 5. 如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解,*   这样做可以检查它是否是一个函数式接口。** 6. 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。*/
public class LambdaTest1 {//语法格式一:无参,无返回值@Testpublic void test1(){Runnable r1 = new Runnable() {@Overridepublic void run() {System.out.println("我爱北京天安门");}};r1.run();System.out.println("***********************");Runnable r2 = () -> {System.out.println("我爱北京故宫");};r2.run();}//语法格式二:Lambda 需要一个参数,但是没有返回值。@Testpublic void test2(){Consumer con = new Consumer() {@Overridepublic void accept(String s) {System.out.println(s);}};con.accept("谎言和誓言的区别是什么?");System.out.println("*******************");Consumer con1 = (String s) -> {System.out.println(s);};con1.accept("一个是听得人当真了,一个是说的人当真了");}//语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”@Testpublic void test3(){Consumer con1 = (String s) -> {System.out.println(s);};con1.accept("一个是听得人当真了,一个是说的人当真了");System.out.println("*******************");Consumer con2 = (s) -> {System.out.println(s);};con2.accept("一个是听得人当真了,一个是说的人当真了");}@Testpublic void test4(){ArrayList list = new ArrayList<>();//类型推断int[] arr = {1,2,3};//类型推断}//语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略@Testpublic void test5(){Consumer con1 = (s) -> {System.out.println(s);};con1.accept("一个是听得人当真了,一个是说的人当真了");System.out.println("*******************");Consumer con2 = s -> {System.out.println(s);};con2.accept("一个是听得人当真了,一个是说的人当真了");}//语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值@Testpublic void test6(){Comparator com1 = new Comparator() {@Overridepublic int compare(Integer o1, Integer o2) {System.out.println(o1);System.out.println(o2);return o1.compareTo(o2);}};System.out.println(com1.compare(12,21));System.out.println("*****************************");Comparator com2 = (o1,o2) -> {System.out.println(o1);System.out.println(o2);return o1.compareTo(o2);};System.out.println(com2.compare(12,6));}//语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略@Testpublic void test7(){Comparator com1 = (o1,o2) -> {return o1.compareTo(o2);};System.out.println(com1.compare(12,6));System.out.println("*****************************");Comparator com2 = (o1,o2) -> o1.compareTo(o2);System.out.println(com2.compare(12,21));}@Testpublic void test8(){Consumer con1 = s -> {System.out.println(s);};con1.accept("一个是听得人当真了,一个是说的人当真了");System.out.println("*****************************");Consumer con2 = s -> System.out.println(s);con2.accept("一个是听得人当真了,一个是说的人当真了");}
}


 2. 函数式(Functional)接口


2.1 什么是函数式(Functional)接口

  • 只包含一个抽象方法的接口,称为函数式接口
  • 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
  • 我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
  • 在java.util.function包下定义了Java 8 的丰富的函数式接口

2.2 如何理解函数式接口

  • Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还可以支持OOF(面向函数编程)
  • 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
  • 简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
  • 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

2.3 函数式接口举例


2.4 自定义函数式接口


2.5 作为参数传递 Lambda 表达式

作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。

2.6 Java 内置四大核心函数式接口


2.7 其他接口


2.8 代码演示

/*** java内置的4大核心函数式接口** 消费型接口 Consumer     void accept(T t)* 供给型接口 Supplier     T get()* 函数型接口 Function   R apply(T t)* 断定型接口 Predicate    boolean test(T t)*/
public class LambdaTest2 {@Testpublic void test1(){happyTime(500, new Consumer() {@Overridepublic void accept(Double aDouble) {System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:" + aDouble);}});System.out.println("********************");happyTime(400,money -> System.out.println("学习太累了,去天上人间喝了口水,价格为:" + money));}public void happyTime(double money, Consumer con){con.accept(money);}@Testpublic void test2(){List list = Arrays.asList("北京","南京","天津","东京","西京","普京");List filterStrs = filterString(list, new Predicate() {@Overridepublic boolean test(String s) {return s.contains("京");}});System.out.println(filterStrs);List filterStrs1 = filterString(list,s -> s.contains("京"));System.out.println(filterStrs1);}//根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定public List filterString(List list, Predicate pre){ArrayList filterList = new ArrayList<>();for(String s : list){if(pre.test(s)){filterList.add(s);}}return filterList;}
}

3. 方法引用与构造器引用


3.1 方法引用(Method References)

  • 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
  • 方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
  • 要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
  • 格式:使用操作符 “::” 将类(或对象) 与 方法名分隔开来。
  • 如下三种主要使用情况:
    • 对象::实例方法名
    • 类::静态方法名
    • 类::实例方法名

注意:当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数)时:ClassName::methodName

3.2 构造器引用

格式: ClassName::new 与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。

3.3 数组引用


3.4 代码演示

/*** 一、构造器引用*      和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。*      抽象方法的返回值类型即为构造器所属的类的类型** 二、数组引用*     大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。*/
public class ConstructorRefTest {//构造器引用//Supplier中的T get()//Employee的空参构造器:Employee()@Testpublic void test1(){Supplier sup = new Supplier() {@Overridepublic Employee get() {return new Employee();}};System.out.println("*******************");Supplier  sup1 = () -> new Employee();System.out.println(sup1.get());System.out.println("*******************");Supplier  sup2 = Employee :: new;System.out.println(sup2.get());}//Function中的R apply(T t)@Testpublic void test2(){Function func1 = id -> new Employee(id);Employee employee = func1.apply(1001);System.out.println(employee);System.out.println("*******************");Function func2 = Employee :: new;Employee employee1 = func2.apply(1002);System.out.println(employee1);}//BiFunction中的R apply(T t,U u)@Testpublic void test3(){BiFunction func1 = (id,name) -> new Employee(id,name);System.out.println(func1.apply(1001,"Tom"));System.out.println("*******************");BiFunction func2 = Employee :: new;System.out.println(func2.apply(1002,"Tom"));}//数组引用//Function中的R apply(T t)@Testpublic void test4(){Function func1 = length -> new String[length];String[] arr1 = func1.apply(5);System.out.println(Arrays.toString(arr1));System.out.println("*******************");Function func2 = String[] :: new;String[] arr2 = func2.apply(10);System.out.println(Arrays.toString(arr2));}
}
public class Employee {private int id;private String name;private int age;private double salary;public int getId() {return id;}public void setId(int id) {this.id = id;}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 double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public Employee() {System.out.println("Employee().....");}public Employee(int id) {this.id = id;System.out.println("Employee(int id).....");}public Employee(int id, String name) {this.id = id;this.name = name;}public Employee(int id, String name, int age, double salary) {this.id = id;this.name = name;this.age = age;this.salary = salary;}@Overridepublic String toString() {return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}';}@Overridepublic boolean equals(Object o) {if (this == o)return true;if (o == null || getClass() != o.getClass())return false;Employee employee = (Employee) o;if (id != employee.id)return false;if (age != employee.age)return false;if (Double.compare(employee.salary, salary) != 0)return false;return name != null ? name.equals(employee.name) : employee.name == null;}@Overridepublic int hashCode() {int result;long temp;result = id;result = 31 * result + (name != null ? name.hashCode() : 0);result = 31 * result + age;temp = Double.doubleToLongBits(salary);result = 31 * result + (int) (temp ^ (temp >>> 32));return result;}
}
/*** 提供用于测试的数据*/
public class EmployeeData {public static List getEmployees(){List list = new ArrayList<>();list.add(new Employee(1001, "马化腾", 34, 6000.38));list.add(new Employee(1002, "马云", 12, 9876.12));list.add(new Employee(1003, "刘强东", 33, 3000.82));list.add(new Employee(1004, "雷军", 26, 7657.37));list.add(new Employee(1005, "李彦宏", 65, 5555.32));list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));list.add(new Employee(1007, "任正非", 26, 4333.32));list.add(new Employee(1008, "扎克伯格", 35, 2500.32));return list;}
}
/*** 方法引用的使用* 1.使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!** 2.方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以*   方法引用,也是函数式接口的实例。** 3. 使用格式:  类(或对象) :: 方法名** 4. 具体分为如下的三种情况:*    情况1     对象 :: 非静态方法*    情况2     类 :: 静态方法**    情况3     类 :: 非静态方法** 5. 方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的*    形参列表和返回值类型相同!(针对于情况1和情况2)*/
public class MethodRefTest {// 情况一:对象 :: 实例方法//Consumer中的void accept(T t)//PrintStream中的void println(T t)@Testpublic void test1() {Consumer con1 = str -> System.out.println(str);con1.accept("北京");System.out.println("*******************");PrintStream ps = System.out;Consumer con2 = ps::println;con2.accept("beijing");}//Supplier中的T get()//Employee中的String getName()@Testpublic void test2() {Employee emp = new Employee(1001,"Tom",23,5600);Supplier sup1 = () -> emp.getName();System.out.println(sup1.get());System.out.println("*******************");Supplier sup2 = emp::getName;System.out.println(sup2.get());}// 情况二:类 :: 静态方法//Comparator中的int compare(T t1,T t2)//Integer中的int compare(T t1,T t2)@Testpublic void test3() {Comparator com1 = (t1,t2) -> Integer.compare(t1,t2);System.out.println(com1.compare(12,21));System.out.println("*******************");Comparator com2 = Integer::compare;System.out.println(com2.compare(12,3));}//Function中的R apply(T t)//Math中的Long round(Double d)@Testpublic void test4() {Function func = new Function() {@Overridepublic Long apply(Double d) {return Math.round(d);}};System.out.println("*******************");Function func1 = d -> Math.round(d);System.out.println(func1.apply(12.3));System.out.println("*******************");Function func2 = Math::round;System.out.println(func2.apply(12.6));}// 情况三:类 :: 实例方法  (有难度)// Comparator中的int comapre(T t1,T t2)// String中的int t1.compareTo(t2)@Testpublic void test5() {Comparator com1 = (s1,s2) -> s1.compareTo(s2);System.out.println(com1.compare("abc","abd"));System.out.println("*******************");Comparator com2 = String :: compareTo;System.out.println(com2.compare("abd","abm"));}//BiPredicate中的boolean test(T t1, T t2);//String中的boolean t1.equals(t2)@Testpublic void test6() {BiPredicate pre1 = (s1,s2) -> s1.equals(s2);System.out.println(pre1.test("abc","abc"));System.out.println("*******************");BiPredicate pre2 = String :: equals;System.out.println(pre2.test("abc","abd"));}// Function中的R apply(T t)// Employee中的String getName();@Testpublic void test7() {Employee employee = new Employee(1001, "Jerry", 23, 6000);Function func1 = e -> e.getName();System.out.println(func1.apply(employee));System.out.println("*******************");Function func2 = Employee::getName;System.out.println(func2.apply(employee));}
}

结语


本人会持续更新文章的哦!希望大家一键三连,你们的鼓励就是作者不断更新的动力

相关内容

热门资讯

【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AsusVivobook无法开... 首先,我们可以尝试重置BIOS(Basic Input/Output System)来解决这个问题。...
ASM贪吃蛇游戏-解决错误的问... 要解决ASM贪吃蛇游戏中的错误问题,你可以按照以下步骤进行:首先,确定错误的具体表现和问题所在。在贪...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...