技术分享——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() {
@Override
public 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, 输出5
Consumer 接收 参数 i 开始处理
Consumer 输入2, 输出3
Consumer 接收 参数 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:
aaa
bbb
ccc
ddd
eee
*/
- 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);
// 65148723
list.stream().forEach(System.out::print);
// 12345678
list.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:ss
SimpleDateFormat 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:ss
SimpleDateFormat 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:ss
SimpleDateFormat 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-dd
LocalDate date = LocalDate.now();
System.out.println(String.format("date format : %s", date));
result:date format : 2023-03-14
------------------------------------------------------------------
//format HH:mm:ss
LocalTime 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:ss
LocalDateTime 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();
//方法1
LocalDate after = localDate.plus(1, ChronoUnit.WEEKS);
//方法2
LocalDate 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));
}