Java SE : java8新特性(上)

简介: Java SE : java8新特性(上)

编译软件:IntelliJ IDEA 2019.2.4 x64

运行环境:win10 家庭中文版

jdk版本:1.8.0_361

一. 函数式编程思想

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。编程中的函数,也有类以的概念,你调用我的时候,给我实参为形参赋值,然后通过运行方法体,给你返回一个结果。

对于调用者来做,关注汶个方法具备什么样的功能。相对而言,面向对象过分强调"必须涌过对象的形式来做事情",而函数式思想则尽量忽略面向对象的复杂语法一一强调做什么,而不是以什么形式做。

面向对象的思想:

做一件事情,找一个能解决这个事情的对象调用对象的方法,完成事情

函数式编程思想:

只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程


二. 函数式接口

函数式接口是指一种特殊的接口,SAM接口 (Single Abstract Method) 只有唯一抽象方法的接口。

说明: 抽象只有唯一的一个,但是对于非抽象方法不限制。

函数式接口的概念时JDK1.8引入的,然后它除了要求接口是SAM特征之外,还强烈建议接口的声明上方加一个注解:

@FunctionalInterface 函数式接口的注解

回忆之前学过的接口中,哪些接口符合SAM接口特征的?

(1)java.util.Comparator int compare(T t1 ,T t2)

(2)java.lang.Comparable int compareTo(T t)

(3)java.lang.Runnable void run()

(4)java.util.function.Predicate boolean test()

(5)java.lang.Iterable<>接口 Iterator iterator()

(6)java.io.FileFilter boolean accept(File pathname)

java.io.Serializable接口,不是,没有抽象方法

java.Lang.CLoneabLe接口,不是,没有抽象方法

java.util.Iterator接口,不是,有两个抽象方法boolean hasNext()

E next)

java.util.Collection、List、Set、Oueue、Deque、Map<K,V>,不是,有很多抽象方法

上面这些SAM接口中,哪些有@FunctionalInterface注解:

(1)java.util.Comparator int compare(T t1 ,T t2)

(2)java.lang.Runnable void run() boolean test() (这个其实也是新的)

(3)java.util.function .Predicate

(4) java.io.FileFilter boolean accept(File pathname)

Java8在java.util.function包中新增了很多很多的函数式接口。

三. 新版函数式接口的四大经典代表

(1) 消费型接口代表

Consumer

抽象方法: void accept(T t)

特点: 有一个参数,返回值是void,表示无返回值。

调用这个抽象方法时,相当于你要给它一个实参,但是得不到返回值,相当于 有去无回,纯消费行为。

(2) 供给型接口代表/泰献型接口代表

Supplier

抽象方法 T get()

特点:没有参数,有返回值

调用这个抽象方法时,相当于你不用给它传参数,却可以得到一个返回值,相当于 空手套白狼。

(3)判断型接口/断定型接口代表

Predicate

抽 象方法 boolean test(T t)

特点: 有一个参数,返回值类型是固定的boolean

调用这个抽象方法时,相当于你给它一个参数之后,它会告诉你这个参数是否满足xx条件,满足就返回true,否则就返回false

(4)功能型接口代表

Function<T,R>

抽象方法 RLapply(T t)

特点是:有一个参数,有一个返回值

调用这个抽象方法时,相当于你要给它一个实参,同时也 可以得到一个返回值,相当于礼尚往来

四. Lambda 表达式

4.1 Lambda表达式的作用

给函数式接口的变量或形参赋值用的。传递一段代码这段代码本质上就是西数式接口的抽象方法的方法体。

4.2 语法格式

(形参列表) ->{Lambda体)

(形参列表) : 是函数式接口的抽象方法的形参列表

->: 称为Lambda操作符,中间不能有空格

{Lambda体: 就是函数式接口的抽象方法的方法体

代码演示如下

//为何不用JUite的test方法去测试,该方法有一个缺点,在其内部使用多线程,有可能test里的线程还没启动就被test干掉了
//相比而言,main方法会等待里面的线程全部执行完,相比较而言,main方法比较保险
public static void main(String[] args) {
    //使用lambda表达式对Runnable接口的变量赋值。要实现打印一句话: hello lambda
    //Runnable接口的抽象方法: void run()
    Runnable r= () -> {
        System.out.println("hello lambda");  //里面的;是输出语句的
    }; //外面的;是 赋值语句 + lambda表达式的
    new Thread(r).start(); //完全等价于下面的代码
    System.out.println("-----------------");
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("hello lambda");
        }
    }).start();
}

4.3 Lambda表达式在某些情况下,可以简化

(1)当{Lambda体}里面只有一个语句时,那么可以省略 {},同时省略里面的;

代码演示如下:

Runnable r= () -> {
        System.out.println("hello lambda");  //里面的;是输出语句的
    }; //外面的;是 赋值语句 + lambda表达式的
    new Thread(r).start();
@Test
public void test01(){
    //精简如下
    Runnable r= () -> System.out.println("hello lambda");
    new Thread(r).start();
}

