Java8
主要内容
- Lambda 表达式
- 函数式接口
- 方法引用 / 构造器引用
- Stream API
- 接口中的默认方法 / 静态方法
- 新时间日期 API
- 其他新特性
新特性
-
速度更快,效率增加
(Hashmap 底层结构/原理。数组+链表—> 数组+链表+红黑树;1.8之后jv m没有永久区,方法区增加了metaspace元空间【使用物理内存】,垃圾回收机制运行效率提升)
-
代码更少(增加了Lambda表达式)
-
强大的 Stream API
-
便于并行
-
最大化减少空指针异常 Optional (提供了一个容器类Kotlin ?)
Lambda表达式
Lambda是一个匿名函数,可以理解为一段可以传递的代码(将代码像数据一样传递)
可以写出更简洁、更灵活的代码;作为一种更紧凑的代码风格,使Java语言表达能力得到提升。
Lambda表达式简单案例
匿名内部类与Lambda表达式对比
public class TestLambda {
// 原来的匿名内部类
@Test
public void test1(){
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
// 调用
TreeSet<Integer> ts = new TreeSet<>(com);
}
}
// Lambda 表达式
public void test2(){
Comparator<Integer> comparator = (x,y)->Integer.compare(x,y);
TreeSet<Integer> ts = new TreeSet<>(comparator);
}
需求:获取当前公司中 员工年龄大于35的员工信息
List<Employee> employees = Arrays.asList(
new Employee("123", 12, 333.33),
new Employee("222", 35, 333.44),
new Employee("333", 42, 333.55),
new Employee("144", 56, 333.44)
);
public List<Employee> filterEmployees(List<Employee> list) {
List<Employee> emps = new ArrayList<>();
for (Employee emp : list) {
if (emp.getAge() >= 35) {
emps.add(emp);
}
}
return emps;
}
@Test
public void test3() {
List<Employee> employees = filterEmployees(this.employees);
for (Employee employee : employees) {
System.out.println(employee);
}
}
// 需求:获取当前员工工资大于5000的员工信息
public List<Employee> filterEmployees2(List<Employee> list) {
List<Employee> emps = new ArrayList<>();
for (Employee emp : list) {
if (emp.getSalary() >= 5000) {
emps.add(emp);
}
}
return emps;
}
代码过于冗余
优化方式一:采用设计模式(策略设计模式)
public interface MyPredicate<T> {
public boolean test(T t);
}
public class FilterEmployeeByAge implements MyPredicate<Employee>{
@Override
public boolean test(Employee employee) {
return employee.getAge()>=35;
}
}
public class TestLambda {
// 优化方式一:
public List<Employee> filterEmployee(List<Employee> list , MyPredicate<Employee> mp){
List<Employee> emps = new ArrayList<>();
for (Employee employee : list) {
if(mp.test(employee)){
emps.add(employee);
}
}
return emps;
}
@Test
public void test4() {
List<Employee> employees = filterEmployee(this.employees,new FilterEmployeeByAge());
for (Employee employee : employees) {
System.out.println(employee);
}
}
}
// 需求:获取当前员工工资大于5000的员工信息
public class FilterEmployeeBySalary implements MyPredicate<Employee>{
@Override
public boolean test(Employee employee) {
return employee.getSalary()>=5000;
}
}
public class TestLambda {
// 优化方式一:
public List<Employee> filterEmployee(List<Employee> list , MyPredicate<Employee> mp){
List<Employee> emps = new ArrayList<>();
for (Employee employee : list) {
if(mp.test(employee)){
emps.add(employee);
}
}
return emps;
}
@Test
public void test4() {
List<Employee> employees = filterEmployee(this.employees,new FilterEmployeeBySalary());
for (Employee employee : employees) {
System.out.println(employee);
}
}
}
优化方式二:匿名内部类
@Test
public void test5(){
List<Employee> employees = filterEmployee(this.employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getSalary() <= 500;
}
});
for (Employee employee : employees) {
System.out.println(employee);
}
}
优化方式三:lambda表达式
@Test
public void test6(){
List<Employee> employees = filterEmployee(this.employees, (e) -> e.getSalary() <= 5000);
employees.forEach(System.out::println);
}
优化方式四:Stream API
@Test
public void test7(){
this.employees.stream()
.filter((e)->e.getSalary()>=5000)
.limit(2)
.forEach(System.out::println);
this.employees.stream()
.map(Employee::getName).forEach(System.out::println);
}
Lambda基本语法
java8引入了一个新的操作符“->“,该操作符称为箭头操作符或lambda操作符
箭头操作符将lambda表达式拆分成两部分:
- 左侧:lambda表达式的参数列表
- 右侧:lambda表达式中所需要执行的功能,即 lambda 体
函数式接口:接口中只有一个抽象方法的接口
语法格式
一:无参数,无返回值
()->System.out.println("hello");
@Test
public void test1(){
int num = 0; // jdk 1.7 以前必须是final(但本质上还是final)
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("hello"+num);
}
};
r.run()
System.out.println("---------------");
Runnable r1 = ()-> System.out.println("hello"+num);
r1.run();
}
二:有一个参数,并且无返回值(若只有一个参数,小括号可以省略不写)
@Test
public void Test2(){
Consumer<String> con = (x)-> System.out.println(x);
con.accept("wooo");
}
//对accept方法的实现
@Test
public void Test2(){
Consumer<String> con = x-> System.out.println(x);
con.accept("wooo");
}
三:有两个以上的参数,有返回值,并且lambda体中有多条语句
@Test
public void Test3(){
Comparator<Integer> com = (x,y)->{
System.out.println("函数式接口");
return Integer.compare(x,y);
};
}
四:若lambda体中只有一条语句,return 和 {}都可以省略不写
@Test
public void Test4(){
Comparator<Integer> com = (x,y)-> Integer.compare(x,y);
}
五:lambda表达式的参数列表的数据类型可以省略不写,因为jvm 编译器可以通过上下文推断出,数据类型,即【类型推断】
@Test
public void Test4(){
Comparator<Integer> com = (Integer x,Integer y)-> Integer.compare(x,y);
// jdk 1.8 类型推断升级
show(new HashMap<>());
}
public void show(Map<String,Integer> map){
}
@Test
public void Test5(){
String[] strs = {"aaa","bbb","ccc"};// 推断出来的类型
// String[] strs;
// strs = {"aaa","bbb","ccc"} // 报错 推断不出
List<String> list = new ArrayList<>();
}
左右遇一括号省 ,左侧推断类型省
lambda表达式需要函数式接口的支持
函数式接口
接口中只有一个抽象方法的接口,称为函数式接口
可以使用注解 @FunctionalInterface
修饰 (可以检查是否是函数式接口)
@FunctionalInterface
public interface MyPredicate<T> {
public boolean test(T t);
}
需求:对一个数进行运算
@FunctionalInterface
public interface MyFun {
public Integer getValue (Integer integer);
}
public class TestLambda {
public Integer operation(Integer num, MyFun myFun) {
return myFun.getValue(num);
}
@Test
public void Test6() {
Integer operation = operation(100, (x) -> x * x);
System.out.println(operation);
System.out.println(operation(200, (y) -> y + 200));
MyFun myFun = (x)->x*20;
myFun.getValue(200);
}
}
lambda练习
一:调用 Collections.sort() 方法,通过定制排序 比较两个 Employee (先按照年龄比,年龄相同按照姓名比),使用 Lambda 表达式作为参数传递
public class TestLambda {
List<Employee> employees = Arrays.asList(
new Employee("123", 12, 3222.33),
new Employee("222", 35, 333.44),
new Employee("333", 42, 5555.55),
new Employee("144", 56, 6666.44)
);
@Test
public void test1(){
Collections.sort(employees,(e1,e2)->{
if(e1.getAge()== e2.getAge()){
return e1.getName().compareTo(e2.getName());
}else {
return -Integer.compare(e1.getAge(), e2.getAge());
}
});
for (Employee employee : employees) {
System.out.println(employee);
}
}
}
二:
1、声明函数式接口,接口中声明抽象方法 ,public String getValue(String str);
2、声明类 TestLambda,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值;
3、再将一个字符串的第2个和第4个索引位置进行截取字串
@FunctionalInterface
public interface MyFunction {
public String getValue(String str);
}
public class TestLambda {
// 需求:用于处理字符串的方法
public String strHandler(String str,MyFunction mf){
return mf.getValue(str);
}
@Test
public void test2(){
String s = strHandler("\t\t\t hello ", (str) -> str.trim());
System.out.println(s);//hello
String abdc = strHandler("abdc", str -> str.toUpperCase());
System.out.println(abdc);//ABDC
String hello = strHandler("hello", str -> str.substring(2, 4));
System.out.println(hello);//ll
}
}
三:
1、声明一个带两个泛型的函数式接口,泛型类型为<T, R> T 为参数,R 为返回值;
2、接口中声明对应的抽象方法;
3、在 TestLambda 类中声明方法,使用接口作为参数,计算两个 Long 类型参数的和;
4、在计算两个 Long 类型参数的乘积
public interface MyFunction2<T,R> {
public R getValue(T t1,T t2);
}
public class TestLambda {
// 需求:对于两个Long型数据进行处理
public void op(Long l1, Long l2, MyFunction2<Long, Long> mf) {
System.out.println(mf.getValue(l1, l2));
}
@Test
public void test3() {
op(100L, 200L, (x, y) -> x + y);
op(100L, 200L, (x, y) -> x * y);
}
}
四大内置核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer<T> 消费型接口 | T | void | 对类型为T的对象应用操作:void accept(T t) |
Supplier<T> 提供型接口 | 无 | T | 返回类型为T的对象:T get() |
Function<T, R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果为R类型的对象:R apply(T t) |
Predicate<T> 断言型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值:boolean test(T t) |
Consumer <T>
消费型接口
void accept(T t);
// Consumer<T> 消费型接口
@Test
public void test1(){
happy(10000,(money)-> System.out.println("消费型接口" + money));
Consumer<Integer> consumer = (x)-> System.out.println(x);
consumer.accept(100);
}
public void happy(double money, Consumer<Double> con){
con.accept(money);
}
Supplier <T>
供给型接口
T get();
// Supplier <T> 供给型接口
@Test
public void test2() {
List<Integer> numList = getNumList(10, () -> (int) Math.random() * 100);
for (Integer integer : numList) {
System.out.println(integer);
}
}
// 需求:产生一些整数,并放入集合
public List<Integer> getNumList(int num, Supplier<Integer> sup) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = sup.get();
list.add(n);
}
return list;
}
Function<T, R>
函数型接口
R apply(T t);
//Function(T,R) 函数型接口
@Test
public void test3() {
String s = strHandler("\t\t\thello", (str) -> str.trim());
System.out.println(s);
String hello = strHandler("hello", (str) -> str.substring(2, 4));
System.out.println(hello);
}
// 需求:用于处理字符串
public String strHandler(String str, Function<String, String> fun) {
return fun.apply(str);
}
Predicate<T>
断言型接口
boolean test(T t)
// Predicate<T>` 断言型接口
@Test
public void Test4() {
List<String> list = Arrays.asList("hello", "atguigu", "lambda", "www", "ok");
List<String> stringList = filterStr(list, (s) -> s.length() > 3);
for (String s : stringList) {
System.out.println(s);
}
}
// 需求:将满足条件的字符串,放入集合中
public List<String> filterStr(List<String> list, Predicate<String> pre) {
List<String> stringList = new ArrayList<>();
for (String s : list) {
if (pre.test(s)) {
stringList.add(s);
}
}
return stringList;
}
其他接口
方法引用和构造器引用
方法引用
若lambda体 中的内容有方法已经实现了,我们可以使用“方法引用”
(可以理解位方法引用是lambda 表达式的另外一种表现形式)
语法格式
一、对象::实例方法名
二、类::静态方法名
三、类::实例方法名
对象::实例方法名
@Test
public void test1() {
Consumer<String> consumer = (x) -> System.out.println(x);
PrintStream ps = System.out;
Consumer<String> con1 = ps::println;
}
注意:== lambda 表达式中需要实现的接口中的抽象方法的参数列表与返回值(accept) 必须与当前调用方法的参数列表和返回值类型(println)保持一直==
@Test
public void test1() {
PrintStream ps1 = System.out;
Consumer<String> consumer = (x) -> ps1.println(x);
PrintStream ps = System.out;
Consumer<String> con1 = ps::println;
Consumer<String> con2 = System.out::println;
con2.accept("abcdf");
}
@Test
public void test2(){
Employee employee = new Employee();
Supplier<String> sup = ()-> employee.getName();
String s = sup.get();
System.out.println(s);
Supplier<Integer> sup2 = employee::getAge;
Integer integer = sup2.get();
System.out.println(integer);
}
类::静态方法名
// 类::静态方法名
@Test
public void test3() {
Comparable<Integer> com = (x, y) -> Integer.compare(x, y);
Comparable<Integer> com1 = Integer::compare;
}
注意: lambda体中的调用方法的参数列表和返回值类型,要与函数式接口中抽象方法的参数列表和返回值类型相同
类::实例方法名
// 类::实例方法名
@Test
public void test4() {
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
BiPredicate<String, String> bp2 = String::equals;
}
注意: 若lambda参数列表中第一个参数是实例方法的调用者,第二个参数是实例方法的参数时,可以使用ClassName::method
构造器引用
ClassName::new
// 构造器引用
@Test
public void test5() {
Supplier<Employee> sup = () -> new Employee();
sup.get();
//构造器引用方式
Supplier<Employee> sup2 = Employee::new;//匹配无参构造器
Employee employee = sup2.get();
System.out.println(employee);
}
@Test
public void test6() {
Function<Integer, Employee> fun = (x) -> new Employee(x);
Function<Integer,Employee>fun2 = Employee::new;// 匹配一个有参构造器
Employee apply = fun2.apply(101);
System.out.println(apply);
BiFunction<Integer,Integer,Employee> bf =Employee::new;// 匹配两个参数的有参构造器
}
注意: 需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致
数组引用
Type::new
// 数组引用
@Test
public void test7() {
Function<Integer, String[]> fun = (x) -> new String[x];
String[] strs = fun.apply(10);
System.out.println(strs.length);
Function<Integer, String[]> fun2 = String[]::new;
String[] apply = fun2.apply(20);
System.out.println(strs.length);
}
Stream API
什么是Stream
数据源转化成流, 做一系列的中间操作,产生一个新的流,而原来的数据源不会收到影响
流(Stream):是数据渠道,用于操作数据源(集合\数组等)所生产的元素序列.“集合讲的是数据,流讲的是计算”
注意:
- Stream 流自己不会存储元素
- Stream 不会改变源对象.相反,他们会返回一个持有结果的新Stream
- Stream 操作是延迟执行的.这意味着他们会等到需要结果的时候才执行
Stream的操作三个步骤
一:创建Stream
一个数据源(集合\数组等),获取一个流
二:中间操作
一个中间操作链,对数据源的数据进行处理
三:终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
创建Stream
// 创建Stream
@Test
public void test1() {
// 1: 可以通过 Collection 系列集合提供的Stream()串行流 或 paralStream()并行流
// - Collection.stream() 串行流
// - Collection.parallelStream() 并行流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
// 2: 通过Arrays中的静态方法 stream() 也可以获得数组流
// Arrays.stream(array)
Employee[] employees = new Employee[10];
Stream<Employee> stream1 = Arrays.stream(employees);
// 3: 通过Stream类中的静态方法 of()
// Stream.of(...)
Stream<String> stream2 = Stream.of("aa", "bb", "cc");
// 4: 创建无限流
// 迭代
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2);
stream3.limit(10).forEach(System.out::println);
// 生成
Stream.generate(() -> Math.random())
.limit(5)
.forEach(System.out::println);
}
中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为"惰性求值"
筛选与切片
- filter:接收 Lambda ,从流中排除某些元素
- limit:截断流,使其元素不超过给定数量
- skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补
- distinct:筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素
filter:接收 Lambda ,从流中排除某些元素
public class TestStreamAPI2 {
List<Employee> employees = Arrays.asList(
new Employee("123", 12, 3222.33),
new Employee("222", 35, 333.44),
new Employee("333", 42, 5555.55),
new Employee("144", 56, 6666.44)
);
// 中间操作 : 不会执行任何操作
// 内部迭代: 迭代操作由Stream API 完成
@Test
public void test1() {
// filter:接收 Lambda ,从流中排除某些元素
Stream<Employee> stream = employees.stream()
.filter((x) -> {
System.out.println("Stream api 的中间操作");
return x.getAge() > 35;
});
stream.forEach(System.out::println);//终止操作 :一次性执行全部内容,惰性求值
}
}
内部迭代:由stream API 完成
外部迭代:我们通过迭代器完成
// 外部迭代:
@Test
public void test2(){
Iterator<Employee> iterator = employees.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
limit:截断流,使其元素不超过给定数量
// limit:截断流,使其元素不超过给定数量
@Test
public void test3() {
employees.stream()
.filter((e) -> {
System.out.println("短路");
return e.getSalary() > 5000;
})
.limit(2) // 短路 达到满足条件以后不再内部迭代,提高效率(这里只遍历了2次)
.forEach(System.out::println);
;
}
skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补
//skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补
@Test
public void test4(){
employees.stream()
.filter((e) -> {
System.out.println("短路");
return e.getSalary() > 5000;
})
.skip(2)
.forEach(System.out::println);
}
distinct:筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素
// distinct 去重
@Test
public void test5(){
employees.stream()
.filter((e) -> {
System.out.println("短路");
return e.getSalary() > 5000;
})
.skip(2)
.distinct()
.forEach(System.out::println);
}
findany : 为该流的某些元素返回一个Optional,如果该流为空,则返回一个空的Optional。在这里,Optional是一个容器对象,可以包含也可以不包含非null值。
List<Integer> list = Arrays.asList(1, 3, 5, 7);
Optional<Integer> any = list.stream().findAny();
// 这样出来的话,这个any肯定就是1了,因为此时stream是有顺序的,相当于一个list,把list中的数字按顺序去执行findany,所以第一个是1, 既然找到了1,因此直接返回1
List<Integer> list = Arrays.asList(1, 3, 5, 7);
Optional<Integer> any = list.stream().filter(i -> i>4).findAny();
// 这样的话,这个any肯定就是5了,因为此时根据filter过滤了一把,把大于4的数字留下来了,因此在执行findany之前,此时stream中只有5和7了,所以按照顺序,取到第一个是5,找了一个,因此直接返回5
List<Integer> list = Arrays.asList(1, 3, 5, 7);
Optional<Integer> any = list.stream().filter(i -> i>9).findAny();
// 这样的话,这个any就是Optional.empty,是一个空的了,因此根据filter过滤完了之后,没有大于9的,此时stream中没有元素,因此findany没有找到任何一个,所以返回空Optional
List<Integer> list = Arrays.asList(1, 3, 5, 7);
Optional<Integer> any = list.stream().parallel().findAny();
// 此时stream中加了parallel()方法,所以这个流是一个并行流,是无序的,那这个findany就无法确定到底返回谁了,1,3,5,7,哪一个都有可能返回了
参考:
Java8 findany : https://zhidao.baidu.com/question/1498838916214323339.html
java8中stream的findAny踩坑:
https://blog.csdn.net/qq_41574321/article/details/94634351
映射
- map:接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
- flatMap:接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流
// 映射
// map 接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
@Test
public void test6(){
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
list.stream().map((str)->str.toUpperCase())
.forEach(System.out::println);
System.out.println("-----------------");
employees.stream().map(Employee::getName)
.forEach(System.out::println);//提取名字
System.out.println("-----------------");
Stream<Stream<Character>> streamStream = list.stream()
.map(TestStreamAPI2::filterCharacter);//{{a,a,a},{b,b,b}}
streamStream.forEach((sm)->{
sm.forEach(System.out::println);
});
System.out.println("-----------------");
// flatMap:接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流
Stream<Character> characterStream = list.stream()
.flatMap(TestStreamAPI2::filterCharacter);// {a,a,a,b,b,b,}
characterStream.forEach(System.out::println);
}
public static Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
map和flatmap的区别类似于add 和 addALL的区别
map(是将各个流添加到当前新的流中),flatmap(是将流中的各个元素,添加到新的流中)
@Test
public void test7(){
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
List list2 = new ArrayList();
list2.add(11);
list2.add(22);
// list2.add(list2);// [11,22,[aaa,bbb,ccc,ddd,eee]]
list2.addAll(list2);//[11,22,aaa,bbb,ccc,ddd,eee]
System.out.println(list2);
}
排序
sorted():自然排序(Comparable)
sorted(Comparator com):定制排序
// 排序
@Test
public void test8(){
List<String> list = Arrays.asList("ccc","aaa","bbb","ccc","ddd","eee");
// 自然排序(Comparable)
list.stream().sorted().forEach(System.out::println);
// 定制排序
employees.stream().sorted((e1,e2)->{
if(e1.getAge().equals(e2.getAge())){
return e1.getName().compareTo(e2.getName());
}else{
return e1.getAge().compareTo(e2.getAge());
}
}).forEach(System.out::println);
}
终止操作
终止操作会从流的流水线生成结果,其结果可以是任何不适流的值,例如:List,Integer ,void
查找/匹配
- allMatch:检查是否匹配所有元素
- anyMatch:检查是否至少匹配一个元素
- noneMatch:检查是否没有匹配所有元素
- findFirst:返回第一个元素
- findAny:返回当前流中的任意元素
- count:返回流中元素的总个数
- max:返回流中最大值
- min:返回流中最小值
- forEach(Consumer c)内部迭代
public class TestStreamAPI3 {
List<Employee> employees = Arrays.asList(
new Employee("123", 12, 3222.33, Employee.Status.FREE),
new Employee("222", 35, 333.44, Employee.Status.BUSY),
new Employee("333", 42, 5555.55, Employee.Status.VOCATION),
new Employee("144", 56, 6666.44, Employee.Status.FREE)
);
// 查找/匹配
@Test
public void test1() {
// allMatch 是否匹配所有的元素
boolean b1 = employees.stream()
.allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b1);//false
// anyMatch 是否至少匹配一个元素
boolean b2 = employees.stream()
.anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b2);//true
// noneMatch 是否没有匹配所有元素
boolean b3 = employees.stream()
.noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b3);//false
// findFirst 返回第一个元素
// Optional避免空指针异常 如果结果有可能为空时,就会把这样的值封装到optional容器中去
Optional<Employee> op = employees.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
System.out.println(op.get());// 取出对象
// findAny 返回当前流中的任意元素
// parallelStream()并行流,多个线程一起找
Optional<Employee> op2 = employees.parallelStream()
.filter((e) -> e.getStatus().equals(Employee.Status.FREE))
.findAny();
System.out.println(op2.get());
}
@Test
public void test2(){
// count 返回流中元素的总个数
long count = employees.stream().count();
System.out.println(count);
// max
Optional<Employee> max = employees.stream()
.max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(max);
// min
Optional<Double> min = employees.stream()
.map(Employee::getSalary)
.min(Double::compareTo);
System.out.println(min);
}
}
归约
reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
- map和reduce的连接称为map-reduce模式,因goole用他来进行网络搜索而出名
// 归约
// reduce(T identity, BinaryOperator) / reduce(BinaryOperator)
// 可以将流中的数据反复结合起来,得到一个值
@Test
public void test3() {
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);
Optional<Double> op = employees.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(op.get());
}
收集
collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
- Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到list,set,map)。
- 但时Conllectors实用类提供了很多静态方法,可以方便地创建常见收集器实例
// 收集
// collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
@Test
public void test4(){
List<String> list = employees.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
Set<String> set = employees.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
HashSet<String> hs = employees.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hs.forEach(System.out::println);
}
@Test
public void test5() {
// 总数
Long count = employees.stream()
.collect(Collectors.counting());
System.out.println(count);
// 平均值
Double avg = employees.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
// 总和
Double sum = employees.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
// 最大值
Optional<Employee> max = employees.stream()
.collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(max.get());
// 最小值
Optional<Double> min = employees.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compareTo));
System.out.println(min);
}
// 分组
@Test
public void test6() {
// 分组
Map<Employee.Status, List<Employee>> map = employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
// 多级分组
Map<Employee.Status, Map<String, List<Employee>>> map1 = employees.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(map1);
}
// 分区
@Test
public void test7(){
Map<Boolean, List<Employee>> map = employees.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() > 6000));
System.out.println(map);
}
@Test
public void test8(){
DoubleSummaryStatistics dss = employees.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getSum());
System.out.println(dss.getAverage());
System.out.println(dss.getMax());
}
// 连接字符串
@Test
public void test9(){
String collect = employees.stream()
.map(Employee::getName)
.collect(Collectors.joining(",","===","==="));
System.out.println(collect);// ===1,2,3===
}
练习
练习一:给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?(如:给定【1,2,3,4,5】,返回【1,4,9,16,25】)
@Test
public void test1() {
Integer[] num = new Integer[]{1, 2, 3, 4, 5};
Arrays.stream(num).map((x) -> x * x)
.forEach(System.out::println);
}
练习二:怎样使用 map 和 reduce 数一数流中有多少个 Employee 呢?
List<Employee> employees = Arrays.asList(
new Employee("123", 12, 3222.33, Employee.Status.FREE),
new Employee("222", 35, 333.44, Employee.Status.BUSY),
new Employee("333", 42, 5555.55, Employee.Status.VOCATION),
new Employee("144", 56, 6666.44, Employee.Status.FREE)
);
@Test
public void test2(){
Optional<Integer> reduce = employees.stream().map((e) -> 1)
.reduce(Integer::sum);
System.out.println(reduce.get());
}
练习三:
// 交易员类
public class Trader {
private String name;
private String city;
}
// 交易类
public class Transaction {
private Trader trader;
private int year;
private int value;
}
public class TestTransaction {
List<Transaction> transactions = null;
@Before
public void before() {
Trader raoul = new Trader("Raoul", "Cambridge");
Trader mario = new Trader("Mario", "Milan");
Trader alan = new Trader("Alan", "Cambridge");
Trader brian = new Trader("Brian", "Cambridge");
transactions = Arrays.asList(
new Transaction(brian, 2011, 300),
new Transaction(raoul, 2012, 1000),
new Transaction(raoul, 2011, 400),
new Transaction(mario, 2012, 710),
new Transaction(mario, 2012, 700),
new Transaction(alan, 2012, 950)
);
}
// 1、找出2011年发生的所有交易,并按交易额排序(从低到高)
@Test
public void test1() {
transactions.stream()
.filter((t) -> t.getYear() == 2011)
.sorted((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()))
.forEach(System.out::println);
}
// 2、交易员都在哪些不同的城市工作过?
@Test
public void test2() {
transactions.stream()
.map((t) -> t.getTrader().getCity())
.distinct()// 去重
.forEach(System.out::println);
}
// 3、查找所有来自剑桥的交易员,并按名字排序
@Test
public void test3() {
transactions.stream()
.filter((t) -> t.getTrader().getCity().equals("Cambridge"))
.map(Transaction::getTrader)
.sorted((t1, t2) -> t1.getName().compareTo(t2.getName()))
.distinct()
.forEach(System.out::println);
}
// 4、返回所有交易员的名字字符串,并按字母顺序排序
@Test
public void test4() {
transactions.stream()
.map((t) -> t.getTrader().getName())
.sorted()
.forEach(System.out::println);
String reduce = transactions.stream()
.map((t) -> t.getTrader().getName())
.sorted()
.reduce("", String::concat);// 拼成一个字符串
System.out.println(reduce);//AlanBrianMarioMarioRaoulRaoul
transactions.stream()
.map((t) -> t.getTrader().getName())
.flatMap(TestTransaction::filterCharacter)
.sorted((s1, s2) -> s1.compareToIgnoreCase(s2))//忽略大小写的排序
.forEach(System.out::print);//aaaaaAaBiiilllMMnnoooorRRrruu
}
public static Stream<String> filterCharacter(String str) {
List<String> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch.toString());
}
return list.stream();
}
// 5、有没有交易员在米兰工作的?
@Test
public void test5() {
boolean b1 = transactions.stream()
.anyMatch((t) -> t.getTrader().getCity().equals("Milan"));
System.out.println(b1);
}
// 6、打印生活在剑桥的交易员的所有交易额
@Test
public void test6() {
Optional<Integer> sum = transactions.stream()
.filter((t) -> t.getTrader().getCity().equals("Cambridge"))
.map(Transaction::getValue)
.reduce(Integer::sum);
System.out.println(sum.get());//2650
}
// 7、所有交易中,最高的交易额是多少
@Test
public void test7(){
Optional<Integer> max = transactions.stream()
.map(Transaction::getValue)
.max(Integer::compare);
System.out.println(max.get());//1000
}
// 8、找到交易额最小的交易
@Test
public void test8(){
Optional<Transaction> min = transactions.stream()
.min((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()));
System.out.println(min);
}
}
并行流与顺序流
并行流:就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流
java8中将并行进行了优化,可以很容易的对数据进行并行操作。Stream API 可以声明性地通过parallel()与sequential()在并行流与顺序流之间进行切换
Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不能再拆时),再将一个个的小任务运算的结果进行 join汇总
Fork/Join框架与传统线程池的区别:
Fork/Join 实现:
public class ForkJoinCalculate extends RecursiveTask<Long> {
public static final long serialVersionUID = 13465970987L;
private long start;
private long end;
// 临界值
private static final long THRESHOLD = 10000;
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();
}
}
}
public class TestForkJoin {
@Test
public void test1() {
Instant start = Instant.now();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinCalculate(0, 10000_0000L);
Long sum = pool.invoke(task);
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗费时间"+Duration.between(start, end).toMillis());
}
// 普通for循环
@Test
public void test2(){
Instant start = Instant.now();
long sum = 0;
for (long i = 0; i <= 10000_0000L; i++) {
sum +=i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗费时间"+Duration.between(start, end).toMillis());
}
}
Java8 并行流
@Test
public void test3(){
LongStream.rangeClosed(0,10000_0000L)
.reduce(0,Long::sum);//顺序流 (单线程):切换为并行流 parallel()
LongStream.rangeClosed(0,10000_0000L)
.parallel()//底层:ForkJoin
.reduce(0,Long::sum);//并行流 切换为串行流 sequential()
}
Optional 类
定义: Optional 类 (java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在用 Optional 可以更好的表达这个概念;并且可以避免空指针异常
常用方法:
- Optional.of(T t):创建一个 Optional 实例
- Optional.empty(T t):创建一个空的 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 {
// Optional.of(T t) 创建一个 Optional 实例
@Test
public void test1(){
Optional<Employee> op = Optional.of(new Employee());
// Optional.of(null) 发生空指针异常
Employee employee = op.get();
System.out.println(employee);
}
// Optional.empty(T t):创建一个空的 Optional 实例
@Test
public void test2(){
Optional<Employee> op = Optional.empty();
System.out.println(op.get());
}
// Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则空实例
@Test
public void tets3(){
Optional<Employee> op = Optional.ofNullable(new Employee());
Optional<Employee> op2 = Optional.ofNullable(null);// 不会发生空指针异常,只会报错没有值
System.out.println(op.get());
}
// isPresent():判断是否包含某值
@Test
public void test4(){
Optional<Employee> op = Optional.ofNullable(null);
if(op.isPresent()){
System.out.println(op.get());
}
}
// orElse(T t):如果调用对象包含值,返回该值,否则返回 t
@Test
public void test5(){
Optional<Employee> op = Optional.ofNullable(null);
Employee employee = op.orElse(new Employee("zs", 18, 988.1, Employee.Status.FREE));
System.out.println(employee);// Employee{name='zs', age=18, salary=988.1, status=FREE}
}
// orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回 s 获取的值
@Test
public void test6(){
Optional<Employee> op = Optional.ofNullable(null);
Employee employee = op.orElseGet(() -> new Employee());
System.out.println(employee);
}
//map(Function f):如果有值对其处理,并返回处理后的 Optional,否则返回 Optional.empty()
//flatmap(Function mapper):与 map 相似,要求返回值必须是 Optional
@Test
public void test7(){
Optional<Employee> op = Optional.ofNullable(new Employee("zs", 18, 988.1, Employee.Status.FREE));
Optional<String> str = op.map((e) -> e.getName());
System.out.println(str.get());//zs
Optional<String> str2 = op.flatMap((e) -> Optional.of(e.getName()));
System.out.println(str2.get());//zs
}
}
例题:获取一个男人心中女神的名字
// 原始方法
public class Godness {
private String name;
}
public class Man {
private Godness godness;
}
public class TestOptional {
// 例题:获取一个男人心中女神的名字
@Test
public void test8(){
Man man = new Man();
String godnessName = getGodnessName(man);
System.out.println(godnessName);
}
public String getGodnessName(Man man){
// return man.getGodness().getName();// 空指针异常
if(man!=null){
Godness godness = man.getGodness();
if(godness!=null){
return godness.getName();
}
}
return "xxx";
}
}
java8
public class NewMan {
private Optional<Godness> godness = Optional.empty();
}
public class TestOptional {
public String getGodnessName2(Optional<NewMan> man){
return man.orElse(new NewMan())
.getGodness()
.orElse(new Godness("xxx"))
.getName();
}
@Test
public void test9(){
Optional<Godness> gn= Optional.ofNullable(new Godness("123"));
Optional<NewMan> op = Optional.ofNullable(new NewMan(gn));
String str = getGodnessName2(op);
System.out.println(str);
}
}
接口中的默认方法与静态方法
默认方法
public interface MyFun {
// 默认方法
default String getName(){
return "123";
}
}
public class MyClass {
public String getName(){
return "222";
}
}
public class SubClass extends MyClass implements MyFun{
}
public class TestDefaultInterface {
public static void main(String[] args) {
SubClass subClass = new SubClass();
System.out.println(subClass.getName());// 222
}
}
接口默认方法的 “类优先” 原则!!!
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
- 选择父类中的方法.(如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略)
- 接口冲突.(如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法,[不管方法是否是默认方法],那么必须覆盖该方法来解决冲突)
// 接口冲突
public interface MyInterface {
default String getName(){
return "333";
}
}
public interface MyFun {
default String getName(){
return "123";
}
}
public class SubClass /*extends MyClass*/ implements MyFun,MyInterface{
@Override
public String getName() {
return MyFun.super.getName();// 实现类必须实现该方法
}
}
静态方法
public interface MyInterface {
// 接口中的静态方法
public static void show(){
System.out.println("接口中的静态方法");
}
}
新时间日期API
传统时间格式化的线程安全问题
public class TestSimpleDateFormat {
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return sdf.parse("20161218");
}
};
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) {
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
pool.shutdown();
}
加锁:
public class DateFormatThreadLocal {
private static final ThreadLocal<DateFormat> df = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static Date convert(String source) throws ParseException{
return df.get().parse(source);
}
}
@Test
public void test02(){
Callable<Date> task = () -> DateFormatThreadLocal.convert("20200517");
ExecutorService pool = Executors.newFixedThreadPool(10);
ArrayList<Future<Date>> result = new ArrayList<>();
for (int i = 0; i < 10; i++) {
result.add(pool.submit(task));
}
for (Future<Date> future : result) {
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
pool.shutdown();
}
java8:localDate DateTimeFormatter
public class TestSimpleDate8Format {
public static void main(String[] args) {
DateTimeFormatter dtf =DateTimeFormatter.ofPattern("yyyyMMdd");
Callable<LocalDate> task = new Callable<LocalDate>() {
@Override
public LocalDate call() throws Exception {
return LocalDate.parse("20161218",dtf);
}
};
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) {
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
pool.shutdown();
}
}
本地时间与时间戳
本地时间
使用LocalDate(表示日期) LocalTime(表示时间) LocalDateTime(时间日期)
- 这三个类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期\时间\日期和时间.他们提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息
- ISO-8601日历系统是国际标准化组织指定的现代公民的日期和时间的表示法
常用方法:
方法名 | 返回值类型 | 解释 |
---|---|---|
now( ) | static LocalDateTime | 从默认时区的系统时钟获取当前日期 |
of(int year, int month, int dayOfMonth, int hour, int minute, int second) | static LocalDateTime | 从年,月,日,小时,分钟和秒获得 LocalDateTime的实例,将纳秒设置为零 |
plus(long amountToAdd, TemporalUnit unit) | LocalDateTime | 返回此日期时间的副本,并添加指定的数量 |
get(TemporalField field) | int | 从此日期时间获取指定字段的值为 int |
public class TestLocalDateTime {
// 1:LocalDate LocalTime LocalDateTime
@Test
public void test1(){
// 获取当前系统时间 now
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime);// 2021-04-10T22:09:50.753
// 指定时间 of
LocalDateTime of = LocalDateTime.of(2015, 10, 10, 13, 22, 33);
System.out.println(of);// 2015-10-10T13:22:33
// 加时间 plus
LocalDateTime time = localDateTime.plusYears(2);
System.out.println(time);// 2023-04-10T22:11:37.045
// 减时间 minus
LocalDateTime time1 = localDateTime.minusMonths(2);
// 获取指定的你年月日时分秒... get
System.out.println(localDateTime.getYear());//2021
System.out.println(localDateTime.getMonthValue());// 4
System.out.println(localDateTime.getDayOfMonth());// 10
System.out.println(localDateTime.getHour());// 22
System.out.println(localDateTime.getMinute()); // 14
System.out.println(localDateTime.getSecond());// 16
}
}
Instant 时间戳
Instant:以 Unix 元年 1970-01-01 00:00:00 到某个时间之间的毫秒值
// 2:Instant 时间戳 (以Unix元年 1970/1/1 00:00:00 到某个时间之间的毫秒值)
@Test
public void test2(){
// 获取当前时间戳 默认获取 UTC 时区 (UTC:世界协调时间)
Instant now = Instant.now();
System.out.println(now);// 2021-04-10T14:22:59.010Z(默认获取的UTC时区)
// 带偏移量的时间日期 (如:UTC + 8)
OffsetDateTime odt = now.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);// 2021-04-10T22:22:59.010+08:00
//转换成对应的毫秒值
long epochMilli = now.toEpochMilli();
System.out.println(epochMilli);// 1618064579010
//构建时间戳(相较于元年进行的运算)
Instant instant = Instant.ofEpochSecond(60);
System.out.println(instant);// 1970-01-01T00:01:00Z
}
计算时间之间的间隔
Duration:计算两个时间之间的间隔
Period:计算两个日期之间的间隔
@Test
public void test3(){
Instant ins1= Instant.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant ins2= Instant.now();
Duration duration = Duration.between(ins1, ins2);
System.out.println(duration);//PT1.012S
System.out.println(duration.getSeconds());// 秒 1
System.out.println(duration.toMillis());// 毫秒 1011
}
@Test
public void test4() {
LocalTime localTime = LocalTime.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LocalTime localTime2 = LocalTime.now();
System.out.println(Duration.between(localTime,localTime2).toMillis());//1004
}
@Test
public void test5(){
LocalDate ld1 = LocalDate.of(2020,1,1);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LocalDate ld2 = LocalDate.now();
Period period = Period.between(ld1, ld2);
System.out.println(period);//P1Y3M9D
System.out.println(period.getYears());//1
System.out.println(period.getMonths());//3
System.out.println(period.getDays());//9
}
时间校正器
日期的操纵:
- TemporalAdjuster:时间校正器(例如:将日期调整到下个周日等操作)
- TemporalAdjusters:该类通过静态方法提供了大量用TemporalAdjuster的实现
// TemporalAdjuster 时间校正器
@Test
public void test6(){
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);//2021-04-10T22:38:22.611
// //指定日期时间中的 年 月 日 ... .withXXX
LocalDateTime ldt2 = ldt.withDayOfMonth(5);
System.out.println(ldt2);//2021-04-05T22:38:33.388
//指定时间校正器 (指定为下一个周日)
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);// 2021-04-11T22:42:00.215
//自定义时间校正器 (指定下一个工作日)
LocalDateTime time = 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(time);//2021-04-12T22:46:02.216
}
时间格式化和时区的处理
DateTimeFormatter:格式化时间 / 日期
@Test
public void test7(){
//默认格式化
DateTimeFormatter dtf =DateTimeFormatter.ISO_DATE;
LocalDateTime ldt = LocalDateTime.now();
String strDate = ldt.format(dtf);
System.out.println(strDate);//2021-04-10
// 自定义格式化
DateTimeFormatter dtf2= DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String strDate2 = dtf2.format(ldt);
System.out.println(strDate2);//2021年04月10日 22:53:15
//解析
LocalDateTime newDate = ldt.parse(strDate2,dtf2);
System.out.println(newDate);// 2021-04-10T22:54:35
}
时区的处理
ZonedDate
ZonedTime
ZonedDateTime
其中每个时区都对应着ID,地区ID都为"{区域}/{城市}"的格式 [ 例如:Asia/Shanghai ]
- ZoneId:该类包含了所有的时区信息
- getAvailableZoneIds:可以获取所有时区的信息
- of(id):用指定的时区信息获取ZoneId对象
// 时区的处理
@Test
public void test8(){
// 获取所有的时区
Set<String> ids = ZoneId.getAvailableZoneIds();
ids.forEach(System.out::println);
// 指定时区
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
System.out.println(ldt);//2021-04-10T18:07:41.626
// 在已构建好的日期时间上指定时区
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime zdt = now.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(zdt);// 2021-04-10T18:07:41.6260+03:00[Europe/Tallinn]
}
可重复注解与类型注解
可重复注解
定义注解:
@Repeatable(MyAnnotations.class) // 指定容器类
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "atlll";
}
定义容器:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
MyAnnotation[] value();
}
测试:
public class TestAnnotation {
@Test
public void test() throws NoSuchMethodException {
Class<TestAnnotation> clazz = TestAnnotation.class;
Method m1 = clazz.getMethod("show");
MyAnnotation[] mas= m1.getAnnotationsByType(MyAnnotation.class);
for (MyAnnotation ma : mas) {
System.out.println(ma.value());//hello //world
}
}
@MyAnnotation("hello")
@MyAnnotation("world")
public void show(){
}
}
类型注解
Java 8 新增注解:
- 新增ElementType.TYPE_USE
- ElementType.TYPE_PARAMETER(在Target上,注解类型)
ElementType.TYPE_PARAMETER注解任意类型
@Repeatable(MyAnnotations.class) // 指定容器类
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "atlll";
}
@MyAnnotation("hello")
@MyAnnotation("world")
public void show(@MyAnnotation("abc") String str){
}
// checker framework
private @NotNull Optional obj= null;
Java8新特性-Lambda表达式-Stream API等_尚硅谷__李贺飞
https://www.bilibili.com/video/BV1ut411g7E9?p=21