24.JDK1.8特性
本章目标
- Lambda表达式
- 函数式接口
- 方法引用
- Stream流
- Date Time API
本章内容
本章内容了解即可
JDK1.8新增了非常多的特性,我们主要讨论以下几个:
- Lambda表达式:Lambda允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
- 函数式接口(
functional Interface
),有且仅有一个抽象方法的接口,但可以有多个非抽象的方法 - 方法引用:方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
- 默认方法:默认方法就是一个在接口里面有了一个实现的方法。
- Stream API:新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
- Date Time API:加强对日期与时间的处理。
- Optional类:Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
一、Lambda表达式
Lambda
表达式是一种匿名函数
,基于数学中的λ演算得名,简单的理解,它是没有声明的方法
,即:没有名称,但它有参数列表、方法主体、返回类型,可能还有可以抛出的异常。
1、 Lambda表达式语法
(parameters) -> expression 或 (parameters) -> { statements; }
参数说明:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
简单实例如下:
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
2、 排序示例
按照Java传统的语法规则编写拥有People
对象的List
集合peopleList
,需要对age
进行排序,JDK1.8之前:
// 创建People比较器
Comparator<People> comparator = new Comparator<People>() {
@Override
public int compare(People o1, People o2) {
return o1.getAge() > o2.getAge();
}
};
// 排序
Collections.sort(peopleList, comparator);
JDK1.8后,使用Lambda表达式:
// Lambda表达式方式
Comparator<People> comparator = (o1, o2) -> {
return o1.getAge() > o2.getAge();
};
// 排序
Collections.sort(peopleList, comparator);
更简洁的写法如下:
peopleList.sort(Comparator.comparingInt(People::getAge));
由上例可知Lambda表达式代替了new Comparator()内部类 ,通过Lambda表达式来创建该接口的对象。
总结:在Java中,Lambda表达式是一种简化的代码写法,可以用来创建匿名函数。它们可以替代传统的内部类或者定义接口的方式
3、 使用场景:
使用场景 | Lambda表达式示例 |
---|---|
布尔表达式 | (List |
创建对象 | () -> new People(10) |
消费一个对象 | (People p) -> |
从一个对象中选择/抽取 | (int i) -> i.getAge() |
组合两个值 | (int a, int b) -> a * b |
比较两个对象 | (People p1, People p2) -> p1.getAge().compareTo(p2.getAge()) |
Lambda表达式必须接合函数式接口一起使用
二、函数式接口
函数式接口
(functional Interface),有且仅有一个抽象方法的接口,但可以有多个非抽象的方法。
1、 格式
修饰符 interface 接口名 {
public abstract 返回值类型 方法名 (可选参数列表);
}
public abstract可以省略(因为默认修饰为public abstract)
示例:
public interface MyFunctionalInterface {
public abstract void method();
}
2、 注解@FunctionalInterface
@FunctionalInterface
,是JDK1.8中新引入的一个注解,专门指代函数式接口,用于一个接口的定义上。
和@Override注解的作用类似,@FunctionalInterface注解可以用来检测接口是否是函数式接口。如果是函数式接口,则编译成功,否则编译失败(接口中没有抽象方法或者抽象方法的个数多余1个)。
3、 无参无返回类型
接口:
package com.it;
public interface FunctionInterface {
public void show();
}
传统方式实现
FunctionInterface fi = new FunctionInterface() {
@Override
public void show() {
System.out.println("aa");
}
};
fi.show();
光标放在FunctionInterface()后,按alt+enter之后会有提示转换为lamdba
lamdba方式实现:
package com.it;
public class Test {
public static void main(String[] args) {
//传统方式创建接口实现类或内部类实现接口方法
//使用Lambda表达式代替匿名内部类
FunctionInterface fi = () -> System.out.println("hello lamdba");
fi.show();
}
}
4、 有参无返回类型
接口:
package com.it;
public interface FunctionInterface {
public void show(String name);
}
实现类:
package com.it;
public class Test {
public static void main(String[] args) {
FunctionInterface fi = (name) -> System.out.println("hello lamdba"+name);
fi.show("tom");
}
}
5、 有参有返回类型
接口:
package com.it;
public interface FunctionInterface {
public int sum(int a, int b);
}
实现类:
package com.it;
public class Test {
public static void main(String[] args) {
FunctionInterface fi = (a,b) -> a+b;
int sum = fi.sum(1, 1);
System.out.println(sum);
}
}
6 、常用的函数式接口
上面示例中我们自己写了些函数式接口,实际上1.8中也提供了一些函数式接口,来完成我们常见的操作,Supplier,Consumer,Function,Predicate这些就是用来完成基本应用的
JDK 1.8 之前已有的函数式接口:
java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
- java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
- java.beans.PropertyChangListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
在JDK1.8新增了java.util.function
包下的很多函数式接口,用来支持Java的函数式编程,从而丰富了Lambda表达式的使用场景。
接口名 | 功能描述 | 使用示例 |
---|---|---|
Supplier<T> |
无参数,返回一个结果 | Supplier |
Consumer<T> |
接受一个参数并且不返回结果 | Consumer |
BiConsumer<T, U> | 接受两个参数并且不返回结果 | BiConsumer<String, Integer> biConsumer = (s, i) -> System.out.println(s + i); |
Function<T, R> |
接受一个参数并返回结果 | Function<Integer, String> function = (i) -> “Result:” + i; |
BiFunction<T, U, R> | 接受两个参数并返回结果 | BiFunction<String, Integer, String> biFunction = (s, i) -> s + i; |
Predicate<T> |
接受一个参数并返回布尔值 | Predicate |
BiPredicate<T, U> | 接受两个参数并返回布尔值 | BiPredicate<String, Integer> biPredicate = (s, i) -> s.length() > i; |
UnaryOperator |
接受一个参数并返回相同类型的结果 | UnaryOperator |
BinaryOperator |
接受两个参数并返回相同类型的结果 | BinaryOperator |
三、方法引用
适合于调用无参的方法及构造方法,不适合调用有参的。但不要为了Lamdba而Lamdba
方法引用
可以看作是Lambda表达式深层次的表达, Lambda表达中可以直接引用已有Java类或对象的方法或构造器。方法引用与lambda表达式结合使用,可以进一步简化代码。
1、 方法引用的通用特性:
方法引用所使用方法的入参和返回值与lambda表达式实现的函数式接口的入参和返回值一致
2、 格式:
使用操作符 “::”将类(或对象)与方法名分割开来
具体分为如下的四种情况:
类型 | 语法 | 对应的Lambda表达式 |
---|---|---|
静态方法引用 | 类名::staticMethod | () -> 类名.staticMethod() |
实例方法引用 | 类名::instMethod | (inst) -> inst.instMethod() |
带参方法引用 | 类名::instMethod | (inst,args) -> inst.instMethod(args) |
构建方法引用 | 类名::new | () -> new 类名() |
方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖
示例Person类
package com.woniuxy.lambda;
/**
* @author :fengsir
* @date :Created in 2023/02/7 10:09
* @description:TODO
* @modified By:
* @version: 1.0
*/
public class Person {
private int id;
private String name;
private int age;
public Person() {
}
public Person(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public static void testStatic() {
System.out.println("static");
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
下面的示例围绕着调用Person中的静态方法和非静态方法等展开
3、 类名::静态方法名
函数接口:
package com.it;
public interface FunctionInterface {
public void testLamdba();
}
实现类:
public class Test {
public static void main(String[] args) {
//Lamdba方式调用: 相当于在匿名内部实现中调用了Person的testStatic()
FunctionInterface fi = () -> Person.testStatic();
//方法引用的方式调用
FunctionInterface fi = Person::testStatic;
fi.testLamdba();
}
}
调用带参的方法参考示例3.7,需要在接口上定义参数,比较麻烦,不要为了Lamdba而Lamdba
4、 类名::无参实例方法名
函数接口:
调用Person类中的getName方法,返回String,注意参数里需要传入Person对象
public interface FunctionInterface {
public String testLamdba(Person person);
}
实现类:
public class Test {
public static void main(String[] args) {
//Lamdba方式调用
FunctionInterface fi = (person) -> person.getName();
//方法引用方式
FunctionInterface fi = Person::getName;
String result = fi.testLamdba(new Person(1, "tom", 20));
System.out.println(result);
}
}
5、 类名::带参实例方法名
函数接口:
要调用带参的方法,在函数接口中即要定义传入的对象,又要传参数
public interface FunctionInterface {
public void testLamdba(Person person,String name);
}
实现类:
public class Test {
public static void main(String[] args) {
//Lamdba方式调用
FunctionInterface fi = (person,name) -> person.setName(name);
//方法引用方式
FunctionInterface fi = Person::setName;
Person person = new Person();
fi.testLamdba(person,"tom");
System.out.println(person.getName());
}
}
表达式的第一个入参为实例方法的调用者,后面的入参与实例方法的入参一致
6、 构造器引用类名::new
函数接口:
期望调用方法返回一个对象
public interface FunctionInterface {
public Person testLamdba();
}
实现类:
public class Test {
public static void main(String[] args) {
//Lamdba方式调用
//FunctionInterface fi = () -> new Person();
//方法引用方式
FunctionInterface fi = Person::new;
Person person = fi.testLamdba();
System.out.println(person);
}
}
方法引用并不适合创建带参的构造方法,不要为了方法引用而方法引用
四、Stream
在实际Java程序中,集合的使用往往随着业务需求、复杂度而变得更加复杂,在这其中将可能会涉及到更多的运算,如:求和、平均值、分组、过滤、排序等等。如何这些操作混合出现,又该如何实现?难道遍历、再遍历、再运算么?
Stream是自JDK1.8 版本后Java API中的新成员,它允许你以声明的方式处理数据集合,你可以把它看成是遍历数据集的高级迭代器,通过声明方式,对集合中的每个元素进行一系列并行或串行的流水线操作。此外,Stream还可以透明地并行处理,而无需写任何多线程代码了。
整个流操作就是一条流水线,将元素放在流水线上一个个地进行处理
1、 Stream操作
Stream操作分为三步:
- 创建操作符
- 中间操作符
- 终止操作符
4.1、创建操作符
- 通过Collection对象的stream()或parallelStream()方法。
- 通过Arrays类的stream()方法。
- 通过Stream接口的of()、iterate()、generate()方法。
- 通过IntStream、LongStream、DoubleStream接口中的of、range、rangeClosed方法
4.2、 中间操作符
中间操作符在执行指定处理逻辑后,数据流依然可以传递给下一级的操作符。中间操作符包含8种:
-
map(mapToInt,mapToLong,mapToDouble) 转换操作:把比如A->B,这里默认提供了转int,long,double的操作符。
-
flatmap(flatmapToInt,flatmapToLong,flatmapToDouble)拍平操作:比如把int[]{2,3,4}拍平变成 2,3,4,即从原来的一个数据变成了3个数据,这里默认提供了拍平成int,long,double的操作。
-
limit限流操作:比如数据流中有10个,我只要前3个就可以使用。
-
distinct去重操作:重复元素去重。
-
filter过滤操作:对集合数据进行过滤。
-
peek消费操作:如果想对数据进行某些操作,如:读取、编辑修改等。
-
skip跳过操作:跳过某些元素。
-
sorted排序操作:对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。
(具体可参照源码java.util.stream.Stream)
4.3、 终止操作符
终止操作符就是用来对数据进行收集或者消费的,数据到了终止操作这里就不会向下流动了,终止操作符只能使用一次。
- collect收集操作:将所有数据收集起来,这个操作非常重要,官方的提供的Collectors 提供了非常多收集器,可以说Stream的核心在于Collectors。
- count统计操作:统计最终的数据个数。
- findFirst、findAny查找操作:查找第一个、查找任何一个,返回的类型为Optional。
- noneMatch、allMatch、anyMatch匹配操作:数据流中是否存在符合条件的元素,返回值为bool 值。
- min、max最值操作:需要自定义比较器,返回数据流中最大、最小的值。
- reduce规约操作:将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。
- forEach、forEachOrdered遍历操作:这里就是对最终的数据进行消费了。
- toArray数组操作:将数据流的元素转换成数组。
2、 示例
2.1、 传统方式实现查找和排序
实现筛选出名字中包含“xc”字符串的人,并按照其年龄进行排序。
List<People> peoples = new ArrayList<>();
// 遍历 + 判断
for (People people : allPeoples) {
if (people.getName().contains("xc")) {
peoples.add(people);
}
}
// 对年龄排序
Collections.sort(peoples, new Comparator<People>() {
@Override
public int compare(People p1, People p2) {
return Integer.compare(p1.getAge(), p2.getAge());
}
});
2.2、 Stream方式
List<People> peoples2 = allPeoples.stream() //创建Stream流
.filter(people -> people.getName().contains("xc")) //中间操作
.sorted(Comparator.comparing(People::getAge)) // 中间操作
.collect(Collectors.toList()); //终止流
2.3、 通过forEach来终止流
list.stream() //创建流
.filter(person -> person.getName().contains("e")) //中间操作
.sorted(Comparator.comparing(Person::getAge)) //中间操作
.forEach(person -> System.out.println(person)); //结束流
2.4、Stream方式有以下显而易见的好处:
- 代码以声明方式写的:说明想要完成什么(筛选出满足条件的数据)而不是说明如何实现一个操作(利用循环和if条件等控制流语句)。
- 多个基本操作链接起来:将多个基础操作链接起来,来表达复杂的数据处理流水线(如下图),同时体现了代码的清晰、可读性。
2.5、集合操作
参考1.8API中关于Iterable的forEach方法
import java.util.Arrays;
import java.util.List;
public class LambdaExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("Apple", "Banana", "Orange");
// 使用Lambda表达式迭代集合
list.forEach(item -> System.out.println(item));
// 如果只是简单的操作,还可以进一步简化Lambda表达式
list.forEach(System.out::println);
}
}
————————————————-以下内容自行查看————————————————–—
4.3 实战演练(了解)
Stream的一系列操作,必须要使用终止操作符,否则整个数据流是不会执行起来的。
4.3.1. map
转换、映射操作,将元素转换成其他形式或提取一些信息。
比如,从People集合中获取所有人的年龄:
allPeoples.stream()
.map(People::getAge)
.forEach(System.out::println);
4.3.2. flatmap
将元素拍平拍扁 ,将拍扁的元素重新组成Stream,并将这些Stream 串行合并成一条Stream。
比如,带有-字符的字符串进行拆分,并输出:
Stream.of("x-c-b-e-y-o-n-d","a-b-c-d")
.flatMap(m -> Stream.of(m.split("-")))
.forEach(System.out::println);
输出:
x
c
b
e
y
o
n
d
a
b
c
d
4.3.3. limit
限流操作,限制集合元素的个数。
比如,集合中有10个元素,我只要前4个就可以使用:
Stream.of(1,2,3,4,5,6,7,8,9,10)
.limit(4)
.forEach(System.out::println);
输出:
1
2
3
4
4.3.4. distint
去重操作,重复元素去重,类似数据库中的关键字distinct
。
比如,集合中可能存在一些重复的数据,需要进行去重操作:
Stream.of("xcbeyond","Niki","Liky","xcbeyond")
.distinct()
.forEach(System.out::println);
输出:
xcbeyond
Niki
Liky
4.3.5. filter
过滤、筛选,对某些元素进行过滤,不符合筛选条件的将无法进入流的下游。
比如,筛选出一个数字集合中的所有偶数:
Stream.of(1,2,3,4,5,6,7,8,9,10)
.filter(n -> 0 == n%2)
.forEach(System.out::println);
输出:
2
4
6
8
10
4.3.6. peek
消费操作,如果想对数据进行某些操作,如:读取、编辑修改等。
比如,将People集合中name统一修改为name+age的形式:
allPeoples.stream()
.peek(people -> people.setName(people.getName() + people.getAge()))
.forEach(people -> System.out.println(people.getName()));
4.3.7. skip
跳过操作,跳过某些元素。
比如,一个数字集合,跳过前4个元素:
Stream.of(1,2,3,4,5,6,7,8,9,10)
.skip(4)
.forEach(System.out::println);
输出:
5
6
7
8
9
10
4.3.8. sorted
排序操作,对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。
比如,将People集合按照年龄排序:
allPeoples.stream()
.sorted(Comparator.comparing(People::getAge))
.forEach(System.out::println);
4.3.9. collect
收集操作,终止操作符,用于将最终的数据收集到新的集合中,如,List、Set、Map等集合。
比如,将People集合按照年龄排序,并存放在一个新的集合中,供后续使用:
List<People> sortedPeople = allPeoples.stream()
.sorted(Comparator.comparing(People::getAge))
.collect(Collectors.toList());
4.3.10. count
统计操作,用于对集合元素个数的统计,返回类型是long。
比如,计算People集合中年龄大于30的人数:
long count = allPeoples.stream()
.filter(people -> people.getAge() > 30)
.count();
4.3.11. findFirst、findAny
查找操作,查找第一个、任何一个,返回的类型为Optional
。常用于查询集中符合条件的元素,并结合Optional.isPresent()
进行判断,防止出现未找到而强制获取数据元素的异常情况。
比如,查找People集合中名字为xcbeyond的人:
People xcbeyondPeople = null;
Optional<People> optional = allPeoples.stream()
.filter(people -> "xcbeyond".equals(people.getName()))
.findFirst();
if (optional.isPresent()) {
xcbeyondPeople = optional.get();
}
4.3.12. noneMatch、allMatch、anyMatch
匹配操作,判断数据流中是否存在符合条件的元素,返回值为boolean
值。
- noneMatch:没有匹配条件的元素
- allMatch、anyMatch:全匹配
比如,判断People集合中是否有名字为xcbeyond的人:
boolean bool = allPeoples.stream()
.allMatch(people -> "xcbeyond".equals(people.getName()));
4.3.13. min、max
最值操作,根据自定义比较器,返回数据流中最大、最小的元素。
比如,找到People集合中最大年龄的人:
People maxAgePeople = null;
Optional<People> maxAgeOptional = allPeoples.stream()
.max(Comparator.comparing(People::getAge));
if (maxAgeOptional.isPresent()) { // 可能没有,则需要进行判断
maxAgePeople = maxAgeOptional.get();
}
4.3.14. reduce
规约操作:将整个数据流的值规约为一个值,其中count、min、max底层就是使用reduce。
比如,对一个整数集合进行求和:
int sum = Stream.of(1,9,8,4,5,6,-1)
.reduce(0,(e1,e2)->e1+e2);
System.out.println(sum);
输出:
32
五、 Date Time API
1、 为什么我们需要新的Java日期/时间API?
1.8之前JDK自带的日期处理类非常不方便,我们处理的时候经常是使用的第三方工具包,比如commons-lang包等。不过1.8出现之后这个改观了很多,比如日期时间的创建、比较、调整、格式化、时间间隔等。这些类都在java.time包下。比原来实用了很多。
2、Java8日期/时间API的特点
不变性:新的日期/时间API中,所有的类都是不可变的,这对多线程环境有好处。
关注点分离:新的API将人可读的日期时间和机器时间(unix timestamp)明确分离,它为日期(Date)、时间(Time)、日期时间(DateTime)、时间戳(unix timestamp)以及时区定义了不同的类。
清晰:在所有的类中,方法都被明确定义用以完成相同的行为。举个例子,要拿到当前实例我们可以使用now()方法,在所有的类中都定义了format()和parse()方法,而不是像以前那样专门有一个独立的类。为了更好的处理问题,所有的类都使用了工厂模式和策略模式,一旦你使用了其中某个类的方法,与其他类协同工作并不困难。
实用操作:所有新的日期/时间API类都实现了一系列方法用以完成通用的任务,如:加、减、格式化、解析、从日期/时间中提取单独部分等。
3、 Java日期/时间API包
Java日期/时间API包含以下相应的包:
java.time包:这是新的Java日期/时间API的基础包,所有的主要基础类都是这个包的一部分,如:LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration等等。所有这些类都是不可变的和线程安全的,在绝大多数情况下,这些类能够有效地处理一些公共的需求。
java.time.chrono包:这个包为非ISO的日历系统定义了一些泛化的API,我们可以扩展AbstractChronology类来创建自己的日历系统。
java.time.format包:这个包包含能够格式化和解析日期时间对象的类,在绝大多数情况下,我们不应该直接使用它们,因为java.time包中相应的类已经提供了格式化和解析的方法。
java.time.temporal包:这个包包含一些时态对象,我们可以用其找出关于日期/时间对象的某个特定日期或时间,比如说,可以找到某月的第一天或最后一天。你可以非常容易地认出这些方法,因为它们都具有“withXXX”的格式。
java.time.zone包:这个包包含支持不同时区以及相关规则的类
4、 示例:
4.1、 LocalDate
是一个不可变的类,它表示默认格式(yyyy-MM-dd)的日期
import java.time.*;
public class LocalDateDemo {
/**
* LocalDate是一个不可变的类,它表示默认格式(yyyy-MM-dd)的日期,
* @param args
*/
public static void main(String[] args) {
// 当前日期yyyy-MM-dd
LocalDate localDate1 = LocalDate.now();
System.out.println(localDate1);
// 创建一个日期
LocalDate localDate2 = LocalDate.of(2017, 10, 17);
System.out.println(localDate2);
// 获取指定时区的当前时间
LocalDate localDate3 = LocalDate.now(ZoneId.of("Asia/Kolkata"));
System.out.println(localDate3);
// 格林威治时间+天数
//默认获取的是以UTC时区,世界协调时间,为基础
LocalDate localDate4 = LocalDate.ofEpochDay(365);
System.out.println(localDate4);
// 某年的第几天的日期
LocalDate localDate5 = LocalDate.ofYearDay(2017, 200);
System.out.println(localDate5);
}
}
4.2、 LocalTime
是一个不可变的类,它的实例代表一个符合人类可读格式的时间,默认格式是hh:mm:ss.zzz。
import java.time.LocalTime;
import java.time.ZoneId;
public class LocalTimes {
/**
* LocalTime是一个不可变的类,它的实例代表一个符合人类可读格式的时间,默认格式是hh:mm:ss.zzz。
* @param args
*/
public static void main(String[] args) {
//当前时间
LocalTime time = LocalTime.now();
System.out.println("Current Time="+time);
// 创建当前时间
LocalTime specificTime = LocalTime.of(12,20,25,40);
System.out.println("Specific Time of Day="+specificTime);
// 获取指定时区当前时间
LocalTime timeKolkata = LocalTime.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Current Time in IST="+timeKolkata);
// 当天多少秒的时间
LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000);
System.out.println("10000th second time= "+specificSecondTime);
}
}
4.3、 LocalDateTime
是一个不可变的日期-时间对象,它表示一组日期-时间,默认格式是yyyy-MM-dd-HH-mm-ss.zzz。它提供了一个工厂方法,接收LocalDate和LocalTime输入参数,创建LocalDateTime实例。
import java.time.*;
public class LocalDateTimes {
/**
* LocalDateTime是一个不可变的日期-时间对象,它表示一组日期-时间,默认格式是yyyy-MM-dd-HH-mm-ss.zzz。
* 它提供了一个工厂方法,接收LocalDate和LocalTime输入参数,创建LocalDateTime实例。
* @param args
*/
public static void main(String[] args) {
// 当前日期时间
LocalDateTime today = LocalDateTime.now();
System.out.println("Current DateTime="+today);
// 当前日期时间
today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
System.out.println("Current DateTime="+today);
// 指定时间日期时间
LocalDateTime specificDate = LocalDateTime.of(2014, Month.JANUARY, 1, 10, 10, 30);
System.out.println("Specific Date="+specificDate);
// 当前指定时区日期时间
LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Current Date in IST="+todayKolkata);
// 格林威治后多少分钟的日期时间
LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
System.out.println("10000th second time from 01/01/1970= "+dateFromBase);
}
}
4.4、 Instant类
是用在机器可读的时间格式上的,它以Unix时间戳的形式存储日期时间。
import java.time.Duration;
import java.time.Instant;
public class instants {
/**
* Instant类是用在机器可读的时间格式上的,它以Unix时间戳的形式存储日期时间。
* @param args
*/
public static void main(String[] args) {
// 当前时间戳
Instant timestamp = Instant.now();
System.out.println("Current Timestamp = "+timestamp); // Current Timestamp = 2018-06-26T15:54:21.310Z
Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli()); // Specific Time = 2018-06-26T15:54:21.310Z
System.out.println("Specific Time = "+specificTime);
Duration thirtyDay = Duration.ofDays(30);
System.out.println(thirtyDay); // PT720H
}
}
4.5、 日期运算
大多数日期/时间API类都实现了一系列工具方法,如:加/减天数、周数、月份数等。还有其他的工具方法能够使用TemporalAdjuster调整日期,并计算两个日期间的周期。
import java.time.*;
public class DateUtils {
/**
* 大多数日期/时间API类都实现了一系列工具方法,如:加/减天数、周数、月份数,等等。
* 还有其他的工具方法能够使用TemporalAdjuster调整日期,并计算两个日期间的周期。
* @param args
*/
public static void main(String[] args) {
// 当前日期时间,UTC(世界协调时间格式)
LocalDateTime today = LocalDateTime.now();
System.out.println("Current DateTime="+today);
// 当前日期时间
today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
System.out.println("Current DateTime="+today);
// 指定时间日期时间
LocalDateTime specificDate = LocalDateTime.of(2014, Month.JANUARY, 1, 10, 10, 30);
System.out.println("Specific Date="+specificDate);
// 当前指定时区日期时间
LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Current Date in IST="+todayKolkata);
// 格林威治后多少分钟的日期时间
LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
System.out.println("10000th second time from 01/01/1970= "+dateFromBase);
}
}
4.6、 旧的日期时间支持
旧的日期/时间类已经在几乎所有的应用程序中使用,因此做到向下兼容是必须的。这也是为什么会有若干工具方法帮助我们将旧的类转换为新的类,反之亦然。
public static void main(String[] args) {
//Date to Instant
Instant timestamp = new Date().toInstant();
//Now we can convert Instant to LocalDateTime or other similar classes
LocalDateTime date = LocalDateTime.ofInstant(timestamp,
ZoneId.of(ZoneId.SHORT_IDS.get("PST")));
System.out.println("Date = "+date);
//Calendar to Instant
Instant time = Calendar.getInstance().toInstant();
System.out.println(time);
//TimeZone to ZoneId
ZoneId defaultZone = TimeZone.getDefault().toZoneId();
System.out.println(defaultZone);
//ZonedDateTime from specific Calendar
ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();
System.out.println(gregorianCalendarDateTime);
//Date API to Legacy classes
Date dt = Date.from(Instant.now());
System.out.println(dt);
TimeZone tz = TimeZone.getTimeZone(defaultZone);
System.out.println(tz);
GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);
System.out.println(gc);
}
4.7、 Date与LocalDateTime、LocalDate、LocalTime互转
// 01. java.util.Date --> java.time.LocalDateTime
public void dateToLocalDateTime() {
java.util.Date date = new java.util.Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
}
// 02. java.util.Date --> java.time.LocalDate
public void dateToLocalDate() {
java.util.Date date = new java.util.Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
LocalDate localDate = localDateTime.toLocalDate();
}
// 03. java.util.Date --> java.time.LocalTime
public void dateToLocalTime() {
java.util.Date date = new java.util.Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
LocalTime localTime = localDateTime.toLocalTime();
}
// 04. java.time.LocalDateTime --> java.util.Date
public void localDateTimeToUdate() {
LocalDateTime localDateTime = LocalDateTime.now();
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zone).toInstant();
java.util.Date date = Date.from(instant);
}
// 05. java.time.LocalDate --> java.util.Date
public void localDateToUdate() {
LocalDate localDate = LocalDate.now();
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
java.util.Date date = Date.from(instant);
}
// 06. java.time.LocalTime --> java.util.Date
public void localTimeToUdate() {
LocalTime localTime = LocalTime.now();
LocalDate localDate = LocalDate.now();
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zone).toInstant();
java.util.Date date = Date.from(instant);
}
本文来自博客园,作者:icui4cu,转载请注明原文链接:https://www.cnblogs.com/icui4cu/p/18854963