JDK8新特性-上部

news2024/11/24 0:03:39

文章目录

    • 一、Java发展史
      • 1.1 发展史
      • 1.2 OpenJDK和OracleJDK
      • 1.3 Open JDK 官网介绍
    • 二、Lambda表达式
      • 2.1 需求分析
      • 2.2 Lamada表达式的体验
      • 2.3 Lambda表达式的语法规则
        • 2.3.1 Lambda表达式练习
        • 2.3.2 Lambda表达式练习
      • 2.4 Lambda表达式的使用前提
      • 2.5 @FunctionalInterface注解
      • 2.6 Lambda表达式的原理
      • 2.7 Lambda表达式的省略写法
      • 2.8 Lambda表达式和内部类的区别
    • 三、接口的增强
      • 3.1 JDK8接口新增
      • 3.2 默认方法
      • 3.3 静态方法
      • 3.4 两个方法之间的区别
    • 四、函数式接口
      • 4.1 函数式接口的由来
      • 4.2 函数式接口的介绍
        • 4.2.1 Supplier
        • 4.2.2 Consumer
        • 4.2.3 Function
        • 4.2.4 Predicate
    • 结语

🌕博客x主页:己不由心王道长🌕!
🌎文章说明:JDK8新特性🌎
✅系列专栏:Java基础
🌴本篇内容:对JDK8的新特性进行学习和讲解🌴
☕️每日一语:这个世界本来就不完美,如果我们再不接受不完美的自己,那我们要怎么活。☕️
🚩 交流社区:己不由心王道长(优质编程社区)

一、Java发展史

1.1 发展史

Sun公司在1991年成立了一个称为绿色计划( Green Project )的项目,由James Gosling(高斯林)博土领导,绿色计划的目的是开发一种能够在各种消费性电子产品(机顶盒、冰箱、收音机等)上运行的程序架构。这个项目的产品就是Java语言的前身: Oak(橡树)。Oak当时在消费品市场上并不算成功,但随着1995年互联网潮流的兴起,Oak迅速找到了最适合自己发展的市场定位。

- JDK Beta - 1995v
- JDK 1.0 - 1996年1月 (真正第一个稳定的版本JDK 1.0.2,被称作 Java 1 )
- JDK 1.1 - 1997年2月
- J2SE 1.2 - 1998年12月
- J2ME(Java 2 Micro Edition,Java 2平台的微型版),应用于移动、无线及有限- 资源的环境。
- J2SE(Java 2 Standard Edition,Java 2平台的标准版),应用于桌面环境。
- J2EE(Java 2 Enterprise Edition,Java 2平台的企业版),应用于基于Java的应用服务器。
- J2SE 1.3 - 2000年5月
- J2SE 1.4 - 2002年2月
- J2SE 5.0 - 2004年9月
- Java SE 6 - 2006年12月
- Java SE 7 - 2011年7月
- Java SE 8(LTS) - 2014年3月
- Java SE 9 - 2017年9月
- Java SE 10(18.3) - 2018年3月
- Java SE 11(18.9 LTS) - 2018年9月
- Java SE 12(19.3) - 2019年3月
- Java SE 13(19.9) - 2019年9月
- Java SE 14(20.3) - 2020年3月
- Java SE 15(20.9) - 2020年9月

我们可以看到Java SE的主要版本大约每两年发布一次,直到Java SE 6到Java SE 7开始花了五年时间,之后又花了三年时间到达Java SE 8。

1.2 OpenJDK和OracleJDK

1.2.1 Open JDK来源:

Java 由 Sun 公司发明,Open JDK是Sun在2006年末把Java开源而形成的项目。也就是说Open JDK是Java SE平台版的开源和免费实现,它由 SUN 和 Java 社区提供支持,2009年 Oracle 收购了 Sun 公司,自此 Java 的维护方之一的SUN 也变成了 Oracle

1.2.2 Open JDK 和 Oracle JDK的关系:

大多数 JDK 都是在 Open JDK 的基础上进一步编写实现的,比如 IBM J9, Oracle JDK 和 Azul Zulu,Azul Zing。

Oracle JDK完全由 Oracle 公司开发,Oracle JDK是基于Open JDK源代码的商业版本。此外,它包含闭源组件。Oracle JDK根据二进制代码许可协议获得许可,在没有商业许可的情况下,在2019年1月之后发布的Oracle Java SE 8的公开更新将无法用于商业或生产用途。但是 Open JDK是完全开源的,可以自由使用。

