JDK8新特性(二)

        接着上一章,我们继续学习jdk8新特性

目录

一、Optional

1.使用

1.1创建对象

 1.2安全消费值

1.3获取值

1.4安全获取值

 1.5过滤

1.6判断

1.7数据转换

 二、函数式接口

1.常见函数式接口

2.常用的默认方法

 三、方法引用

1. 推荐用法

2.基本格式

3.语法详解(了解)

3.1引用类的静态方法

4. 引用对象的实例方法

 5.引用类的实例方法

 6. 构造器引用

 四、高级用法

  1.基本数据类型优化

2.并行流

五、结束语


一、Optional

        我们在编写代码的时候出现最多的就是空指针异常。所以在很多情况下我们需要做各种非空的判断。

例如:

        Author author = getAuthor();if(author!=null){System.out.println(author.getName());}

​ 尤其是对象中的属性还是一个对象的情况下。这种判断会更多。

​ 而过多的判断语句会让我们的代码显得臃肿不堪。

​ 所以在JDK8中引入了Optional,养成使用Optional的习惯后你可以写出更优雅的代码来避免空指针异常。

​ 并且在很多函数式编程相关的API中也都用到了Optional,如果不会使用Optional也会对函数式编程的学习造成影响

1.使用

1.1创建对象

        Optional就好像是包装类,可以把我们的具体数据封装Optional对象内部。然后我们去使用Optional中封装好的方法操作封装进去的数据就可以非常优雅的避免空指针异常。

         我们一般使用Optional的静态方法ofNullable来把数据封装成一个Optional对象。无论传入的参数是否为null都不会出现问题。

        Author author = getAuthor();Optional<Author> authorOptional = Optional.ofNullable(author);

你可能会觉得还要加一行代码来封装数据比较麻烦。但是如果改造下getAuthor方法,让其的返回值就是封装好的Optional的话,我们在使用时就会方便很多。

​ 而且在实际开发中我们的数据很多是从数据库获取的。Mybatis从3.5版本可以也已经支持Optional了。我们可以直接把dao方法的返回值类型定义成Optional类型,MyBastis会自己把数据封装成Optional对象返回。封装的过程也不需要我们自己操作。

​ 如果你确定一个对象不是空的则可以使用Optional的静态方法of来把数据封装成Optional对象

   Author author = new Author();Optional<Author> authorOptional = Optional.of(author);

​ 但是一定要注意,如果使用of的时候传入的参数必须不为null。(尝试下传入null会出现什么结果)

​ 如果一个方法的返回值类型是Optional类型。而如果我们经判断发现某次计算得到的返回值为null,这个时候就需要把null封装成Optional对象返回。这时则可以使用Optional静态方法empty来进行封装。

Optional.empty()

 1.2安全消费值

我们获取到一个Optional对象后肯定需要对其中的数据进行使用。这时候我们可以使用其ifPresent方法对来消费其中的值。

​ 这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码。这样使用起来就更加安全了。

​ 例如,以下写法就优雅的避免了空指针异常。

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());authorOptional.ifPresent(author -> System.out.println(author.getName()));

1.3获取值

​ 如果我们想获取值自己进行处理可以使用get方法获取,但是不推荐。因为当Optional内部的数据为空的时候会出现异常。

1.4安全获取值

​ 如果我们期望安全的获取值。我们不推荐使用get方法,而是使用Optional提供的以下方法。

  • orElseGet 获取数据并且设置数据为空时的默认值。如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建对象作为默认值返回。
        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());Author author1 = authorOptional.orElseGet(() -> new Author());
  • orElseThrow 获取数据,如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建异常抛出。
        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());try {Author author = authorOptional.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("author为空"));System.out.println(author.getName());} catch (Throwable throwable) {throwable.printStackTrace();}

 1.5过滤

​ 我们可以使用filter方法对数据进行过滤。如果原本是有数据的,但是不符合判断,也会变成一个无数据的Optional对象。

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());authorOptional.filter(author -> author.getAge()>100).ifPresent(author -> System.out.println(author.getName()));

