Lambda
对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个 lambda 表达式。这种接口称为函数式接口。
在 java.util.function 包中定义了很多通用的函数式接口。
// 函数式接口可以用 @FunctionalInterface 注解,如果接口中声明了不只一个抽象方法会报错。
java.util.function 下几大常用的接口
Consumer<T> // 消费性接口
void accept(T t);
Supplier<T> // 供给型接口
T get();
Function<T, R> // 函数型接口
R apply(T t);
Predicate<T> // 断言型接口
boolean test(T t);
语法
public void test1() {
Runnable runnable = new Runnable() { // Runnable接口中只有一个抽象方法
@Override
public void run() { System.out.println("run"); }
};
runnable.run();
// 可以简化为下面这种写法
Runnable runnable = () -> System.out.println("run1"); // 这是lambda表达式
runnable.run();
}
public void test2() {
Consumer<String> consumer = new Consumer<String>() { // Consumer是函数式接口
@Override
public void accept(String s) { System.out.println(s); }
};
consumer.accept("consumer1");
// 简化一下
Consumer<String> c = (s) -> System.out.println(s); // 这里的 s 没有任何用处
c.accept("hello");
// 所以再次简化
Consumer<String> consumer = System.out::println;
consumer.accept("consumer2");
}
// interface Hah {
// void hello(String name);
// void hi(String you);
// }
public void test3() {
Hah hah = new Hah() { // Hah中声明了两个抽象方法,所以不可以简化为lambda表达式
@Override
public void hello(String name) { System.out.println(name); }
@Override
public void hi(String you) { System.out.println(you); }
};
}
方法引用
对象::实例方法
class Test {
public void jump() {
// 注意: System.out::println 方法的返回类型与 Consumer 中的抽象方法的返回类型要可以对接
// 在这里,即:public void println(String x); 和 void accept(T t); 返回类型都是 void
Consumer<String> consumer2 = System.out::println; // 方法引用
consumer2.accept("hello");
}
}
类::静态方法
public void test2() {
Integer[] integers = new Integer[]{23, 32, 8, 90};
Arrays.sort(integers, Integer::compare); // compare 是一个静态方法
for (Integer i : integers) {
System.out.println(i);
}
}
类::实例方法
public void test3() {
BiPredicate<String, String> b = (x, y) -> x.equals(y);
BiPredicate<String, String> b2 = String::equals; // 类为什么可以直接调用方法,就这么规定可以的
}
类::实例方法
参考如下
https://blog.csdn.net/weixin_41126303/article/details/81187002
https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
你可能会对这种调用感到奇怪,其实这种使用有下面两个限制
第一点:接口方法的参数要比引用方法的参数多一个
第二点:接口方法第一个参数是调用方法所在类或其子类
class Love {
public boolean is(Love love){ // 实例方法:只有一个参数
return true;
}
}
@FunctionalInterface
public interface BiPredicate<T, U> {
boolean test(T t, U u); // 有两个参数(参数多一个)
}
// 上面俩,返回类型一样,都是boolean
// 调用如下
BiPredicate<Love, Love> b4 = Love::is;
class Love {
public void say(Integer param1,String param2){
System.out.println("晴天");
}
public static void main(String[] args) {
World world = Love::say;
world.good(new MyLove(), 3, ""); // 晴天
}
}
class MyLove extends Love {} // 子类 MyLove
@FunctionalInterface
interface World {
void good(MyLove myLove, Integer param1, String param2); // 第一个参数:本类或子类,但不能是父类
}
Comparator 案例分析
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
class Love { // 只是一个普通类
public static int you(Love love, Love love1) { // 必须有两个参数
return 0;
}
}
class Test {
public void test1() { // 可以赋值成功
// 调用方法
Comparator<Love> hello = Love::you; // 没有传递这两个参数,(我想有点延迟加载的意思)
}
}
灵感来源于下面的使用
public final class Integer extends Number implements Comparable<Integer> { // 普通的Integer类
public static int compare(int x, int y) { // Integer::compare ,返回类型是 int
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
// ... 还用很多其他方法
}
public class Arrays {
public static <T> void sort(T[] a, Comparator<? super T> c) {}
// Comparator是一个函数式接口,要2个参数,方法的返回类型是 int
}
class Test {
public void test1() {
Integer[] integers = new Integer[]{23, 32, 8, 90};
Arrays.sort(integers, Integer::compare); // 这里Integer::compare方法有两个参数,但是没传递
}
}
构造器引用
Function<T, R> // 函数型接口
R apply(T t);
class Employee {}
public void test() {
Function<Integer, Employee[]> function = Employee[]::new; // 因为类中只有无参构造器,会调用无参构造器
Employee[] apply = function.apply(3);
System.out.println(apply.length);
}
public void test() {
Function<Integer, Double[]> function = Double[]::new; // 数组也可以这样使用
Double[] apply = function.apply(3);
System.out.println(apply.length);
}
变量作用域
// 1、lambda表达式中不能引用可以在外部改变的变量
for (int i = 0; i < 3; i++) {
ActionListener ac = e -> System.out.println(i); // ERROR: 这里 i 会在外部变化
}
// 2、不能改变捕获的变量,如果改变在并发中不安全
public void coi(boolean life) { // 传入的参数 life
Consumer<String> consumer = (w) -> {
life = false; // ERROR: 不能改变传入的参数
};
}
总结:lambda表达式捕获的变量(如:i life)必须是事实最终变量。
事实最终变量:指这个变量初始化后就不再为它赋新值。
// lambda表达式的体与嵌套块有相同的作用域,所以不可以声明与方法内相同的变量。
// this会使用Person对象的toString()方法,而不是say()实例的方法
class Person {
public void say() {
ActionListener ac = e -> System.out.println(this.toString());
}
public String toString(){}
}
Stream API
Stream API 使用的三个步骤
创建 Stream
一个数据源,如:集合、数组
中间操作
对数据源的数据进行处理
终止操作
执行中间操作链,并产生结果
创建Stream
// 创建流
public void createStream() {
// Java 8 中的 Collection 接口被扩展,提供了两个获取流的方法
// default Stream<E> parallelStream() // 返回一个并行流
// default Stream<E> stream() // 返回一个顺序流
List<String> list = new ArrayList<>(); // public interface List<E> extends Collection<E>
Stream<String> stream1 = list.stream();
// 通过Arrays中的静态方法stream()获取数组流
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 + 2);
stream4.forEach(System.out::println); // 终止操作
// 创建流
Stream<Double> generate = Stream.generate(Math::random);
}
中间操作
多个中间操作连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为“惰性求值”。
筛选 / 切片
filter:接收 Lambda ,从流中排除某些元素limit:截断流,使其元素不超过给定数量skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补distinct:筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素
enum Status {
FREE, BUSY, VOCATION;
}
List<Employee> employees = Arrays.asList(
new Employee("张三", 12, 333.33, Status.FREE),
new Employee("李四", 89, 444.44, Status.BUSY),
new Employee("王五", 34, 555.55, Status.VOCATION),
new Employee("赵六", 54, 666.66, Status.FREE),
new Employee("田七", 66, 777.77, Status.BUSY)
);
@Test
public void test01(){
employees.stream()
.filter(x -> x.getAge() > 50)
.limit(3) //短路?达到满足不再内部迭代
.distinct() // 去重,hashCode() 与 equals()
.skip(1) // 舍弃满足条件的前 n 个元素
.forEach(System.out::println);
}
@Test
public void test3() {
employees.stream()
.filter(employee -> {
int i = 0;
System.out.println("短路" + ++i);
return employee.getAge() > 50;
})
.limit(2) // 迭代操作只执行了 4 次,打印出满足条件的之后就不再迭代了
.forEach(System.out::println);
/*短路1
短路1
Employee{name='李四', age=89, salary=444.44}
短路1
短路1
Employee{name='赵六', age=54, salary=666.66}*/
}
映射
- map:接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
- flatMap:接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流
public void test4() { // map
List<String> list = Arrays.asList("aa", "ab", "ac");
list.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
}
public static Stream<Character> getChar(String s) {
List<Character> list = new ArrayList<>();
for (Character c : s.toCharArray()) {
list.add(c);
}
return list.stream();
}
@Test
public void test5() {
List<String> list = Arrays.asList("aa", "ab", "ac");
// Stream<String> stream = list.stream();
// Stream<Character> getC = getChar("");
Stream<Stream<Character>> streamStream = list.stream()
.map(StreamAPI::getChar); // StreamAPI是getChar所在的类名
streamStream.forEach(s -> s.forEach(System.out::println));
// System.out.println("-----------------------------------------------");
list.stream()
.flatMap(StreamAPI::getChar) // flatMap,直接转化为了最终结果
.forEach(System.out::println);
}
排序
- sorted():自然排序 ( Comparable )
- sorted(Comparator c):定制排序 ( Comparator )
public void test(){
List<Integer> list = Arrays.asList(1,2,3,4,5);
list.stream()
.sorted()
.forEach(System.out::println);
}
public void test(){
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);
}
终止操作
查找 / 匹配
- allMatch:检查是否匹配所有元素
- anyMatch:检查是否至少匹配一个元素
- noneMatch:检查是否没有匹配所有元素
- findFirst:返回第一个元素
- findAny:返回当前流中的任意元素
- count:返回流中元素的总个数
- max:返回流中最大值
- min:返回流中最小值
public void test6() {
Boolean b1 = emps.stream()
.allMatch(employee -> employee.getStatus() == Status.BUSY);
System.out.println(b1);
Boolean b2 = emps.stream()
.anyMatch(employee -> employee.getStatus() == Status.BUSY);
System.out.println(b2);
Boolean b3 = emps.stream()
.noneMatch(employee -> employee.getStatus() == Status.BUSY);
System.out.println(b3);
// Java 8 为了尽可能避免空指针异常,把对象封装到容器里,就可以使用容器中的方法避免问题
Optional<Employee> first = emps.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
System.out.println(first.get());
Optional<Employee> any = emps.stream() // .parallelStream() // 启用多线程查找
.filter(e -> e.getStatus() == Status.FREE)
.findAny(); // 随便获取一个 Status.FREE
System.out.println(any.get());
long count = emps.stream()
.count();
System.out.println(count); // 获取数量
Optional<Employee> max = emps.stream()
.max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(max.get()); // 获取最大工资
Optional<Double> min = emps.stream()
.map(Employee::getSalary)
.min(Double::compare);
System.out.println(min.get()); // 获取最小的工资
// 获取每个字符串然后计数
// Complaint::getMatter 获取一个一个的字符串
Map<String, Long> map = list.stream().map(Complaint::getMatter)
.collect(Collectors.groupingBy(String::toString, Collectors.counting()));
}
归约 / 收集
- 归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
- 收集:collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
public void test7() {
List<Integer> list = Arrays.asList(2, 3, 6, 8, 9);
Integer reduce = list.stream()
.reduce(100, (x, y) -> x + y); // 把 100和所有数相加
System.out.println(reduce);
Optional<Double> reduce1 = emps.stream() // map-reduce
.map(Employee::getSalary) // map就是收集其中的某些信息,如工资
.reduce(Double::sum); // reduce就是把收集到的信息进行处理,如相加
System.out.println(reduce1.get());
List<String> collect = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList()); // 对得到的信息进行加工
collect.forEach(System.out::println);
System.out.println("----");
Set<String> collect1 = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
collect1.forEach(System.out::println);
System.out.println("----");
HashSet<String> collect2 = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new)); // 比如收集到自定义集合
collect2.forEach(System.out::println);
}
public void test8() {
// 对象总和
Long sum = emps.stream()
.collect(Collectors.counting());
System.out.println(sum);
// 工资平均
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
// 工资总和
Double sumSalary = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sumSalary);
// 分组
Map<Status, List<Employee>> group = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(group); // Map没有ForEach
// 多级分组
Map<Status, Map<String, List<Employee>>> mapMap = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(e -> {
if (e.getAge() <= 35)
return "青年";
else if (e.getAge() <= 60)
return "中年";
return "老年";
})));
System.out.println(mapMap);
// 分区
Map<Boolean, List<Employee>> partition = emps.stream()
.collect(Collectors.partitioningBy(employee -> employee.getSalary() > 500));
System.out.println(partition);
}
并行流与顺序流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流
Java 8 中将并行流进行了优化,我们可以很容易地对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。
Fork / Join 框架
Fork / Join框架:就是在必要地时候,将一个大任务进行拆分 (fork) 成若干个小任务(拆到不可再拆时),再将小任务的结果进行 join 汇总。
Fork/Join框架与传统线程池的区别
采用“工作窃取“模式(work-stealing) :当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。
相对于一般的线程池实现, fork / join框架的优势体现在对其中包含的任务的处理方式上。在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态。而在fork / join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行。这种方式减少了线程的等待时间,提高了性能。
public class ForkJoinCalculate extends RecursiveTask<Long> {
private final long start;
private final long end;
private static final long THRESHPLD = 10_000L;
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;
}
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();
}
}
}
class ForkJoin {
// 1、普通的处理方法
@Test
public void test2() {
Instant start = Instant.now();
long sum = 0;
for (long i = 0; i < 10_000_000_000L; i++) {
sum += i;
}
Instant end = Instant.now();
System.out.println(Duration.between(start, end).toMillis());
}
// 2、ForkJoin 框架
@Test
public void test1() {
Instant start = Instant.now();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinCalculate(0, 10_000_000_000L);
Long sum = pool.invoke(task);
System.out.println(sum);
Instant end = Instant.now();
System.out.println(Duration.between(start, end).toMillis());
}
// 3、Java 8 并行流
@Test
public void test3() {
Instant start = Instant.now();
//串行流(单线程):切换为并行流 parallel()
//并行流:切换为串行流 sequential()
LongStream.rangeClosed(0, 10_000_000_000L)
.parallel() //底层:ForkJoin
.reduce(0, Long::sum);
Instant end = Instant.now();
System.out.println(Duration.between(start, end).toMillis());
}
}
Optional 类
定义:Optionaljava.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):如果调用对象包含值,返回该值,否则返回 torElseGet(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();
}
}
Collector
public interface Collector<T, A, R> { ... }
public final class Collectors { ... }
日期、时间
旧的日期时间
import org.junit.jupiter.api.Test;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
class OldDate {
private Callable<Date> task;
// 存在线程安全问题
public void haveProblem() throws ExecutionException, InterruptedException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
task = new Callable<Date>() { // 这里可以转为 Lambda表达式
@Override
public Date call() throws Exception {
return sdf.parse("20030623");
}
};
oldDate();
}
// 用锁
public void solveProblem() throws ExecutionException, InterruptedException {
task = () -> { // lambda 表达式怎么抛异常
return DateFormatThreadLocal.covert("20030623");
};
oldDate();
}
// 公共函数
public void oldDate() throws ExecutionException, InterruptedException {
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();
}
}
class DateFormatThreadLocal { // 多线程异常,用锁
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyyMMdd");
}
};
public static Date covert(String source) throws ParseException {
return df.get().parse(source);
}
}
java 8 的日期时间
java.time 包下
本地日期
使用 LocalDate LocalTime LocalDateTime
这些类的实例是不可变的对象
@Test
public void test1() {
//获取当前时间日期 now
LocalDateTime ldt1 = LocalDateTime.now();
System.out.println(ldt1); // 2021-03-31T17:02:47.289
//指定时间日期 of
LocalDateTime ldt2 = LocalDateTime.of(2020, 5, 17, 16, 24, 33);
System.out.println(ldt2); // 2020-05-17T16:24:33
//加 plus
LocalDateTime ldt3 = ldt2.plusYears(2);
System.out.println(ldt3); // 2022-05-17T16:24:33
//减 minus
LocalDateTime ldt4 = ldt2.minusMonths(3);
System.out.println(ldt4); // 2020-02-17T16:24:33
//获取指定的你年月日时分秒... get
System.out.println(ldt2.getDayOfYear()); // 138
System.out.println(ldt2.getHour()); // 16
System.out.println(ldt2.getSecond()); // 33
}
@Test
public void test02(){
// 默认获取 UTC 时区 (UTC:世界协调时间)
Instant ins1 = Instant.now();
System.out.println(ins1); // 2021-01-30T07:50:48.451Z
//带偏移量的时间日期 (如:UTC + 8)
OffsetDateTime odt1 = ins1.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt1); // 2021-01-30T15:50:48.451+08:00
//转换成对应的毫秒值
long milli1 = ins1.toEpochMilli();
System.out.println(milli1); // 1611993048451
//构建时间戳
Instant ins2 = Instant.ofEpochSecond(1000); // 就是计算机时间加 1000秒
System.out.println(ins2); // 1970-01-01T00:16:40Z
}
- Duration:计算两个时间之间的间隔
- Period:计算两个日期之间的间隔
@Test // 时间间隔
public void test03() throws InterruptedException {
//计算两个时间之间的间隔 between
Instant ins1 = Instant.now();
Thread.sleep(1000); // 毫秒
Instant ins2 = Instant.now();
Duration dura1 = Duration.between(ins1, ins2);
System.out.println(dura1.getSeconds()); // 1
System.out.println(dura1.toMillis()); // 1003
}
@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()); // 4
System.out.println(period.toTotalMonths()); // 54
}
时间校正器
TemporalAdjuster:时间校正器。如:调整到”下个周日“等
TemporalAdjusters:它通过静态方法提供了大量的常用 TemporalAdjuster的实现。
@Test
public void test01(){
LocalDateTime ldt1 = LocalDateTime.now(); // 获取现在的时间
System.out.println(ldt1); // 2021-03-31T17:12:27.646
// 指定日期时间中的 年 月 日 ...
LocalDateTime ldt2 = ldt1.withDayOfMonth(10);
System.out.println(ldt2); // 2021-03-10T17:12:27.646
// 指定时间校正器
LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); // 下个星期日
System.out.println(ldt3); // 2021-04-04T17:12:27.646
// 自定义时间校正器
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); // 2021-04-01T17:12:27.646
}
时间格式化
@Test
public void test9(){
// 默认格式化
DateTimeFormatter dtf1 = DateTimeFormatter.ISO_DATE_TIME;
LocalDateTime ldt1 = LocalDateTime.now();
String str1 = ldt1.format(dtf1);
System.out.println(str1); // 2021-03-31T17:29:36.498
// 解析
LocalDateTime newDate = LocalDateTime.parse(str1, dtf1);
System.out.println(newDate); // 2021-03-31T17:29:36.498
// 自定义格式化 ofPattern
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime ldt2 = LocalDateTime.now();
String str2 = ldt2.format(dtf2);
System.out.println(str2); // 2021-03-31 17:29:36
// 向人类表示的时间
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
ZonedDateTime zonedDateTime = ZonedDateTime.now(); // 时区时间
String format = formatter.format(zonedDateTime);
System.out.println(format); // // 21-3-31 下午5:44
// FormatStyle.FULL --> 2021年3月31日 星期三 下午05时43分08秒 CST
}
时区时间
API 设计者们不推荐使用时区时间
- ZonedDate
- ZonedTime
- ZonedDateTime
@Test
public void test02(){
//查看支持的时区
Set<String> set = ZoneId.getAvailableZoneIds();
set.forEach(System.out::println);
//指定时区
LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Europe/Tallinn")); // Europe/Tallinn 叫做时区ID
System.out.println(ldt1); // 2021-03-31T11:40:32.454
ldt1 = LocalDateTime.now(ZoneId.of("Asia/Chongqing"));
System.out.println(ldt1); // 2021-03-31T16:40:32.454
System.out.println(set.size()); // 601个时区ID
//在已构建好的日期时间上指定时区
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
}
注解
重复注解与类型注解
浙公网安备 33010602011771号