1.3 Open JDK 官网介绍

Open JDK 官网: http://openjdk.java.net/ 。
JDK Enhancement Proposals(JDK增强建议)。通俗的讲JEP就是JDK的新特性.

小结:

Oracle JDK是基于Open JDK源代码的商业版本。我们要学习Java新技术可以去Open JDK 官网学习。

在这里插入图片描述

二、Lambda表达式

在最开始我们先创建一个maven工程,很多东西需要添加依赖

2.1 需求分析

创建一个新的线程,指定线程要执行的任务:

package com.daozhang;

/**
 * @Author Administrator
 * @Date 2023/6/24 20:19
 * @description
 * @Version 1.0
 */
public class application {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("测试线程执行代码:"+Thread.currentThread().getName());
            }
        }).start();
        System.out.println("主线程执行的代码:"+Thread.currentThread().getName());
    }
}

代码分析:

  1. Thread类需要一个Runnable接口作为参数(线程的实现有三种实现方式),其中的抽象方法run方法是用来指定线程任务内容的核心。
  2. 为了指定run方法体,不得不需要Runnable的实现类。
  3. 为了省去定义一个Runnable 的实现类,不得不使用匿名内部类。
  4. 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错。
  5. 而实际上,我们只在乎方法体中的代码。

2.2 Lamada表达式的体验

Lambda表达式是一个匿名函数,可以理解为一段可以传递的代码。

new Thread(() -> { System.out.println("新线程Lambda表达式..."
+Thread.currentThread().getName()); })
.start();

Lambda表达式的优点:简化了匿名内部类的使用,语法更加简单。匿名内部类语法冗余,体验了Lambda表达式后,发现Lambda表达式是简化匿名内部类的一种方式。

2.3 Lambda表达式的语法规则

实际上Lambda省去了面向对象的条条框框,Lambda的标准格式由3个部分组成:

(参数类型 参数名称) -> {
代码体;
}

格式说明:

  • (参数类型 参数名称):参数列表
  • {代码体;} :方法体
  • -> : 箭头,分割参数列表和方法体

2.3.1 Lambda表达式练习

一、练习无参无返回值的Lambda:

1.1 首先定义一个接口:

package com.daozhang.student;

/**
 * @Author Administrator
 * @Date 2023/6/24 20:31
 * @description 定义一个Lambda表达式学生接口
 * @Version 1.0
 */
public interface Student {
    void gotoSchool();//大学生,主打一个喜欢上学
}

1.2 然后我们在主方法创建使用:

 //在这里我们调用loveComeToSchool,首先接口肯定是不能new的,这时候肯定是匿名方式
        //1、传统方式(匿名内部类方式)
        loveComeToSchool(new Student() {
            @Override
            public void gotoSchool() {
                System.out.println("匿名内部类方式上学:"+Thread.currentThread().getName());
            }
        });

        //2、Lambda表达式方式
        loveComeToSchool(()->{
            System.out.println("Lambda表达式方式,同样上学");
        });

1.3 结果展示:

匿名内部类方式上学:main
Lambda表达式方式,同样上学

1.4小结:可以看到,Lambda表达式十分简洁

2.3.2 Lambda表达式练习

二、练习有参无返回值的Lambda:

Java有两种方法,有参和无参,无参说了,聊聊有参!

2.1、创建对象

2.1.1这里使用到了Lombok,先导入依赖:

<dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

2.1.2、person类:

package com.daozhang.pojos;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author Administrator
 * @Date 2023/6/24 20:47
 * @description
 * @Version 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private String name;
    private Integer age;
    private Integer high;
}

2.1.3、然后我们在List集合中保存多个Person对象,然后对这些对象做根据age排序操作

package com.daozhang;

import com.daozhang.pojos.Person;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * @Author Administrator
 * @Date 2023/6/24 20:47
 * @description
 * @Version 1.0
 */