1.6判断

 我们可以使用isPresent方法进行是否存在数据的判断。如果为空返回值为false,如果不为空,返回值为true。但是这种方式并不能体现Optional的好处,更推荐使用ifPresent方法

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());if (authorOptional.isPresent()) {System.out.println(authorOptional.get().getName());}

1.7数据转换

​ Optional还提供了map可以让我们的对数据进行转换,并且转换得到的数据也还是被Optional包装好的,保证了我们的使用安全。

例如我们想获取作家的书籍集合。

    private static void testMap() {Optional<Author> authorOptional = getAuthorOptional();Optional<List<Book>> optionalBooks = authorOptional.map(author -> author.getBooks());optionalBooks.ifPresent(books -> System.out.println(books));}

 二、函数式接口

        只有一个抽象方法的接口我们称之为函数接口。

​        JDK的函数式接口都加上了**@FunctionalInterface** 注解进行标识。但是无论是否加上该注解只要接口中只有一个抽象方法,都是函数式接口。

1.常见函数式接口

  • ​ Consumer 消费接口 根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数进行消费。

  • ​ Function 计算转换接口 根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数计算或转换,把结果返回 

  • ​ Predicate 判断接口 根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数条件判断,返回判断结果 

  • Supplier 生产型接口 根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中创建对象,把创建好的对象返回 

2.常用的默认方法

  • and 我们在使用Predicate接口时候可能需要进行判断条件的拼接。而and方法相当于是使用&&来拼接两个判断条件 例如: 打印作家中年龄大于17并且姓名的长度大于1的作家。

        List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();authorStream.filter(new Predicate<Author>() {@Overridepublic boolean test(Author author) {return author.getAge()>17;}}.and(new Predicate<Author>() {@Overridepublic boolean test(Author author) {return author.getName().length()>1;}})).forEach(author -> System.out.println(author));
  • or 我们在使用Predicate接口时候可能需要进行判断条件的拼接。而or方法相当于是使用||来拼接两个判断条件。 例如: 打印作家中年龄大于17或者姓名的长度小于2的作家。
//        打印作家中年龄大于17或者姓名的长度小于2的作家。List<Author> authors = getAuthors();authors.stream().filter(new Predicate<Author>() {@Overridepublic boolean test(Author author) {return author.getAge()>17;}}.or(new Predicate<Author>() {@Overridepublic boolean test(Author author) {return author.getName().length()<2;}})).forEach(author -> System.out.println(author.getName()));
  • negate Predicate接口中的方法。negate方法相当于是在判断添加前面加了个! 表示取反 例如: 打印作家中年龄不大于17的作家。 
//        打印作家中年龄不大于17的作家。List<Author> authors = getAuthors();authors.stream().filter(new Predicate<Author>() {@Overridepublic boolean test(Author author) {return author.getAge()>17;}}.negate()).forEach(author -> System.out.println(author.getAge()));

 三、方法引用

        我们在使用lambda时,如果方法体中只有一个方法的调用的话(包括构造方法),我们可以用方法引用进一步简化代码。

1. 推荐用法

​        我们在使用lambda时不需要考虑什么时候用方法引用,用哪种方法引用,方法引用的格式是什么。我们只需要在写完lambda方法发现方法体只有一行代码,并且是方法的调用时使用快捷键尝试是否能够转换成方法引用即可。

​ 当我们方法引用使用的多了慢慢的也可以直接写出方法引用。

2.基本格式

​        类名或者对象名::方法名

3.语法详解(了解)

3.1引用类的静态方法

        其实就是引用类的静态方法

        格式 :

类名::方法名

        使用前提: 

        ​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的静态方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个静态方法中,这个时候我们就可以引用类的静态方法。

        例如:

如下代码就可以用方法引用进行简化

        List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();authorStream.map(author -> author.getAge()).map(age->String.valueOf(age));

注意,如果我们所重写的方法是没有参数的,调用的方法也是没有参数的也相当于符合以上规则。

优化后如下:

        List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();authorStream.map(author -> author.getAge()).map(String::valueOf);

4. 引用对象的实例方法

        格式:

对象名::方法名

        使用前提:

        如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个对象的成员方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用对象的实例方法

        例如:

        List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();StringBuilder sb = new StringBuilder();authorStream.map(author -> author.getName()).forEach(name->sb.append(name));

        优化后:

        List<Author> authors = getAuthors();Stream<Author> authorStream = authors.stream();StringBuilder sb = new StringBuilder();authorStream.map(author -> author.getName()).forEach(sb::append);

 5.引用类的实例方法

        格式:

类名::方法名

         使用前提:

​        如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了第一个参数的成员方法,并且我们把要重写的抽象方法中剩余的所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用类的实例方法。

        例如:

    interface UseString{String use(String str,int start,int length);}public static String subAuthorName(String str, UseString useString){int start = 0;int length = 1;return useString.use(str,start,length);}public static void main(String[] args) {subAuthorName("三更草堂", new UseString() {@Overridepublic String use(String str, int start, int length) {return str.substring(start,length);}});}

        优化后如下:

    public static void main(String[] args) {subAuthorName("三更草堂", String::substring);}

 6. 构造器引用

        ​ 如果方法体中的一行代码是构造器的话就可以使用构造器引用。

        格式:

类名::new

        使用前提: 

        如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的构造方法,并且我们把要重写的抽象方法中的所有的参数都按照顺序传入了这个构造方法中,这个时候我们就可以引用构造器。

        例如:

        List<Author> authors = getAuthors();authors.stream().map(author -> author.getName()).map(name->new StringBuilder(name)).map(sb->sb.append("-三更").toString()).forEach(str-> System.out.println(str));

         优化结果:

        List<Author> authors = getAuthors();authors.stream().map(author -> author.getName()).map(StringBuilder::new).map(sb->sb.append("-三更").toString()).forEach(str-> System.out.println(str));

 四、高级用法

  1.基本数据类型优化

​        我们之前用到的很多Stream的方法由于都使用了泛型。所以涉及到的参数和返回值都是引用数据类型。

        即使我们操作的是整数小数,但是实际用的都是他们的包装类。JDK5中引入的自动装箱和自动拆箱让我们在使用对应的包装类时就好像使用基本数据类型一样方便。但是你一定要知道装箱和拆箱肯定是要消耗时间的。虽然这个时间消耗很下。但是在大量的数据不断的重复装箱拆箱的时候,你就不能无视这个时间损耗了。

        所以为了让我们能够对这部分的时间消耗进行优化。Stream还提供了很多专门针对基本数据类型的方法。

​ 例如:mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等。

    private static void test27() {List<Author> authors = getAuthors();authors.stream().map(author -> author.getAge()).map(age -> age + 10).filter(age->age>18).map(age->age+2).forEach(System.out::println);authors.stream().mapToInt(author -> author.getAge()).map(age -> age + 10).filter(age->age>18).map(age->age+2).forEach(System.out::println);}

2.并行流

        当流中有大量元素时,我们可以使用并行流去提高操作的效率。其实并行流就是把任务分配给多个线程去完全。如果我们自己去用代码实现的话其实会非常的复杂,并且要求你对并发编程有足够的理解和认识。而如果我们使用Stream的话,我们只需要修改一个方法的调用就可以使用并行流来帮我们实现,从而提高效率。

​ parallel方法可以把串行流转换成并行流。

    private static void test28() {Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);Integer sum = stream.parallel().peek(new Consumer<Integer>() {@Overridepublic void accept(Integer num) {System.out.println(num+Thread.currentThread().getName());}}).filter(num -> num > 5).reduce((result, ele) -> result + ele).get();System.out.println(sum);}

​ 也可以通过parallelStream直接获取并行流对象。

        List<Author> authors = getAuthors();authors.parallelStream().map(author -> author.getAge()).map(age -> age + 10).filter(age->age>18).map(age->age+2).forEach(System.out::println);

五、结束语

        本文章讲解了Optional使用、函数式接口、方法引用、高级用法等内容,从代码方面去了解关于jdk8新特性的优雅使用方式,最后关于jdk8新特性的结束哨到这里就已经全部讲解完了,希望对你们有所帮助,同时也是希望大家多多支持!

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

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

