一、简介
1.速度快
2.代码更少,增加了新语法Lambda表达式
3.强大的Stream API
4.便于并行
5.最大化减少空指针异常
其中最为核心的是Lambda表达式与Stream API
二、Lambda表达式
1.为什么使用Lambda表达式?
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更加简洁、更灵活的代码。作为一种更加紧凑的代码风格,使Java的语言表达能力得到了提升。
2.Lambda表达式
package com.ming.service;
import com.ming.model.Employee;
public interface MyPredicate<T> {
public boolean test(T t);
}
package com.ming.service.imp;
import com.ming.model.Employee;
import com.ming.service.MyPredicate;
public class FilterEmployeeByAge implements MyPredicate<Employee> {
@Override
public boolean test(Employee employee) {
return employee.getAge()>35;
}
}
package com.ming;
import com.ming.model.Employee;
import com.ming.service.MyPredicate;
import com.ming.service.imp.FilterEmployeeByAge;
import org.junit.Test;
import java.lang.reflect.Array;
import java.util.*;
/**
* Lambda表达式
*/
public class LambdaTest {
/**
* 匿名内部类
*/
@Test
public void test() {
Comparator<Integer> com = new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
TreeSet<Integer> treeSet = new TreeSet<>(com);
}
/**
* Lambda表达式
*/
@Test
public void test1() {
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
TreeSet<Integer> treeSet = new TreeSet<Integer>(com);
}
//需求:获取公司年龄大于35的员工年龄信息
List<Employee> employeeList = Arrays.asList(
new Employee("1", "张三", 15, 8000.88),
new Employee("2", "李四", 30, 7000.88),
new Employee("3", "王五", 25, 9000.88),
new Employee("4", "郭靖", 38, 4000.88),
new Employee("5", "黄蓉", 50, 3000.88)
);
public List<Employee> filterEmployees(List<Employee> list) {
List<Employee> empList = new ArrayList<>();
for (Employee emp : list) {
if (emp.getAge() >= 35) {
empList.add(emp);
}
}
return empList;
}
@Test
public void test2() {
List<Employee> employees = filterEmployees(employeeList);
System.out.println(employees);
}
public List<Employee> getEmployeeListByAge(List<Employee> list, MyPredicate<Employee> my) {
List<Employee> empList = new ArrayList<>();
for (Employee emp : list) {
if (my.test(emp)) {
empList.add(emp);
}
}
return empList;
}
//优化方式一,策略设计模式
@Test
public void test3() {
List<Employee> empList = getEmployeeListByAge(employeeList, new FilterEmployeeByAge());
System.out.println(empList);
}
//优化方式二:匿名内部类
@Test
public void test4() {
List<Employee> empList = getEmployeeListByAge(employeeList, new MyPredicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getSalary() > 5000;
}
});
empList.forEach(System.out::println);
}
//优化方式3Lambda表达式
@Test
public void test5() {
List<Employee> list = getEmployeeListByAge(employeeList, (e) -> e.getSalary() > 5000);
list.forEach(System.out::println);
}
//优化方式4 stream api
@Test
public void test6(){
employeeList.stream()
.filter((e)->e.getSalary()>5000)
.limit(2)
.forEach(System.out::println);
employeeList.stream()
.map(Employee::getName)
.forEach(System.out::println);
}
}
基础语法
Java8中引入了一个新的操作符 ->,该操作符叫做箭头操作符或Lambda操作符、 箭头操作符将Lambda表达式拆分成两部分:
左侧:Lambda表达式的参数列表
右侧:Lambda表达式中所需要执行的功能,即Lambda体
语法格式一:无参数,无返回值,需要函数式接口支持只有一个抽象方法
/**
* 语法格式一:
* 无参数,无返回值,需要函数式接口支持只有一个抽象方法
* () -> System.out.println("hello Lambda!");
*/
@Test
public void test() {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("hello lambda!");
}
};
runnable.run();
System.out.println("----------------------------");
Runnable runnable1 = () -> System.out.println("hello Lambda!");
runnable1.run();
}
语法格式二:有一个参数,并且无返回值
/**
* 语法格式二:有一个参数,并且无返回值
* (x)-> System.out.println(x)
*/
@Test
public void test2() {
Consumer<String> consumer = (x)-> System.out.println(x);
consumer.accept("lambda");
}
语法格式三:若只有一个参数,小括号可以省略不写
/**
* 语法格式三:若只有一个参数,小括号可以省略不写
* x-> System.out.println(x)
*/
@Test
public void test3() {
Consumer<String> consumer = x-> System.out.println(x);
consumer.accept("lambda");
}
语法格式四:有两个以上参数,有返回值,并且Lambda体中有多条语句
/**
* 语法格式四:有两个以上参数,有返回值,并且Lambda体中有多条语句
*
*/
@Test
public void test4() {
Comparator<Integer> comparator = (x,y) -> {
System.out.println("函数式接口编程");
return Integer.compare(x,y);
};
}
语法格式五:有两个以上参数,有返回值,并且Lambda体中只有条语句,return 和{}都可以省略不写
/**
* 语法格式五:有两个以上参数,有返回值,并且Lambda体中只有条语句,return 和{}都可以省略不写
*
*/
@Test
public void test5() {
Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y);
}
语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为jvm编译器可以通过上下文推断出,数据类型,即为类型推断。
/**
* 语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,
* 因为jvm编译器可以通过上下文推断出,数据类型,即为类型推断
* 类型推断就是通过上下文比如前面定义了Comparator<Integer>就是Integer
* String [] str ={"a","b","c"}; 类型推断大括号里面就是String类型的数组new String[]
*/
@Test
public void test6() {
Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y);
}
上联:左右遇一括号省
下联:左侧推断类型省
横批:能省则省
Lambda表达式需要函数式接口的支持
三、函数式接口
1.函数式接口
接口中只有一个抽象方法的接口,称为函数式接口。可以使用注解@FunctionInterface修饰,可以检查是否为函数式接口
package com.ming.service;
/**
* FunctionalInterface修饰的接口必须是函数式接口,只有一个抽象方法
* @param <T>
*/
@FunctionalInterface
public interface MyPredicate<T> {
public boolean test(T t);
}
函数式接口
package com.ming.service;
@FunctionalInterface
public interface MyFun<T> {
public T fun(T t);
}
//需求:对一个数进行运算
public Integer option(Integer num,MyFun<Integer> myFun){
return myFun.fun(num);
}
@Test
public void test7(){
MyFun<Integer> num = (n) -> n*n;
Integer n = num.fun(10);
System.out.println(n);
Integer i = option(10,(x) -> x*x);
System.out.println(i);
}
Lambda表达式作为参数传递
函数式接口
package com.ming.service;
public interface MyString<T> {
public T getStringValue(T t);
}
测试
/**
* 字符串转大写
* @param str
* @param myString
* @return
*/
public String getStringValue(String str,MyString<String> myString){
return myString.getStringValue(str);
}
@Test
public void test9(){
MyString<String> str = (s) ->s.toUpperCase();
System.out.println(str.getStringValue("aa"));
String s = getStringValue("BB",(x)->x.toUpperCase());
System.out.println(s);
}
2.Java8四大内置接口
Consumer :消费型接口、Supplier :供给型接口、Function<T,R> :函数型接口 、Predicate:断言型接口
package com.ming;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* Java8内置的四大核心接口
* <p>
* Consumer<T> :消费型接口
* void accept(T t);
* <p>
* Supplier<T> :供给型接口
* T get();
* <p>
* Function<T,R> :函数型接口
* R apply(T t);
* <p>
* <p>
* Predicate<T>:断言型接口
* boolean test(T t);
*/
public class LambdaTest3 {
/**
* Consumer<T> :消费型接口
*/
public void test() {
happy(88.5, (m) -> System.out.println(m));
}
public void happy(Double money, Consumer<Double> con) {
con.accept(money);
}
/**
* Supplier<T> :供给型接口
* T get();
*/
@Test
public void test1() {
Supplier<Integer> supplier = () -> (int) (Math.random() * 100);
System.out.println(supplier.get());
List<Integer> li = getNumList(5, () -> (int) (Math.random() * 100));
System.out.println(li);
}
public List<Integer> getNumList(int num, Supplier<Integer> sup) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
list.add(sup.get());
}
return list;
}
/**
* Function<T,R> :函数型接口
* R apply(T t);
*/
@Test
public void test2() {
Function<String, String> function = (str) -> str.toUpperCase();
System.out.println(function.apply("aa"));
}
/**
* Predicate<T>:断言型接口
* boolean test(T t);
*/
@Test
public void test3() {
Predicate<String> str = (s) -> s instanceof String;
System.out.println(str.test("a"));
}
}
四、方法引用与构造器引用
1.方法引用
若Lambda体中的内容有方法已经实现了,我们可以使用方法引用
可以理解为:方法引用是Lambda表达式的另外一种表现形式
主要有三种语法格式:对象::实例方法名、类::静态方法名、类::实例方法名
注意:
Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的参数列表和返回值类型保持一致
若Lambda参数列表中第一个参数是实例方法的调用者,第二参数是实例方法的参数时,可以使用类名ClassName::method
package com.ming;
import com.ming.model.Employee;
import org.junit.Test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* 方法引用:若Lambda体中的内容有方法已经实现了,我们可以使用方法引用
* 可以理解为:方法引用是Lambda表达式的另外一种表现形式
* 主要有三种语法格式:
* 对象::实例方法名
* 类::静态方法名
* 类::实例方法名
* 注意:Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的参数列表和返回值类型保持一致
*/
public class TestMethodRef {
/**
* 对象::实例方法名
*/
@Test
public void test1() {
//Lambda表达式
PrintStream pr = System.out;
Consumer<String> consumer = (x) -> pr.println(x);
PrintStream ps = System.out;
Consumer<String> consumer1 = ps::println;
Consumer<String> consumer2 = System.out::println;
consumer.accept("aa");
}
@Test
public void test2() {
Employee employee = new Employee();
Supplier<String> supplier = () -> employee.getName();
System.out.println(supplier.get());
Supplier<Integer> supplier1 = employee::getAge;
System.out.println(supplier.get());
}
/**
* 类::静态方法名
*/
@Test
public void test3() {
Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
Comparator<Integer> comparator1 = Integer::compare;
}
/**
* 类::实例方法名
*/
@Test
public void test4() {
BiPredicate<String, String> biPredicate = (x, y) -> x.equals(y);
System.out.println(biPredicate.test("a", "a"));
BiPredicate<String, String> biPredicate1 = String::equals;
System.out.println(biPredicate1.test("a", "a"));
}
}
2.构造器引用
ClassName::new
注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致
/**
* 构造器引用
*/
@Test
public void test5() {
Supplier<Employee> employeeSupplier = () -> new Employee();
Employee employee = employeeSupplier.get();
Supplier<Employee> employeeSupplier2 = Employee::new;
}
@Test
public void test6() {
Function<String, Employee> function = (x) -> new Employee(x);
Function<String, Employee> function1 = Employee::new;
Employee employee = function1.apply("1001");
System.out.println(employee);
}
3.数组引用
语法:type[]::new
/**
* 数组引用
*/
@Test
public void test7() {
Function<Integer, String[]> function = (x) -> new String[x];
String[] s = function.apply(10);
System.out.println(s.length);
Function<Integer,String[]> function1 =String[]::new;
String[] s2 = function1.apply(10);
System.out.println(s2.length);
}
五、Stream API
1.什么是Stream?
流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。集合讲的是数据,流讲的是计算
注意:
流不会存储元素
流不会改变源对象。相反,他们会返回一个持有结果的新Stream
流操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
2.操作Stream三个的步骤
创建Stream:一个数据源(如:集合、数组),获取一个流
中间操作:一个中间操作链,对数据源的数据进行处理
终止操作(终端操作):一个终止操作,执行中间操作链,并产生结果
创建流
1.可以通过Conllection系列集合提供的Stream()或ParallelStream()获取流
2.通过Arrays中的静态方法stream()获取数组流
3.通过Stream中类中of()静态方法获取流
4.无限流
/**
* 创建流
*/
@Test
public void test() {
//1.可以通过Conllection系列集合提供的Stream()或ParallelStream()获取流
List<String> list = new ArrayList<>();
list.add("a");
Stream<String> Stream1 = list.stream();
//2.通过Arrays中的静态方法stream()获取数组流
Employee[] employees = new Employee[10];
Stream<Employee> Stream2 = Arrays.stream(employees);
//3.通过Stream中类中of()静态方法获取流
Stream<String> Stream3 = Stream.of("aa", "bb", "cc");
//4.无限流
// 迭代 UnaryOperator一元运算
Stream<Integer> Stream4 = Stream.iterate(2, (x) -> x + 2);
Stream4.limit(2).forEach(System.out::println);
//生成
Stream<Integer> stream5 = Stream.generate(() -> (int) (Math.random() * 100));
stream5.limit(5).forEach(System.out::println);
}
中间操作
筛选与切片
filter:接受Lambda,从流中排除某些元素
limit:截断流,使其元素不超过给定数量
skip(n):跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit互补
内部迭代:迭代操作由stream api 完成
/**
* 中间操作
*
* 筛选与切片
* filter:接受Lambda,从流中排除某些元素
* limit:截断流,使其元素不超过给定数量
* skip(n):跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit互补
*/
@Test
public void test2(){
//内部迭代:迭代操作由stream api 完成
//中间操作:不会执行任何操作
Stream<Employee> stream = employeeList.stream()
.filter((e)->e.getAge()>35);
//终止操作:一次性执行全部内容,称为惰性求值
stream.forEach(System.out::println);
}
/**
* 外部迭代
*/
@Test
public void test3(){
Iterator<Employee> employeeIterator = employeeList.iterator();
while (employeeIterator.hasNext()){
System.out.println(employeeIterator.next());
}
}
/**
* limit
*/
@Test
public void test4(){
employeeList.stream()
.filter((e)->e.getSalary()>5000)
.limit(2)
.forEach(System.out::println);
}
@Test
public void test5(){
employeeList.stream()
.filter((e)->e.getSalary()>5000)
.skip(2)
.forEach(System.out::println);
}
@Test
public void test6(){
employeeList.stream()
.filter((e)->e.getSalary()>5000)
.distinct()
.forEach(System.out::println);
}
映射
map:接受lambda,将元素转换成其他形式或提取信息。接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接受一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
map指把当前的流加到一个个的流中,flatMap指把流中的一个个元素加到流中
@Test
public void test7() {
List<String> list = Arrays.asList("aa", "bb", "cc");
list.stream()
.map((str) -> str.toUpperCase())
.forEach(System.out::println);
System.out.println("============映射map=================");
employeeList.stream()
.map(Employee::getName)
.forEach(System.out::println);
Stream<Stream<Character>> stream = list.stream()
.map(StreamTest::filterCharacter);
stream.forEach((sm) -> {
sm.forEach(System.out::println);
});
System.out.println("======flatMap====");
Stream<Character> characterStream = list.stream()
.flatMap(StreamTest::filterCharacter).sorted(Comparator com);
characterStream.forEach(System.out::println);
}
排序
sorted():自然排序
sorted(Comparator com):定制排序
/**
* 排序
*/
@Test
public void test8() {
List<String> list = Arrays.asList("cc", "bb", "aa");
list.stream()
.sorted()
.forEach(System.out::println);
System.out.println("-----定制排序-----");
employeeList.stream()
.sorted((x,y)->{
if (x.getAge()==y.getAge()){
return x.getName().compareTo(y.getName());
}else{
return Integer.compare(x.getAge(),y.getAge());
}
}).forEach(System.out::println);
}
Stream的终止操作
查找与匹配
allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
package com.ming;
import com.ming.model.Employee;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
/**
* 终止操作
*/
public class StreamTest1 {
List<Employee> employeeList = Arrays.asList(
new Employee("1", "张三", 15, 8000.88, Employee.Status.BUSY),
new Employee("2", "李四", 30, 7000.88, Employee.Status.FREE),
new Employee("3", "王五", 25, 9000.88, Employee.Status.VOCATION),
new Employee("4", "郭靖", 38, 4000.88, Employee.Status.BUSY),
new Employee("5", "黄蓉", 50, 3000.88, Employee.Status.FREE)
);
@Test
public void test() {
boolean b = employeeList.stream()
.allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b);
boolean b1 = employeeList.stream()
.anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b1);
boolean b2 = employeeList.stream()
.noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b2);
Optional<Employee> op1 = employeeList.stream()
.sorted((e1, e2) -> -Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
System.out.println(op1.get());
Optional<Employee> op2 = employeeList.parallelStream()
.filter((e) -> e.getStatus().equals(Employee.Status.FREE))
.findAny();
System.out.println(op2.get());
Long l = employeeList.stream()
.count();
System.out.println(l);
Optional<Employee> op3 = employeeList.stream()
.max((x, y) -> -Double.compare(x.getAge(), y.getAge()));
System.out.println(op3.get());
Optional<Employee> op4 = employeeList.stream()
.min((x, y) -> Double.compare(x.getAge(), y.getAge()));
System.out.println(op4.get());
Optional<Double> op5 = employeeList.stream()
.map(Employee::getSalary)
.min(Double::compare);
System.out.println(op5.get());
}
}
规约
reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
/**
* 规约
*/
@Test
public void test2() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
Optional<Double> sum2 = employeeList.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(sum2.get());
}
收集
collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
/**
* 收集
*/
@Test
public void test3() {
List<String> names = employeeList.stream()
.map(Employee::getName)
.collect(Collectors.toList());
System.out.println(names);
System.out.println("-----------");
List<String> name2 = employeeList.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(ArrayList::new));
System.out.println(name2);
}
/**
* 分组
*/
@Test
public void test4() {
Map<Employee.Status, List<Employee>> map = employeeList.stream()
.collect(Collectors.groupingBy((e) -> e.getStatus()));
System.out.println(map);
}
/**
* 多级分组
*/
@Test
public void test5() {
Map<Employee.Status, Map<String, List<Employee>>> map = employeeList.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if (e.getAge() <= 35) {
return "青年";
} else if (e.getAge() <= 50) {
return "中年";
} else {
return "老年";
}
})));
System.out.println(map);
}
/**
* 分区
*/
@Test
public void test6() {
Map<Boolean, List<Employee>> map = employeeList.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() > 8000));
System.out.println(map);
System.out.println("----------");
DoubleSummaryStatistics summaryStatistics = employeeList.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(summaryStatistics.getMax());
System.out.println(summaryStatistics.getAverage());
}
3.并行流与串行流
并行流: 就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与顺序流之间进行切换。
Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个
小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总.
package com.ming.service.imp;
import java.io.Serializable;
import java.util.concurrent.RecursiveTask;
/**
* fork join 框架
*/
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();
}
}
}
package com.ming.service;
import com.ming.service.imp.ForkJoinCalculate;
import org.junit.Test;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
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
}
}
4.Optional容器
Optional< T> 类(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
package com.ming;
import com.ming.model.Employee;
import com.ming.model.Godness;
import com.ming.model.Man;
import com.ming.model.NewMan;
import org.junit.Test;
import java.util.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中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用 default 关键字修饰。
七、新时间日期API
/**
* 日期引发的并发
*/
public class TestSimpleDateFormat {
public static void main(String[] args) throws ExecutionException, InterruptedException {
SimpleDateFormat DATA_FORMAT = new SimpleDateFormat("yyMMdd");
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return DateFormatThreadLocal.convert("20210105");
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(pool.submit(task));
}
for (Future<Date> dateFuture : list) {
System.out.println(dateFuture.get());
}
pool.shutdown();
}
}
用ThredLocal< DateFormat>解决多线程日期并发
package com.ming.service;
import javafx.scene.input.DataFormat;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFormatThreadLocal {
private static final ThreadLocal<DateFormat> THREAD_LOCAL = new ThreadLocal<DateFormat>(){
protected DateFormat initialValue() {
return new SimpleDateFormat("yyMMdd");
}
};
public static Date convert(String source) throws ParseException {
return THREAD_LOCAL.get().parse(source);
}
}
Java8日期解决可以解决并发
@Test
public void dateTest() throws ExecutionException, InterruptedException {
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMdd");
Callable<LocalDate> task = new Callable<LocalDate>() {
@Override
public LocalDate call() throws Exception {
return LocalDate.parse("20210105",df);
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<LocalDate>> list = new ArrayList<>();
for (int i = 0; i <10 ; i++) {
list.add(pool.submit(task));
}
for (Future<LocalDate> localDateFuture : list) {
System.out.println(localDateFuture.get());
}
pool.shutdown();
}
1.LocalDate 、LocalTime 、LocalDateTime
LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。
package com.atguigu.java8;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Set;
import org.junit.Test;
public class TestLocalDateTime {
//1. LocalDate、LocalTime、LocalDateTime
@Test
public void test1(){
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
LocalDateTime ld2 = LocalDateTime.of(2016, 11, 21, 10, 10, 10);
System.out.println(ld2);
LocalDateTime ldt3 = ld2.plusYears(20);
System.out.println(ldt3);
LocalDateTime ldt4 = ld2.minusMonths(2);
System.out.println(ldt4);
System.out.println(ldt.getYear());
System.out.println(ldt.getMonthValue());
System.out.println(ldt.getDayOfMonth());
System.out.println(ldt.getHour());
System.out.println(ldt.getMinute());
System.out.println(ldt.getSecond());
}
}
注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法
2. Instant 时间戳
//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);
}
3.Duration 和 Period
Duration:用于计算两个“时间”间隔、Period:用于计算两个“日期”间隔
//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());
}
4.时间校正器
TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
TemporalAdjusters : 该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。
//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);
}
5.DateTimeFormatter时间格式化
//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);
}
@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();
String strDate = ldt.format(dtf);
System.out.println(strDate);
LocalDateTime newLdt = ldt.parse(strDate, dtf);
System.out.println(newLdt);
}
八、重复注解与类型注解
要定义重复注解必须定义一个容器,重复注解必须用@Repeatable进行修饰同时指定容器类
package com.ming.model;
import java.lang.annotation.*;
/**
* 重复注解必须用Repeatable进行修饰指定容器类
*/
@Repeatable(MyAnnotations.class)
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "自定义注解";
}
package com.ming.model;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
MyAnnotation[] value();
}
package com.ming.service;
import com.ming.model.MyAnnotation;
import org.junit.Test;
import javax.management.Descriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* 重复注解与类型注解
*/
public class TestAnnotation {
@MyAnnotation("hello")
@MyAnnotation("word")
public void show(@MyAnnotation(value = "abc") String str) {
}
@Test
public void test() throws Exception {
Class<TestAnnotation> clazz = TestAnnotation.class;
Method method = clazz.getMethod("show");
MyAnnotation[] myAnnotations = method.getAnnotationsByType(MyAnnotation.class);
for (MyAnnotation my : myAnnotations) {
System.out.println(my.value());
}
}
}