Java8新特性
Java8新特性
Lambda 表达式 (对函数式接口的简写)
概念: Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码 像数据一样进行传递)。可以写出更简洁、更 灵活的代码。作为一种更紧凑的代码风格,使 Java的语言表达能力得到了提升。
Lambda表达式主要是对函数式接口(一个只能有一个抽象方法的接口)的一种简写。
Lambda 表达式在Java 语言中引入了一个新的语法元 素和操作符。这个操作符为 “->” , 该操作符被称 为 Lambda 操作符或剪头操作符。
它将 Lambda 分为 两个部分:
左侧:指定了 Lambda 表达式需要的所有参数
右侧:指定了 Lambda 体,即 Lambda 表达式要执行 的功能。
Lambda表达式的参数部分类型可以省略,主要是编译器有了类型推断,根据接收的接口的方法中的参数类推断的。
//现在的 Lambda 表达式, string x,string y可以省略不写
@Test
public void test2(){
Comparator<String> com = (x, y) -> Integer.compare(x.length(), y.length());
TreeSet<String> ts = new TreeSet<>(com);
}
函数式接口
只包含一个抽象方法的接口,称为函数式接口。
你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方 法上进行声明)。
我们可以在任意函数式接口上使用 @FunctionalInterface 注解, 这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包 含一条声明,说明这个接口是一个函数式接口。
@FunctionalInterface
public interface MyFunction {
public String getValue(String str);
}
方法引用与构造器引用
方法引用:
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用! (实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!) 方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。方法引用的本质是传递已经存在的方法给接口,必须参数和返回值一致。
如下三种主要使用情况:
⚫ 对象::实例方法
//对象的引用 :: 实例方法名
@Test
public void test2(){
Employee emp = new Employee(101, "张三", 18, 9999.99);
Supplier<String> sup = () -> emp.getName();
System.out.println(sup.get());
System.out.println("----------------------------------");
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
⚫ 类::静态方法
//类名 :: 静态方法名
@Test
public void test4(){
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
System.out.println("-------------------------------------");
Comparator<Integer> com2 = Integer::compare;
}
@Test
public void test3(){
BiFunction<Double, Double, Double> fun = (x, y) -> Math.max(x, y);
System.out.println(fun.apply(1.5, 22.2));
System.out.println("--------------------------------------------------");
BiFunction<Double, Double, Double> fun2 = Math::max;
System.out.println(fun2.apply(1.2, 1.5));
}
⚫ 类::实例方法
//类名 :: 实例方法名
@Test
public void test5(){
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
System.out.println(bp.test("abcde", "abcde"));
System.out.println("-----------------------------------------");
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));
System.out.println("-----------------------------------------");
Function<Employee, String> fun = (e) -> e.show();
System.out.println(fun.apply(new Employee()));
System.out.println("-----------------------------------------");
Function<Employee, String> fun2 = Employee::show;
System.out.println(fun2.apply(new Employee()));
}
构造器引用
主要是针对返回值的情况。
格式: ClassName::new 与函数式接口相结合,自动与函数式接口中方法兼容。 可以把构造器引用赋值给定义的方法,与构造器参数 列表要与接口中抽象方法的参数列表一致!
//构造器引用
@Test
public void test7(){
Function<String, Employee> fun = Employee::new;
BiFunction<String, Integer, Employee> fun2 = Employee::new;
}
@Test
public void test6(){
Supplier<Employee> sup = () -> new Employee();
System.out.println(sup.get());
System.out.println("------------------------------------");
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
}
数组引用
//数组引用
@Test
public void test8(){
Function<Integer, String[]> fun = (args) -> new String[args];
String[] strs = fun.apply(10);
System.out.println(strs.length);
System.out.println("--------------------------");
Function<Integer, Employee[]> fun2 = Employee[] :: new;
Employee[] emps = fun2.apply(20);
System.out.println(emps.length);
}
Stream API
概念 Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一 个则是 Stream API(java.util.stream.*)。 Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对 集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数 据库查询。也可以使用 Stream API 来并行执行操作。简而言之, Stream API 提供了一种高效且易于使用的处理数据的方式。
流的概念 流(Stream) 到底是什么呢? 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。 “集合讲的是数据,流讲的是计算!” 注意: ①Stream 自己不会存储元素。 ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。 ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
Stream 的操作三个步骤
⚫ 创建 Stream 一个数据源(如:集合、数组),获取一个流
⚫ 中间操作 一个中间操作链,对数据源的数据进行处理
⚫ 终止操作(终端操作) 一个终止操作,执行中间操作链,并产生结果
创建stream
1.Collection
⚫ default Stream stream() : 返回一个顺序流
⚫ default Stream parallelStream() : 返回一个并行流
2.Arrays 的静态方法 stream() 可 以获取数组流:
3.Stream的静态方法创建 Stream.of()
4.Strean的静态方法创建无限流:Stream.iterate() 和 Stream.generate(), 创建无限流。
//1. 创建 Stream
@Test
public void test1(){
//1. Collection 提供了两个方法 stream() 与 parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
//2. 通过 Arrays 中的 stream() 获取一个数组流
Integer[] nums = new Integer[10];
Stream<Integer> stream1 = Arrays.stream(nums);
//3. 通过 Stream 类中静态方法 of()
Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);
//4. 创建无限流
//迭代
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
stream3.forEach(System.out::println);
//生成
Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
stream4.forEach(System.out::println);
}
中间操作
概念
多个中间操作可以连接起来形成一个流水线,除非流水 线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值” 。
方法
filter——接收 Lambda , 从流中排除某些元素。
limit——截断流,使其元素不超过给定数量,limit是短路操作,只要超过了limit的值就不会在过滤了。
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
映射
map——接收 Lambda , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
sorted()——自然排序
sorted(Comparator com)——定制排序
终止操作
查找与匹配
allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
归约
reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 T
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 Optional
备注:map 和 reduce 的连接通常称为map-reduce 模式,因 Google 用它 来进行网络搜索而出名。 max(Comparator c) 返回流中最大值 min(Comparator c) 返回流中最小值 forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭 代,称为外部迭代。相反,Stream API 使用内部 迭代——它帮你把迭代做了)
@Test
public void test1(){
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
System.out.println("----------------------------------------");
Optional<Double> op = emps.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(op.get());
}
collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
Collector 接口中方法的实现决定了如何对流执行收集操作(如收 集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态 方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
@Test
public void test3(){
List<String> list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("----------------------------------");
Set<String> set = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("----------------------------------");
HashSet<String> hs = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hs.forEach(System.out::println);
}
@Test
public void test4(){
Optional<Double> max = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max.get());
Optional<Employee> op = emps.stream()
.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(op.get());
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count);
System.out.println("--------------------------------------------");
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
}
//分组
@Test
public void test5(){
Map<Status, List<Employee>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
}
//多级分组
@Test
public void test6(){
Map<Status, Map<String, List<Employee>>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if(e.getAge() >= 60)
return "老年";
else if(e.getAge() >= 35)
return "中年";
else
return "成年";
})));
System.out.println(map);
}
//分区
@Test
public void test7(){
Map<Boolean, List<Employee>> map = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
System.out.println(map);
}
//
@Test
public void test8(){
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("," , "----", "----"));
System.out.println(str);
}
@Test
public void test9(){
Optional<Double> sum = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.reducing(Double::sum));
System.out.println(sum.get());
}
Stream中的并行流和串行流
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并 行操作。Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。
. 了解 Fork/Join 框架
Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个 小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总.
Fork/Join 框架与传统线程池的区别
采用 “工作窃取”模式(work-stealing): 当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线 程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。 相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的 处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因 无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果 某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子 问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程 的等待时间,提高了性能
ForkJoinCalculate类
public class ForkJoinCalculate extends RecursiveTask<Long>{
/**
*
*/
private static final long serialVersionUID = 13475679780L;
private long start;
private long end;
private static final long THRESHOLD = 10000L; //临界值
public ForkJoinCalculate(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length = end - start;
if(length <= THRESHOLD){
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else{
long middle = (start + end) / 2;
ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
left.fork(); //拆分,并将该子任务压入线程队列
ForkJoinCalculate right = new ForkJoinCalculate(middle+1, end);
right.fork();
return left.join() + right.join();
}
}
}
测试代码
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
import com.atguigu.java8.ForkJoinCalculate;
import org.junit.Test;
public class TestForkJoin {
@Test
public void test1(){
long start = System.currentTimeMillis();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinCalculate(0L, 10000000000L);
long sum = pool.invoke(task);
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("耗费的时间为: " + (end - start)); //112-1953-1988-2654-2647-20663-113808
}
@Test
public void test2(){
long start = System.currentTimeMillis();
long sum = 0L;
for (long i = 0L; i <= 10000000000L; i++) {
sum += i;
}
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("耗费的时间为: " + (end - start)); //34-3174-3132-4227-4223-31583
}
@Test
public void test3(){
long start = System.currentTimeMillis();
Long sum = LongStream.rangeClosed(0L, 10000000000L)
.parallel()
.sum();
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("耗费的时间为: " + (end - start)); //2061-2053-2086-18926
}
}
接口中的默认方法与静态方法
Java 8中允许接口中包含具有具体实现的方法,该方法称为 “默认方法” ,默认方法使用 default 关键字修饰。
public interface MyFun {
default String getName() {
return "哈哈哈";
}
Integer getValue(Integer integer);
}
接口中的静态方法。
public interface MyInterface {
default String getName(){
return "呵呵呵";
}
public static void show(){
System.out.println("接口中的静态方法");
}
}
//调用:
MyInterface.show();
新时间日期API
使用LocalDate、LocalTime、LocalDateTime
LocalDate、LocalTime、LocalDateTime 类的实 例是不可变的对象(他是线程安全的),分别表示使用 ISO-8601日 历系统的日期、时间、日期和时间。它们提供 了简单的日期或时间,并不包含当前的时间信 息。也不包含与时区相关的信息。
关于以前的时间类型线程安全问题
解决线程安全问题。
package com.atguigu.java8;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFormatThreadLocal {
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
protected DateFormat initialValue(){
return new SimpleDateFormat("yyyyMMdd");
}
};
public static final Date convert(String source) throws ParseException{
return df.get().parse(source);
}
}
测试类
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class TestSimpleDateFormat {
public static void main(String[] args) throws Exception {
/*SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return sdf.parse("20161121");
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future<Date> future : results) {
System.out.println(future.get());
}
pool.shutdown();*/
//解决多线程安全问题
/*Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return DateFormatThreadLocal.convert("20161121");
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future<Date> future : results) {
System.out.println(future.get());
}
pool.shutdown();*/
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
Callable<LocalDate> task = new Callable<LocalDate>() {
@Override
public LocalDate call() throws Exception {
LocalDate ld = LocalDate.parse("20161121", dtf);
return ld;
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<LocalDate>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
for (Future<LocalDate> future : results) {
System.out.println(future.get());
}
pool.shutdown();
}
}
Instant 时间戳
⚫ 用于“时间戳”的运算。它是以Unix元年(传统 的设定为UTC时区1970年1月1日午夜时分)开始 所经历的描述进行运算
Duration 和 Period
⚫ Duration:用于计算两个“时间”间隔
⚫ Period:用于计算两个“日期”间隔
日期的操纵
⚫ TemporalAdjuster : 时间校正器。有时我们可能需要获 取例如:将日期调整到“下个周日”等操作。
⚫ TemporalAdjusters: 该类通过静态方法提供了大量的常 用 TemporalAdjuster 的实现。
解析与格式化
java.time.format.DateTimeFormatter类:该类提供了三种 格式化方法:
⚫ 预定义的标准格式
⚫ 语言环境相关的格式
⚫ 自定义的格式
时区的处理
Java8 中加入了对时区的支持,带时区的时间为分别为: ZonedDate、ZonedTime、ZonedDateTime
其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式
例如 :Asia/Shanghai 等
ZoneId:该类中包含了所有的时区信息
getAvailableZoneIds() : 可以获取所有时区时区信息
of(id) : 用指定的时区信息获取ZoneId 对象
演示代码:
public class TestLocalDateTime {
//6.ZonedDate、ZonedTime、ZonedDateTime : 带时区的时间或日期
@Test
public void test7() {
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(ldt);
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
System.out.println(zdt);
LocalDate now = LocalDate.now(ZoneId.of("US/Pacific"));
System.out.println(now);
}
@Test
public void test6() {
Set<String> set = ZoneId.getAvailableZoneIds();
set.forEach(System.out::println);
}
//5. DateTimeFormatter : 解析和格式化日期或时间
@Test
public void test5() {
// DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
LocalDateTime ldt = LocalDateTime.now();
LocalDateTime ldt1 = LocalDateTime.of(2021, 12, 12, 12, 12, 12);
String strDate = ldt.format(dtf);
System.out.println(strDate);
LocalDateTime newLdt = LocalDateTime.parse(strDate, dtf);
System.out.println(newLdt);
}
//4. TemporalAdjuster : 时间校正器
@Test
public void test4() {
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
LocalDateTime ldt2 = ldt.withDayOfMonth(10);
System.out.println(ldt2);
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);
//自定义:下一个工作日
LocalDateTime ldt5 = ldt.with((l) -> {
LocalDateTime ldt4 = (LocalDateTime) l;
DayOfWeek dow = ldt4.getDayOfWeek();
if (dow.equals(DayOfWeek.FRIDAY)) {
return ldt4.plusDays(3);
} else if (dow.equals(DayOfWeek.SATURDAY)) {
return ldt4.plusDays(2);
} else {
return ldt4.plusDays(1);
}
});
System.out.println(ldt5);
}
//3.
//Duration : 用于计算两个“时间”间隔
//Period : 用于计算两个“日期”间隔
@Test
public void test3() {
Instant ins1 = Instant.now();
System.out.println("--------------------");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
Instant ins2 = Instant.now();
System.out.println("所耗费时间为:" + Duration.between(ins1, ins2));
System.out.println("----------------------------------");
LocalDate ld1 = LocalDate.now();
LocalDate ld2 = LocalDate.of(2011, 1, 1);
Period pe = Period.between(ld2, ld1);
System.out.println(pe.getYears());
System.out.println(pe.getMonths());
System.out.println(pe.getDays());
}
//2. Instant : 时间戳。 (使用 Unix 元年 1970年1月1日 00:00:00 所经历的毫秒值)
@Test
public void test2() {
Instant ins = Instant.now(); //默认使用 UTC 时区
System.out.println(ins);
OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);
System.out.println(ins.getNano());
Instant ins2 = Instant.ofEpochSecond(5);
System.out.println(ins2);
}
//1. LocalDate、LocalTime、LocalDateTime
@Test
public void test1() {
LocalDateTime ldt = LocalDateTime.now();
System.out.println("ldt " + ldt);
LocalDateTime ld2 = LocalDateTime.of(2016, 11, 21, 10, 10, 10);
System.out.println("ld2 " + ld2);
LocalDateTime ldt3 = ld2.plusYears(20);
System.out.println("ldt3 " + ldt3);
LocalDateTime ldt4 = ld2.minusMonths(2);
System.out.println("ldt4 " + ldt4);
System.out.println("ldt.getYear() " + ldt.getYear());
System.out.println("ldt.getMonthValue() " + ldt.getMonthValue());
System.out.println("ldt.getDayOfMonth() " + ldt.getDayOfMonth());
System.out.println("ldt.getHour() " + ldt.getHour());
System.out.println("ldt.getMinute() " + ldt.getMinute());
System.out.println("ldt.getSecond() " + ldt.getSecond());
}
}
其他新特性
Optional 类
Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在, 原来用 null 表示一个值不存在,现在 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
测试代码
import java.util.Optional;
import com.atguigu.java8.Employee;
import com.atguigu.java8.Godness;
import com.atguigu.java8.Man;
import com.atguigu.java8.NewMan;
import org.junit.Test;
/*
* 一、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
*/
public class TestOptional {
@Test
public void test4(){
Optional<Employee> op = Optional.of(new Employee(101, "张三", 18, 9999.99));
Optional<String> op2 = op.map(Employee::getName);
System.out.println(op2.get());
Optional<String> op3 = op.flatMap((e) -> Optional.of(e.getName()));
System.out.println(op3.get());
}
@Test
public void test3(){
Optional<Employee> op = Optional.ofNullable(new Employee());
if(op.isPresent()){
System.out.println(op.get());
}
Employee emp = op.orElse(new Employee("张三"));
System.out.println(emp);
Employee emp2 = op.orElseGet(() -> new Employee());
System.out.println(emp2);
}
@Test
public void test2(){
/*Optional<Employee> op = Optional.ofNullable(null);
System.out.println(op.get());*/
// Optional<Employee> op = Optional.empty();
// System.out.println(op.get());
}
@Test
public void test1(){
Optional<Employee> op = Optional.of(new Employee());
Employee emp = op.get();
System.out.println(emp);
}
@Test
public void test5(){
Man man = new Man();
String name = getGodnessName(man);
System.out.println(name);
}
//需求:获取一个男人心中女神的名字
public String getGodnessName(Man man){
if(man != null){
Godness g = man.getGod();
if(g != null){
return g.getName();
}
}
return "苍老师";
}
//运用 Optional 的实体类
@Test
public void test6(){
Optional<Godness> godness = Optional.ofNullable(new Godness("林志玲"));
Optional<NewMan> op = Optional.ofNullable(new NewMan(godness));
String name = getGodnessName2(op);
System.out.println(name);
}
public String getGodnessName2(Optional<NewMan> man){
return man.orElse(new NewMan())
.getGodness()
.orElse(new Godness("苍老师"))
.getName();
}
}
重复注解与类型注解
Java 8对注解处理提供了两点改进:可重复的注解及可用于类 型的注解。
浙公网安备 33010602011771号