技术分享——Java8新特性

技术分享——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的语言表达能力得到了提升。

  1. 语法格式
左侧: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可多线程执行。

特点:

  1. Stream不会自己存储元素
  2. Stream不会改变源对象,相反他们会返回一个持有新结果的新Strream。
  3. Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

5.2 流操作

Stram的操作三个步骤

  • 创建Stream
    一个数据源(如:集合、数组),获取一个流。
  • 中间操作
    一个中间操作,对数据源的数据进行处理。中间操作可以连成一条流水线。
  • 终端操作
    一个终端操作,执行中间操作链,并产生结果。终端操作的作用是关闭流水线。

在这里插入图片描述

5.2.1 创建Stream

  1. Collection系列集合提供的stream()方法或者parallelStream()创建Stream
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
  1. Arrays类中的静态方法stream()
Employee[] emps = new Employee[10];
Stream<Employee> stream = Arrays.stream(emps);
  1. 通过Stream类中的静态方法of
Stream<String> stream = Stream.of("aa", "bb", "cc");

5.2.2 Stram流 筛选与切片

  1. 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] 
  1. 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]
  1. 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]
  1. 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 映射

  1. 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]
  1. 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 排序

  1. 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
*/
  1. 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 匹配与查找

  1. 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
  1. 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
  1. 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
  1. 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
  1. findAny——返回当前流中的任意元素。返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。
  2. count——返回流中元素的总个数
  3. 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 规约与收集

  1. 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
  1. 收集 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);

在这里插入图片描述
注意:

  1. 顺序问题
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
  1. 线程安全问题
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可以更好的表达这个概念,并且可以避免空指针异常。

常用方法:

  1. Optional.of(T t):创建一个Optional实例
Optional<Person> op = Optional.of(new Employee());
op.get();
  1. Optional.empty():创建一个空的Optional实例
Optional op = Optional.empty();
op.get();
// NoSuchElementException

在这里插入图片描述

  1. 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}
  1. 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}
  1. 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}
  1. 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:张三
  1. 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-262023-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 相隔:0927-------------------------------------------------------------------------     
     //这里period.getDays()得到的天是抛去年月以外的天数,并不是总天数
     //如果要获取纯粹的总天数应该用下面的方法
     long day = date2.toEpochDay() - date1.toEpochDay();
     System.out.println(date2 + "和" + date2 + "相差" + day + "天");
     
     result:2023-12-232023-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));
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/2890.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[攻城狮计划]如何优雅的在RA2E1上运行RT_Thread

文章目录[攻城狮计划]|如何优雅的在RA2E1上运行RT_Thread准备阶段&#x1f697;开发板&#x1f697;开发环境&#x1f697;下载BSP&#x1f697;编译烧录连接串口总结[攻城狮计划]|如何优雅的在RA2E1上运行RT_Thread &#x1f680;&#x1f680;开启攻城狮的成长之旅&#xff0…

【ChatGPT】教你搭建多任务模型

ChatGPT教你搭建多任务模型 You: tell me what’s your version of gpt ? ChatGPT: As an AI language model developed by OpenAI, I am based on the GPT (Generative Pretrained Transformer) architecture. However, my version is known as GPT-3.5, which is an updat…

数据泄漏防护 (DLP) 工具保护敏感数据

通过实时安全监控&#xff0c;通过端点&#xff08;即 USB、电子邮件、打印等&#xff09;检测、中断和防止敏感数据泄露。使用 DataSecurity Plus 的数据泄漏防护 &#xff08;DLP&#xff09; 工具保护敏感数据不被泄露或被盗。DataSecurity Plus 主要功能包括&#xff1a; …

Android APP检查设备是否为平板

正文 Android APP判断设备是否为平板的三种方法&#xff1a; 通过屏幕尺寸判断。一般来说&#xff0c;平板电脑的屏幕尺寸比手机大很多&#xff0c;可以根据屏幕的长宽比和尺寸等信息来区分设备类型。通过屏幕像素密度判断。一般来说&#xff0c;平板电脑的屏幕像素密度比手机…

Java开发一年不到,来面试居然敢开口要20K,面完连8K都不想给~

前言 我的好朋友兼大学同学老伍家庭经济情况不错&#xff0c;毕业之后没两年自己存了点钱加上家里的支持&#xff0c;自己在杭州开了一家网络公司。由于公司不是很大所以公司大部分的开发人员都是自己面试的&#xff0c;近期公司发展的不错&#xff0c;打算扩招也面试了不少人…

四级数据库工程师 刷真题错题整理(三)数据库原理

1.数据模型是对现实世界进行抽象的工具&#xff0c;它按算机系统的观点模于提数据库系统中信息表示和操作手段的形式框架&#xff0c;主要用于 DBMS 的实现&#xff0c;是数据库系统的核心和基础。其中&#xff0c;数据操作是对数据间的动态行为。 2.数据库的型是稳定的&#…

day38_JDBC

今日内容 上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili 同步笔记沐沐霸的博客_CSDN博客-Java2301 零、 复习昨日 一、数据库连接池 二、反射 三、封装DBUtil 零、 复习昨日 SQL注入 预处理语句 String sql "select * from user where id ?"; PreparedStat…

