01.Lambda表达式
Lambda是一个匿名函数,可以理解为一段可以传递的代码(将代码像数据一样传递);可以写出更简洁、更灵活的代码;作为一种更紧凑的代码风格,是Java语言表达能力得到提升。
举个例子:
在还没有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);
}
以上代码实际上只有Integer.compare(o1,o2);是真正产生作用的,其他的只是一些辅助代码,使用Lamda之后去除了辅助代码,使得代码更加简洁。
Lambda实现:
@Test
public void test02(){
// Lambda 表达式
Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
TreeSet<Integer> set = new TreeSet<>(comparator);
}
在 Java 8 里面,所有的 Lambda 的类型都是一个接口,而 Lambda 表达式本身,需要是这个接口的实现。这是理解 Lambda 的一个关键所在,简而言之就是,Lambda 表达式本身就是一个接口的实现。Lambda表达式的使用有一个前提,就是只有是函数式接口才可以使用Lambda表达式,下面有对函数式接口的说明。
基础语法:
中间是一个操作符“->”;
左侧是参数,右侧是方法体;
如:(x,y)->{int z = x+y; return z}
当左侧只有一个参数时候,可以省略();
当右侧只有一行代码时候,可省略{};
右侧只有一行return语句,可以省略return;
语法格式:
- 无参数,无返回值:() -> sout
例如 Runnable接口:
public class Test {
int num = 10; //jdk 1.7以前 必须final修饰
@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("Hello Lambda");
}
- 有一个参数,无返回值 (小括号可以省略不写)
@Test
public void test03(){
Consumer<String> consumer = a -> System.out.println(a);
consumer.accept("Hello Lambda");
}
- 有两个及以上的参数,有返回值,并且 Lambda 体中有多条语句
@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);
}
- Lambda 表达式 参数的数据类型可以省略不写 Jvm可以自动进行 “类型推断”,这在Java编译期间对类型做校验的时候进行,不会有额外的性能损耗。
02.函数式接口
所谓函数式接口就是只有一个抽象方法的接口。
我们可以在接口上添加一个声明 @FunctionalInterface, 这样接口中就只能由一个抽象方法了。
Java 8 API 包含了很多内建的函数式接口,在老 Java 中常用到的比如Comparator或者Runnable接口。
- 定义一个函数式接口:
@FunctionalInterface
public interface MyFun {
Integer count(Integer a, Integer b);
}
- 使用
public void test6() {
MyFun myFun = (a, b) -> a + b;
Integer count = myFun.count(1, 3);
}
Java内置核心函数式接口:
- Comparator(比较器接口)
源代码及使用示例如下:
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
Comparator<Integer> comparator = (x, y)->Integer.compare(x,y);
- Consumer(消费型接口)
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
public void test08(){
Consumer<String> consumer = (x) -> System.out.println(x);
consumer.accept("hello Lambda!");
}
- Supplier(供应型接口)
Supplier 接口是不需要参数并返回一个任意范型的值。Supplier 其实表达的不是从一个参数空间到结果空间的映射能力,而是表达一种生成能力
@FunctionalInterface
public interface Supplier<T> {
T get();
}
public void testSupplier() {
Supplier<Integer> supplier = () -> (int) Math.random() * 10;
Integer num = supplier.get();
}
- Predicate(断言型接口)
Predicate 接口只有一个参数,返回 boolean 类型。
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
public void testPredicate() {
Integer age = 60;
Predicate<Integer> predicate = (i) -> i >= 60;
if(predicate.test(age)){
System.out.println("退休了");
}else{
System.out.println("继续干活");
}
}
- Function(功能型接口)
Function 接口有一个参数并且返回一个结果。
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
public void testFunction() {
String oldStr = "abc123456xyz";
Function<String, String> function = (s) -> s.substring(1, s.length() - 1);
System.out.println(function.apply(oldStr));
}
03.引用
在 Java 8 中,我们可以直接通过方法引用来简写 Lambda 表达式中已经存在的方法。方法引用是一个 Lambda 表达式,其中方法引用的操作符是双冒号::。
语法格式:
对象 :: 实例方法
类 :: 静态方法
类 :: 实例方法
- 对象::实例方法
public void test09(){
PrintStream out = System.out;
Consumer<String> con = s-> out.println(s);
con.accept("aaa");
Consumer<String> consumer = out::println;
consumer.accept("bbb");
}
注意:Lambda 表达实体中调用方法的参数列表、返回类型必须和函数式接口中抽象方法保持一致
- 类 :: 静态方法
public void test10(){
Comparator<Integer> comparator = (x, y)->Integer.compare(x, y);
Comparator<Integer> comparatorNew = Integer::compare;
}
- 类::实例方法
public void test11(){
BiPredicate<String, String> predicate = (x,y) -> x.equals(y);
System.out.println(predicate.test("1", "2"));
BiPredicate<String, String> predicate1 = String :: equals;
predicate1.test("1", "3");
}
条件:Lambda 参数列表中的第一个参数是方法的调用者,第二个参数是方法的参数时,才能使用 ClassName :: Method
- 构造器引用
格式:ClassName :: new
@Test
public void test12(){
Supplier<List> sup1 = () -> new ArrayList();
Supplier<List> sup2 = ArrayList::new;
}
需要调用的构造器的参数列表要与函数时接口中抽象方法的参数列表保持一致。
04.Stream API
流是一种数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据1,流讲的是计算!”
注意:
Stream 自己不会存储元素。
Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。、
特点:
- 内部迭代
可以把它简单的当成一种高级的迭代器(Iterator),或者是高级的 for 循环,区别在于,前面两者都是属于外部迭代,而流采用内部迭代。
采用内部迭代,项目可以透明地并行处理,或者用优化的顺序进行处理,要是使用 Java 过去的外部迭代方法,这些优化都是很困难的。 - 只能遍历一次
和迭代器一样,流只能遍历一次。当流遍历完之后,我们就说这个流已经被消费掉了,你可以从原始数据那里重新获得一条新的流,但是却不允许消费已消费掉的流。例如下面代码就会抛出一个异常,说流已被消费掉了:
List<String> title = Arrays.asList("Wmyskxz", "Is", "Learning", "Java8", "In", "Action");
Stream<String> s = title.stream();
s.forEach(System.out::println);
s.forEach(System.out::println);
// 运行上面程序会报以下错误
/*
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at Test1.main(Tester.java:17)
*/
- 方便的并行处理
只需要加上 .parallel() 就行。
4.1.创建流
/**
* 创建流
*/
@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);
}
4.2.筛选 / 切片
中间操作:
filter:接收 Lambda ,从流中排除某些元素
limit:截断流,使其元素不超过给定数量
skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补
distinct:筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素
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)
);
public void test112{
emps.stream()
.filter(x -> x.getAge() > 30)
.limit(3)
.distinct()
.skip(1)
.forEach(System.out::println);
}
4.3.映射
- map:接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
- flatMap:接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流。
- map:
public void test13(){
emps.stream()
.map(e -> e.getSalary())
.forEach(System.out::println);
}
flatMap:
public static Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for(char c: str.toCharArray()){
list.add(c);
}
return list.stream();
}
@org.junit.Test
public void test14(){
List<String> strList = Arrays.asList("abc", "dfdi", "dfd");
strList.stream()
.flatMap(Test::filterCharacter)
.forEach(System.out::print);
}
4.4.排序
sorted():自然排序
sorted(Comparator c):定制排序
自然排序:
@org.junit.Test
public void test15(){
List<Integer> list = Arrays.asList(5,4,1,3,2);
list.stream()
.sorted()
.forEach(System.out::print);
}
定制排序:先按照年龄排序,再按照名字排序。
public void test16(){
emps.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::print);
}
4.5.查找/匹配
- allMatch:检查是否匹配所有元素
- anyMatch:检查是否至少匹配一个元素
- noneMatch:检查是否没有匹配所有元素
- findFirst:返回第一个元素
- findAny:返回当前流中的任意元素
- count:返回流中元素的总个数
- max:返回流中最大值
- min:返回流中最小值
@org.junit.Test
public void test17(){
boolean b = emps.stream()
.allMatch(e -> e.getAge() > 30);
System.out.println(b);
boolean b1 = emps.stream().anyMatch(e -> "Tom".equals(e.getName()));
System.out.println(b1);
//避免空指针异常
Optional<Employee> first = emps.stream().findFirst();
System.out.println(first.get());
long count = emps.stream().count();
//年龄最大的员工
Optional<Employee> max = emps.stream()
.max((e1, e2)->Integer.compare(e1.getAge(), e2.getAge()));
System.out.println(max.get());
}
4.6.规约/收集
- 归约:reduce(T identity, BinaryOperator) / reduce(BinaryOperator)
可以将流中的数据反复结合起来,得到一个值 - 收集:collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
reduce:
@org.junit.Test
public void test18(){
//从0开始计算,将0作为x,第一个数作为y,接着将x+y当做x,第二个数为y。
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(reduce);
}
collect:
@org.junit.Test
public void test19(){
//将emps中的数据映射到另外一个list
List<NameAndAge> list = emps.stream()
.map(e -> new NameAndAge(e.getName(), e.getAge()))
.collect(Collectors.toList());
list.forEach(System.out::println);
// 放入Set
Set<String> set = emps.stream().map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::print);
//放入List
List<String> list2 = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list2.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);
}
4.7.并行流
- 并行流:就是把一个内容分成几个数据块,并用不同的线程分别处理每个数据块的流
- Java 8 中将并行进行了优化,我们可以很容易的对数据进行操作;Stream API 可以声明性地通过 parallel() 与
sequential() 在并行流与串行流之间切换
Fork / Join框架:
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());
}
}
Java8 并行流:
@Test
public void test03(){
//串行流(单线程):切换为并行流 parallel()
//并行流:切换为串行流 sequential()
LongStream.rangeClosed(0, 100000000L)
.parallel() //底层:ForkJoin
.reduce(0, Long::sum);
}
5.Optional
Optional 类 (java.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):如果调用对象包含值,返回该值,否则返回 t
- orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回 s 获取的值
- map(Function f):如果有值对其处理,并返回处理后的 Optional,否则返回 Optional.empty()
- flatmap(Function mapper):与 map 相似,要求返回值必须是 Optional
只有当 Optional 结合 Lambda 一起使用的时候,才能发挥出其真正的威力!
我们现在就来对比一下下面四种常见的 null 处理中,Java 8 的 Lambda + Optional 和传统 Java 两者之间对于 null 的处理差异。
@org.junit.Test
public void test22(){
//情况一:存在则返回
Optional<Employee> op = Optional.empty();
op.ifPresent(System.out::print);
Employee e = null;
if(e != null){
System.out.println(e);
}
//情况二:存在则返回,无则创建
Employee employee = op.orElse(new Employee());
if(e != null){
employee = e;
}else{
employee = new Employee();
}
//存在则返回,无则由函数产生
employee = op.orElseGet(() -> new Employee());
if(e != null){
employee = e;
}else{
new Employee();
}
// 情况四:夺命连环 null 检查
op.map(m -> m.getSalary())
.map(n -> n*10)
.orElse(null);
if(employee != null){
Double salary = employee.getSalary();
if(salary != null){
salary = salary * 10;
}else{
salary = null;
}
}else{
employee = null;
}
}
由上述四种情况可以清楚地看到,Optional + Lambda 可以让我们少写很多 ifElse 块。尤其是对于情况四那种夺命连环 null 检查,传统 Java 的写法显得冗长难懂,而新的 Optional +Lambda 则清新脱俗,清楚简洁。
6.接口
6.1 默认方法
public interface MyFun {
default String getName(){
return "libo";
}
default Integer getAge(){
return 22;
}
}
类优先原则:
6.2静态方法
public interface MyFun {
static void getAddr(){
System.out.println("addr");
}
static String Hello(){
return "Hello World";
}
}
7 . Date / Time API
7.1 安全问题
SimpleDateFormat是线程非安全的。在多线程中会出现问题。
@org.junit.Test
public void test23(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable<Date> task = () -> sdf.parse("20200921");
ExecutorService pool = Executors.newFixedThreadPool(1000);
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 e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
pool.shutdown();;
}
这时候可以通过ThreadLocal解决SimpleDateFormat的线程问题。
public class DateFormatThreadLocal {
private static final ThreadLocal<DateFormat> df = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd"));
public static Date convert(String source) throws ParseException, ParseException {
return df.get().parse(source);
}
}
@org.junit.Test
public void test24(){
Callable<Date> task = () -> DateFormatThreadLocal.convert("20200921");
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();
}
java8中就不需要这么麻烦了。它提供了LocalDate类和DateTimeFormat类。以下实现是线程安全的。
@org.junit.Test
public void test25(){
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
Callable<LocalDate> task = () -> LocalDate.parse("20200921", dtf);
ExecutorService pool = Executors.newFixedThreadPool(10);
ArrayList<Future<LocalDate>> result = new ArrayList<>();
for (int i = 0; i < 100; 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();
}
7.2 本地时间 / 日期
ISO标准:
@org.junit.Test
public void test26(){
// 获取当前时间日期
LocalDateTime ldt1 = LocalDateTime.now();
System.out.println(ldt1);
//指定时间日期 of
LocalDateTime ldt2 = LocalDateTime.of(2020, 1, 1, 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());
}
7.3 时间戳
Instant:以 Unix 元年 1970-01-01 00:00:00 到某个时间之间的毫秒值
@org.junit.Test
public void test26(){
// 默认获取 UTC 时区 (UTC:世界协调时间)
Instant ins1 = Instant.now();
System.out.println(ins1);
// 将instant从 指向UTC时间 改为 指向北京时间
Instant inss = ins1.plus(8, ChronoUnit.HOURS);
System.out.println(" 时间偏移量(毫秒) => Instant : " + inss);
//带偏移量的时间日期 (如: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);
//计算两个时间之间的间隔 between
Instant begin = Instant.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant end = Instant.now();
Duration dura1 = Duration.between(begin, end);
System.out.println(dura1.getSeconds());
System.out.println(dura1.toMillis());
}
7.4 时间校正器
- 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);
}
7.5 格式化
- 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);
}