(2)当Lambda体)里面只有一个语句时,那么可以省略,同时省略里面的;如果此时这个语句是一个return语句,那么要连同return省掉

代码演示如下:

//案例:在一个全是字符串的集合中,删除 包含“o”字母的单词
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("atguigu");
list.add("java");
list.add("bigdata");
//删除 包含“o”字母的单词
//匿名类的形式
list.removeIf(new Predicate<String>() {
    @Override
    public boolean test(String s) {
        return s.contains("o");
    }
});
System.out.println(list);
 //从匿名类转换为lambda表达式
        list.removeIf(
                (String s) -> {
                    return s.contains("o");
                }
        );
        //上面的lambda式子精简如下
        list.removeIf(
               (String s) -> s.contains("o")
        );
        System.out.println(list);

(3)当(形参列表)的形参类型是已知的,或者可以自动推断的,那么形参列表的类型可以省略

代码演示如下:

//接上面的案例
//精简如下
list.removeIf(
        (s) -> s.contains("o")
);

💡如果此时省略了形参的数据类型之后,只剩下一个形参,它是这样的(形参名),那么此时()也可以省略

代码演示如下:

//再精简lambda表达式
list.removeIf(
        s -> s.contains("o")
);

🔔说明:

如果形参列表是(),()不能省略

如果形参不止一个,()也不能省略

如果Lambda体中不止一个语句等也不能省略。

代码演示如下:

@Test
public void test03(){
    String[] arr = {"hello","Bob","Rose","java","chai"};
    //使用匿名类
    Arrays.sort(arr, new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o1.compareToIgnoreCase(o2);
        }
    }
    );
    //使用lambda表达式
    Arrays.sort(arr, (o1,o2) -> o1.compareToIgnoreCase(o2) );
}

五. 消费型接口与lambda 表达式

消费型接口Consumer抽象方法:

void accept(T t)

有参无返回值

例子:

JDK1.8 java.Lang.terable接口增加了一个默认方法,

public default void forEach(Consumer<? super T> action)

Iterable接口增加了方法,意味着所有的CoLLection系列的集合都有这个方法。因为ColLection接口继承了Iterable接口

代码演示如下:

@Test
    public void test04(){
        ArrayList<String> list = new ArrayList<>();list.add("hello");
        list.add("world");
        list.add("atguigu");
        list.add("java");
        list .add("bigdata");
        //forEach方法的功能是,遍历集合,并对集合的每一个元素做xx事情,具体做什么事情
        // 由Consumer接口的accept抽象方法决定
        //使用匿名内部类的形式
        /*list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
*/
        //使用lambda表达式
        list.forEach((s) -> System.out.println(s) );
    }


六. 供给型接口与lambda表达式

供给型接口Supplier 抽象方法:

T get() 无参有返回值

例如: 使用Lambda表达式给一个Supplier类型的变量赋值一个“轨道”

代码演示如下:

@Test
public void test05(){
    Supplier<String> s= ()-> {return "轨道";};
    //镜检如下
    Supplier<String> str= ()-> "轨道";
    methiod(s);
    //注意以下,在方法()中并非为lambda式的精简缩写,而是str的替换
    methiod(()-> "轨道");
}
//泛型方法
public <T> void  methiod(Supplier<T> s){
    System.out.println(s.get());
}


七. 判断型接口与lambda表达式

案例:在一个全是字符串的集合中,删除 包含“o”字母的单词

代码演示如下:

ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("atguigu");
list.add("java");
list.add("bigdata");
//删除 包含“o”字母的单词
//匿名类内部类的形式
list.removeIf(new Predicate<String>() {
    @Override
    public boolean test(String s) {
        return s.contains("o");
    }
});
//精简如下
list.removeIf(
        s -> s.contains("o")
);
System.out.println(list);

八. 功能型接口与lambda表达式

案例一:使用Lambda表达式,给一个Function接口的变量赋值完成给某个字符串的首字母转为大写。

思路:

把字符串的首字母变为大写怎么实现?

(1) 把字符串的首字母拿出来,转为大写 (2) 把转换后的首字母 拼接上原来字符串除了首字母的部分,例如"heLlo"

把h拿出来,转为H,然后用H 与eLLo"拼接

Function<T,R> 抽象方法 R apply(T t) 有参有返回值

代码演示如下:

@Test
public void test06(){
    //某个字符串的首字母转为大写
    //Function<T,R> T是指定形参类型,R是指定返回值类型
    Function<String,String> f= s -> Character.toUpperCase(s.charAt(0))+s.substring(1);
    System.out.println(f.apply("hello"));
}

JDK1.8中Map接口新增了一个方法:

default void replaceAll(BiFunction<? super K,? super V,? extends V> function)

BiFunction<T,U,R>函数式接口,它的抽象方法 R apply(T t,u u)有2个参数,一个返回值。

案例二:将所有的value转换为大写

代码演示如下:

@Test
public void test07(){
    //将所有的value转换为大写
    HashMap<Integer,String> map=new HashMap<>();
    map.put(1,"jack");
    map.put(2,"hello");
    map.replaceAll((key,value) -> value.toUpperCase());
    System.out.println(map);
}