相关文章

动态规划和字符串结合leetcode题集(java实现版)

目录 leetcode5.最长回文子串 leetcode718.最长重复子数组 leetcode300.最长递归子序列 leetcode72.编辑距离 leetcode10.正则表达式匹配 leetcode122.买卖股票的最佳时机II leetcode714.买卖股票的最佳时机含手续费 leetcode121.买卖股票的最佳时机 leetcode123.买卖…

ESP32 操作AT24C32或AT24C64

AT24C32或AT24C64两款芯片容量不一样&#xff0c;其他都一样。程序无法动态识别容量AT24C32容量32K&#xff0c;地址范围0x~0x7FFF.AT24C64容量64K,地址范围0x~0xFFFF 电气参数 电压2.7V-5.5V IIC通信 有引脚控制数据保护 有引脚可以配置IIC的地址。 每个page 32字节 1百…

二叉树计算 - 华为OD统一考试

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 200分 题解&#xff1a; Java / Python / C 题目描述 给出一个二叉树如下图所示&#xff1a; 6 / \ 7 9 \ / -2 6 请由该二叉树生成一个新的二叉树&#xff0c;它满足其树中的每个节点将包含原始树中的左子树和右子树…

构建全面有效的监控运维考核评分标准体系

在现代企业的IT运维管理中&#xff0c;监控运维考核评分标准体系不仅是衡量运维团队工作效果的重要工具&#xff0c;更是推动企业IT服务持续改进和优化的关键所在。一个完善、合理的考核评分标准体系能够客观地反映运维团队的实际工作表现&#xff0c;为管理层提供决策支持&…

30岁以就业为目标学前端,快歇着吧;反之50岁都不晚。

Hi&#xff0c;我是贝格前端工场&#xff0c;首先声明声明我们不搞前端培训&#xff0c;有很多老铁在留言中问我关于前端学习的问题&#xff0c;最普遍的一个问题就是30岁以后学前端晚了吗&#xff1f;今天借着此篇文章回答一下。 一、30岁学前端的三种人 首先抛开年龄不说&am…

macbook air(M1 2020)安装graphviz和python pip或conda 安装pygraphviz

第一步,先用Homebrew转graphviz包 brew install graphviz 能够在以下路径中找到graphviz文件:(/opt/homebrew/Cellar/graphviz) 第二步. 安装pygraphviz时提供搜索路径 pip3 install --global-optionbuild_ext --global-option"-I$(brew --prefix graphviz)/include"…

【EI会议征稿通知】第三届能源、电力与电气国际学术会议(ICEPET 2024)

第三届能源、电力与电气国际学术会议&#xff08;ICEPET 2024&#xff09; 2024 3rd International Conference on Energy, Power and Electrical Technology 第三届能源、电力与电气国际学术会议&#xff08;ICEPET 2024&#xff09;由西华大学主办&#xff0c;西华大学能源…

C语言系列-浮点数在内存中的存储

&#x1f308;个人主页: 会编程的果子君 ​&#x1f4ab;个人格言:“成为自己未来的主人~” 目录 浮点数在内存中的存储 浮点数的存储 浮点数存的过程 浮点数取的过程 题目解析 浮点数在内存中的存储 常见的浮点数&#xff1a;3.14159.1E10等&#xff0c;浮点数家族包括&…

【HDFS】一天一个RPC系列--updateBlockForPipeline

本文目标是: 弄清updateBlockForPipeline这个RPC的作用。弄清updateBlockForPipeline RPC的使用场景,代码里的调用点。一、updateBlockForPipeline的作用 其定义在ClientProtocol接口里,是Client与NameNode之间的接口。 看其代码注释描述: 为一个under construction状态下…

前端JavaScript篇之什么是 JavaScript 包装类型?将包装类型转换为基本类型的方法

目录 什么是 JavaScript 包装类型&#xff1f;将包装类型转换为基本类型的方法什么是 JavaScript 包装类型&#xff1f;包装类型的作用字符串包装类型数字包装类型布尔包装类型总结 将包装类型转换为基本类型的方法总结 什么是 JavaScript 包装类型&#xff1f;将包装类型转换为…