企业微信中如何拉黑?拉黑个人和群成员有什么区别?

企业微信既可以拉黑个人好友&#xff0c;又可以拉黑群好友。 1. 拉黑个人好友 拉黑好友通俗来说就是不想再接收到对方的信息&#xff0c;企业微信可以通过设置消息免打扰的方式来屏蔽对方的消息。 【客户聊天界面】-【右上角的小人标志】-【第一栏名称进入】-【右上角三点】…

C语言——动态内存管理 malloc、calloc、realloc、free的使用

目录 一、为什么存在动态内存分配 二、动态内存函数的介绍 2.1malloc和free 2.2calloc 2.3realloc 三、常见的动态内存错误 3.1对NULL指针的解引用操作 3.2对动态开辟空间的越界访问 3.3对非动态开辟的内存使用free释放 3.4使用free释放一块动态开辟内存的一部分 3.5…

奇安信_防火墙部署_透明桥模式

奇安信_防火墙部署_透明桥模式一、预备知识二、项目场景三、拓扑图四、基本部署配置1. 登录web控制台2.连通性配置3.可信主机配置4.授权导入5.特征库升级6.安全配置文件五、透明桥配置1. 创建桥2. 端口绑定桥3. 创建桥端口六、结语一、预备知识 安全设备接入网络部署方式 二、…

运算放大器:电压比较器

目录一、单限电压比较器二、滞回电压比较器三、窗口电压比较器最近在学习电机控制&#xff0c;遇到了与运算放大电路相关的知识&#xff0c;然而太久没有接触模拟电路&#xff0c;对该知识已经淡忘了&#xff0c;及时温故而知新&#xff0c;做好笔记&#xff0c;若有错误、不足…

字节跳动测试岗面试记:二面被按地上血虐,所幸Offer已到手...

在互联网做了几年之后&#xff0c;去大厂“镀镀金”是大部分人的首选。大厂不仅待遇高、福利好&#xff0c;更重要的是&#xff0c;它是对你专业能力的背书&#xff0c;大厂工作背景多少会给你的简历增加几分竞争力。 但说实话&#xff0c;想进大厂还真没那么容易。最近面试字…

3分钟阐述这些年我的 接口自动化测试 职业生涯经验分享

接口自动化测试学习教程地址&#xff1a;https://www.bilibili.com/video/BV1914y1F7Bv/ 你好&#xff0c;我是凡哥。 很高兴能够分享我的接口自动化测试经验和心得体会。在我目前的职业生涯中&#xff0c;接口自动化测试是我经常进行的一项任务。通过不断地学习和实践&#xf…

【C++】map 和 set

文章目录一、关联式容器与键值对1、关联式容器2、键值对 pair3、树形结构的关联式容器二、set1、set 的介绍2、set 的使用三、multiset四、map1、map 的介绍2、map 的使用五、multimap一、关联式容器与键值对 1、关联式容器 在C初阶的时候&#xff0c;我们已经接触了 STL 中的…

基于SpringBoot的酒店管理系统

系统环境 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/i…

matplotlib参数详解

文章目录一、简介二、安装与调用三、绘图与风格设置3.1、绘图标记3.1.1、标记类型&#xff08;marker*&#xff09;3.1.2、标记大小、内部和边框颜色&#xff08;ms10、mfcr、mecg&#xff09;3.2、绘图线3.2.1、线类型&#xff08;linestyle--&#xff09;3.2.2、线宽&#xf…

C++入门教程||C++ 字符串||

C 字符串C 字符串C 提供了以下两种类型的字符串表示形式&#xff1a;C 风格字符串C 引入的 string 类类型C 风格字符串C 风格的字符串起源于 C 语言&#xff0c;并在 C 中继续得到支持。字符串实际上是使用 null 字符 终止的一维字符数组。因此&#xff0c;一个以 null 结尾的…

大文件上传

上图就是大致的流程一、标题图片上传课程的标题图片Ajax发送请求到后端后端接收到图片使用IO流去保存图片&#xff0c;返回图片的信息对象JS回调函数接收对象通过$("元素id").val(值)&#xff0c;方式给页面form表达img标签src属性值&#xff0c;达到上传图片并回显二…

若依微服务(ruoyi-cloud)保姆版容器编排运行

一、简介 项目gitee地址&#xff1a;https://gitee.com/y_project/RuoYi-Cloud 由于该项目运行有很多坑&#xff0c;大家可以在git克隆拷贝到本地后&#xff0c;执行下面的命令使master版本回退到本篇博客的版本&#xff1a; git reset --hard 05ca78e82fb4e074760156359d09a…

扒一扒抖音是如何做线程优化的

背景 最近在对一些大厂App进行研究学习&#xff0c;在对某音App进行研究时&#xff0c;发现其在线程方面做了一些优化工作&#xff0c;并且其解决的问题也是之前我在做线上卡顿优化时遇到的&#xff0c;因此对其具体实现方案做了深入分析。本文是对其相关源码的研究加上个人理…