1、Lambda表达式
lambda简介
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。
对接口的要求
虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法
@FunctionalInterface是一个标志注解,被修饰的接口中的抽象方法只能有一个。 这个注解往往会和 lambda 表达式一起出现。
lambda初体验
在传统的写法中,我们创建一个线程通常的写法是:
new Thread(new Runnable() {
@Override
public void run() {
//业务代码
}
}).start();
使用lambda表达式时写法是:
new Thread(()->{
//业务代码
}).start();
上下代码两者实现的功能是完全一样的,由此可见lanbda表达式的使用会简化我们的代码,使代码更加优雅。
lambda语法
lambda表达式由组成三部分:
() -> { }
// () 为参数列表 -> 为运算符(读作goes to) { } 为方法体
由此可见lambda表达式简化了方法的修饰符,返回值,方法名
注:
1、所有参数都可以不写参数类型
2、只有一个参数时可省略(),方法体只有一条语句时可以省略{}
Lambda 表达式常用示例
- lambda 表达式引用方法
有时候我们不是必须要自己重写某个匿名内部类的方法,我们可以可以利用 lambda表达式的接口快速指向一个已经被实现的方法。
语法
方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象
public class Exe1 {
public static void main(String[] args) {
ReturnOneParam lambda1 = a -> doubleNum(a);
System.out.println(lambda1.method(3));
//lambda2 引用了已经实现的 doubleNum 方法 此种用法在mybatisplus中用来构建sql语句时常用
ReturnOneParam lambda2 = Exe1::doubleNum;
System.out.println(lambda2.method(3));
Exe1 exe = new Exe1();
//lambda4 引用了已经实现的 addTwo 方法
ReturnOneParam lambda4 = exe::addTwo;
System.out.println(lambda4.method(2));
}
/**
* 要求
* 1.参数数量和类型要与接口中定义的一致
* 2.返回值类型要与接口中定义的一致
*/
public static int doubleNum(int a) {
return a * 2;
}
public int addTwo(int a) {
return a + 2;
}
}
- 构造方法的引用
一般我们需要声明接口,该接口作为对象的生成器,通过 类名::new 的方式来实例化对象,然后调用方法返回对象。
interface ItemCreatorBlankConstruct {
Item getItem();
}
interface ItemCreatorParamContruct {
Item getItem(int id, String name, double price);
}
public class Exe2 {
public static void main(String[] args) {
ItemCreatorBlankConstruct creator = () -> new Item();
Item item = creator.getItem();
ItemCreatorBlankConstruct creator2 = Item::new;
Item item2 = creator2.getItem();
ItemCreatorParamContruct creator3 = Item::new;
Item item3 = creator3.getItem(112, "鼠标", 135.99);
}
}
- lambda 表达式创建线程
我们以往都是通过创建 Thread 对象,然后通过匿名内部类重写 run() 方法,一提到匿名内部类我们就应该想到可以使用 lambda 表达式来简化线程的创建过程。
Thread t = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(2 + ":" + i);
}
});
t.start();
- 遍历集合
我们可以调用集合的 public void forEach(Consumer<? super E> action)
方法,通过 lambda 表达式的方式遍历集合中的元素。以下是 Consumer 接口的方法以及遍历集合的操作。Consumer 接口是 jdk 为我们提供的一个函数式接口。
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1,2,3,4,5);
//lambda表达式 方法引用
list.forEach(System.out::println);
list.forEach(element -> {
if (element % 2 == 0) {
System.out.println(element);
}
});
- 删除集合中的某个元素
我们通过public boolean removeIf(Predicate<? super E> filter)
方法来删除集合中的某个元素,Predicate 也是 jdk 为我们提供的一个函数式接口,可以简化程序的编写。
ArrayList<Item> items = new ArrayList<>();
items.add(new Item(11, "小牙刷", 12.05 ));
items.add(new Item(5, "日本马桶盖", 999.05 ));
items.add(new Item(7, "格力空调", 888.88 ));
items.add(new Item(17, "肥皂", 2.00 ));
items.add(new Item(9, "冰箱", 4200.00 ));
items.removeIf(ele -> ele.getId() == 7);
//通过 foreach 遍历,查看是否已经删除
items.forEach(System.out::println);
- 集合内元素的排序
在以前我们若要为集合内的元素排序,就必须调用 sort 方法,传入比较器匿名内部类重写 compare 方法,我们现在可以使用 lambda 表达式来简化代码。
ArrayList<Item> list = new ArrayList<>();
list.add(new Item(13, "背心", 7.80));
list.add(new Item(11, "半袖", 37.80));
list.add(new Item(14, "风衣", 139.80));
list.add(new Item(12, "秋裤", 55.33));
/*
list.sort(new Comparator<Item>() {
@Override
public int compare(Item o1, Item o2) {
return o1.getId() - o2.getId();
}
});
*/
list.sort((o1, o2) -> o1.getId() - o2.getId());
System.out.println(list);
Lambda 表达式中的闭包问题
这个问题我们在匿名内部类中也会存在,如果我们把注释放开会报错,告诉我 num 值是 final 不能被改变。这里我们虽然没有标识 num 类型为 final,但是在编译期间虚拟机会帮我们加上 final 修饰关键字。
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) {
int num = 10;
Consumer<String> consumer = ele -> {
System.out.println(num);
};
//num = num + 2;
consumer.accept("hello");
}
}
2、接口增强
在1.8之前接口中只能有:静态常量,抽象方法。
在1.8之后接口中可以有:静态常量,抽象方法,默认方法,静态方法
默认方法
语法格式
interface 接口名{
修饰符 default 返回值 方法名(){
//方法体
}
}
使用方式
1、接口的实现类直接调用接口的默认方法
2、子类重写接口的默认方法
静态方法
interface 接口名{
修饰符 static 返回值 方法名(){
//方法体
}
}
使用方式:只能通过 接口名.方法名
注:默认方法可以被继承和重新,而静态方法都不能
3、函数式接口
由来:使用Lambda表达式的前提是有函数式接口,而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表和返回值类型,因此为了更加方便的使用Lambda表达式,JDK提供了大量常用的函数式接口(java.util.function包下)
Supplier
无参有返回值,用于生产数据
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
使用:
public static void main(String[] args) {
fun1(()->{
int[] arr={10,22,78,3,56,43,29,75};
Arrays.sort(arr);
return arr[arr.length-1];
});
}
private static void fun1(Supplier<Integer> supplier){
Integer max = supplier.get();
System.err.println("max = "+max);
}
Consumer
有参无返回值,用于消费数据
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
}
使用:
public static void main(String[] args) {
fun1( msg->{
System.err.println(msg+" ---转换为大写--- "+msg.toUpperCase());
});
}
private static void fun1(Consumer<String> consumer){
consumer.accept("consumer accept");
}
默认方法andThen使用:(进行组合操作)
public static void main(String[] args) {
fun1( msg->{
System.err.println(msg+" ---转换为大写--- "+msg.toUpperCase());
},msg2->{
System.err.println(msg2+" ---转换成小写--- "+msg2.toLowerCase());
});
}
private static void fun1(Consumer<String> c1,Consumer<String> c2){
String str = "consumer accept";
c1.andThen(c2).accept(str); //c1先执行,执行完毕再执行c2
}
Function
有参有返回值,根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
使用:
public static void main(String[] args) {
fun1( msg->{
return Integer.parseInt(msg);
});
}
private static void fun1(Function<String,Integer> function){
System.err.println(function.apply("123345"));
}
默认方法andThen使用:(进行组合操作)同上
Predicate
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
}
4、Stream流
什么是Stream?
Java8 中,Collection 新增了两个流方法,分别是 Stream() 和 parallelStream()
Java8 中添加了一个新的接口类 Stream,相当于高级版的 Iterator,它可以通过 Lambda 表达式对集合进行大批量数据操作,或 者各种非常便利、高效的聚合数据操作。
Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream API能让我们快速完成很多复杂的操作,如筛选、切片、映射、查找、去重、统计、匹配和规约。
为什么要使用 Stream?
在 Java8 之前,我们通常是通过 for 循环或者 Iterator 迭代来重新排序合并数据,又或者通过重新定义 Collections.sorts 的 Comparator 方法来实现,这两种方式对于大数据量系统来说,效率并不是很理想。Stream 的聚合操作与数据库 SQL 的聚合操作 sorted、filter、map 等类似。我们在应用层就可以高效地实现类似数据库 SQL 的 聚合操作了,而在数据操作方面,Stream 不仅可以通过串行的方式实现数据操作,还可以通过并行的方式处理大批量数据,提高数据 的处理效率。
Stream流的获取方式
根据Collection
Collection中加入了default方法获取Stream流
List<String> list = new ArrayList<>();
list.stream();
Set<String> set = new HashSet<>();
set.stream();
Map<String,Object> map=new HashMap<>();
map.keySet().stream();
map.values().stream();
map.entrySet().stream();
通过Stream的of方法
String[] s1={"aa","bb","cc"};
Stream.of(s1);
Stream的常用方法
方法名 | 方法作用 | 返回值类型 | 方法种类 | 是否支持链式调用 |
---|---|---|---|---|
count | 统计个数 | long | 终结 | 否 |
forEach | 遍历 | void | 终结 | 否 |
filter | 过滤 | Stream | 函数拼接 | 是 |
limit | 取用前几个 | Stream | 函数拼接 | 是 |
skip | 跳过几个 | Stream | 函数拼接 | 是 |
map | 映射 | Stream | 函数拼接 | 是 |
concat | 组合 | Stream | 函数拼接 | 是 |
终结方法:返回值类型不再是Stream接口本身类型的方法,例如:forEach方法和count方法
非终结方法/延迟方法:返回值类型仍然是Stream接口自身类型的方法,除了终结方法都是延迟方法
注:
- Stream只能操作一次
- Stream返回的是新的流
- Stream不调用终结方法,中间的操作不会执行
forEach
void forEach(Consumer<? super T> action):逐一处理流中的元素(遍历流)
参数 Consumer<? super T> action : 函数式接口, 只有一个抽象方法:void accept(T t);
注意:
1.此方法并不保证元素的逐一消费动作在流中是有序执行的
2.Consumer 是一个消费接口(可以获取流中的元素进行遍历操作,输出出去),可以使用lambda表达式
public class Tests_forEach {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张三");
list.add("张三四");
list.add("王五");
list.add("赵六");
list.add("张三三");
list.add("周八");
// 函数模型:获取流 --> 逐一消费流中的元素
// 函数模型:获取流 --> 逐一消费流中的元素
list.stream().forEach(System.err::println);
/*
* 虽然方法名称叫做【forEach】,但是与for循环中的【for-each】昵称不同,该方法并不保证元素的逐一消费动作在流中是被有序执行的
* */
System.out.println("==========================");
// parallelStream()方法可以得到并行流
list.parallelStream().forEach((String name)->{
System.out.println(name);
});
}
}
count
long count(); 统计流中的元素,返回long类型数据
public class Tests_count {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张三");
list.add("张三四");
list.add("王五");
list.add("赵六");
list.add("张三三");
list.add("周八");
long count = list.stream().count();
System.out.println("流中的属于个数:"+count);
}
}
filter
Stream
参数Predicate:函数式接口,抽象方法:boolean test(T t);
Predicate接口:是一个判断接口
public class Tests_filter {
public static void main(String[] args) {
// 获取stream流
Stream<String> stream = Stream.of("张三", "张三四", "王五", "赵六", "张三三", "周八");
// 需求:过滤处
stream.filter((String name)->{
return name.startsWith("张");
}).forEach((String name)->{
System.out.println(name);
});
}
}
limit
Stream
注意: 参数是一个long类型,如果流的但钱长度大于参数则进行截取;否则不进行操作
public class Tests_limit {
public static void main(String[] args) {
// 获取stream流
Stream<String> stream = Stream.of("张三", "张三四", "王五", "赵六", "张三三", "周八");
// 需求:保留前3个元素
stream.limit(3).forEach((String name) -> {
System.out.println(name);
});
System.out.println("===========================");
// 如果流的获取长度 大于 流当前的长度,则不操作
stream.limit(10).forEach(name-> System.out.println(name)); // 异常: stream has already
// been operated upon or closed 流已被操作或关闭
Stream<String> stream1 = Stream.of("张三", "张三四", "王五", "赵六", "张三三", "周八");
stream1.limit(10).forEach(name-> System.out.println(name));
}
}
skip
Stream
注意:如果流的当前长度大于 n,则跳过前n个,否则将会得到一个长度为0的空流
public class Tests_skip {
public static void main(String[] args) {
// 获取stream流
Stream<String> stream = Stream.of("张三", "张三四", "王五", "赵六", "张三三", "周八");
// 需求:跳过前3个
stream.skip(3).forEach(name-> System.out.println(name));
}
}
map
在对集合进行操作的时候,我们经常会从某些对象中选择性的提取某些元素的值,就像编写sql一样,指定获取表 中特定的数据列
参数 Function<T,R>:函数式接口,抽象方法: R apply(T t);
Function<T,R>:其实就是一个类型转换接口(T和R的类型可以一致,也可以不一致)
案例:获取所有学生的姓名,并形成一个新的集合
public class MapDemo {
public static void main(String[] args) {
//获取所有学生的姓名,并形成一个新的集合
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,19,"张三","M",true));
students.add(new Student(1,18,"李四","M",false));
students.add(new Student(1,21,"王五","F",true));
students.add(new Student(1,20,"赵六","F",false));
List<String> collect = students.stream().map(Student::getName).collect(Collectors.toList());
collect.forEach(s -> System.out.print(s + " "));
}
}
//结果:张三 李四 王五 赵六
sorted
sort可以对集合中的所有元素进行排序
List<Integer> sortLists = new ArrayList<>();
sortLists.add(1);
sortLists.add(4);
sortLists.add(6);
sortLists.add(3);
sortLists.add(2);
List<Integer> afterSortLists = sortLists.stream().sorted((In1,In2)->
In1-In2).collect(Collectors.toList());
concat
如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat:
static
注:这是一个静态方法,与 java.lang.String 当中的 concat 方法是不同的。
public static void main(String[] args) {
Stream<String> original1 = Stream.of("Java", "C", "Python");
Stream<String> original2 = Stream.of("Hadoop", "Spark");
Stream<String> result = Stream.concat(original1, original2);
result.forEach(System.out::println);
}
distinct
去除重复元素,这个方法是通过类的 equals 方法来判断两个元素是否相等的
注:基本类型可以直接去重,对于自定义类型需要重写hashcode和equals方法
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 2, 2, 2, 2);
integers.stream().distinct().collect(Collectors.toList());
}
reduce
如果将所有数据归纳为一个数据,可以使用reduce
String s = Stream.of("1", "2", "3", "4", "5", "6")
// identity 是默认值
.reduce("0",(x, y) -> {
System.out.println("x= "+x+" y= "+y);
return x +y;
});
System.out.println(s);
/**
输出结果:
x= 0 y= 1
x= 01 y= 2
x= 012 y= 3
x= 0123 y= 4
x= 01234 y= 5
x= 012345 y= 6
0123456
*/
min/max
max,min可以寻找出流中最大或者最小的元素
List<String> maxLists = new ArrayList<>();
maxLists.add("a");
maxLists.add("b");
maxLists.add("c");
maxLists.add("d");
maxLists.add("e");
maxLists.add("f");
maxLists.add("hahaha");
int maxLength = maxLists.stream().mapToInt(s->s.length()).max().getAsInt();
System.out.println("字符串长度最长的长度为"+maxLength);
match
有的时候,我们只需要判断集合中是否全部满足条件,或者判断集合中是否有满足条件的元素,这时候就可以使用match方法:
allMatch:Stream 中全部元素符合传入的 predicate,返回 true
anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
// 判断集合中没有有为‘c’的元素:
List<String> matchList = new ArrayList<>();
matchList.add("a");
matchList.add("a");
matchList.add("c");
matchList.add("d");
boolean isExits = matchList.stream().anyMatch(s -> s.equals("c"));
//判断集合中是否全不为空
List<String> matchList = new ArrayList<>();
matchList.add("a");
matchList.add("");
matchList.add("a");
matchList.add("c");
matchList.add("d");
boolean isNotEmpty = matchList.stream().noneMatch(s -> s.isEmpty());
collect
Stream流中提供了一个方法,可以把流中的数据收集到单列集合中:
<R, A> R collect(Collector<? super T, A, R> collector); 把流中的数据收集到单列集合中
返回值类型是R。R指定为什么类型,就是收集到什么类型的集合
参数Collector<? super T, A, R>中的R类型,决定把流中的元素收集到哪个集合中
参数Collector如何得到 ?,可以使用 java.util.stream.Collectors工具类中的静态方法:
public static
Collector<T, ?, List > toList():转换为List集合
taticCollector<T, ?, Set > toSet() :转换为Set集合 public class Tests { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("张三"); list.add("张三四"); list.add("王五"); list.add("赵六"); list.add("张三三"); list.add("周八"); // 需求:过滤出姓张的并且长度为3的元素 Stream<String> stream1 = list.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3); // stream 收集到单列集合中 List<String> list1 = stream1.collect(Collectors.toList()); System.out.println(list1); // stream 收集到单列集合中 Set<String> set1 = stream1.collect(Collectors.toSet()); System.out.println(set1); // 将stream流中的元素转成Array数组对象 Object[] arr = stream1.toArray(); System.out.println(Arrays.toString(arr)) } }
串行流/并行流
串行流:流默认使用的就是串行的,即按序执行
并行流:parallelStream是一个并行执行流,它通过默认的ForkJoinPool,提高多线程的任务速度。
获取并行流的两种方式:
//串行流转换为并行流
Stream<String> parallel = Stream.of("1", "2", "3", "4", "5", "6")
.parallel();
//List接口直接获取并行流
Stream<Object> parallelStream = new ArrayList<Object>().parallelStream();
注:并行流可能会有数据安全问题,应对业务逻辑加锁
5、Optional类
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。
public final class Optional<T> extends Object
基本使用
Optional对象的创建方式
ofNullable:允许一个可能为空的对象
of:需要一个不为空的对象
这里需要自行判断,初始化的对象是否必然非空。
//1、通过of
Optional<String> op1 = Optional.of("nihao");
//of不支持null会报空指针异常
// Optional<Object> op2 = Optional.of(null);
//2、 通过ofNullable
Optional<String> op3 = Optional.ofNullable("hello");
Optional<Object> op4 = Optional.ofNullable(null);
//3、通过entity
Optional<Object> op5 = Optional.empty();
Optional常用方法
- empty:可以创建一个空的Optional对象
- get:直接获取Optional内部对象,但是不建议单独使用
- isPresent:判断内部对象是否为空,返回一个布尔值
- ifPresent:一样判断内部对象是否为空,如果不为空会执行lambda表达式
- filter:通过一定条件过滤对象
- flatMap:和map的区别为lambda入参的对象封装入了Optional
- orElse:如果值为空,返回一个对象
- orElseGet:如果值为空,执行一段lambda并返回一个对象
- orElseThrow:如果值为空,抛出一个异常
public static void main(String[] args) {
Optional<String> op1 = Optional.of("nihao");
Optional<Object> op2 = Optional.empty();
//1、get()方法获取值,有值返回值,无值抛出NoSuchElementException 异常
System.out.println(op1.get()); //nihao
System.out.println(op2.get()); //No value present
//2、isPresent() 判断是否包含值,包含值返回true,不包含返回false
// ifPresent(T->{...}) 如果存在值就执行Lambda表达式
System.out.println(op1.isPresent()); //true
System.out.println(op2.isPresent()); //false
//3、 orElse(T t) 如果调用对象包含值,就返回该值,否则返回t
// orElseGet(Supplier s) 如果调用对象包含值,就返回该值,否则返回Lambda表达式的返回值
System.out.println(op1.orElse("hi")); //nihao
System.out.println(op2.orElse("123")); //123
}
6、新时间日期API
java 8通过发布新的Date-Time API进一步加强对日期与时间的处理。在旧版的java中,日期时间APi存在诸多问题。java 8引入的新的一系列API,对时间日期处理提供了更好的支持,清楚的定义了时间日期的一些概念,比如说,瞬时时间(Instant),持续时间(duration),日期(date),时间(time),时区(time-zone)以及时间段(Period)。同时,借鉴了Joda库的一些优点,比如将人和机器对时间日期的理解区分开的。
新时间常用类
jdk8中新增加了一套全新的日期时间API,这套API设计合理,并且是线程安全的。API位于java.time包下:
- LocalDate:表示日期,包含年月日,格式为2020-01-11。
- LocalTime:表示时间,包含时分秒,格式为11:07:03.580。
- LocalDateTime:表示日期和时间组合,包含年月日,时分秒,格式为2020-01-11T11:07:03.580
- DateTimeFormatter:日期时间格式化类
- Instant:时间戳,表示一个特定的时间瞬间
- Duration:用于计算两个时间(LocalTime,时分秒)的距离
- Period:用于计算2个日期(LocalDate,年月日)的距离
- ZoneDateTime:包含时区的时间
基本使用
时间获取
//获取当前日期
LocalDate currDate=LocalDate.now();
//指定日期
LocalDate noeDay=LocalDate.of(2020, 1, 11);
//通过字符串指定日期
LocalDate towDay=LocalDate.parse("2020-01-11");
// 年
int year = currDate.getYear();
// 月
int month = currDate.getMonthValue();
// 一月的第几天
int day = currDate.getDayOfMonth();
// 一周的第几天
int week = currDate.getDayOfWeek().getValue();
时间操作
// 时间比较(LocalDate重写了equals方法,让日期的比较也变得简单了。)
System.out.println("比较两个日期是否相同:"+date1.equals(date2)); //true
//isBefore在之前
boolean isBefore=LocalDate.parse("2020-01-11").isBefore(LocalDate.parse("2020-01-10"));
//isAfter在之后
boolean isAfter=LocalDate.parse("2020-01-11").isAfter(LocalDate.parse("2020-01-10"));
// 日期加减
System.out.println("当前时间"+LocalDate.now());
System.out.println("当前时间加1天"+LocalDate.now().plusDays(1));
System.out.println("当前时间加1月"+LocalDate.now().plusMonths(1));
System.out.println("当前时间加1年"+LocalDate.now().plusYears(1));
//LocalDate的atTime()方法或LocalTime的atDate()方法将LocalDate或LocalTime合并成一个LocalDateTime。
//LocalDateTime与LocalDate和LocalTime之间可以相互转化
LocalDateTime datetime4=LocalDate.now().atTime(LocalTime.now());
LocalDateTime datetime5=LocalTime.now().atDate(LocalDate.now());
//ZonedDateTime-创建时区时间。用于处理带时区的日期和时间。ZoneId表示不同的时区。大约有40不同的时区。
Set<String> allZoneIds=ZoneId.getAvailableZoneIds();
//创建时区:
ZoneId zoneId=ZoneId.of("Asia/Shanghai");
//把LocalDateTime转换成特定的时区:
ZonedDateTime zonedDateTime=ZonedDateTime.of(LocalDateTime.now(), zoneId);
//获取当前时区
ZoneId z=ZoneId.systemDefault();
//获取日期时间:
ZonedDateTime dd = ZonedDateTime.now();
ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");
日期格式化
java.time.format.DateTimeFormatter类下:
Java8对日期的格式化操作非常简单,首先看到上面的类大多都提供了parse方法,可以直接通过解析字符串得到对应的对象。
而日期和时间的格式化可通过LocalDateTime的format方法进行格式化。
LocalDateTime dateTime=LocalDateTime.now();
String str=dateTime.format(DateTimeFormatter.ISO_DATE_TIME);
System.out.println(str);
str = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(str);
可以使用DateTimeFormatter预置的格式,也可以通过DateTimeFormatter.ofPattern方法来指定格式。
计算时间差
可以使用以下类来计算日期时间差异:1.Period 2.Duration 3.ChronoUnit
//日期时间差
LocalDate today = LocalDate.now();
LocalDate birthDate = LocalDate.of(1993, Month.OCTOBER, 19);
Period p = Period.between(birthDate, today); // birthDate - today
System.out.printf("年龄 : %d 年 %d 月 %d 日", p.getYears(), p.getMonths(), p.getDays());
//时分秒时间差
Instant inst1 = Instant.now();
Instant inst2 = inst1.plus(Duration.ofSeconds(10));
System.out.println("Difference in milliseconds : " + Duration.between(inst1, inst2).toMillis());
System.out.println("Difference in seconds : " + Duration.between(inst1, inst2).getSeconds());
//ChronoUnit类可用于在单个时间单位内测量一段时间,例如天数或秒
LocalDate startDate = LocalDate.of(1993, Month.OCTOBER, 19);
System.out.println("开始时间 : " + startDate);
LocalDate endDate = LocalDate.of(2017, Month.JUNE, 16);
System.out.println("结束时间 : " + endDate);
long daysDiff = ChronoUnit.DAYS.between(startDate, endDate);
System.out.println("两天之间的差在天数 : " + daysDiff);//两天之间的差在天数 : 8641