西安杰商网络网站建设全球搜钻是什么公司
技术分享——Java8新特性
- 1.背景
- 2. 新特性主要内容
- 3. Lambda表达式
- 4. 四大内置核心函数式接口
- 4.1 Consumer<T>消费型接口
- 4.2 Supplier<T>供给型接口
- 4.3 Function<T,R>函数型接口
- 4.4 Predicate<T> 断定型接口
- 5. Stream流操作
- 5.1 什么是流以及流的类型
- 5.2 流操作
- 5.2.1 创建Stream
- 5.2.2 Stram流 筛选与切片
- 5.2.3 映射
- 5.2.4 排序
- 5.2.5 匹配与查找
- 5.2.6 规约与收集
- 6. 并行流 parallelStream
- 7. Optional 类
- 8. 新的时间API
- 8.1 java.time 主要类
- 8.2 应用对比
- 8.2.1 格式化对比
- 8.2.2 字符串转日期格式
- 8.2.3 日期计算
- 8.2.4 获取指定日期
1.背景
目前企业级开发语言主要是Java,Java 8 是目前最常用的 JDK 版本,相比 Java 7 增加了很多功能,比如 Lambda表达式、Stream 流操作、并行流(ParallelStream)、Optional 可空类型、新时间API等。
新特性给我们带来的好处
- 代码量更少(lambda表达式)
- 强大的Stream API(提供了一种新的处理集合数据的方式,允许以声明性的方式处理数据)
- 便于并行(ParallelStream)
- 最大化减少NPE(Optional)
2. 新特性主要内容
Lambda表达式
四大内置核心函数式接口
Stream流操作
ParallelStrean并行流
Optional类
新的时间API
3. Lambda表达式
Lambda表达式是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
- 语法格式
左侧:Lambda表达式的参数列表
右侧:Lambda表达式中所需执行的功能,既Lambda体 语法格式一:无参数,无返回值
Runnable r1 = ()-> System.out.println("Hello World");语法格式二:有一个参数,并无返回值
Consumer<String> con = (x)-> System.out.println(x);语法格式三:若只有一个参数,参数的()可以不写
Consumer<String> con = x-> System.out.println(x);语法格式四:有两个以上参数,有返回值,并且Lambda体中有多条语句
Comparator<Integer> com = (x, y) -> {System.out.println("Hello World");return Integer.compare(x, y);
};语法格式五:若Lambda体中只有一条语句,return和{}都可以不写
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
这里注意Lambda表达式需要"函数式接口"的支持。
函数式接口:接口中只有一个抽象方法,称为函数式接口(@FunctionalInterface 注解声明该接口是一个函数式接口)。
2. 举个🌰
匿名内部类的格式
new 父类或接口(){//进行方法重写
};// 用匿名内部类的方式来创建线程
new Thread(new Runnable() {@Overridepublic void run() {System.out.println("The runable now is using!");}
}).start();------------------------------------------------------------------------
// 使用Lambda来创建线程,返回的是Runnable对象实例
new Thread(() -> System.out.println("The runable now is using!")).start();
4. 四大内置核心函数式接口
4.1 Consumer消费型接口
接受一个参数,无返回值,常用于对参数进行处理、修改等操作。
举个🌰
public static void main(String[] args) {Consumer<Integer> consumer = i -> {System.out.println("Consumer 接收 参数 i 开始处理");int step = 1;System.out.printf("Consumer 输入%d, 输出%d%n", i, i + step);};List<Integer> list = Arrays.asList(4, 2, 6);list.forEach(consumer);
}
result:Consumer 接收 参数 i 开始处理Consumer 输入4, 输出5Consumer 接收 参数 i 开始处理Consumer 输入2, 输出3Consumer 接收 参数 i 开始处理Consumer 输入6, 输出7
4.2 Supplier供给型接口
不接受参数,返回一个结果,常用于生成某些对象。
举个🌰
// 获取长度为10的整数集合
public static List<Integer> getNumList(int num, Supplier<Integer> sup) {List<Integer> list = new ArrayList<>();for (int i = 0; i < num; i++) {Integer n = sup.get();list.add(n);}return list;
}getNumList(10, () -> (int) (Math.random() * 100));
4.3 Function<T,R>函数型接口
接受一个参数,返回一个结果,常用于对参数进行处理、转换等操作。
这里举个🌰
有User、UserEntity两个实体类定义如下
@Data
@AllArgsConstructor
public class User {private Integer age;private String name;
}
@Data
@AllArgsConstructor
public class UserEntity{private Integer age;private String name;private Integer type;
}
将一组User转换成一组UserEntity, 根据User的年龄来定义用户级别(普通用户,vip,svip),其中用户级别是User Entity的一个字段,所以输入参数是List,返回结果是List 。
构造数据
List<User> users = new ArrayList<>();
users.add(new User(10, "张三"));
users.add(new User(15, "李四"));
users.add(new User(16, "王五"));
users.add(new User(20, "赵六"));
users.add(new User(25, "田七"));
private static Function<List<User>, List<UserEntity>> multiUsersToEntities(List<User> users) {Function<List<User>, List<UserEntity>> function = t -> {List<UserEntity> userEntityList = new ArrayList<>();for (User user : t) {UserEntity userEntity = new UserEntity(user.getAge(), user.getName(), "普通用户");Integer age = user.getAge();if (age > 15 && age <= 20) {userEntity.setType("vip");}if (age > 20) {userEntity.setType("svip");}userEntityList.add(userEntity);}return userEntityList;};return function;
}List<UserEntity> uEntities = multiUsersToEntities(users).apply(users);
4.4 Predicate 断定型接口
接受一个参数,返回一个布尔值,常用于条件判断、筛选等操作。
Predicate<String> predicate = p -> p.length() == 21;
Stream<String> stream = stringList().stream().filter(predicate);
总结:
这四个接口的作用在于提供常用的函数式编程中常用的基础操作,提高了代码的复用性和简洁性。它们可以被Java8中的Lamba表达式直接调用,从而实现更加简洁而清晰的代码。
如果你需要进行一些简单的操作,这些接口可以让你无需自己编写函数或者创建单独的类,而使用Java8提供的函数式编程工具来解决问题。
除了四大核心函数接口外,Java8还提供了一些其他的函数式接口。
5. Stream流操作
5.1 什么是流以及流的类型
流是Java API的新成员,它允许通过声明的方式处理数据集合。我们可以把他看成遍历数据集的高级迭代器。它的源数据可以是 Collection、Array 等。由于它的方法参数都是函数式接口类型,所以一般和 Lambda 配合使用。
流的类型有stream 串行流和parallelStream 并行流,parallelStream可多线程执行。
特点:
- Stream不会自己存储元素
- Stream不会改变源对象,相反他们会返回一个持有新结果的新Strream。
- Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
5.2 流操作
Stram的操作三个步骤
- 创建Stream
一个数据源(如:集合、数组),获取一个流。 - 中间操作
一个中间操作,对数据源的数据进行处理。中间操作可以连成一条流水线。 - 终端操作
一个终端操作,执行中间操作链,并产生结果。终端操作的作用是关闭流水线。
5.2.1 创建Stream
- Collection系列集合提供的stream()方法或者parallelStream()创建Stream
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
- Arrays类中的静态方法stream()
Employee[] emps = new Employee[10];
Stream<Employee> stream = Arrays.stream(emps);
- 通过Stream类中的静态方法of
Stream<String> stream = Stream.of("aa", "bb", "cc");
5.2.2 Stram流 筛选与切片
- filter——接收Lambda, 从流中排除某些元素
Stream<T> filter(Predicate<? super T> predicate); 断言型接口
List<String> s = Arrays.asList("a","b","c",null);List<String> s2 = s.stream().filter(e -> e != null).collect(Collectors.toList());
System.out.println(s2);
// result:[a, b, c]
- limit ——截断流,使其不超过给定的数量
List<String> s = Arrays.asList("a","b","c");List<String> s2 = s.stream().limit(2L).collect(Collectors.toList());
System.out.println(s2);
// result:[a, b]
- skip(n)—— 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流
List<String> s = Arrays.asList("a","b","c");List<String> resultList = demoList.stream().skip(2).collect(Collectors.toList());
System.out.println(resultList);
// result: [c]
- distinct——筛选,通过流所生成元素的hashCode()和equals()去除重复元素
List<String> s = Arrays.asList("a","a","b","c");List<String> resultList = s.stream().distinct().collect(Collectors.toList());
System.out.println(resultList);
// result: [a,b,c]
5.2.3 映射
- map——支持Lambda语法,将元素转换成其他形式提取信息。接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
List<String> demoList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");List<String> result = demoList.stream().map(str -> str.toUpperCase()).collect(Collectors.toList());
System.out.println(result);
// result: [AAA, BBB, CCC, DDD, EEE]
- flatMap——将流中的每个值都替换成另一个流,然后把所有的流连接成一个流。
举个🌰
给定的单词集合[hello,world],返回集合[h,e,l,l,o, w,o,r,l,d]
List<String> stringList = Arrays.asList("hello", "world");
List<String[]> collect = stringList.stream().map(str -> str.split("")).collect(Collectors.toList());
collect.forEach(col -> System.out.println(Arrays.toString(col)));/* result:[h, e, l, l, o][w, o, r, l, d]
*/
大家可以看到返回结果是两个数组,并没有达到我们的要求。map的操作只是将元素放入map中的函数中使其返回另一个Stream<String[]>类型的,但我们真正想要的是一个Stream类型的,所以我们需要扁平化处理,将多个数组放入一个数组中。
List<String> stringList = Arrays.asList("hello", "world");
List<String> collect = stringList.stream().map(str -> str.split("")).flatMap(item -> {return Arrays.stream(item);}).collect(Collectors.toList());System.out.println(collect);// result:[h, e, l, l, o, w, o, r, l, d]
5.2.4 排序
- sorted()——自然排序(实现Comparable的compareTo方法)
List<String> list = Arrays.asList("ccc", "aaa", "bbb", "ddd", "eee");
list.stream().sorted().forEach(System.out::println);/* result: aaabbbcccdddeee
*/
- sorted(Comparator com)——定制排序
Stream<T> sorted(Comparator<? super T> comparator);
List<Employee> employeeList = new ArrayList<Employee>();
employeeList.add(new Employee(15, "18801171255", "张三", 8000));
employeeList.add(new Employee(18, "18801171256", "李四", 9000));
employeeList.add(new Employee(20, "18801171257", "王五", 1000));
employeeList.add(new Employee(20, "18801171258", "赵六", 1000));employeeList.stream().sorted((e1,e2)->{if(e1.getAge().equals(e2.getAge())){return e1.getName().compareTo(e2.getName());}else{return e1.getAge().compareTo(e2.getAge());}}).collect(Collectors.toList());System.out.println(employeeList);
/*retult:[Employee{age=15, mobile='18801171255', name='张三', salary=8000}, Employee{age=18, mobile='18801171256', name='李四', salary=9000}, Employee{age=20, mobile='18801171257', name='王五', salary=1000}, Employee{age=20, mobile='18801171258', name='赵六', salary=1000}]
*/
5.2.5 匹配与查找
- allMatch——检查是否匹配所有元素
判断数据列表中全部元素都符合设置的predicate条件,如果是就返回true,否则返回false,流为空时总是返回true。
boolean allMatch(Predicate<? super T> predicate);
List<String> typeList1 = Arrays.asList("1", "2");
List<String> typeList2 = Arrays.asList("1", "2", "3", "4");
// 集合列表中全部元素必须在allMatch里面的那些字符串,只要全部元素中有任意一个不同的元素在AllMatch中就返回false
boolean isMatch1 = typeList1.stream().allMatch(a -> a.equals("1") || a.equals("2") || a.equals("3"));
boolean isMatch2 = typeList2.stream().allMatch(a -> a.equals("1") || a.equals("2") || a.equals("3"));
System.out.println(isMatch1); // result:true
System.out.println(isMatch2); // fresult:false
- anyMatch——检查是否至少匹配一个元素(只要有一个满足条件就返回true)
List<Integer> list = Arrays.asList(10, 12, 14, 16);
boolean flag = list.stream().anyMatch(item -> item > 13);
System.out.println(flag); // result:true
- noneMath——检查是否没有匹配所有元素
判断数据列表中全部元素都不符合设置的predicate条件,如果是就返回true,否则返回false,流为空时总是返回true。
List<String> list = Arrays.asList("dddd", "ee", "qqq", "bcfff");
boolean isMatch = list.stream().noneMatch(str -> str.startsWith("a"));
System.out.println(isMatch); // result:true
- findFirst——返回第一个元素
Optional<T> findFirst();
List<String> list = Arrays.asList("dddd", "ee", "qqq", "bcfff");
Optional<String> result = list.stream().findFirst();
System.out.println(result.get()); // result:dddd
- findAny——返回当前流中的任意元素。返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。
- count——返回流中元素的总个数
- max——返回流中最大值、min——返回流中最小值
List<Employee> employeeList = new ArrayList<Employee>();
employeeList.add(new Employee(15, "18801171255", "张三", 8000));
employeeList.add(new Employee(18, "18801171256", "李四", 9000));
employeeList.add(new Employee(20, "18801171257", "王五", 1000));
employeeList.add(new Employee(30, "18801171258", "赵六", 1000));Employee maxAgeemployee = employeeList.stream().max((e1, e2) -> Integer.compare(e1.age, e2.getAge())).get();
System.out.println(maxAgeemployee);
// result:Employee{age=30, mobile='18801171258', name='赵六', salary=1000}Employee minAgeemployee = employeeList.stream().min((e1, e2) -> Integer.compare(e1.age, e2.getAge())).get();
System.out.println(minAgeemployee);
// result:Employee{age=15, mobile='18801171255', name='张三', salary=8000}
5.2.6 规约与收集
- Reduce——它将所有元素组合成单个结果。Reduce的目的是提供一种累积元素的方法,将所有元素组合为同一结果,可以使用reduce进行sum,min,max,count等操作
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(sum); // result: 55
- 收集 collect——将流转化为其他形式。接收一个Collector接口的实现。Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors实用类提供了很多静态方法,可以方便地创建常见的收集器实例。
List<Employee> employeeList = new ArrayList<Employee>();
employeeList.add(new Employee(15, "18801171255", "张三", 8000));
employeeList.add(new Employee(18, "18801171256", "李四", 9000));
employeeList.add(new Employee(20, "18801171257", "王五", 1000));
employeeList.add(new Employee(30, "18801171258", "赵六", 1000));// 把名字收集成List集合
List<String> nameList = employeeList.stream().map(Employee::getName).collect(Collectors.toList());// 把年龄收集成set集合
Set<Integer> ageList = employeeList.stream().map(Employee::getAge).collect(Collectors.toSet());// 收集成Map key:age value:mobile
Map<Integer, String> ageAndMobileMap = employeeList.stream().collect(Collectors.toMap(Employee::getAge, Employee::getMobile));
6. 并行流 parallelStream
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java8中将并行流进行了优化,我们可以很容易的对数据进行并行操作,Stream API 可以声明性地通过parallel()与sequential()在并行流与顺序流之间进行切换。
这里举个🌰
Integer sum = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8).parallelStream().reduce(0, Integer::sum);
注意:
- 顺序问题
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);list.parallelStream().forEach(System.out::print);
// 65148723list.stream().forEach(System.out::print);
// 12345678list.parallelStream().forEachOrdered(System.out::print);
// 12345678
- 线程安全问题
public static void main(String[] args) {printFun();
}
public static void printFun() {List<Integer> integersList = new ArrayList<>();for (int i = 0; i < 100; i++) {integersList.add(i);}//普通集合 存储List<Integer> parallelStorage = new ArrayList<>();//同步集合 存储List<Integer> parallelStorage2 = Collections.synchronizedList(new ArrayList<>());//通过并行流存入普通集合parallelStorage中integersList.parallelStream().filter(i -> i % 2 == 0).forEach(i -> parallelStorage.add(i));System.out.println("开始打印普通集合parallelStorage");parallelStorage.stream().forEach(e -> System.out.print(e + " "));System.out.println();System.out.print("------------------------------------");System.out.println();//通过并行流存入同步集合parallelStorage2中integersList.parallelStream().filter(i -> i % 2 == 0).forEach(i -> parallelStorage2.add(i));System.out.println("开始打印同步集合parallelStorage");parallelStorage2.stream().forEach(e -> System.out.print(e + " "));
}
List<String> resultList = new CopyOnWriteArrayList<>();
List<String> resultList = Collections.synchronizedList(new ArrayList<>());
7. Optional 类
Optional<T>
类(java.util.Optional)是一个容器类,代表一个值存在或者不存在,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异常。
常用方法:
- Optional.of(T t):创建一个Optional实例
Optional<Person> op = Optional.of(new Employee());
op.get();
- Optional.empty():创建一个空的Optional实例
Optional op = Optional.empty();
op.get();
// NoSuchElementException
- Optional.ofNullable(T t):若t不为null,创建Optional实例否则创建空实例
Optional<Person> op = Optional.ofNullable(new Employee());
System.out.println(op.get());
// result: Employee{age=null, mobile='null', name='null', salary=null}
- orElse(T t):如果调用对象包含值,返回该值,否则返回t
Optional<Employee> op = Optional.ofNullable(null);
Employee employee = op.orElse(new Employee(15, "18801171255", "张三", 8000));
System.out.println(employee);
// result: Employee{age=15, mobile='18801171255', name='张三', salary=8000}
- orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取到的值
Optional<Employee> op = Optional.ofNullable(null);
Employee employee = op.orElseGet(() -> new Employee(15, "18801171255", "张三", 8000));
System.out.println(employee);
// result: Employee{age=15, mobile='18801171255', name='张三', salary=8000}
- map(Function f):如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
Optional<Employee> op = Optional.ofNullable(new Employee(15, "18801171255", "张三", 8000)));
Optional<String> opStr = op.map(e -> e.getName());
System.out.println(opStr.get());
// result:张三
- flatMap(Function mapper):与map类似,要求返回值必须是Optional
Optional<Employee> op = Optional.ofNullable(new Employee(15, "18801171255", "张三", 8000));
Optional<String> opStr = op.flatMap(e -> Optional.of(e.getName()));
System.out.println(opStr.get());
// result:张三
8. 新的时间API
8.1 java.time 主要类
LocalDateTime.class //日期+时间 format: yyyy-MM-ddTHH:mm:ss.SSS
LocalDate.class //日期 format: yyyy-MM-dd
LocalTime.class //时间 format: HH:mm:ss
8.2 应用对比
8.2.1 格式化对比
Java 8 之前:
import java.text.SimpleDateFormat;
import java.util.Date;public void oldFormat(){Date now = new Date();//format yyyy-MM-dd HH:mm:ssSimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");String date = sdf.format(now);System.out.println(String.format("date format : %s", date));result: date format : 2023-03-14
------------------------------------------------------------------//format HH:mm:ssSimpleDateFormat sdft = new SimpleDateFormat("HH:mm:ss");String time = sdft.format(now);System.out.println(String.format("time format : %s", time));result: time format : 15:45:32
------------------------------------------------------------------//format yyyy-MM-dd HH:mm:ssSimpleDateFormat sdfdt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String datetime = sdfdt.format(now);System.out.println(String.format("dateTime format : %s", datetime));dateTime format : 2023-03-14 15:45:32
}
Java 8 之后:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;public void newFormat(){//format yyyy-MM-ddLocalDate date = LocalDate.now();System.out.println(String.format("date format : %s", date));result:date format : 2023-03-14
------------------------------------------------------------------//format HH:mm:ssLocalTime time = LocalTime.now().withNano(0);System.out.println(String.format("time format : %s", time));result:time format : 15:49:16
------------------------------------------------------------------//format yyyy-MM-dd HH:mm:ssLocalDateTime dateTime = LocalDateTime.now();DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");String dateTimeStr = dateTime.format(dateTimeFormatter);System.out.println(String.format("dateTime format : %s", dateTimeStr));result:dateTime format : 2023-03-14 15:49:16
}
8.2.2 字符串转日期格式
Java 8 之前:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date1 = sdf.parse("2022-09-20");
Java 8 之后:
public static LocalDate parse(CharSequence text) {return parse(text, DateTimeFormatter.ISO_LOCAL_DATE);
}
LocalDate.parse("2022-07-06");LocalDateTime.parse("2021-01-26 12:12:22");LocalTime.parse("12:12:22");
8.2.3 日期计算
Java 8 之前:
SimpleDateFormat formatDate = new SimpleDateFormat("yyyy-MM-dd");
Calendar ca = Calendar.getInstance();
ca.add(Calendar.DATE, 7);
Date d = ca.getTime();
String after = formatDate.format(d);
System.out.println("一周后日期:" + after);result:一周后日期:2023-03-21
-----------------------------------------------------------------------//算两个日期间隔多少天,计算间隔多少年,多少月方法类似
String dates1 = "2023-12-23";
String dates2 = "2023-02-26";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date1 = format.parse(dates1);
Date date2 = format.parse(dates2);
int day = (int) ((date1.getTime() - date2.getTime()) / (1000 * 3600 * 24));
System.out.println(dates2 + "和" + dates2 + "相差" + day + "天");result:2023-02-26和2023-02-26相差300天
Java 8 之后:
public void pushWeek(){//一周后的日期LocalDate localDate = LocalDate.now();//方法1LocalDate after = localDate.plus(1, ChronoUnit.WEEKS);//方法2LocalDate after2 = localDate.plusWeeks(1);System.out.println("一周后日期:" + after);result:一周后日期:2023-03-21
-----------------------------------------------------------------------//算两个日期间隔多少天,计算间隔多少年,多少月LocalDate date1 = LocalDate.parse("2021-02-26");LocalDate date2 = LocalDate.parse("2021-12-23");Period period = Period.between(date1, date2);System.out.println("date1 到 date2 相隔:"+ period.getYears() + "年"+ period.getMonths() + "月"+ period.getDays() + "天");result:date1 到 date2 相隔:0年9月27天
------------------------------------------------------------------------- //这里period.getDays()得到的天是抛去年月以外的天数,并不是总天数//如果要获取纯粹的总天数应该用下面的方法long day = date2.toEpochDay() - date1.toEpochDay();System.out.println(date2 + "和" + date2 + "相差" + day + "天");result:2023-12-23和2023-12-23相差300天
}
8.2.4 获取指定日期
Java 8 之前:
public void getDay() {SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");//获取当前月第一天:Calendar c = Calendar.getInstance();c.set(Calendar.DAY_OF_MONTH, 1);String first = format.format(c.getTime());System.out.println("first day:" + first);//获取当前月最后一天Calendar ca = Calendar.getInstance();ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));String last = format.format(ca.getTime());System.out.println("last day:" + last);//当年最后一天Calendar currCal = Calendar.getInstance();Calendar calendar = Calendar.getInstance();calendar.clear();calendar.set(Calendar.YEAR, currCal.get(Calendar.YEAR));calendar.roll(Calendar.DAY_OF_YEAR, -1);Date time = calendar.getTime();System.out.println("last day:" + format.format(time));
}
Java 8 之后:
public void getDayNew() {LocalDate today = LocalDate.now();//获取当前月第一天:LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());// 取本月最后一天LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());//取下一天:LocalDate nextDay = lastDayOfThisMonth.plusDays(1);//当年最后一天LocalDate lastday = today.with(TemporalAdjusters.lastDayOfYear());//2023年最后一个周日,如果用Calendar是不得烦死。LocalDate lastMondayOf2023 = LocalDate.parse("2023-12-31").with(TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY));
}