public class test02 {
    public static void main(String[] args) {
        List<Person> list = new ArrayList<>();
        list.add(new Person("王道长",18,185));//男神!!!
        list.add(new Person("诸葛青",20,183));//不听八卦
        list.add(new Person("不要碧莲",19,182));//月下光观鸟
        list.add(new Person("宝儿姐",18,168)); //阿威十八式
        //使用Collections对list进行排序
        //1、传统方式
        Collections.sort(list, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                //按照年龄排序
                return o1.getAge()-o2.getAge();
            }
        });
        System.out.println("传统:");
        for(Person person:list){
            System.out.println(person);
        }
        //2、Lambda方式
        Collections.sort(list,(Person o1,Person o2)->{
            return o1.getAge()-o2.getAge();
        });

        System.out.println("Lambda:");
        for(Person person:list){
            System.out.println(person);
        }
    }
}

结果:

"C:\Program Files\Java\jdk1.8.0_102\bin\java.exe" "-javaagent:D:\idea\IntelliJ IDEA 2021.3.2\lib\idea_rt.jar=7588:D:\idea\IntelliJ IDEA 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_102\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\rt.jar;D:\JDK8\JDK8\target\classes" com.daozhang.test02
传统:
Person(name=王道长, age=18, high=185)
Person(name=宝儿姐, age=18, high=168)
Person(name=不要碧莲, age=19, high=182)
Person(name=诸葛青, age=20, high=183)
Lambda:
Person(name=王道长, age=18, high=185)
Person(name=宝儿姐, age=18, high=168)
Person(name=不要碧莲, age=19, high=182)
Person(name=诸葛青, age=20, high=183)

Process finished with exit code 0

2.4 Lambda表达式的使用前提

可以看出,Lambda表达式的语法十分简洁,但是Lambda表达式不是随便使用的,使用的使用有几个条件我们需要特别注意:

一、方法的参数或局部变量类型必须为接口才能使用Lambda。
二、接口中有且只有一个抽象方法(在第五小段会提及一个注解@FunctionalInterfa)

2.5 @FunctionalInterface注解

我们在上面提及,Lambda表达式接口中有且只有一个抽象方法,那么我们怎么做到这一点呢?就是使用FunctionalInterface注解修饰

package com.daozhang.student;

/**
 * @Author Administrator
 * @Date 2023/6/24 20:31
 * @description 定义一个Lambda表达式学生接口
 * @Version 1.0
 */
@FunctionalInterface
public interface Student {
    /**
     * @FunctionalInterface:一个标准注解,被该注解修饰的接口只能声明一个抽象方法
     */
    void gotoSchool();
}

2.6 Lambda表达式的原理

匿名内部类的原理:匿名内部类的本质是在编译时生成一个Class 文件。XXXXX$1.class

package com.daozhang;

import com.daozhang.student.Student;

/**
 * @Author Administrator
 * @Date 2023/6/24 20:19
 * @description
 * @Version 1.0
 */
public class test01 {
    public static void main(String[] args) {
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                System.out.println("测试线程执行代码:"+Thread.currentThread().getName());
//            }
//        }).start();
//        System.out.println("主线程执行的代码:"+Thread.currentThread().getName());

        //在这里我们调用loveComeToSchool,首先接口肯定是不能new的,这时候肯定是匿名方式
        //1、传统方式(匿名内部类方式)
        loveComeToSchool(new Student() {
            @Override
            public void gotoSchool() {
                System.out.println("匿名内部类方式上学:"+Thread.currentThread().getName());
            }
        });

        //2、Lambda表达式方式
        loveComeToSchool(()->{
            System.out.println("Lambda表达式方式,同样上学");
        });
    }


    public static void loveComeToSchool(Student student){
        student.gotoSchool();
    }
}

在这里插入图片描述
还可以通过反编译工具来查看生成的代码 XJad 工具来查看:

// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space 
// Source File Name:   test01.java

package com.daozhang;

import com.daozhang.student.Student;
import java.io.PrintStream;

// Referenced classes of package com.daozhang:
//			test01

static class test01$1
	implements Student
{

	public void gotoSchool()
	{
		System.out.println((new StringBuilder()).append("匿名内部类方式上学:").append(Thread.currentThread().getName()).toString());
	}

	test01$1()
	{
	}
}

那么Lambda表达式的原理是什么呢?我们也通过反编译工具来查看:不仅看不了,还关不了!!!在这里插入图片描述
解决办法:ctrl+alt+'.'打开任务管理器,找到对应位置选择结束任务

那我们怎么反编译Lambda表达式的字节码文件呢?,使用JDK自带的javap命令

  • javap -c -p 文件名.class
    -c:表示对代码进行反汇编
    -p:显示所有的类和成员

