目录
Java8
Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。
- Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
- 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
- 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
- Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
- Date Time API − 加强对日期与时间的处理。
- Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
- Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。
其中核心:Lambda表达式和Stream API
第一节:Lambda表达式
Lambda表达式可以看成是匿名内部类,Lambda 允许把函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递,使用 Lambda 表达式可以使代码变的更加简洁紧凑。
Lambda表达需要函数式接口的支持。
基本语法:
<函数式接口> <变量名> = (参数1,参数2...) -> {
//方法体
};
注意其中的参数1,参数2是接口中抽象方法的参数,如果方法的参数为空,则使用(),如果仅含有一个参数则可以省略()直接使用e->{},如果方法体内仅含有一条语句,那么可以省略大括号,如果带返回值且仅含有一条语句,则省略return语句和大括号。
实际上该式子相当于创建一个匿名内部类对象,该匿名内部类实现了接口的方法,并让变量名去引用该对象。
1.1为什么使用lambda表达式
使用lambda表达式可以让方法的参数中放入一个接口,然后在具体使用方法的时候由lambda表达式中的代码来执行具体的判断,提高程序的灵活性,同时减少代码的冗余。
相当于在方法A中通过使用接口参数B,同时在方法A中调用B的方法,通过方法B的实现类来确定B方法的具体实现。以此来实现方法A代码的灵活性提升
2:使用Lambada表达式优化策略模式。
public static void main(String[] args) {
List<Employee> employees=new ArrayList<>();
employees.add(new Employee("xxx", 30, 10000));//添加元素
employees.add(new Employee("yyy", 29, 8000));
employees.add(new Employee("zzz", 22, 12000));
employees.add(new Employee("张三", 21, 20000));
employees.add(new Employee("李四", 32, 22000));
//按照年龄过滤
System.out.println("-------------按照年龄过滤--------------");
//第二个方法参数传入的是一个接口的对象,对象用lambda表达式创建
//同时在lambda表达式中提供具体的判断标准
List<Employee> list=filterEmployee(employees,(e)->e.getAge()>=25);
for (Employee employee : list) {
System.out.println(employee.toString());
}
System.out.println("-------------按照工资过滤--------------");
//按照工资过滤
List<Employee> list2=filterEmployee(employees,(e)->e.getSalary()>=10000);
for (Employee employee : list2) {
System.out.println(employee.toString());
}
}
public static List<Employee> filterEmployee(List<Employee> employees,MyPredicate<Employee> predicate) {
List<Employee> list=new ArrayList<>();
for (Employee e : employees) {
if(predicate.test(e)) {//方法的具体判断交给相应的接口实现类
list.add(e);
}
}
return list;
}
3:使用Stream API再优化lambda表达式
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("xxx", 30, 10000));
employees.add(new Employee("yyy", 29, 8000));
employees.add(new Employee("zzz", 22, 12000));
employees.add(new Employee("张三", 21, 20000));
employees.add(new Employee("李四", 32, 22000));
// 按照年龄过滤
System.out.println("-------------按照年龄过滤--------------");
employees
.stream()
.filter((e) -> e.getAge() >= 25)//Stream中也使用了lambda表达式来进行条件判断
.forEach(System.out::println);
// 按照工资过滤
System.out.println("-------------按照工资过滤--------------");
employees
.stream()
.filter((e) -> e.getSalary() >= 10000)
.forEach(System.out::println);
}
1.2使用lambda表达式注意事项
Lambda引入了新的操作符:->(箭头操作符),->将表达式分成两部分 左侧:(参数1,参数2…)表示参数列表; 右侧:{}内部是方法体
1、形参列表的数据类型会自动推断; 2、如果形参列表为空,只需保留(); 3、如果形参只有1个,()可以省略,只需要参数的名称即可; 4、如果执行语句只有1句,{}可以省略。若有返回值,且若想省去{},则必须同时省略return,且执行语句也保证只有1句;
5、lambda不会生成一个单独的内部类文件;
6、lambda表达式若访问了局部变量,则局部变量必须是final的,若是局部变量没有加final关键字,系统会自动添加,此后在修改该局部变量,会报错。同匿名内部类
1.3函数式接口
如果一个接口只有一个抽象方法,则该接口称之为函数式接口,函数式接口可以使用Lambda表达式,lambda表达式会被匹配到这个抽象方法上 。
为了确保你的接口一定达到这个要求,你只需要给你的接口添加 @FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。
Java为了程序员方便使用Lambda表达式,Java内置了四个核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 说明 |
---|---|---|---|
Consumer 消费型接口 | T | void | void accept(T t);对类型为T的对象应用操作 |
Supplier 供给型接口 | 无 | T | T get(); 返回类型为T的对象 |
Function<T,R> 函数型接口 | T | R | R apply(T t);对类型为T的对象应用操作,并返回类型为R类型的对象。 |
Predicate 断言型接口 | T | boolean | boolean test(T t);确定类型为T的对象是否满足条件,并返回boolean类型。 |
E、T、K、V、R
package per.damon.mor.functionalInterface;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* @Classname Demo1
* @Date 2019/8/17 10:58
* @Created by damon
* @Description 四类java自带的函数式接口
*/
public class Demo1 {
public static void main(String[] args) {
//消费型接口,需要一个参数,这里参数名是m,仅返回一条语句省略大括号
//调用接口的方法使用.accept
happy(1000, m-> System.out.println("花天酒地"+m));
//供给型接口,打印10-99数字,调用方法使用.get
int[] num = getNum(10, () -> new Random().nextInt(90)+10);
for (int i : num) {
System.out.println(i);
}
//函数型接口,一个参数,一个返回值调用方法使用.apply
String str = handlerString("hello", s -> s.toUpperCase());
System.out.println(str);
//断言型接口,判断对象是否满足条件,对象就是参数,调用方法使用.test
judge(100, (x)->x>5);
}
//系统帮你写好了Consumer接口,可以直接调用
public static void happy(double money, Consumer<Double> comsumer){
comsumer.accept(money);
}
//供给型接口
public static int[] getNum(int len, Supplier<Integer> supplier){
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i]=supplier.get();
}
return arr;
}
//函数型接口,一个参数一个返回值
public static String handlerString(String s, Function<String,String> fun){
return fun.apply(s);
}
//断言型接口
public static void judge(int i, Predicate<Integer> p){
if(p.test(i)){
System.out.println("断言成功");
}
}
}
第二节:方法引用
使用Lambda表达式创建匿名方法。但是,有时候Lambda表达式什么都没做,仅仅是调用了一个已经存在的方法。这种情况下,引用已存在方法的方法名通常是更清晰的。方法引用允许你这么做,它是一种简洁的、可读性强的有名方法的Lambda表达式。
使用“::”操作符将方法名和对象或类的名字分隔开来。以下是四种使用情况:
- 对象::实例方法
- 类::静态方法
- 类::实例方法
- 类::new
package com.qf3;
import com.qf.Employee;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;
/*
* wgy 2019/8/17 11:29
* 佛祖保佑,永无BUG!
*/
public class Demo1 {
public static void main(String[] args) {
// - 对象::实例方法
//条件 方法的参数个数类型和返回值类型和接口中的方法一致
Consumer<String> consumer=s-> System.out.println(s); //println方法
Consumer<String> consumer2=System.out::println;
consumer2.accept("hello");
Employee shaobo=new Employee("少泊", 20, 30000);
Supplier<String> supplier=()->shaobo.getName();
Supplier<String> supplier2=shaobo::getName;
// - 类::静态方法
//条件 方法的参数个数类型和返回值类型和接口中的方法一致
Comparator<Integer> comparator=(o1,o2)->Integer.compare(o1, o2);
Comparator<Integer> comparator2=Integer::compare;
// - 类::实例方法(了解)
//条件 接口中两个参数,有一个作为方法的调用者,另一个作为参数,返回类型一致
BiPredicate<String,String> p=(x,y)->x.equals(y);
BiPredicate<String,String> p2=String::equals;
// - 类::new
Supplier<Employee> sup=()->new Employee();
Supplier<Employee> sup2=Employee::new;
Employee e = sup2.get();
System.out.println(e.toString());
//思考:方法引用只能调用无参的构造吗?能? 如何调用
//可以调用有参的构造方法,但是需要创建相应的接口
MySupplier<Employee> sup3=Employee::new;
Employee shao = sup3.get("少泊", 10, 1000);
System.out.println(shao.toString());
}
}
第三节:Stream API
Stream是Java8中处理数组、集合的抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。
简单应用:统计一个字符串类型集合中,所有长度大于3的元素个数。
public static void main(String[] args) {
//传统实现
List<String> data=new ArrayList<>();
data.add("hello");
data.add("world");
data.add("ni");
data.add("apple");
data.add("china");
int count = 0;
for (String s : data) {
if (s.length() > 3)
count++;
}
System.out.println(count);
//Stream API
long count2= data
.stream()
.filter(s->s.length()>3)
.count();
System.out.println(count2);
}
使用时先创建Stream,然后再调用里面的方法,最后alt+enter直接返回一个long类型的变量,上面代码中stream方法会为字符串列表生成一个Stream。filter方法会返回只包含字符串长度大于3的一个Stream,然后通过count方法计数。
3.1 什么是Stream?
一个Stream表面上与一个集合很类似,集合中保存的是数据,而流中对数据的操作。
特点:
Stream 自己不会存储元素,如果需要存储元素,则需要把Stream的结果放入新的集合或数组中
Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
Stream 每一次操作之后就会失效,下次使用必须重新创建
Stream 采取惰性求值的方法,如果没有终止操作,即便代码写了中间操作,Stream什么也不做,仅在运行到终止操作的时候Stream才会一次将所有操作全部做完,以此来提高效率,防止多余步骤。
Stream遵循“做什么,而不是怎么去做”的原则。只需要描述需要做什么,而不用考虑程序是怎样实现的。Stream相当于一个工具类,用于对集合进行便捷的数据处理。
3.2 如何使用Stream API
使用Stream,会有三个阶段(步骤):
- 创建一个Stream。 (创建)
- 在一个或多个步骤中,将初始Stream转化到另一个Stream的中间操作。 (中间操作)
- 使用一个终止操作来产生一个结果。该操作会强制他之前的延迟操作立即执行。在这之后,该Stream就不会在被使用了。(终止操作)
3.2.1 Stream的创建方法
// 1. Stream.of方法
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays.of方法
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. 集合方法,常用方式
List<String> list = Arrays.asList(strArray);
stream = list.stream();
stream = list.parallelStream();//并行流
//4.创建无限流
//迭代
Stream<Integer> stream = Stream.iterate(0, (x)->x+2);
stream.limit(10)
.forEach(System.out::println);
//生成
Stream<Double> stream2 = Stream.generate(()->Math.random());
stream2.limit(5)
.forEach(System.out::println);
3.2.2 Stream中间操作
中间操作包括:映射/map (mapToInt, flatMap 等)、过滤/filter、去重/distinct、排序/sorted、peek、限数/limit、跳过/skip、parallel、sequential、去除排序规则/unordered。
多个中间操作可以连接起来形成一个流水线,除非流水 线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值”。
筛选和切片
public static void main(String[] args) {
List<Employee> employees=new ArrayList<>();
employees.add(new Employee("xxx", 30, 10000));
employees.add(new Employee("yyy", 29, 8000));
employees.add(new Employee("zzz", 22, 12000));
employees.add(new Employee("张三", 21, 20000));
employees.add(new Employee("李四", 32, 22000));
//employees.add(new Employee("李四", 32, 22000));
//1 筛选和切片
// filter---从流中排除元素
// limit——截断流,使其元素不超过给定数量。
// skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
// distinct——筛选,通过流所生成元素的 equals() 去除重复元素
System.out.println("-----------filter------------");
Stream<Employee> stream = employees.stream().filter((e)->e.getAge()>=25);
stream.forEach(System.out::println);
System.out.println("---------limit-------");
Stream<Employee> stream2 = employees.stream().limit(3);
stream2.forEach(System.out::println);
System.out.println("---------skip-------");
Stream<Employee> stream3= employees.stream().skip(2);
stream3.forEach(System.out::println);
System.out.println("---------distinct-------");//通过equals方法去重复
Stream<Employee> stream4= employees.stream().distinct();
stream4.forEach(System.out::println);
}
映射
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("xxx", 30, 10000));
employees.add(new Employee("yyy", 29, 8000));
employees.add(new Employee("zzz", 22, 12000));
employees.add(new Employee("张三", 21, 20000));
employees.add(new Employee("李四", 32, 22000));
// map——接收 Lambda ,
// 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
System.out.println("----------获取员工姓名-----------");
Stream<String> str = employees.stream()//获取Stream
.map((e) -> e.getName());//中间操作进行映射
str.forEach(System.out::println);//终止操作 打印映射结果
System.out.println("-------------------转成大写------------------------");
List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
Stream<String> stream = strList.stream()
.map(String::toUpperCase);
stream.forEach(System.out::println);
}
排序
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("xxx", 30, 10000));
employees.add(new Employee("yyy", 29, 8000));
employees.add(new Employee("zzz", 22, 12000));
employees.add(new Employee("张三", 21, 20000));
employees.add(new Employee("李四", 32, 22000));
/*
sorted()——自然排序
sorted(Comparator com)——定制排序
*/
System.out.println("-----------------自然排序-------------------");
employees.stream()
.map(Employee::getName)
.sorted()
.forEach(System.out::println);
System.out.println("-----------------定制排序-------------------");
employees.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);
}
3.2.3 Stream 的终止操作
终止操作包括:遍历/forEach、顺序遍历/forEachOrdered、返回数组/toArray、归约/reduce、返回集合/collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator。
遍历
forEach
查找和匹配
allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("xxx", 30,"男",10000));
employees.add(new Employee("yyy", 29,"男",8000));
employees.add(new Employee("zzz", 22, "男",12000));
employees.add(new Employee("张三", 21,"男",20000));
employees.add(new Employee("李四", 32,"男",22000));
//-----------foreach--------------
employees.stream().forEach(System.out::println);
/**
* allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
*/
boolean b = employees.stream().allMatch((e)->e.getSex().equals("男"));
boolean b2 = employees.stream().anyMatch((e)->e.getSex().equals("女"));
boolean b3=employees.stream().noneMatch((e)->e.getSex().equals("女"));
System.out.println(b);
System.out.println(b2);
System.out.println(b3);
Optional<Employee> findFirst = employees.stream().findFirst();
System.out.println(findFirst.get());
Optional<Employee> findAny = employees.parallelStream().findAny();
System.out.println(findAny.get());
Optional<Employee> max = employees.stream().max((o1,o2)->o1.getAge()-o2.getAge());
System.out.println(max.get());
Optional<Double> min = employees.stream().map(Employee::getSalary).min(Double::compareTo);
System.out.println(min.get());
}
归约和收集
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("xxx", 30, "男", 10000));
employees.add(new Employee("yyy", 29, "男", 8000));
employees.add(new Employee("zzz", 22, "男", 12000));
employees.add(new Employee("张三", 21, "男", 20000));
employees.add(new Employee("李四", 32, "男", 22000));
// -----------foreach--------------
employees.stream().forEach(System.out::println);
/**
* reduce归约 reduce(T identity, BinaryOperator) / reduce(BinaryOperator)
* ——可以将流中元素反复结合起来,得到一个值
*/
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);//初始值为0,加上其中的所有元素
System.out.println(sum);
System.out.println("----------------------------------------");
Optional<Double> op = employees.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(op.get());
/**
* collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
*
*/
System.out.println("-------------List集合---------------------");
List<String> list2 = employees.stream().map(Employee::getName).collect(Collectors.toList());
list2.forEach(System.out::println);
System.out.println("---------------Set集合-------------------");
Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());
set.forEach(System.out::println);
}
3.2.4 并行操作
Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。
int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
UUID uuid = UUID.randomUUID();
values.add(uuid.toString());
}
计算一下排序这个Stream要耗时多久:
public static void main(String[] args) {
int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
UUID uuid = UUID.randomUUID();
values.add(uuid.toString());
}
System.out.println("----------串行------------");
long t0 = System.currentTimeMillis();
long count = values.stream().sorted().count();
System.out.println(count);
long t1 = System.currentTimeMillis();
long millis = t1-t0;
System.out.println(millis);
// System.out.println("-------------并行----------------");
// long t0 = System.currentTimeMillis();
// long count = values.parallelStream().sorted().count();
// System.out.println(count);
// long t1 = System.currentTimeMillis();
// long millis = t1-t0;
// System.out.println(millis);
}
第四节:新时间日期API
Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。
在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:
- 非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
- 设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
- 时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:
- Local(本地) − 简化了日期时间的处理,没有时区的问题。
- ZoneId (时区) − 通过定制的时区处理日期时间。
线程安全问题演示:
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();
}
使用新时间日期API解决
public static void main(String[] args) throws Exception{
DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyyMMdd");
Callable<LocalDate> task = new Callable<LocalDate>() {
@Override
public LocalDate call() throws Exception {
return LocalDate.parse("20161002", formatter);
}
};
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();
}
4.1 本地化日期时间 API
LocalDate/LocalTime 和 LocalDateTime 类可以在处理时区不是必须的情况。
LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使用 ISO-8601日 历系统的日期、时间 、日期和时间。它们提供了简单的日期或时间,不包含与时区相关的信息。
public static void main(String[] args) {
//当前时间
LocalDateTime ldt=LocalDateTime.now();
System.out.println(ldt);
//其他时间
LocalDateTime ldt2=LocalDateTime.of(2012, 10, 1, 10, 10,10);
System.out.println(ldt2);
//加时间
LocalDateTime ldt3=ldt2.plusDays(2);
System.out.println(ldt3);
//减时间
LocalDateTime ldt4 = ldt3.minusHours(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());
}
4.2 Instant、ZoneId
Instant 时间戳 类似以前的Date、Timestamp
它是以Unix元年(传统 的设定为UTC时区1970年1月1日午夜时分)开始 所经历的描述进行运算
ZoneId 时区
public static void main(String[] args) throws Exception {
//时间戳
Instant instant=Instant.now();
System.out.println(instant);
Thread.sleep(1000);
Instant instant2=Instant.now();
long millis = Duration.between(instant, instant2).toMillis();
System.out.println(millis);
//时区
System.out.println(ZoneId.getAvailableZoneIds());
ZoneId zone1 = ZoneId.of("Europe/Berlin");
ZoneId zone2 = ZoneId.of("Brazil/East");
System.out.println(zone1.getRules());
System.out.println(zone2.getRules());
//时间转换
//Date-----Instant-------LocalDateTime
System.out.println("--------Date-----Instant-------LocalDateTime---------");
Date date=new Date();
//把date转成instant
Instant instant1 = date.toInstant();
//intant转成LocalDateTime
LocalDateTime localDateTime = instant1.atZone(ZoneId.systemDefault()).toLocalDateTime();
System.out.println(localDateTime);
System.out.println("--------LocalDateTime-----Instant-------Date---------");
Instant instant3 = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
Date date2 = Date.from(instant3);
System.out.println(date2);
}
4.3 时间矫正器 TemporalAdjuster
TemporalAdjuster : 时间校正器。有时我们可能需要获 取例如:将日期调整到“下个周日”等操作。
TemporalAdjusters : 该类通过静态方法提供了大量的常 用 TemporalAdjuster 的实现。
例如获取下个周日:
LocalDate date=LocalDate.now();
//下一个周5
System.out.println(date.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)));
//下一个周2
System.out.println(date.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)));
//下一个周日(周日为每周的第一天)
System.out.println(date.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)));
4.4 DateTimeFormatter
java.time.format.DateTimeFormatter 类:该类提供了三种 格式化方法:
预定义的标准格式
语言环境相关的格式
自定义的格式
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);
总结:
1 Lambda表达式:把代码(方法)作为方法的参数传递。
操作符 ->
函数式接口:只有一个抽象方法的接口
Consumer 消费型接口 void accept(T)
Supplier 供给型接口 T get();
Function 函数型接口 R apply(T)
Predicate 断言性接口 boolean test(T)
2 方法引用:
对象::实例方法
类名::静态方法
类名::实例方法
类名::new
3 Stream API
Stream: 包含了对集合或的数组的操作,延迟执行。调用终止方法,中间操作立即执行。
三步: (1) 创建流
Stream.of();
Arrays.stream();
集合.stream()
.parallelStream();
(2) 添加中间操作
filter skip limit map sort
(3) 终止操作
foreach count max min allMatch anyMatch noneMatch findFirst findAny findLast … reduce collect
4 时间
LocalDateTime
Instant
ZoneId
Date和Instant Instant和LocalDateTime之间的转换
矫正器
时间格式化
DateTimeFormatter