写在前面

最近,很多读者出去面试都在Java8上栽了跟头,事后自己分析,确实对Java8的新特性一知半解。然而,却在简历显眼的技能部分写着:熟练掌握Java8的各种新特性,能够迅速使用Java8开发高并发应用!这不,又一名读者因为写了熟练掌握Java8的新特性而被面试官虐的体无完肤!我不是说不能写,可以这样写!但是,咱在写熟练掌握Java8新特性的时候,应该静下心来好好想想自己是否真的掌握了Java8。如果自己心中对是否掌握了Java8这个问题模棱两可的话,那确实要好好静下心来为自己充电了!一定要从模棱两可到彻底掌握Java8,那到时就不是面试官虐你了,而是你吊打面试官!!

什么是Optional类?

Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。

Optional类常用方法:

  • Optional.of(T t) : 创建一个 Optional 实例。

  • Optional.empty() : 创建一个空的 Optional 实例。

  • Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例。

  • isPresent() : 判断是否包含值。

  • orElse(T t) : 如果调用对象包含值,返回该值,否则返回t。

  • orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值。

  • map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()。

  • flatMap(Function mapper):与 map 类似,要求返回值必须是Optional。

Optional类示例

1.创建Optional类

(1)使用empty()方法创建一个空的Optional对象:

Optional<String> empty = Optional.empty();

(2)使用of()方法创建Optional对象:

String name = "binghe";
Optional<String> opt = Optional.of(name);
assertEquals("Optional[binghe]", opt.toString());

传递给of()的值不可以为空,否则会抛出空指针异常。例如,下面的程序会抛出空指针异常。

String name = null;
Optional<String> opt = Optional.of(name);

如果我们需要传递一些空值,那我们可以使用下面的示例所示。

String name = null;
Optional<String> opt = Optional.ofNullable(name);

使用ofNullable()方法,则当传递进去一个空值时,不会抛出异常,而只是返回一个空的Optional对象,如同我们用Optional.empty()方法一样。

2.isPresent

我们可以使用这个isPresent()方法检查一个Optional对象中是否有值,只有值非空才返回true。

Optional<String> opt = Optional.of("binghe");
assertTrue(opt.isPresent()); opt = Optional.ofNullable(null);
assertFalse(opt.isPresent());

在Java8之前,我们一般使用如下方式来检查空值。

if(name != null){
System.out.println(name.length);
}

在Java8中,我们就可以使用如下方式来检查空值了。

Optional<String> opt = Optional.of("binghe");
opt.ifPresent(name -> System.out.println(name.length()));

3.orElse和orElseGet

(1)orElse

orElse()方法用来返回Optional对象中的默认值,它被传入一个“默认参数‘。如果对象中存在一个值,则返回它,否则返回传入的“默认参数”。

String nullName = null;
String name = Optional.ofNullable(nullName).orElse("binghe");
assertEquals("binghe", name);

(2)orElseGet

与orElse()方法类似,但是这个函数不接收一个“默认参数”,而是一个函数接口。

String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(() -> "binghe");
assertEquals("binghe", name);

(3)二者有什么区别?

要想理解二者的区别,首先让我们创建一个无参且返回定值的方法。

public String getDefaultName() {
System.out.println("Getting Default Name");
return "binghe";
}

接下来,进行两个测试看看两个方法到底有什么区别。

String text;
System.out.println("Using orElseGet:");
String defaultText = Optional.ofNullable(text).orElseGet(this::getDefaultName);
assertEquals("binghe", defaultText); System.out.println("Using orElse:");
defaultText = Optional.ofNullable(text).orElse(getDefaultName());
assertEquals("binghe", defaultText);

在这里示例中,我们的Optional对象中包含的都是一个空值,让我们看看程序执行结果:

Using orElseGet:
Getting default name...
Using orElse:
Getting default name...

两个Optional对象中都不存在value,因此执行结果相同。

那么,当Optional对象中存在数据会发生什么呢?我们一起来验证下。

String name = "binghe001";

System.out.println("Using orElseGet:");
String defaultName = Optional.ofNullable(name).orElseGet(this::getDefaultName);
assertEquals("binghe001", defaultName); System.out.println("Using orElse:");
defaultName = Optional.ofNullable(name).orElse(getDefaultName());
assertEquals("binghe001", defaultName);

运行结果如下所示。

Using orElseGet:
Using orElse:
Getting default name...

可以看到,当使用orElseGet()方法时,getDefaultName()方法并不执行,因为Optional中含有值,而使用orElse时则照常执行。所以可以看到,当值存在时,orElse相比于orElseGet,多创建了一个对象。如果创建对象时,存在网络交互,那系统资源的开销就比较大了,这是需要我们注意的一个地方。

4.orElseThrow

orElseThrow()方法当遇到一个不存在的值的时候,并不返回一个默认值,而是抛出异常。