反汇编结果:

final class com.daozhang.test02$1 implements java.util.Comparator<com.daozhang.pojos.Person> {
  com.daozhang.test02$1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public int compare(com.daozhang.pojos.Person, com.daozhang.pojos.Person);
    Code:
       0: aload_1
       1: invokevirtual #2                  // Method com/daozhang/pojos/Person.getAge:()Ljava/lang/Integer;
       4: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
       7: aload_2
       8: invokevirtual #2                  // Method com/daozhang/pojos/Person.getAge:()Ljava/lang/Integer;
      11: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
      14: isub
      15: ireturn

  public int compare(java.lang.Object, java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #4                  // class com/daozhang/pojos/Person
       5: aload_2
       6: checkcast     #4                  // class com/daozhang/pojos/Person
       9: invokevirtual #5                  // Method compare:(Lcom/daozhang/pojos/Person;Lcom/daozhang/pojos/Person;)I
      12: ireturn
}

小结:
匿名内部类在编译的时候会产生一个class文件。
Lambda表达式在程序运行的时候会形成一个类。

  1. 在类中新增了一个方法,这个方法的方法体就是Lambda表达式中的代码。
  2. 还会形成一个匿名内部类,实现接口,重写抽象方法。
  3. 在接口中重写方法会调用新生成的方法。

2.7 Lambda表达式的省略写法

在lambda表达式的标准写法基础上,可以使用省略写法的规则为:

  1. 小括号内的参数类型可以省略
  2. 如果小括号内有且仅有一个参数,则小括号可以省略
  3. 如果大括号内有且仅有一个语句,可以同时省略大括号,return 关键字及语句分号。
package com.daozhang;

import com.daozhang.student.Student;

/**
 * @Author Administrator
 * @Date 2023/6/25 18:52
 * @description
 * @Version 1.0
 */
public class test03 {
    public static void main(String[] args) {
        //省略写法一:省略参数、大括号
       gotos(()->System.out.println("上学去了"));
    }
    public static void gotos(Student student){
        student.gotoSchool();
    }
}

结果:

上学去了

2.8 Lambda表达式和内部类的区别

Lambda和匿名内部类的对比

  1. 所需类型不一样
    匿名内部类的类型可以是 类,抽象类,接口
    Lambda表达式需要的类型必须是接口
  2. 抽象方法的数量不一样
    匿名内部类所需的接口中的抽象方法的数量是随意的
    Lambda表达式所需的接口中只能有一个抽象方法
  3. 实现原理不一样
    匿名内部类是在编译后形成一个class
    Lambda表达式是在程序运行的时候动态生成class

三、接口的增强

3.1 JDK8接口新增

一、JDK8之前:

interface 接口名{
    1、静态常量
    2、抽象方法
};

二、JDK8之后: 可以有默认方法和静态方法

interface 接口名{
   1、静态常量;
   2、抽象方法;
   3、默认方法;
   4、静态方法;
}

3.2 默认方法

一、为什么要有默认方法?
在JDK8以前接口中只能有抽象方法和静态常量,会存在以下的问题:
如果接口中新增抽象方法,那么实现类都必须要抽象这个抽象方法,非常不利于接口的扩展的:
当接口只有一个方法时,实现类去重写:

package com.daozhang;

/**
 * @Author Administrator
 * @Date 2023/6/25 19:33
 * @description
 * @Version 1.0
 */
public class test04 {
    public static void main(String[] args) {

    }
}
interface animal{
    void eat();
}
class cat implements animal{

    @Override
    public void eat() {

    }
}
class dog implements animal{

    @Override
    public void eat() {
    }
}

当接口有很多方法时,实现类也可以重写,但是每一个实现类都去重写它不需要的方法,这合理吗?:

package com.daozhang;

/**
 * @Author Administrator
 * @Date 2023/6/25 19:33
 * @description
 * @Version 1.0
 */
public class test04 {
    public static void main(String[] args) {

    }
}
interface animal{
    void run();
    void sing();
    void eat();
}
class cat implements animal{
     //必须重写,不然报错
    @Override
    public void eat() {

    }
}
class dog implements animal{
   //必须重写,不然报错

