JDK8新特性
1.1、匿名函数
Lambda是一个匿名函数,可以理解为一段可以传递的代码(将代码像数据一样传递);可以写出更简洁、更灵活的代码;作为一种更紧凑的代码风格,是Java语言表达能力得到提升。
之前我们需要使用某个接口,需要通过实现接口,重写需要实现的功能,有了lambda后,省去了实现接口的代码操作,通过lambda表达式编写核心逻辑。
@Test public void test01(){ //匿名内部类 Comparator<Integer> comparator = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; //调用 TreeSet<Integer> set = new TreeSet<>(comparator); }
@Test public void test02(){ // Lambda 表达式 Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b); TreeSet<Integer> set = new TreeSet<>(comparator); }
演变过程:
垃圾代码 --> 策略模式 --> 匿名内部类 --> Lambda表达式
基础语法:
操作符:->
左侧:参数列表
右侧:执行代码块 / Lambda 体
语法格式:Lambda 表达式在Java 语言中引入了一个操作符。这个操作符为 “-> ”,该操作符被称为 Lambda 操作符。它将 Lambda 分为两个部分:
-
-
左侧:指定了 Lambda 表达式需要的所有参数
-
右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。
-
public class Test02 { int num = 10; @Test public void test01(){ //匿名内部类 new Runnable() { @Override public void run() { //在局部类中引用同级局部变量 //只读 System.out.println("Hello World" + num); } }; } @Test public void test02(){ //语法糖 Runnable runnable = () -> { System.out.println("Hello Lambda"); }; } }
@Test public void test03(){ Consumer<String> consumer = (a) -> System.out.println(a); consumer.accept("我觉得还行!"); } //有一个参数,无返回值 (小括号可以省略不写) @Test public void test03(){ Consumer<String> consumer = a -> System.out.println(a); consumer.accept("我觉得还行!"); }
@Test public void test04(){ Comparator<Integer> comparator = (a, b) -> { System.out.println("比较接口"); return Integer.compare(a, b); }; } //有两个及以上的参数,有返回值,并且 Lambda 体中只有1条语句 (大括号 与 return 都可以省略不写) @Test public void test04(){ Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b); }
@FunctionalInterface public interface MyFun { Integer count(Integer a, Integer b); }
@Test public void test05(){ MyFun myFun1 = (a, b) -> a + b; MyFun myFun2 = (a, b) -> a - b; MyFun myFun3 = (a, b) -> a * b; MyFun myFun4 = (a, b) -> a / b; } ////////////////////////// public Integer operation(Integer a, Integer b, MyFun myFun){ return myFun.count(a, b); } @Test public void test06(){ Integer result = operation(1, 2, (x, y) -> x + y); System.out.println(result); }
@Data @NoArgsConstructor @AllArgsConstructor public class Employee { private Integer id; private String name; private Integer age; private Double salary; }
List<Employee> emps = Arrays.asList( new Employee(101, "Z3", 19, 9999.99), new Employee(102, "L4", 20, 7777.77), new Employee(103, "W5", 35, 6666.66), new Employee(104, "Tom", 44, 1111.11), new Employee(105, "Jerry", 60, 4444.44) );
@Test public void test01(){ Collections.sort(emps, (e1, e2) -> { if (e1.getAge() == e2.getAge()){ return e1.getName().compareTo(e2.getName()); } else { return Integer.compare(e1.getAge(), e2.getAge()); } }); for (Employee emp : emps) { System.out.println(emp); } }
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer 消费型接口 | T | void | 对类型为T的对象应用操作:void accept(T t) |
Supplier 提供型接口 | 无 | T | 返回类型为T的对象:T get() |
Function<T, R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果为R类型的对象:R apply(T t) |
Predicate 断言型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值:boolean test(T t) |
返回Consumer<T>对象封装了我们需要的逻辑,调用Consumer<Integer>对象的accept方法调用我们的逻辑
//一段重复使用代码不想重新定义一个私有方法,可以用这种 @Test public void test01(){ //Consumer Consumer<Integer> consumer = (x) -> System.out.println("消费型接口" + x); //test consumer.accept(100); }
返回Supplier<T>对象封装了我们需要的逻辑,调用Supplier<Integer>对象的get方法调用我们的逻辑
@Test public void test02(){ List<Integer> list = new ArrayList<>(); List<Integer> integers = Arrays.asList(1,2,3); list.addAll(integers); //Supplier<T> Supplier<Integer> supplier = () -> (int)(Math.random() * 10); list.add(supplier.get()); System.out.println(supplier); for (Integer integer : list) { System.out.println(integer); } }
2.3、函数型接口
返回Function<T,R>对象封装了我们需要的逻辑,调用Function<T,R>对象的get方法调用我们的逻辑
@Test public void test03(){ //Function<T, R> String oldStr = "abc123456xyz"; Function<String, String> function = (s) -> s.substring(1, s.length()-1); //test System.out.println(function.apply(oldStr)); }
@Test public void test04(){ //Predicate<T> Integer age = 35; Predicate<Integer> predicate = (i) -> i >= 35; if (predicate.test(age)){ System.out.println("你该退休了"); } else { System.out.println("我觉得还OK啦"); } }
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
BiFunction<T,U,R> | T,U | R | 对类型为T,U参数应用操作,返回R类型的结果包含方法为R apply(T t,R r) |
UnaryOperator<T> | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果,包含方法为 T apply(T t) |
BinaryOperator<T>(BiFunction的子接口) | T,T | T | 对类型为T的对象进行一元运算,并返回T类型的结果,包含方法为 T apply(T t1,T t2) |
BiConsumer<T,U> | T,U | void | 对类型为T,U参数应用操作,包含方法为 void accept(T t,R r) |
ToIntFunction<T> TolongFunction<T> ToDoubleFunction<T> | T | int/long/double | 分别计算int/long/double值的函数 |
IntFunction<R> LongFunction<R> DoubleFunction<R> | int/long/double | R | 参数分别为int/long/double类型的函数 |
3.1、方法引用
定义:若 Lambda 表达式体中的内容已有方法实现,则我们可以使用“方法引用”
语法格式:
-
-
对象 :: 实例方法
-
类 :: 静态方法
-
类 :: 实例方法
-
@Test public void test01(){ PrintStream ps = System.out; Consumer<String> con1 = (s) -> ps.println(s); con1.accept("aaa"); Consumer<String> con2 = ps::println; con2.accept("bbb"); }
注意:Lambda 表达实体中调用方法的参数列表、返回类型必须和函数式接口中抽象方法保持一致
- 类::静态方法
@Test public void test02(){ Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y); System.out.println(com1.compare(1, 2)); //Integer类中实现了compare方法 Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(2, 1)); }
@Test public void test03(){ BiPredicate<String, String> bp1 = (x, y) -> x.equals(y); System.out.println(bp1.test("a","b")); //String类中实现了equals方法 BiPredicate<String, String> bp2 = String::equals; System.out.println(bp2.test("c","c")); }
@Test public void test04(){ Supplier<List> sup1 = () -> new ArrayList(); Supplier<List> sup2 = ArrayList::new; }
/** * 创建数组 */ @Test public void test05(){ Function<Integer,Integer[]> function = (n) ->new Integer[n]; //等价于 Function<Integer,Integer[]> fun =Integer[]::new; }
-
Stream自己不会存存储元素
-
Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
-
Stream操作时延迟执行的。这意味着他们会等到需要结果的时候才执行。
Stream的操作三个步骤:
-
-
中间操作:一个中间操作连,对数据源的数据进行处理
-
创建流:(的几种方法如下)
@Test public void test01(){ /** * 集合流 * - Collection.stream() 穿行流 * - Collection.parallelStream() 并行流 */ List<String> list = new ArrayList<>(); Stream<String> stream1 = list.stream(); //数组流 //Arrays.stream(array) String[] strings = new String[10]; Stream<String> stream2 = Arrays.stream(strings); //Stream 静态方法 //Stream.of(...) Stream<Integer> stream3 = Stream.of(1, 2, 3); //无限流 //迭代 Stream<Integer> stream4 = Stream.iterate(0, (i) -> ++i+i++); stream4.forEach(System.out::println); //生成 Stream.generate(() -> Math.random()) .limit(5) .forEach(System.out::println); }
方法 | 描述 |
---|---|
filter(Predicate p) | 接收 Lambda , 从流中排除某些元素。 |
distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量。 |
skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
List<Employee> emps = Arrays.asList( new Employee(101, "Z3", 19, 9999.99), new Employee(102, "L4", 20, 7777.77), new Employee(103, "W5", 35, 6666.66), new Employee(104, "Tom", 44, 1111.11), new Employee(105, "Jerry", 60, 4444.44) ); @Test public void test01(){ emps.stream() .filter((x) -> x.getAge() > 35) .limit(3) //短路?达到满足不再内部迭代 .distinct() .skip(1) .forEach(System.out::println); }
Stream的中间操作: 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为惰性求值。
-
-
内部迭代:迭代操作由 Stream API 完成
-
外部迭代:我们通过迭代器完成
-
4.3、中间操作--映射
-
map
:接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 -
flatMap:接收一个函数作为参数,返回一个流,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流。
//map @Test public void test02(){ List<String> list = Arrays.asList("a", "b", "c"); list.stream() .map((str) -> str.toUpperCase()) .forEach(System.out::println); }
//flatMap public Stream<Character> filterCharacter(String str){ List<Character> list = new ArrayList<>(); for (char c : str.toCharArray()) { list.add(c); } return list.stream(); } @Test public void test03(){ List<String> list = Arrays.asList("a", "b", "c"); Test02 test02 = new Test02(); list.stream() .flatMap(test02::filterCharacter) .forEach(System.out::println); }
-
sorted
():自然排序 -
sorted
(Comparator c):定制排序
// Comparable:自然排序 @Test public void test04(){ List<Integer> list = Arrays.asList(1,2,3,4,5); list.stream() .sorted() //comparaTo() .forEach(System.out::println); }
//Comparator:定制排序 @Test public void test05(){ emps.stream() .sorted((e1, e2) -> { //compara() if (e1.getAge().equals(e2.getAge())){ return e1.getName().compareTo(e2.getName()); } else { return e1.getAge().compareTo(e2.getAge()); } }) .forEach(System.out::println); }
方法 | 描述 |
---|---|
allMatch | 检查是否匹配所有元素 |
anyMatch | 检查是否至少匹配一个元素 |
noneMatch | 检查是否没有匹配所有元素 |
findFirst | 返回第一个元素 |
findAny | 返回当前流中的任意元素 |
count | 返回流中元素的总个数 |
max | 返回流中最大值 |
min | 返回流中最小值: |
public enum Status { FREE, BUSY, VOCATION; } @Test public void test01(){ List<Status> list = Arrays.asList(Status.FREE, Status.BUSY, Status.VOCATION); boolean flag1 = list.stream() .allMatch((s) -> s.equals(Status.BUSY)); System.out.println(flag1); boolean flag2 = list.stream() .anyMatch((s) -> s.equals(Status.BUSY)); System.out.println(flag2); boolean flag3 = list.stream() .noneMatch((s) -> s.equals(Status.BUSY)); System.out.println(flag3); // 避免空指针异常 Optional<Status> op1 = list.stream() .findFirst(); // 如果Optional为空 找一个替代的对象 Status s1 = op1.orElse(Status.BUSY); System.out.println(s1); Optional<Status> op2 = list.stream() .findAny(); System.out.println(op2); long count = list.stream() .count(); System.out.println(count); }
-
归约:
reduce
(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值 -
收集:
collect
将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
reduce:
/** * Java: * - reduce:需提供默认值(初始值) * Kotlin: * - fold:不需要默认值(初始值) * - reduce:需提供默认值(初始值) */ @Test public void test01(){ List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); Integer integer = list.stream() .reduce(0, (x, y) -> x + y); System.out.println(integer); }
collect: 收集
Stream的终止操作
方法 | 描述 |
---|---|
collect(Collector c) | 将流转换成其他形式,接受一个Collector接口的实现,用于给Stream中元素做汇总的方法。 |
Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
方法 | 返回类型 | 作用 | 示例 |
---|---|---|---|
toList | List<T> | 把流中元素收集到List | List<Employee> emps= list.stream().collect(Collectors.toList()); |
toSet | Set<T> | 把流中元素收集到Set | Set<Employee> emps= list.stream().collect(Collectors.toSet()); |
toCollection | Collection<T> | 把流中元素收集到创建的集合 | Collection<Employee>emps= list.stream().collect(Collectors.toCollection(ArrayList::new)); |
counting | Long | 计算流中元素的个数 | long count = list.stream().collect(Collectors.counting()); |
summingInt | Integer | 对流中元素的整数属性求和 | inttotal= list.stream().collect(Collectors.summingInt(Employee::getSalary)); |
averagingInt | Double | 计算流中元素Integer属性的平均值 | doubleavg= list.stream().collect(Collectors.averagingInt(Employee::getSalary)); |
summarizingInt | IntSummaryStatistics | 收集流中Integer属性的统计值。如:平均值 | IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); |
joining | String | 连接流中每个字符串 | String str= list.stream().map(Employee::getName).collect(Collectors.joining()); |
maxBy | Optional<T> | 根据比较器选择最大值 | Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary))); |
minBy | Optional<T> | 根据比较器选择最小值 | Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary))); |
reducing | 归约产生的类型 | 从一个作为累加器的初始值<开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值 | inttotal=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum)); |
collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器,对其结果转换函数 | inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); |
groupingBy | Map<K, List<T>> | 根据某属性值对流分组,属性为K,结果为V | Map<Emp.Status, List<Emp>> map= list.stream() .collect(Collectors.groupingBy(Employee::getStatus)); |
partitioningBy | Map<Boolean, List<T>> | 根据true或false进行分区 | Map<Boolean,List<Emp>>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage)); |
List<Employee> emps = Arrays.asList( new Employee(101, "Z3", 19, 9999.99), new Employee(102, "L4", 20, 7777.77), new Employee(103, "W5", 35, 6666.66), new Employee(104, "Tom", 44, 1111.11), new Employee(105, "Jerry", 60, 4444.44) ); @Test public void test02(){ //放入List List<String> list = emps.stream() .map(Employee::getName) .collect(Collectors.toList()); list.forEach(System.out::println); //放入Set Set<String> set = emps.stream() .map(Employee::getName) .collect(Collectors.toSet()); set.forEach(System.out::println); //放入LinkedHashSet LinkedHashSet<String> linkedHashSet = emps.stream() .map(Employee::getName) .collect(Collectors.toCollection(LinkedHashSet::new)); linkedHashSet.forEach(System.out::println); } @Test public void test03(){ //总数 Long count = emps.stream() .collect(Collectors.counting()); System.out.println(count); //平均值 Double avg = emps.stream() .collect(Collectors.averagingDouble(Employee::getSalary)); System.out.println(avg); //总和 Double sum = emps.stream() .collect(Collectors.summingDouble(Employee::getSalary)); System.out.println(sum); //最大值 Optional<Employee> max = emps.stream() .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))); System.out.println(max.get()); //最小值 Optional<Double> min = emps.stream() .map(Employee::getSalary) .collect(Collectors.minBy(Double::compare)); System.out.println(min.get()); } @Test public void test04(){ //分组 Map<Integer, List<Employee>> map = emps.stream() .collect(Collectors.groupingBy(Employee::getId)); System.out.println(map); //多级分组 Map<Integer, Map<String, List<Employee>>> mapMap = emps.stream() .collect(Collectors.groupingBy(Employee::getId, Collectors.groupingBy((e) -> { if (e.getAge() > 35) { return "开除"; } else { return "继续加班"; } }))); System.out.println(mapMap); //分区 Map<Boolean, List<Employee>> listMap = emps.stream() .collect(Collectors.partitioningBy((e) -> e.getSalary() > 4321)); System.out.println(listMap); } @Test public void test05(){ //总结 DoubleSummaryStatistics dss = emps.stream() .collect(Collectors.summarizingDouble(Employee::getSalary)); System.out.println(dss.getMax()); System.out.println(dss.getMin()); System.out.println(dss.getSum()); System.out.println(dss.getCount()); System.out.println(dss.getAverage()); //连接 String str = emps.stream() .map(Employee::getName) .collect(Collectors.joining("-")); //可传入分隔符 System.out.println(str); }
-
并行流:就是把一个内容分成几个数据块,并用不同的线程分别处理每个数据块的流
-
Java 8 中将并行进行了优化,我们可以很容易的对数据进行操作;Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与串行流之间切换
Fork / Join 框架: 就是在必要的情况下,将一个大任务,进行拆分(fork)成如干个小任务(拆到不可再拆分时),再将一个个的小任务运算的结果进行join汇总。
public class ForkJoinCalculate extends RecursiveTask<Long> { private static final long serialVersionUID = 1234567890L; private long start; private long end; private static final long THRESHPLD = 10000; public ForkJoinCalculate(long start, long end) { this.start = start; this.end = end; } @Override protected Long compute() { long length = end - start; if (length <= THRESHPLD) { long sum = 0; for (long i = start; i <= end; i++) { sum += i; } } else { long middle = (start + end) / 2; ForkJoinCalculate left = new ForkJoinCalculate(start, end); left.fork(); //拆分子任务 压入线程队列 ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end); right.fork(); return left.join() + right.join(); } return null; } } public class TestForkJoin { /** * ForkJoin 框架 */ @Test public void test01(){ Instant start = Instant.now(); ForkJoinPool pool = new ForkJoinPool(); ForkJoinCalculate task = new ForkJoinCalculate(0, 100000000L); Long sum = pool.invoke(task); System.out.println(sum); Instant end = Instant.now(); System.out.println(Duration.between(start, end).getNano()); } /** * 普通 for循环 */ @Test public void test02(){ Instant start = Instant.now(); Long sum = 0L; for (long i = 0; i < 100000000L; i++) { sum += i; } Instant end = Instant.now(); System.out.println(Duration.between(start, end).getNano()); } }
@Test public void test03(){ //串行流(单线程):切换为并行流 parallel() //并行流:切换为串行流 sequential() LongStream.rangeClosed(0, 100000000L) .parallel() //底层:ForkJoin .reduce(0, Long::sum); }
五、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 |
//Optional.of(T t):
@Test public void test01(){ Optional<Employee> op = Optional.of(new Employee()); Employee employee = op.get(); }
//Optional.empty(T t): @Test public void test02(){ Optional<Employee> op = Optional.empty(); Employee employee = op.get(); }
//Optional.ofNullable(T t): @Test public void test03(){ Optional<Employee> op = Optional.ofNullable(new Employee()); Employee employee = op.get(); }
//isPresent(): @Test public void test03(){ Optional<Employee> op = Optional.ofNullable(new Employee()); if (op.isPresent()) { Employee employee = op.get(); } }
六、Date / Time API
6.1、安全问题
传统的日期格式化:
@Test public void test01(){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Callable<Date> task = () -> sdf.parse("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(); }
加锁:
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(); }
DateTimeFormatter:
@Test public void test03(){ DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE; Callable<LocalDate> task = () -> LocalDate.parse("20200517",dtf); ExecutorService pool = Executors.newFixedThreadPool(10); ArrayList<Future<LocalDate>> result = new ArrayList<>(); for (int i = 0; i < 10; i++) { result.add(pool.submit(task)); } for (Future<LocalDate> future : result) { try { System.out.println(future.get()); } catch (InterruptedException | 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 |
@Test public void test01(){ //获取当前时间日期 now LocalDateTime ldt1 = LocalDateTime.now(); System.out.println(ldt1); //指定时间日期 of LocalDateTime ldt2 = LocalDateTime.of(2020, 05, 17, 16, 24, 33); System.out.println(ldt2); //加 plus LocalDateTime ldt3 = ldt2.plusYears(2); System.out.println(ldt3); //减 minus LocalDateTime ldt4 = ldt2.minusMonths(3); System.out.println(ldt4); //获取指定的你年月日时分秒... get System.out.println(ldt2.getDayOfYear()); System.out.println(ldt2.getHour()); System.out.println(ldt2.getSecond()); }
Instant:以 Unix 元年 1970-01-01 00:00:00 到某个时间之间的毫秒值
@Test public void test02(){ // 默认获取 UTC 时区 (UTC:世界协调时间) Instant ins1 = Instant.now(); System.out.println(ins1); //带偏移量的时间日期 (如:UTC + 8) OffsetDateTime odt1 = ins1.atOffset(ZoneOffset.ofHours(8)); System.out.println(odt1); //转换成对应的毫秒值 long milli1 = ins1.toEpochMilli(); System.out.println(milli1); //构建时间戳 Instant ins2 = Instant.ofEpochSecond(60); System.out.println(ins2); }
6.4、时间 / 日期 差
- Duration:计算两个时间之间的间隔
- Period:计算两个日期之间的间隔
@Test public void test03(){ //计算两个时间之间的间隔 between Instant ins1 = Instant.now(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Instant ins2 = Instant.now(); Duration dura1 = Duration.between(ins1, ins2); System.out.println(dura1.getSeconds()); System.out.println(dura1.toMillis()); } @Test public void test04(){ LocalDate ld1 = LocalDate.of(2016, 9, 1); LocalDate ld2 = LocalDate.now(); Period period = Period.between(ld1, ld2); // ISO 标准 System.out.println(period.getYears()); System.out.println(period.toTotalMonths()); }
操纵日期:
- TemporalAdjuster:时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
- TemporalAdjusters:该类通过静态方法提供了大量的常用TemporalAdjuster的实现。
LocalDate nextSunday = LocalDate.now().with(TemporalAdjusters.next(DayofWeek,SUNDAY));
@Test public void test01(){ //TemporalAdjusters:时间校正器 LocalDateTime ldt1 = LocalDateTime.now(); System.out.println(ldt1); //指定日期时间中的 年 月 日 ... LocalDateTime ldt2 = ldt1.withDayOfMonth(10); System.out.println(ldt2); //指定时间校正器 LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); System.out.println(ldt3); //自定义时间校正器 LocalDateTime ldt5 = ldt1.with((ta) -> { LocalDateTime ldt4 = (LocalDateTime) ta; DayOfWeek dow1 = ldt4.getDayOfWeek(); if (dow1.equals(DayOfWeek.FRIDAY)) { return ldt4.plusDays(3); } else if (dow1.equals(DayOfWeek.SATURDAY)) { return ldt4.plusDays(2); } else { return ldt4.plusDays(1); } }); System.out.println(ldt5); }
//DateTimeFormatter:格式化时间 / 日期 @Test public void test01(){ //默认格式化 DateTimeFormatter dtf1 = DateTimeFormatter.ISO_DATE_TIME; LocalDateTime ldt1 = LocalDateTime.now(); String str1 = ldt1.format(dtf1); System.out.println(str1); //自定义格式化 ofPattern DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDateTime ldt2 = LocalDateTime.now(); String str2 = ldt2.format(dtf2); System.out.println(str2); //解析 LocalDateTime newDate = ldt1.parse(str1, dtf1); System.out.println(newDate); }
-
-
ZonedTime
-
@Test public void test02(){ //查看支持的时区 Set<String> set = ZoneId.getAvailableZoneIds(); set.forEach(System.out::println); //指定时区 LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Europe/Tallinn")); System.out.println(ldt1); //在已构建好的日期时间上指定时区 LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Europe/Tallinn")); ZonedDateTime zdt1 = ldt2.atZone(ZoneId.of("Europe/Tallinn")); System.out.println(zdt1); }
一些转换:
@Test public void test03(){ // Date 转 LocalDateTime Date date = new Date(); Instant instant = date.toInstant(); ZoneId zoneId = ZoneId.systemDefault(); LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); // LocalDateTime 转 Date LocalDateTime localDateTime = LocalDateTime.now(); ZoneId zoneId = ZoneId.systemDefault(); ZonedDateTime zdt = localDateTime.atZone(zoneId); Date date = Date.from(zdt.toInstant()); // 原则:利用 时间戳Instant }