String nullName = null;
String name = Optional.ofNullable(nullName).orElseThrow( IllegalArgumentException::new);

5.get

get()方法表示是Optional对象中获取值。

Optional<String> opt = Optional.of("binghe");
String name = opt.get();
assertEquals("binghe", name);

使用get()方法也可以返回被包裹着的值。但是值必须存在。当值不存在时,会抛出一个NoSuchElementException异常。

Optional<String> opt = Optional.ofNullable(null);
String name = opt.get();

6.filter

接收一个函数式接口,当符合接口时,则返回一个Optional对象,否则返回一个空的Optional对象。

String name = "binghe";
Optional<String> nameOptional = Optional.of(name);
boolean isBinghe = nameOptional.filter(n -> "binghe".equals(name)).isPresent();
assertTrue(isBinghe);
boolean isBinghe001 = nameOptional.filter(n -> "binghe001".equals(name)).isPresent();
assertFalse(isBinghe001);

使用filter()方法会过滤掉我们不需要的元素。

接下来,我们再来看一例示例,例如目前有一个Person类,如下所示。

public class Person{
private int age;
public Person(int age){
this.age = age;
}
//省略get set方法
}

例如,我们需要过滤出年龄在25岁到35岁之前的人群,那在Java8之前我们需要创建一个如下的方法来检测每个人的年龄范围是否在25岁到35岁之前。

public boolean filterPerson(Peron person){
boolean isInRange = false;
if(person != null && person.getAge() >= 25 && person.getAge() <= 35){
isInRange = true;
}
return isInRange;
}

看上去就挺麻烦的,我们可以使用如下的方式进行测试。

assertTrue(priceIsInRange1(new Peron(18)));
assertFalse(priceIsInRange1(new Peron(29)));
assertFalse(priceIsInRange1(new Peron(16)));
assertFalse(priceIsInRange1(new Peron(34)));
assertFalse(priceIsInRange1(null));

如果使用Optional,效果如何呢?

public boolean filterPersonByOptional(Peron person){
return Optional.ofNullable(person)
.map(Peron::getAge)
.filter(p -> p >= 25)
.filter(p -> p <= 35)
.isPresent();
}

使用Optional看上去就清爽多了,这里,map()仅仅是将一个值转换为另一个值,并且这个操作并不会改变原来的值。

7.map

如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()。

List<String> names = Arrays.asList("binghe001", "binghe002", "", "binghe003", "", "binghe004");
Optional<List<String>> listOptional = Optional.of(names); int size = listOptional
.map(List::size)
.orElse(0);
assertEquals(6, size);

在这个例子中,我们使用一个List集合封装了一些字符串,然后再把这个List使用Optional封装起来,对其map(),获取List集合的长度。map()返回的结果也被封装在一个Optional对象中,这里当值不存在的时候,我们会默认返回0。如下我们获取一个字符串的长度。

String name = "binghe";
Optional<String> nameOptional = Optional.of(name); int len = nameOptional
.map(String::length())
.orElse(0);
assertEquals(6, len);

我们也可以将map()方法与filter()方法结合使用,如下所示。

String password = " password ";
Optional<String> passOpt = Optional.of(password);
boolean correctPassword = passOpt.filter(
pass -> pass.equals("password")).isPresent();
assertFalse(correctPassword); correctPassword = passOpt
.map(String::trim)
.filter(pass -> pass.equals("password"))
.isPresent();
assertTrue(correctPassword);

上述代码的含义就是对密码进行验证,查看密码是否为指定的值。

8.flatMap

与 map 类似,要求返回值必须是Optional。

假设我们现在有一个Person类。

public class Person {
private String name;
private int age;
private String password; public Optional<String> getName() {
return Optional.ofNullable(name);
} public Optional<Integer> getAge() {
return Optional.ofNullable(age);
} public Optional<String> getPassword() {
return Optional.ofNullable(password);
}
// 忽略get set方法
}

接下来,我们可以将Person封装到Optional中,并进行测试,如下所示。

Person person = new Person("binghe", 18);
Optional<Person> personOptional = Optional.of(person); Optional<Optional<String>> nameOptionalWrapper = personOptional.map(Person::getName);
Optional<String> nameOptional = nameOptionalWrapper.orElseThrow(IllegalArgumentException::new);
String name1 = nameOptional.orElse("");
assertEquals("binghe", name1); String name = personOptional
.flatMap(Person::getName)
.orElse("");
assertEquals("binghe", name);

注意:方法getName返回的是一个Optional对象,如果使用map,我们还需要再调用一次get()方法,而使用flatMap()就不需要了。

写在最后

如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Java8新特性。

最后,附上Java8新特性核心知识图,祝大家在学习Java8新特性时少走弯路。