    @Override
    public void eat() {
    }
}

可以看出,上面接口显然不符合我们的要求,所以在JDK8之后,就引入(增加)了默认方法。

二、默认方法的格式
以下是默认方法的格式:

interface 接口名{
修饰符 default 返回值类型 方法名{
方法体;
}
}

默认方法就是你想重写就重写,不重写也不会强制你实现

package com.daozhang;

/**
 * @Author Administrator
 * @Date 2023/6/25 19:33
 * @description
 * @Version 1.0
 */
public class test04 {
    public static void main(String[] args) {
        animal cat = new cat();
        cat.gotoSchool();
    }
}
interface animal{
    void eat();
    public default String gotoSchool(){
        System.out.println("谁说动物不用上学的?");
        return null;
    }
}
class cat implements animal{

    @Override
    public void eat() {

    }
}
class dog implements animal{

    @Override
    public void eat() {
    }
}

二、接口中默认方法的使用:

  • 接口中的默认方法有两种使用方式
    1. 实现类直接调用接口的默认方法
    1. 实现类重写接口的默认方法

3.3 静态方法

一、在JDK8中在接口中增加了静态方法,作用同样是为了增加接口的扩展性

二、语法规则:

interface 接口名{
修饰符 static 返回值类型 方法名{
方法体;
}
}
package com.daozhang;

/**
 * @Author Administrator
 * @Date 2023/6/25 19:33
 * @description
 * @Version 1.0
 */
public class test04 {
    public static void main(String[] args) {
        animal cat = new cat();
        cat.gotoSchool();
    }
}
interface animal{
    void eat();
    public default String gotoSchool(){
        System.out.println("谁说动物不用上学的?");
        return null;
    }
    public static void makeFriend(){
        System.out.println("想找个女朋友");
    }
}
class cat implements animal{

    @Override
    public void eat() {

    }
}
class dog implements animal{

    @Override
    public void eat() {
    }
}

静态方法的使用:
接口中的静态方法在实现类中是不能被重写的,调用的话只能通过接口类型来实现: 接口名.静态方法名();
注意:这里是不能重写,而不是不需要

3.4 两个方法之间的区别

  1. 默认方法通过实例调用,静态方法通过接口名调用
  2. 默认方法可以被继承,实现类可以直接调用接口默认方法,也可以重写接口默认方法
  3. 静态方法不能被继承,实现类不能重写接口的静态方法,只能使用接口名调用

四、函数式接口

4.1 函数式接口的由来

我们知道使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方法,在JDK中提供了大量常用的函数式接口。

/**
 * @Author Administrator
 * @Date 2023/6/25 21:01
 * @description
 * @Version 1.0
 */
public class functionTest01 {
    public static void main(String[] args) {
        funSum(arr->{
            int sum = 0;
            for(int i:arr){
                sum+=i;
            }
            return sum;
        });
    }
    public static void funSum(Operator operator){
        int[] arr ={5,6,7,8,9};
        int sum = operator.getArr(arr);
        System.out.println(sum);
    }
}
/*
* 函数式接口
*/
@FunctionalInterface
interface Operator{
    int getArr(int[] arr);
}

4.2 函数式接口的介绍

在JDK中帮我们提供的有函数式接口,主要是在 java.util.function 包中。

4.2.1 Supplier

一、无参有返回值的接口,对于的Lambda表达式需要提供一个返回数据的类型。

/**
 * Represents a supplier of results.
 *
 * <p>There is no requirement that a new or distinct result be returned each
 * time the supplier is invoked.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #get()}.
 *
 * @param <T> the type of results supplied by this supplier
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

二、简单使用

/**
 * @Author Administrator
 * @Date 2023/6/25 21:17
 * @description
 * @Version 1.0
 */
public class Supplier {
    public static void main(String[] args) {
        fun(()->{
            int[] arr = {250,361,520};
            Arrays.sort(arr);
            return arr[arr.length-1];
        });
    }
    public static void fun(java.util.function.Supplier<Integer> supplier){
        Integer number = supplier.get();
        System.out.println(number);
    }
}

4.2.2 Consumer

一、有参无返回值得接口,前面介绍的Supplier接口是用来生产数据的,而Consumer接口是用来消费数据的,使用的时候需要指定一个泛型来定义参数类型。

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

二、简单使用

/**
 * @Author Administrator
 * @Date 2023/6/25 21:49
 * @description
 * @Version 1.0
 */
public class ConsumerTest {
    public static void main(String[] args) {
       fun2((t)->{
       //在方法体有动作才会执行方法,否则不会执行方法
           System.out.println(t);
       });
    }
    public static  void fun2(Consumer<String> consumer){
        consumer.accept("唱跳rap");
    }
}

默认方法:andThen
如果一个方法的参数和返回值全部是Consumer类型,那么就可以实现效果,消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合,而这个方法就是Consumer接口中的default方法

andThen方法