九. lambda 表达式综合案例

9.1 案例一

创建一个ArrayList,并添加26个小写字母到List中,并使用forEach遍历输出

方法思路:

第一步:

搞清楚forEach 方法的方法签名
方法 = 方法头 + 方法体,方法头又被称为方法签名。
方法头:[修饰符] 返回值类型
方法名([形参列表] )[throws 异常列表] 方法体:[语句代码}

void forEach(Consumerk? super E> action)搞清楚Consumer接口的抽象方法,它是一个函数式接口,就可以使用Lambda表达式

第二步:

抽象方法: void accept(T t) 【有参无返回值】
对于函数式接口的抽象方法来说,用Lambda表达式给他赋值时,不关心方法名,关心形参列表和返回值类型。

代码演示如下:

@Test
public void test08(){
    ArrayList<Character> list=new ArrayList<>();
    for (char i = 'a'; i <='z' ; i++) {
        list.add(i);
    }
    System.out.println(list);
    System.out.println("------------------");
    list.forEach(c -> System.out.print(c+"\t"));
}

9.2 案例二

创建一个HashMap,并添加如下编程语言排名和语言名称到map中,并使用forEach遍历输出

排名 语言
1 Java
2 c
3 python
4 c++
5 c#

代码演示如下:

@Test
public void test09(){
    HashMap<Integer,String> map=new HashMap<>();
    map.put(1,"java");
    map.put(2,"c");
    map.put(3,"python");
    map.put(4,"c++");
    map.put(5,"c#");
    map.forEach((key,value) -> System.out.println(key+":"+value));
}

相关文章
|
24天前
|
安全 前端开发 Java
随着企业应用复杂度提升,Java Spring框架以其强大与灵活特性简化开发流程,成为构建高效、可维护应用的理想选择
随着企业应用复杂度提升,Java Spring框架以其强大与灵活特性简化开发流程,成为构建高效、可维护应用的理想选择。依赖注入使对象管理交由Spring容器处理,实现低耦合高内聚;AOP则分离横切关注点如事务管理,增强代码模块化。Spring还提供MVC、Data、Security等模块满足多样需求,并通过Spring Boot简化配置与部署,加速微服务架构构建。掌握这些核心概念与工具,开发者能更从容应对挑战,打造卓越应用。
32 1
|
14天前
|
安全 Java API
告别繁琐编码,拥抱Java 8新特性:Stream API与Optional类助你高效编程,成就卓越开发者!
【8月更文挑战第29天】Java 8为开发者引入了多项新特性,其中Stream API和Optional类尤其值得关注。Stream API对集合操作进行了高级抽象,支持声明式的数据处理,避免了显式循环代码的编写;而Optional类则作为非空值的容器,有效减少了空指针异常的风险。通过几个实战示例,我们展示了如何利用Stream API进行过滤与转换操作,以及如何借助Optional类安全地处理可能为null的数据,从而使代码更加简洁和健壮。
42 0
|
2天前
|
安全 Java API
Java 18 概述:新特性一览
Java 18 作为 Java 平台的最新版本,引入了多项令人振奋的新特性和改进,包括模式匹配、记录类型、流库改进、外部函数与内存 API 以及并发处理增强。这些新功能不仅提升了开发者的生产力,还显著增强了 Java 的性能和安全性。本文将详细介绍 Java 18 的主要新特性,并通过代码示例帮助读者更好地理解和应用这些功能。
|
12天前
|
Java API
Java 8新特性:Lambda表达式与Stream API的深度解析
【7月更文挑战第61天】本文将深入探讨Java 8中的两个重要特性:Lambda表达式和Stream API。我们将首先介绍Lambda表达式的基本概念和语法,然后详细解析Stream API的使用和优势。最后,我们将通过实例代码演示如何结合使用Lambda表达式和Stream API,以提高Java编程的效率和可读性。
|
15天前
|
Java 开发者
Java 8新特性之Lambda表达式与函数式接口
【7月更文挑战第59天】本文将介绍Java 8中的一个重要新特性——Lambda表达式,以及与之密切相关的函数式接口。通过对比传统的匿名内部类,我们将探讨Lambda表达式的语法、使用方法和优势。同时,我们还将了解函数式接口的定义和用途,以及如何将Lambda表达式应用于函数式编程。
|
24天前
|
分布式计算 Java API
Java 8带来了流处理与函数式编程等新特性,极大提升了开发效率
Java 8带来了流处理与函数式编程等新特性,极大提升了开发效率。流处理采用声明式编程模型,通过filter、map等操作简化数据集处理,提高代码可读性。Lambda表达式支持轻量级函数定义,配合Predicate、Function等接口,使函数式编程无缝融入Java。此外,Optional类及新日期时间API等增强功能,让开发者能更优雅地处理潜在错误,编写出更健壮的应用程序。
24 1
|
26天前
|
自然语言处理 Java 应用服务中间件
Java 18 新特性解析
Java 18 新特性解析
|
8天前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
64 6
【Java学习】多线程&JUC万字超详解
|
2天前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。
|
1天前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。