Java中的四种线程池详解及使用场景

前言 在Java并发编程中&#xff0c;JDK提供了一套强大的线程池工具类java.util.concurrent.ThreadPoolExecutor以及它的四个便捷工厂方法&#xff0c;这四种线程池分别对应不同的使用场景和特性。下面将详细介绍每种线程池的创建方式、工作原理以及适用场景。 1. CachedThrea…

Linux文本三剑客---grep

grep&#xff08;从文本或字符串种过滤特定内容。&#xff09; 格式&#xff1a;Usage: grep [OPTION]... PATTERNS [FILE]... 常用选项&#xff1a; -E 等价于 egrep 扩展正则 -i 忽略大小写 -w 匹配单词 -o 仅显示匹配内容 -r 递归匹配 -c 统计匹配的行数 -v 取反 -n 行号 -A…

react 什么是h函数

React 中的 H 函数&#xff0c;通常是指 Hooks&#xff08;钩子&#xff09;函数。Hooks 是 React 16.8 版本引入的新特性&#xff0c;允许你在不写 class 的情况下使用 state 以及其他的 React 特性。Hooks 提供了一种更简洁、更直观的方式来使用 React 的功能&#xff0c;使得…

11.2 Web开发_CSS入门(❤❤)

11.2 Web开发_CSS入门❤❤ 1. CSS简介1.1 基础案例2. CSS书写的位置2.1 行内式2.2 内嵌式2.3 外链式3. CSS基础选择器3.1 标签选择器3.2 id选择器3.3 类选择器3.4 选择器优先级3.5 通配符选择器4. 多类名5. 样式的两种特性5.1 层叠性

仰暮计划|“老师说我其实很聪明,就是家里太穷了没条件,不然我现在也是……”

吴桂荣老人回忆录 在我外婆家的时候&#xff0c;我跟几位老奶奶坐在门口一起聊天&#xff0c;我询问她们是否能帮助我完成一份作业&#xff0c;她们笑着答应了&#xff0c;最后我选择了其中的一位老奶奶作为了解对象&#xff0c;她邀请我去家中交谈。通过了解&#xff0c;我得知…

HCIA学习第四天:静态路由与动态路由

静态路由&#xff1a; 选路原则&#xff1a;尽量选择路径最短的路由条目 扩展配置&#xff1a; 1、负载均衡&#xff1a;当路由器访问同一个目标且目标且目标具有多条开销相似的路径时&#xff0c;可以让设备将流量拆分后延多条路径同时进行传输&#xff0c;以达到叠加带宽的…

(七)springboot实战——springboot3集成R2DBC实现webflux响应式编程服务案例

前言 本节主要内容是关于使用新版springboot3集成响应式数据库R2DBC,完成响应式web服务案例。需要注意的是&#xff0c;此次项目使用的JDK版本是JDK17&#xff0c;springboot版本使用3.2.2版本&#xff0c;数据库使用关系型数据库mysql。WebFlux 是一个基于响应式编程模型的框…

K8s-持久化(持久卷,卷申明,StorageClass,StatefulSet持久化)

POD 卷挂载 apiVersion: v1 kind: Pod metadata:name: random-number spec:containers:- image: alpinename: alpinecommand: ["/bin/sh","-c"]args: ["shuf -i 0-100 -n 1 >> /opt/number.out;"]volumeMounts:- mountPath: /optname: da…

04-Nacos-服务注册基于spring boot实现

官方参考 在不依赖spring cloud 组件基础上&#xff0c;单独的微服务项目&#xff0c;实现nacos接入 1、依赖文件pom.xml <dependency><groupId>com.alibaba.boot</groupId><artifactId>nacos-discovery-spring-boot-starter</artifactId><…

RUST笔记: 动态链接库的创建和使用

生成动态链接库 // https://github.com/vvvm23/funny-shapes # 项目元信息 [package] name "funnyshapes" # 项目名称 version "0.1.0" # 版本号 edition "2021" # Rust语言版本# 更多配置信息可查阅&#xff1…