    /**
    * Returns a composed {@code Consumer} that performs, in sequence, this
    * operation followed by the {@code after} operation. If performing either
    * operation throws an exception, it is relayed to the caller of the
    * composed operation.  If performing this operation throws an exception,
    * the {@code after} operation will not be performed.
    *
    * @param after the operation to perform after this operation
    * @return a composed {@code Consumer} that performs in sequence this
    * operation followed by the {@code after} operation
    * @throws NullPointerException if {@code after} is null
    */
   default Consumer<T> andThen(Consumer<? super T> after) {
       Objects.requireNonNull(after);
       return (T t) -> { accept(t); after.accept(t); };
   }

测试:

/**
 * @Author Administrator
 * @Date 2023/6/25 22:08
 * @description
 * @Version 1.0
 */
public class ConsumerAndThenTest {
    public static void main(String[] args) {
        Test2(msg1->{
            //转大写
            System.out.println(msg1.toUpperCase());
        },msg2->{
            //转小写
            System.out.println(msg2.toLowerCase());
        });
    }
    public static void Test2(Consumer<String> t1,Consumer<String> t2){
        String str = "Out of control";
        t2.andThen(t1).accept(str);
    }
}

结果:

out of control
OUT OF CONTROL

分析,观察到与我们在调用的时候写的顺序不一样,为什么呢?
原理:

我们在Test2方法里使用的是t2.andThen(t1),也就是在t2作为参数传递执行对应的方法之后执行t1,andThen的意思就是什么之后做这件事情。

4.2.3 Function

有参有返回值的接口,Function接口是根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。

@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);


使用:输入一个字符串,返回一个整数。


/**
 * @Author Administrator
 * @Date 2023/6/25 22:35
 * @description
 * @Version 1.0
 */
public class FunctionTest {
    public static void main(String[] args) {
        Test04((arg)->{
            Integer res = Integer.valueOf(arg);
            return res;
        });
    }
    public static void Test04(Function<String,Integer> function){
        int gif = function.apply("18");
        System.out.println(gif);
    }
}

结果:

18

默认方法:andThen,也是用来进行组合操作。

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

使用:

/**
 * @Author Administrator
 * @Date 2023/6/25 22:49
 * @description
 * @Version 1.0
 */
public class FunctionAndThenTest {
    public static void main(String[] args) {
        fun(arg1->{
            return Integer.valueOf(arg1);
        },arg2->{
            return arg2*10;
        });
    }
    public static void fun(Function<String,Integer> t1,Function<Integer,Integer> t2){
        int res = t1.andThen(t2).apply("18");
        System.out.println(res);
    }
}

注意,由于t1——》t2具有传递性,所有t2的 泛型要跟t1转换后的泛型对应

默认的compose方法的作用顺序和andThen方法刚好相反
而静态方法identity则是,输入什么参数就返回什么参数

4.2.4 Predicate

有参且返回值为Boolean的接口

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

使用:

/**
 * @Author Administrator
 * @Date 2023/6/25 23:00
 * @description
 * @Version 1.0
 */
public class PredicateTest {
    public static void main(String[] args) {
        fun((arg)->{
            boolean res = arg.equals("不需要吗?");
            return res;
        });
    }
    public static void fun(Predicate<String> msg){
        boolean res = msg.test("爱一个人需要理由吗?");
        System.out.println(res);
    }
}

其他扩展方法:

    /**
     * Returns a predicate that represents the logical negation of this
     * predicate.
     *
     * @return a predicate that represents the logical negation of this
     * predicate
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * OR of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code true}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ORed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * OR of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * Returns a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}.
     *
     * @param <T> the type of arguments to the predicate
     * @param targetRef the object reference with which to compare for equality,
     *               which may be {@code null}
     * @return a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }

在Predicate中的默认方法提供了逻辑关系操作 and or negate isEquals方法:
使用:

/**
 * @Author Administrator
 * @Date 2023/6/25 23:09
 * @description
 * @Version 1.0
 */