【Java8新特性】不了解Optional类,简历上别说你懂Java8!!的更多相关文章

  1. Java8新特性探索之Optional类

    为什么引入Optional类? 身为一名Java程序员,大家可能都有这样的经历:调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法.我们首先要判断这个返回值是否为null,只有在非空的前 ...

  2. Java8新特性之五:Optional

    NullPointerException相信每个JAVA程序员都不陌生,是JAVA应用程序中最常见的异常.之前,Google Guava项目曾提出用Optional类来包装对象从而解决NullPoin ...

  3. Java8新特性之空指针异常的克星Optional类

    Java8新特性系列我们已经介绍了Stream.Lambda表达式.DateTime日期时间处理,最后以"NullPointerException" 的克星Optional类的讲解 ...

  4. Java8新特性之二:方法引用

    上一节介绍了Java8新特性中的Lambda表达式,本小节继续讲解Java8的新特性之二:方法引用.方法引用其实也离不开Lambda表达式. 1.方法引用的使用场景 我们用Lambda表达式来实现匿名 ...

  5. java8新特性学习:函数式接口

    本文概要 什么是函数式接口? 如何定义函数式接口? 常用的函数式接口 函数式接口语法注意事项 总结 1. 什么是函数式接口? 函数式接口其实本质上还是一个接口,但是它是一种特殊的接口:SAM类型的接口 ...

  6. java8新特性之Optional类

    NullPointException可以说是所有java程序员都遇到过的一个异常,虽然java从设计之初就力图让程序员脱离指针的苦海,但是指针确实是实际存在的,而java设计者也只能是让指针在java ...

  7. Java8 新特性 Optional 类

    Optional 类的简介   Optional类的是来自谷歌Guava的启发,然后就加入到Java8新特性中去了.Optional类主要就是为子决解价值亿万的错误,空指针异常.   Optional ...

  8. 【Java8新特性】Optional类在处理空值判断场景的应用 回避空指针异常 编写健壮的应用程序

    一.序言 空值异常是应用运行时常见的异常,传统方式为了编写健壮的应用,常常使用多层嵌套逻辑判断回避空指针异常.Java8新特性之Optional为此类问题提供了优雅的解决方式. 广大程序员朋友对空值异 ...

  9. 乐字节-Java8新特性之Optional

    上一篇小乐带大家了解了Java新特性之Stream,接下来将会继续述说Java新特性之Optional Optional<T>类(java.util.Optional)是一个容器类,代表一 ...

随机推荐

  1. UML笔记之类图

    1.类与类之间关系在UML类图中,常见的有以下几种关系: 泛化(Generalization), 实现(Realization),关联(Association),聚合(Aggregation),组合( ...

  2. 基于tp3开发的cms-前台代码执行

        内核 thinkphp PbootCMS-V1.2.1 ├─ apps         应用程序 │  ├─ admin     后台模块 │  ├─ api       api模块 │  ├ ...

  3. schedule定时任务出现问题 (大坑已填)!!

    因为python每次运行完,并不清除内存,nowtime一直不变,这导致了一个致命问题,使我的脚本一直运行失败,具体如下: 我设置的是每隔30分钟登陆一次,代码如下 if __name__ == &q ...

  4. NPM验证邮件,手机接受验证时出现service unavailable

    NPM验证邮件,手机接受验证时出现service unavailable 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 问题原 ...

  5. P2201 数列编辑器

    传送门呀呀呀呀呀呀呀呀呀呀呀呀呀 \(乍一看题目好像很难\)(实际也确实很难) \(但是我们仔细看就发现,整个数列分成了光标前和光标后两组数列\) \(我们有什么理由不分开储存呢??\) \(然后光标 ...

  6. SpringCloud 踩坑之 注册中心绑定端口一直是8080

    今天在启动注册中心服务时,突然端口一直是8080,找了好久一直没找到原因,先看看我有问题的配置 spring: application: name: eureka-server profiles: d ...

  7. 王颖奇 20171010129《面向对象程序设计(java)》第十六周学习总结

    实验十六  线程技术 实验时间 2018-12-8 理论部分: 并发⚫ 线程的概念⚫ 中断线程⚫ 线程状态⚫ 多线程调度⚫ 线程同步 1.线程的概念 程序是一段静态的代码,它是应用程序执行的蓝本. 进 ...

  8. Coursera课程笔记----计算导论与C语言基础----Week 10

    C程序中的数组(Week 10) 一维数组 数组的定义 类型 数组名[常量表达式] int sheep[10] 定义数组时,[]内必须为常量表达式 可以用const int 可以在main函数前,#d ...

  9. 字节码编程,Javassist篇一《基于javassist的第一个案例helloworld》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 目录 @ 目录 目录 一.前言 二.开发环境 三.案例目标 四.技术实现 五.测试结果 1. ...

  10. [hdu1506 Largest Rectangle in a Histogram]笛卡尔树

    题意:http://acm.hdu.edu.cn/showproblem.php?pid=1506 如图,求最大的矩形面积 思路: 笛卡尔树:笛卡尔树是一棵二叉树,树的每个节点有两个值,一个为key, ...