public class PredicateDemoTest {
    public static void main(String[] args) {
        fun(arg1->{
            return arg1.contains("爱");
        },arg2->{
            return arg2.contains("pf");
        });
    }
    public static void fun(Predicate<String> arg1,Predicate<String> arg2){
        boolean And = arg1.and(arg2).test("不说一句的爱有多好");
        boolean Or = arg1.or(arg2).test("不说一句的爱有多好");
        boolean Negate = arg1.negate().test("不说一句的爱有多好");
        System.out.println(And);
        System.out.println(Or);
        System.out.println(Negate);
    }
}

结果:

false
true
false

结语

写到这里总字数也有两万+了,然后觉得文章太长,看的人恐怕不多,一般就看看就划走了,故而分为上下两部,上部就先到函数式接口。归根结底,只有练习才能真正理解用法和体会这种思维,加油。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/685156.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MATLAB | 如何使用MATLAB获取顶刊《PNAS》绘图(附带近3年图像)

千呼万唤始出来&#xff0c;《PNAS》绘图获取的代码来啦&#xff0c;不过这次研究了半天也没想到如何获取付费文章的绘图&#xff0c;就只下载了免费文章(主要也怕侵权)&#xff0c;不过光免费文章的图片三年了也有接近1.7w张了&#xff0c;同时使用代码下载时依旧需要科学上网…

【Redis】Redis的数据结构

【Redis】Redis的数据结构 文章目录 【Redis】Redis的数据结构1. 动态字符串SDS2. IntSet2.1 IntSet升级 3. Dict3.1 Dict的扩容3.2 Dict的收缩3.3 Dict的rehash 4. ZipList4.1 ZipList中的Entry4.1.1 Encoding编码 4.2 ZipList的连锁更新问题4.3 特性 5. QuickList 1. 动态字符…

【软考网络管理员】2023年软考网管初级常见知识考点(7)-生成树协议

涉及知识点 STP的原理&#xff0c;端口的状态&#xff0c;RSTP协议&#xff0c;MSTP协议&#xff0c;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 原创于&#xff1a;CSDN博主-《拄杖盲学轻声码》&#xff0c;更多考点汇总可…

模拟电路系列分享-运放的关键参数2

目录 概要 整体架构流程 技术名词解释 1.输入偏置电流&#xff1a; 2.输入失调电流 技术细节 总结; 概要 提示&#xff1a;这里可以添加技术概要 实际运放与理想运放具有很多差别。理想运放就像一个十全十美的人&#xff0c;他学习100 分&#xff0c;寿命无限长&#xff0c;长…

Modal对话框(antd-design组件库)展示所有配置选项和onChange的作用

1.Modal对话框 模态对话框。 2.何时使用 需要用户处理事务&#xff0c;又不希望跳转页面以致打断工作流程时&#xff0c;可以使用 Modal 在当前页面正中打开一个浮层&#xff0c;承载相应的操作。 另外当需要一个简洁的确认框询问用户时&#xff0c;可以使用 App.useApp 封装的…

创建微信小程序的几种方式

创建微信小程序的几种方式 1. 使用原生方式 在官网上下载微信开发者工具&#xff0c;之后使用微信开发者工具新建项目即可。 微信这边提供了多个模板&#xff0c;可以直接下载模板快速搭建上线&#xff0c;也可以使用空白模板根据需求自行编写。 空白模板项目结构&#xff1…

C语言:打印0-100000中的自幂数(水仙花数是其中一种)

题目&#xff1a; 求出 0&#xff5e;100000 之间的所有 自幂数 并输出。 自幂数是指一个n位数&#xff0c;其各位数字的n次方之和恰好等于该数本身&#xff0c; 如:153&#xff1d;1^3&#xff0b;5^3&#xff0b;3^3&#xff0c; 则153是一个自幂数。 思路&#xff1a; 总体…

【数据网格架构】分布式数据网格作为集中式数据单体的解决方案

企业数据架构师不应构建大型集中式数据平台&#xff0c;而应创建分布式数据网格。 ThoughtWorks 的首席技术顾问 Zhamak Dehghani 在旧金山 QCon 的演讲和相关文章中表示&#xff0c;这种方法的改变需要范式转变。随着数据变得越来越普遍&#xff0c;传统的数据仓库和数据湖架构…

Linux基础+命令操作+mysql、tomcat、nginx、RabbitMQ、Redis,ElasticSearch

配置代理 一、永久设置 //编辑配置文件 vi /etc/profile //在该配置文件的最后添加代理配置 export http_proxyhttp://f1336515:password10.137.255.169:3128 //代理服务器ip地址和端口号 export https_proxyhttp://f1336515:password10.137.255.169:3128 //代理服务器ip…

【软考网络管理员】2023年软考网管初级常见知识考点(11)-TCP和UDP详解

涉及知识点 传输控制协议TCP是什么&#xff0c;三次握手的概念理解&#xff0c;用户数据报协议UDP是什么&#xff0c;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 原创于&#xff1a;CSDN博主-《拄杖盲学轻声码》&#xff0…

AntV G6新版源码浅析

前言 AntV是蚂蚁金服全新一代数据可视化解决方案&#xff0c;其中G6主要用于解决图可视领域相关的前端可视化问题&#xff0c;其是一个简单、易用、完备的图可视化引擎。本文旨在通过简要分析G6 5.x版本源码来对图可视领域的一些底层引擎进行一个大致了解&#xff0c;同时也为…

【玩转Linux操作】详细讲解expr,read,echo,printf,test,[]等命令

&#x1f38a;专栏【玩转Linux操作】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【free loop】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 文章目录 &#x1f354;expr命令⭐表达式说明 &#x1f3…

JAVA:Springboot动态装配Druid多数据源

1、简介 最近打算搭建一个鉴权中心服务&#xff0c;采用springbootFastMybatis装配Druid&#xff0c;考虑后续拓展采用Druid多数据源配置&#xff0c;以一个数据源为主&#xff0c;多个动态数据源为辅的结构。除了数据库&#xff0c;后续会结合shiro安全框架来搭建。 2、引用…

【Leetcode60天带刷】day33回溯算法——1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果

​ 题目&#xff1a; 1005. K 次取反后最大化的数组和 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&am…

将视频转为幻灯片图像:利用OpenCV实现视频资料转换的指南

视频成为了传播知识和信息的重要媒介之一。然而&#xff0c;有时我们需要以静态的形式保存视频内容&#xff0c;例如将视频讲座转换为幻灯片或图像&#xff0c;以便于分享、存档或打印。幸运的是&#xff0c;OpenCV这一功能强大的计算机视觉库提供了各种技术和工具&#xff0c;…

机器学习之线性回归算法

目录 线性回归算法 求导法推导 梯度下降法推导 线性回归实现人脸识别 导入数据 构建标签矩阵 经典线性回归求导法实现 经典线性回归梯度下降法实现 岭回归实现 套索回归实现 局部加权线性回归实现 可视化 人脸识别 线性回归算法 求导法推导 梯度下降法推导 线性回…

chatgpt赋能python:Title:Python编程中的空格怎么用?详细教程!

Title: Python编程中的空格怎么用&#xff1f;详细教程&#xff01; Introduction: Python编程的空格使用一直是令人困惑的话题之一&#xff0c;但它却是Python语言中非常重要的一部分。空格在Python程序中用来表示代码块的开始和结束&#xff0c;因此不同的空格使用方式可能…

【夜深人静学数据结构与算法 | 第十篇】动态规划

目录 前言&#xff1a; 动态规划&#xff1a; 常见应用&#xff1a; 解题步骤&#xff1a; 动态规划的简化步骤&#xff1a; 案例&#xff1a; 509. 斐波那契数 - 力扣&#xff08;LeetCode&#xff09; 70. 爬楼梯 - 力扣&#xff08;LeetCode&#xff09; 62. 不同路…

【软考网络管理员】2023年软考网管初级常见知识考点(10)- 网际协议IP及IPV6,IPV4详解

涉及知识点 分类的IP地址&#xff0c;子网划分&#xff0c;CIDR和路由汇聚&#xff0c;IPV4数据报格式&#xff0c;IPV6协议&#xff0c;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 原创于&#xff1a;CSDN博主-《拄杖盲学…

Java的理论知识部分

文章目录 前言 一、Java的发展 1.1、Java的出现 1.2、Java官方网址 1.3、Java的平台 1.4、Java各版本新加的内容 1.5、java特点 1.6、Java的三种运行机制 1.7、Java的编译与运行 1.8、补充内容——华为鲲鹏jdk以及鲲鹏计算 二、面向对象程序编程 2.1、对象与类 2.2、Ja…