Java 【JDK8特性】【lambda表达式、函数式接口、Optional、LocalDate、LocalTime、LocalDateTime、运算语法】

 1、Lambda表达式


 java8中一个非常重要的特性就是lambda表达式,我们可以把它看成是一种闭包,它允许把函数当做参数来使用,是面向函数式编程的思想,一定程度上可以使代码看起来更加简洁。例如以前我们使用匿名内部类来实现代码:

1      //匿名内部类写法
2         new Thread(new Runnable() {
3             @Override
4             public void run() {
5                 System.out.println("内部类写法");
6             }
7         }).start();

使用lambda则更加简洁:

1 //lambda 写法
2 new Thread(() -> System.out.println("lambda写法")).start(); 

lambda表达式语法

1 (paramters) -> expression;

或者

1     (paramters) -> {statements;}  
2     展开如:
3     (Type1 param1, Type2 param2, Type2 param2, ...) -> {
4         statement1;
5         statement2;
6         statement3;
7         ...
8         return statementX;
9     }   

lambda表达式特征

  •  可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  •  可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  •  可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  •  可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

示例:

 1         //入参为空
 2         TestDemo no_param = () -> "hi, no param";
 3         TestDemo no_param2 = () -> { return "hi, no param"; };
 4         System.out.println(no_param.hi());
 5 
 6         //单个参数
 7         TestDemo2 param = name -> name;
 8         TestDemo2 param2 = name -> { return name;};
 9         System.out.println(param.hei("hei, grils"));
10 
11         //多个参数
12         TestDemo3 multiple = (String hello, String name) -> hello + " " + name;
13         //一条返回语句,可以省略大括号和return
14         TestDemo3 multiple2 = (hello, name) -> hello + name;
15         //多条处理语句,需要大括号和return
16         TestDemo3 multiple3 = (hello, name) -> {
17             System.out.println("进入内部");
18             return hello + name;
19         };
20         System.out.println(multiple.greet("hello", "lambda"));

方法引用

  在使用lambda时,如果方法体中只有一个方法的调用(包含构造方法),则可以使用方法引用进一步简化
  有以下几种类型
  ☆ 对象::实例方法,将lambda的参数当做方法的参数使用
objectName::instanceMethod
  示例:
1 Consumer<String> sc = System.out::println;
2 //等效
3 Consumer<String> sc2 = (x) -> System.out.println(x);
4 sc.accept("618, 狂欢happy");
  代码示例:

 

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22);
 5         StringBuilder sb = new StringBuilder(); // 创建实例
 6         // map方法中使用的是 引用类的静态方法 ->  类名::静态方法名
 7         list.stream().map(String::valueOf).forEach(new Consumer<String>() {
 8             @Override
 9             public void accept(String s) {
10                 sb.append(s);
11             }
12         });
13 
14         // forEach方法中使用 引用对象的实例方法 -> 对象名::实例方法名
15         list.stream().map(String::valueOf).forEach(sb::append);
16     }
17 }

  ☆ 类::静态方法,将lambda的参数当做方法的参数使用
  如果在重写方法的时候,方法体中只有一行代码,并且这行代码调用了某个类的静态方法,我们把要重写的抽象方法中所有的参数都按照顺序传入这个静态方法中,这个时候可以引用类的静态方法

ClassName::staticMethod      // 类名::静态方法名
  示例:
1 //ClassName::staticMethod  类的静态方法:把表达式的参数值作为staticMethod方法的参数
2 
3 Function<Integer, String> sf = String::valueOf;
4 //等效
5 Function<Integer, String> sf2 = (x) -> String.valueOf(x);
6 String apply1 = sf.apply(61888);
  示例代码:
 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22);
 5         // 匿名内部类写法
 6         list.stream().map(new Function<Integer, String>() {
 7             @Override
 8             public String apply(Integer x) {
 9                 return String.valueOf(x);
10             }
11         });
12         // lambda简写
13         list.stream().map(x -> String.valueOf(x));
14         // 引用类的静态方法简写    
15         list.stream().map(String::valueOf);
16     }
17 }

  ☆ 类::实例方法,将lambda的第一个参数当做方法的调用者,其他的参数作为方法的参数。开发中尽量少些此类写法,减少后续维护成本。

ClassName::instanceMethod

  示例:

1 //ClassName::instanceMethod  类的实例方法:把表达式的第一个参数当成instanceMethod的调用者,其他参数作为该方法的参数
2 
3 BiPredicate<String, String> sbp = String::equals;
4 //等效
5 BiPredicate<String, String> sbp2 = (x, y) -> x.equals(y);
6 boolean test = sbp.test("a", "A");

构造函数

  无参的构造方法就是类::实例方法模型,如:

1 Supplier<User> us = User::new;
2 //等效
3 Supplier<User> us2 = () -> new User();
4 //获取对象
5 User user = us.get();

  当有参数时

 1 //一个参数,参数类型不同则会编译出错
 2 Function<Integer, User> uf = id -> new User(id);
 3 //或加括号
 4 Function<Integer, User> uf2 = (id) -> new User(id);
 5 //等效
 6 Function<Integer, User> uf3 = (Integer id) -> new User(id);
 7 User apply = uf.apply(61888);
 8 
 9 //两个参数
10 BiFunction<Integer, String, User> ubf = (id, name) -> new User(id, name);
11 User 狂欢happy = ubf.apply(618, "狂欢happy");

继承及实现具有相同默认方法的父类或接口问题

  接口A:

 1 public interface A {
 2 
 3     String hi();
 4 
 5     String greet();
 6 
 7     default void hello() {
 8         System.out.println("A.hello");
 9     }
10 
11 }

  接口B:

 1 public interface B {
 2 
 3     String hi();
 4 
 5     String hh();
 6 
 7     default void hello() {
 8         System.out.println("B.hello");
 9     }
10 
11 }

  类C实现A,B:

 1 public class C implements A, B{
 2 
 3     @Override
 4     public String hi() {
 5         return "C.hi";
 6     }
 7 
 8     @Override
 9     public String greet() {
10         return "C.greet";
11     }
12 
13     @Override
14     public String hh() {
15         return "C.hh";
16     }
17 
18     /**
19   * 子类优先继承父类的方法, 如果父类没有相同签名的方法,才继承接口的默认方法。
20   * 编译报错解决1:覆盖法
21      */
22     @Override
23     public void hello() {
24         System.out.println("C.hello");
25     }
26 
27     /**
28   * 编译报错解决2:指定实现的父接口
29      */
30 //    @Override
31 //    public void hello() {
32 //        A.super.hello();
33 //        B.super.hello();
34 //    }
35 
36 }
  此时若不处理hello方法时,类C将编译出错,解决方式要么覆盖,要么指定实现父接口的该方法。
  进一步测试继承具有相同方法的父类:
  类D:
public class D {

    public void hello() {
        System.out.println("D.hello");
    }
}

  类C继承类D:

 1 public class C extends D implements A, B{
 2 
 3     @Override
 4     public String hi() {
 5         return "C.hi";
 6     }
 7 
 8     @Override
 9     public String greet() {
10         return "C.greet";
11     }
12 
13     @Override
14     public String hh() {
15         return "C.hh";
16     }
17 
18     /**
19      * 子类优先继承父类的方法, 如果父类没有相同签名的方法,才继承接口的默认方法。
20      * 编译报错解决1:覆盖法
21      */
22 //    @Override
23 //    public void hello() {
24 //        System.out.println("C.hello");
25 //    }
26 
27     /**
28   * 编译报错解决2:指定实现的父接口
29      */
30 //    @Override
31 //    public void hello() {
32 //        A.super.hello();34 //    }
36 }

  此时若不覆盖或指定父接口的方法时,类C将继承类D的hello方法。

总结

  java8引入lambda表达式是接收了函数式编程语言的思想,例如scala之类的,它将函数视为一等公民,可以使用高阶函数等。
  和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。
  和过程化编程相比,函数式编程里函数的计算可随时调用。
  写在最后,lambda表达式可以使代码看起来简洁,但一定程度上增加了代码的可读性以及调试的复杂性,所以在使用时应尽量是团队都熟悉使用,要么干脆就别用,不然维护起来是件较痛苦的事。

2、Optional


java8 推出的Optional的目的就是为了杜绝空指针异常,帮助开发者开发出更优雅的代码,使用Optional不正确时,将会违背设计者的初衷。

Optional的构造方式:

  1、Optional.of(T)          该方式的入参不能为null,否则会有NPE,在确定入参不为空时使用该方式。  
  2、Optional.ofNullable(T)        该方式的入参可以为null,当入参不确定为非null时使用。  
  3、Optional.empty()                  这种方式是返回一个空Optional,等效Optional.ofNullable(null)

 如何正确使用Optional

  尽量避免使用的地方:

    1、避免使用Optional.isPresent()来检查实例是否存在,因为这种方式和null != obj没有区别,这样用就没什么意义了。    
    2、避免使用Optional.get()方式来获取实例对象,因为使用前需要使用Optional.isPresent()来检查实例是否存在,否则会出现NPE问题。    
    3、避免使用Optional作为类或者实例的属性,而应该在返回值中用来包装返回实例对象。    
    4、避免使用Optional作为方法的参数,原因同3。

  正确使用方式:

    1、实例对象存在则返回,否则提供默认值或者通过方法来设置返回值,即使用orElse/orElseGet方式:

1 // 存在则返回
2 User king = new User(1, "king");
3 Optional<User> userOpt = Optional.of(king);
4 User user =  userOpt.orElse(null);
5 System.out.println(user.getName());

 

1 // 不存在提供默认值
2 User user2 = null;
3 Optional<User> userOpt2 = Optional.ofNullable(user2);
4 User user3 = userOpt2.orElse(new User("张三")); // 如果User为null, 则返回orElse中提供的数据
5 System.out.println(user3.getName());

 

1 // 通过方法提供值
2 User user4 = userOpt2.orElseGet(() -> new User(0, "DEFAULT")); 
3 System.out.println(user4.getName())

  不建议这样使用:

1 if(userOpt.isPresent()) {
2       System.out.println(userOpt.get().getName());
3 } else {
4       //。。。
5 }

  2、使用ifPresent()来进行对象操作,存在则操作,否则不操作。

1 //实例存在则操作,否则不操作
2 userOpt.ifPresent(u -> System.out.println(u.getName()));
3 userOpt2.ifPresent(u -> System.out.println(u.getName()));

  3、使用map/flatMap来获取关联数据

 1 //使用map方法获取关联数据
 2 System.out.println(userOpt.map(u -> u.getName()).orElse("Unknown"));
 3 System.out.println(userOpt2.map(u -> u.getName()).orElse("Default"));
 4 //使用flatMap方法获取关联数据
 5 List<String> interests = new ArrayList<String>();
 6 interests.add("a");interests.add("b");interests.add("c");
 7 user.setInterests(interests);
 8 List<String> interests2 = Optional.of(user)
 9        .flatMap(u -> Optional.ofNullable(u.getInterests()))
10        .orElse(Collections.emptyList());
11 System.out.println(interests2.isEmpty());

总结

  正确使用Optiona不仅可以避免NPE问题,还可以使代码看起来更加简洁。

① 创建Optional

 1 public class Test {
 2 
 3     public static void main(String[] args) throws Exception {
 4         // 创建optional对象
 5         User user = getUser();
 6         // 通过Optional中的方法对对象进行包装
 7         Optional<User> optional = Optional.ofNullable(user);
 8         // 如果这个User对象不为空, 则会执行匿名内部类中的方法, 如果对象为空, 则不会执行
 9         optional.ifPresent(new Consumer<User>() {
10             @Override
11             public void accept(User user) {
12                 System.out.println(user.getName());
13             }
14         });
15         
16     }
17 
18     public static User getUser() {
19         return new User(1, "张三");
20     }
21 }
22 
23 @Data
24 @AllArgsConstructor
25 class User {
26     private Integer id;
27     private String name;
28 }
  ☆ 在使用的时候还是比较繁琐,需要包装一下,那么可以在获取对象的方法上进行修改,直接返回Optional对象  
  实际的开发中,我们的数据很多都是从数据库中获取的,Mybatis从3.5版本开始已经支持Optional了,可以直接将Dao层方法的返回值类型定义为Optional类型,Mybatis会自己把数据封装成Optional对象返回,封装的过程也不需要自己操作。
 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         // 创建optional对象
 5         Optional<User> optional = getUser();
 6         optional.ifPresent(x -> System.out.println(x.getName()));
 7     }
 8 
 9     public static Optional<User> getUser() {
10         return Optional.ofNullable(new User(1, "张三"));
11     }
12 }
13 
14 @Data
15 @AllArgsConstructor
16 class User {
17     private Integer id;
18     private String name;
19 }

  ☆ 如果确定一个对象不是空的,则可以使用Optional的静态方法of()来把数据进行封装成Optional对象

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 5         User user = new User(1, "张三");
 6         Optional<User> optional = Optional.of(user);// 明确了对象不可能为空, 使用of方法是没问题的, 但是一旦对象为空, 则会报错, 因为of调用的Optional构造方法中校验为空就会抛空指针异常
 7         optional.ifPresent(x-> System.out.println(x.getName()));
 8     }
 9 }
10 
11 @Data
12 @AllArgsConstructor
13 class User {
14     private Integer id;
15     private String name;
16 }

  ☆ Optional的构造方法

 1     private Optional(T var1) {
 2         this.value = Objects.requireNonNull(var1);
 3     }
 4     // Objects.requireNonNull源码部分
 5     public static <T> T requireNonNull(T var0) {
 6         if (var0 == null) {
 7             throw new NullPointerException();
 8         } else {
 9             return var0;
10         }
11     }

  ☆ 如果一个方法的返回值类型为Optional类型,并且经过判断发现返回值为null,这个时候需要把null封装成Optional对象返回,则可以使用Optional的静态方法empty()来进行封装

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         Optional<User> userById = getUserById(2);
 5         // 当Optional中的值为null的时候是不会执行ifPresent内部的方法的
 6         userById.ifPresent(x -> System.out.println(x.getName()));
 7         System.out.println("结束");
 8     }
 9 
10     public static Optional<User> getUserById(Integer id) {
11         User user = new User(1, "张三");
12         if (user.getId().equals(id)) {
13             return Optional.of(user);
14         }
15         return Optional.empty();
16     }
17 }
18 
19 @Data
20 @AllArgsConstructor
21 class User {
22     private Integer id;
23     private String name;
24 }

② 安全消费值

  调用Optional的ifPresent方法, 优雅的避免空指针异常,安全的消费数据

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         Optional<User> userById = getUserById(1);
 5         // ifPresent方法内部判断了当对象不为空才会执行消费操作, 保证了消费的安全
 6         userById.ifPresent(new Consumer<User>() {
 7             @Override
 8             public void accept(User user) {
 9                 System.out.println(user.getName());
10             }
11         });
12     }
13 
14     public static Optional<User> getUserById(Integer id) {
15         User user = new User(1, "张三");
16         if (user.getId().equals(id)) {
17             return Optional.of(user);
18         }
19         return Optional.empty();
20     }
21 }
22 
23 @Data
24 @AllArgsConstructor
25 class User {
26     private Integer id;
27     private String name;
28 }

③ 获取值

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         Optional<User> optional = getUserById(1);
 5         // 使用get获取值的时候, 如果值为null, 则会直接抛异常
 6         User user = optional.get();
 7         System.out.println(user);
 8     }
 9 
10     public static Optional<User> getUserById(Integer id) {
11         User user = new User(1, "张三");
12         if (user.getId().equals(id)) {
13             return Optional.of(user);
14         }
15         return Optional.empty();
16     }
17 }
18 
19 @Data
20 @AllArgsConstructor
21 class User {
22     private Integer id;
23     private String name;
24 }

  get方法的源码

1     public T get() {
2         if (this.value == null) {
3             throw new NoSuchElementException("No value present");  // 当对象为null 直接抛异常
4         } else {
5             return this.value;
6         }
7     }

④ 安全的获取值

  orElseGet

  源码:

1     public T orElseGet(Supplier<? extends T> var1) {
2         return this.value != null ? this.value : var1.get();
3     }

  示例:

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         Optional<User> optional = getUserById(1);
 5         // 如果Optional中的值不为null, 则返回该值, 如果为null, 则返回Supplier中get方法提供的默认值
 6         User user = optional.orElseGet(new Supplier<User>() {
 7             @Override
 8             public User get() {
 9                 return new User(2, "李四");
10             }
11         });
12     }
13 
14     public static Optional<User> getUserById(Integer id) {
15         User user = new User(1, "张三");
16         if (user.getId().equals(id)) {
17             return Optional.of(user);
18         }
19         return Optional.empty();
20     }
21 }
22 
23 @Data
24 @AllArgsConstructor
25 class User {
26     private Integer id;
27     private String name;
28 }

  orElseThrow

  源码:

1     public <X extends Throwable> T orElseThrow(Supplier<? extends X> var1) throws X {
2         if (this.value != null) {
3             return this.value;
4         } else {
5             throw (Throwable)var1.get();
6         }
7     }

  示例:

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         Optional<User> optional = getUserById(1);
 5         // 如果Optional中的值不为null, 则返回该值, 如果为null, 则抛出指定的异常
 6         try {
 7             User user = optional.orElseThrow(new Supplier<Throwable>() {
 8                 @Override
 9                 public Throwable get() {
10                     return new RuntimeException("数据为null");
11                 }
12             });
13             System.out.println(user.getName());
14         } catch (Throwable e) {
15             e.printStackTrace();
16         }
17     }
18 
19     public static Optional<User> getUserById(Integer id) {
20         User user = new User(1, "张三");
21         if (user.getId().equals(id)) {
22             return Optional.of(user);
23         }
24         return Optional.empty();
25     }
26 }
27 
28 @Data
29 @AllArgsConstructor
30 class User {
31     private Integer id;
32     private String name;
33 }

⑤ 过滤

  使用filter方法对数据进行过滤,如果原本有数据,但是不符合判断,也会编程一个无数据的Optional对象

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         Optional<User> optional = getUserById(1);
 5         // 当optional中存在值, 则执行filter中的条件判断, 当有满足的条件, 则返回该值
 6         // 当optional中存在值, 则执行filter中的条件判断, 当没有有满足的条件, 则返回Optional.empty()
 7         // 当optional中不存在值, 则不会执行filter中的条件判断, 且会返回Optional.empty()
 8         Optional<User> userOptional = optional.filter(new Predicate<User>() {
 9             @Override
10             public boolean test(User user) {
11                 return user.getName().equals("李四");
12             }
13         });
14         // 在经过过滤之后, 也会返回空的值, 所以获取数据的时候也需要进行安全的获取值
15         User user = userOptional.orElseGet(()-> new User(2, "李四"));
16     }
17 
18     public static Optional<User> getUserById(Integer id) {
19         User user = new User(1, "张三");
20         if (user.getId().equals(id)) {
21             return Optional.of(user);
22         }
23         return Optional.empty();
24     }
25 }
26 
27 @Data
28 @AllArgsConstructor
29 class User {
30     private Integer id;
31     private String name;
32 }

⑥ 判断

   在使用isPresent方法进行是否存在数据的判断,如果为空返回值为false,如果不为空,返回值为true,但是这种方式并不能体现Optional的好处,更推荐使用ifPresent方法

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         Optional<User> optional = getUserById(1);
 5         // 判断Optional中的值不为null
 6         if (optional.isPresent()) {
 7             System.out.println(optional.get());
 8         }
 9     }
10 
11     public static Optional<User> getUserById(Integer id) {
12         User user = new User(1, "张三");
13         if (user.getId().equals(id)) {
14             return Optional.of(user);
15         }
16         return Optional.empty();
17     }
18 }
19 
20 @Data
21 @AllArgsConstructor
22 class User {
23     private Integer id;
24     private String name;
25 }

⑦ 数据转换

  Optional还提供了map方法可以让我们对数据进行转换,并且转换得到的数据也还是被Optional包装好的,保证使用的安全

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         Optional<User> optional = getUserById(1);
 5         // 将Optional中的User转换成id
 6         Optional<Integer> idOptional = optional.map(new Function<User, Integer>() {
 7             @Override
 8             public Integer apply(User user) {
 9                 return user.getId();
10             }
11         });
12         // 如果这个Optional中的值不为null, 则打印
13         idOptional.ifPresent(System.out::println);
14     }
15 
16     public static Optional<User> getUserById(Integer id) {
17         User user = new User(1, "张三");
18         if (user.getId().equals(id)) {
19             return Optional.of(user);
20         }
21         return Optional.empty();
22     }
23 }
24 
25 @Data
26 @AllArgsConstructor
27 class User {
28     private Integer id;
29     private String name;
30 }

 3、日期时间


   Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。java.util.Date和SimpleDateFormatter都不是线程安全的,而LocalDate和LocalTime和最基本的String一样,是不变类型,不但线程安全,而且不能修改。以下为一些常用时间对象:
    • Instant:表示时刻,不直接对应年月日信息,需要通过时区转换
      •   

    • LocalDateTime: 表示与时区无关的日期和时间信息,不直接对应时刻,需要通过时区转换
    • LocalDate:表示与时区无关的日期,与LocalDateTime相比,只有日期信息,没有时间信息
    • LocalTime:表示与时区无关的时间,与LocalDateTime相比,只有时间信息,没有日期信息
    • ZonedDateTime: 表示特定时区的日期和时间
    • ZoneId/ZoneOffset:表示时区

LocalDate

 1      LocalDate now = LocalDate.now();
 2         LocalDate now1 = LocalDate.now(Clock.systemUTC());
 3 
 4    System.out.println(now);
 5    System.out.println(now1);
 6 
 7         LocalDate of = LocalDate.of(2019, 3, 6);
 8         //严格按照ISO yyyy-MM-dd验证,03写成3都不行
 9         LocalDate parse = LocalDate.parse("2019-03-06");
10    System.out.println(of);
11    System.out.println(parse);
12 14 
15         //当天开始时间
16    System.out.println(now.atStartOfDay()); // 2024-03-19T00:00
17         //当月第一天日期
18    System.out.println(now.with(TemporalAdjusters.firstDayOfMonth()));
19         //本月第二天日期
20         System.out.println(now.withDayOfMonth(2));
21         //当月最后一天
22    System.out.println(now.with(TemporalAdjusters.lastDayOfMonth()));
23    System.out.println(now.getDayOfMonth());
24         //当月下一天
25         System.out.println(now.plusDays(1));
26         //当月上一天
27         System.out.println(now.minusDays(1));
28    System.out.println(now.getDayOfWeek());
29         //当月下一周
30         System.out.println(now.plusWeeks(1));
31         //当月上一周
32         System.out.println(now.minusWeeks(1));
33         System.out.println(now.getMonth() + "-" + now.getMonthValue());
34         //当月下一个月
35         System.out.println(now.plusMonths(1));
36         //当月上一个月
37         System.out.println(now.minusMonths(1));
38 
39         //时间比较
40         System.out.println(now.isEqual(LocalDate.of(2019, 03, 06)));

LocalTime 

 1 LocalTime now = LocalTime.now();
 2 //指定时区
 3 LocalTime now1 = LocalTime.now(Clock.system(ZoneId.systemDefault()));
 4 LocalTime now2 = LocalTime.now(Clock.systemUTC());
 5 
 6 System.out.println(now);
 7 System.out.println(now1);
 8 System.out.println(now2);
 9 
10 System.out.println("************now************");
11 //清除毫秒位
12 System.out.println(now.withNano(0));
13 //获取当前的小时
14 System.out.println(now.getHour());
15 //解析时间时间也是按照ISO格式识别,但可以识别以下3种格式: 12:00 12:01:02 12:01:02.345
16 System.out.println(LocalTime.parse("11:58:12"));
17 
18 //时间比较
19 LocalTime other = LocalTime.of(13, 45, 59);
20 System.out.println(now.isBefore(other));
21 System.out.println(now.isAfter(other));

LocalDateTime

 1 LocalDateTime now = LocalDateTime.now();
 2 LocalDateTime now1 = LocalDateTime.now(Clock.system(ZoneId.systemDefault()));
 3 LocalDateTime now2 = LocalDateTime.now(Clock.systemUTC());
 4 
 5 System.out.println(now);
 6 System.out.println(now1);
 7 System.out.println(now2); 
 8 // 时间格式转换
 9 System.out.println(now.format(DateTimeFormatter.ofPattern(PATTERN_1)));
10 System.out.println(now.format(DateTimeFormatter.ofPattern(PATTERN_2)));
11 System.out.println(now.format(DateTimeFormatter.ofPattern(PATTERN_3)));

Date转换为LocalDateTime

1     /**
2      * Date转换为LocalDateTime
3      */
4     public static LocalDateTime date2LocalDateTime() {
5        Date date = new Date();
6        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
7     }

LocalDateTime转换为Date

1     /**
2      * LocalDateTime转换为Date
3      */
4     public static Date localDateTime2Date() {
5         LocalDateTime now = LocalDateTime.now();
6         Instant instant = now.atZone(ZoneId.systemDefault()).toInstant();
7         return Date.from(instant);
8     }

日期时间比较

1 比如在15:30:30之前:
2 LocalTime.now().isBefore(LocalTime.of(15, 30,30))
3 
4 或15:30:30之后:
5 LocalTime.now().isAfter(LocalTime.of(15, 30,30))
6 
7 相等:
8 LocalTime.now().equal(LocalTime.of(15, 30,30))

LocalDateTime、LocalDate、LocalTime示例

  1 import java.time.LocalDate;
  2 import java.time.LocalDateTime;
  3 import java.time.LocalTime;
  4 import java.time.temporal.ChronoField;
  5 import java.time.temporal.TemporalAdjusters;
  6 
  7 public class Test {
  8 
  9     public static void main(String[] args) {
 10         LocalTime localTime = LocalTime.now(); // 时分秒
 11         LocalDate localDate = LocalDate.now(); // 年月日
 12         LocalDateTime localDateTime = LocalDateTime.now(); // 年月日时分秒
 13         System.out.println(localTime);      // 14:36:30.402
 14         System.out.println(localDate);      // 2024-03-19
 15         System.out.println(localDateTime);  // 2024-03-19T14:36:30.402
 16 
 17         // 获取当月第一天
 18         System.out.println(localDate.withDayOfMonth(1));                            // 2024-03-01
 19         System.out.println(localDate.with(TemporalAdjusters.firstDayOfMonth()));    // 2024-03-01
 20         System.out.println(localDate.with(ChronoField.DAY_OF_MONTH, 1L));         // 2024-03-01
 21 
 22         LocalDate ofLocalDate = LocalDate.of(2020, 10, 20);
 23         System.out.println(ofLocalDate); // 2020-10-20
 24         LocalTime ofLocalTime = LocalTime.of(10, 20, 30);
 25         System.out.println(ofLocalTime); // 10:20:30
 26         LocalDateTime ofLocalDateTime = LocalDateTime.of(2020, 10, 20, 10, 30, 20);
 27         System.out.println(ofLocalDateTime); // 2020-10-20T10:30:20
 28 
 29         // 字符串解析
 30         LocalDate parseLocalDate = LocalDate.parse("2022-03-03");
 31         System.out.println(parseLocalDate);     // 2022-03-03
 32         LocalTime parseLocalTime = LocalTime.parse("10:30:01");
 33         System.out.println(parseLocalTime);     // 10:30:01
 34         LocalDateTime parseLocalDateTime = LocalDateTime.parse("2022-03-03T10:30:01");
 35         System.out.println(parseLocalDateTime); // 2022-03-03T10:30:01
 36 
 37         // 获取年、月、日 (LocalDate中没有时分秒, 只能获取年月日)
 38         int year = localDate.getYear();
 39         int month = localDate.getMonth().getValue();
 40         int dayOfYear = localDate.getDayOfYear();
 41         int dayOfMonth = localDate.getDayOfMonth();
 42         int dayOfWeek = localDate.getDayOfWeek().getValue();
 43         System.out.println(year);       // 2024
 44         System.out.println(month);      // 3
 45         System.out.println(dayOfYear);  // 79   (当年的第几天)
 46         System.out.println(dayOfMonth); // 19   (当月的第几天, 几号)
 47         System.out.println(dayOfWeek);  // 2    (当周的第几天, 周几)
 48 
 49         // 修改年月日, 会返回新的对象, 不修改原LocalDate
 50         LocalDate withYear = localDate.withYear(2000);              // 修改成2000年
 51         LocalDate withMonth = localDate.withMonth(1);               // 修改成1月
 52         LocalDate withDayOfYear = localDate.withDayOfYear(78);      // 修改成一年的第78天
 53         LocalDate withDayOfMonth = localDate.withDayOfMonth(18);    // 修改成一个月的第18天
 54         System.out.println(withYear);       // 2000-03-19
 55         System.out.println(withMonth);      // 2024-01-19
 56         System.out.println(withDayOfYear);  // 2024-03-18
 57         System.out.println(withDayOfMonth); // 2024-03-18
 58 
 59         // 年月日加减
 60         LocalDate plusYears = localDate.plusYears(1);       // 加1年
 61         LocalDate plusMonths = localDate.plusMonths(1);     // 加1个月
 62         LocalDate plusDays = localDate.plusDays(1);         // 加1天
 63         LocalDate plusDaysOfWeek = localDate.plusDays(7);   // 加7天
 64         LocalDate plusWeeks = localDate.plusWeeks(1);       // 加一周(7天)
 65         LocalDate plusLocalDate = localDate.plusYears(1).plusMonths(1).plusDays(1);
 66         System.out.println(plusYears);      // 2025-03-19
 67         System.out.println(plusMonths);     // 2024-04-19
 68         System.out.println(plusDays);       // 2024-03-20
 69         System.out.println(plusDaysOfWeek); // 2024-03-26
 70         System.out.println(plusWeeks);      // 2024-03-26
 71         System.out.println(plusLocalDate);  // 2025-04-20
 72         System.out.println("===================================");
 73 
 74         LocalDate minusYears = localDate.minusYears(1);
 75         LocalDate minusMonths = localDate.minusMonths(1);
 76         LocalDate minusDays = localDate.minusDays(1);
 77         LocalDate minusWeeks = localDate.minusWeeks(1);
 78         LocalDate minusLocalDate = localDate.minusYears(1).minusMonths(1).minusDays(1);
 79         System.out.println(minusYears);     // 2023-03-19
 80         System.out.println(minusMonths);    // 2024-02-19
 81         System.out.println(minusDays);      // 2024-03-18
 82         System.out.println(minusWeeks);     // 2024-03-12
 83         System.out.println(minusLocalDate); // 2023-02-18
 84         System.out.println("===================================");
 85 
 86 
 87         // 获取时、分、秒 (LocalTime中没有年月日, 只能获取时分秒)
 88         int hour = localTime.getHour();
 89         int minute = localTime.getMinute();
 90         int second = localTime.getSecond();
 91         int nano = localTime.getNano();
 92         System.out.println(hour);       // 14
 93         System.out.println(minute);     // 36
 94         System.out.println(second);     // 30
 95         System.out.println(nano);       // 402000000
 96 
 97         // 修改时分秒
 98         LocalTime withHour = localTime.withHour(10);        // 修改为10点
 99         LocalTime withMinute = localTime.withMinute(20);    // 修改分为20分
100         LocalTime withSecond = localTime.withSecond(30);    // 修改秒为30秒
101         LocalTime withNano = localTime.withNano(0);         // 修改纳秒为0
102         LocalTime withLocalTime = localTime.withHour(10).withMinute(20).withSecond(30).withNano(0); // 同时修改时分秒纳秒
103         System.out.println(withHour);       // 10:36:30.402
104         System.out.println(withMinute);     // 14:20:30.402
105         System.out.println(withSecond);     // 14:36:30.402
106         System.out.println(withNano);       // 14:36:30
107         System.out.println(withLocalTime);  // 10:20:30
108 
109         // 时分秒加
110         System.out.println(localTime);                      // 14:36:30.402 (当前时分秒)
111         System.out.println(localTime.plusHours(1));         // 15:36:30.402 (基于当前时分秒加1小时)
112         System.out.println(localTime.plusMinutes(1));       // 14:37:30.402 (基于当前时分秒加1分钟)
113         System.out.println(localTime.plusSeconds(1));       // 14:36:31.402 (基于当前时分秒加1秒)
114         System.out.println(localTime.plusNanos(1));         // 14:36:30.402000001 (基于当前时分秒加1纳秒)
115         System.out.println(localTime.plusHours(1).plusMinutes(1).plusSeconds(1).plusNanos(1));  // 15:37:31.402000001 (基于当前时分秒加1小时1分1秒1纳秒)
116         // 时分秒减
117         System.out.println(localTime.minusHours(1));        // 13:36:30.402
118         System.out.println(localTime.minusMinutes(1));      // 14:35:30.402
119         System.out.println(localTime.minusSeconds(1));      // 14:36:29.402
120         System.out.println(localTime.minusNanos(1));        // 14:36:30.401999999
121         System.out.println(localTime.minusHours(1).minusMinutes(1).minusSeconds(1).minusNanos(1));  // 13:35:29.401999999
122     }
123 }

ZoneId、ZonedDateTime时区

 1 public static void main(String[] args) {
 2     ZoneId zoneId = ZoneId.systemDefault();
 3     System.out.println(zoneId.getId());     // Asia/Shanghai
 4     System.out.println(zoneId.toString());  // Asia/Shanghai
 5     System.out.println(zoneId);             // Asia/Shanghai
 6 
 7     // 获取全部时区Id
 8     System.out.println(ZoneId.getAvailableZoneIds());
 9     // [Asia/Aden, America/Cuiaba, Etc/GMT+9, Etc/GMT+8, Africa/Nairobi, America/Marigot, Asia/Aqtau, Pacific/Kwajalein, America/El_Salvador, Asia/Pontianak, Africa/Cairo, Pacific/Pago_Pago, Africa/Mbabane, Asia/Kuching, Pacific/Honolulu, Pacific/Rarotonga, America/Guatemala, Australia/Hobart, Europe/London, America/Belize, America/Panama, Asia/Chungking, America/Managua, America/Indiana/Petersburg, Asia/Yerevan, Europe/Brussels, GMT, Europe/Warsaw, America/Chicago, Asia/Kashgar, Chile/Continental, Pacific/Yap, CET, Etc/GMT-1, Etc/GMT-0, Europe/Jersey, America/Tegucigalpa, Etc/GMT-5, Europe/Istanbul, America/Eirunepe, Etc/GMT-4, America/Miquelon, Etc/GMT-3, Europe/Luxembourg, Etc/GMT-2, Etc/GMT-9, America/Argentina/Catamarca, Etc/GMT-8, Etc/GMT-7, Etc/GMT-6, Europe/Zaporozhye, Canada/Yukon, Canada/Atlantic, Atlantic/St_Helena, Australia/Tasmania, Libya, Europe/Guernsey, America/Grand_Turk, Asia/Samarkand, America/Argentina/Cordoba, Asia/Phnom_Penh, Africa/Kigali, Asia/Almaty, US/Alaska, Asia/Dubai, Europe/Isle_of_Man, America/Araguaina, Cuba, Asia/Novosibirsk, America/Argentina/Salta, Etc/GMT+3, Africa/Tunis, Etc/GMT+2, Etc/GMT+1, Pacific/Fakaofo, Africa/Tripoli, Etc/GMT+0, Israel, Africa/Banjul, Etc/GMT+7, Indian/Comoro, Etc/GMT+6, Etc/GMT+5, Etc/GMT+4, Pacific/Port_Moresby, US/Arizona, Antarctica/Syowa, Indian/Reunion, Pacific/Palau, Europe/Kaliningrad, America/Montevideo, Africa/Windhoek, Asia/Karachi, Africa/Mogadishu, Australia/Perth, Brazil/East, Etc/GMT, Asia/Chita, Pacific/Easter, Antarctica/Davis, Antarctica/McMurdo, Asia/Macao, America/Manaus, Africa/Freetown, Europe/Bucharest, Asia/Tomsk, America/Argentina/Mendoza, Asia/Macau, Europe/Malta, Mexico/BajaSur, Pacific/Tahiti, Africa/Asmera, Europe/Busingen, America/Argentina/Rio_Gallegos, Africa/Malabo, Europe/Skopje, America/Catamarca, America/Godthab, Europe/Sarajevo, Australia/ACT, GB-Eire, Africa/Lagos, America/Cordoba, Europe/Rome, Asia/Dacca, Indian/Mauritius, Pacific/Samoa, America/Regina, America/Fort_Wayne, America/Dawson_Creek, Africa/Algiers, Europe/Mariehamn, America/St_Johns, America/St_Thomas, Europe/Zurich, America/Anguilla, Asia/Dili, America/Denver, Africa/Bamako, Europe/Saratov, GB, Mexico/General, Pacific/Wallis, Europe/Gibraltar, Africa/Conakry, Africa/Lubumbashi, Asia/Istanbul, America/Havana, NZ-CHAT, Asia/Choibalsan, America/Porto_Acre, Asia/Omsk, Europe/Vaduz, US/Michigan, Asia/Dhaka, America/Barbados, Europe/Tiraspol, Atlantic/Cape_Verde, Asia/Yekaterinburg, America/Louisville, Pacific/Johnston, Pacific/Chatham, Europe/Ljubljana, America/Sao_Paulo, Asia/Jayapura, America/Curacao, Asia/Dushanbe, America/Guyana, America/Guayaquil, America/Martinique, Portugal, Europe/Berlin, Europe/Moscow, Europe/Chisinau, America/Puerto_Rico, America/Rankin_Inlet, Pacific/Ponape, Europe/Stockholm, Europe/Budapest, America/Argentina/Jujuy, Australia/Eucla, Asia/Shanghai, Universal, Europe/Zagreb, America/Port_of_Spain, Europe/Helsinki, Asia/Beirut, Asia/Tel_Aviv, Pacific/Bougainville, US/Central, Africa/Sao_Tome, Indian/Chagos, America/Cayenne, Asia/Yakutsk, Pacific/Galapagos, Australia/North, Europe/Paris, Africa/Ndjamena, Pacific/Fiji, America/Rainy_River, Indian/Maldives, Australia/Yancowinna, SystemV/AST4, Asia/Oral, America/Yellowknife, Pacific/Enderbury, America/Juneau, Australia/Victoria, America/Indiana/Vevay, Asia/Tashkent, Asia/Jakarta, Africa/Ceuta, Asia/Barnaul, America/Recife, America/Buenos_Aires, America/Noronha, America/Swift_Current, Australia/Adelaide, America/Metlakatla, Africa/Djibouti, America/Paramaribo, Asia/Qostanay, Europe/Simferopol, Europe/Sofia, Africa/Nouakchott, Europe/Prague, America/Indiana/Vincennes, Antarctica/Mawson, America/Kralendijk, Antarctica/Troll, Europe/Samara, Indian/Christmas, America/Antigua, Pacific/Gambier, America/Indianapolis, America/Inuvik, America/Iqaluit, Pacific/Funafuti, UTC, Antarctica/Macquarie, Canada/Pacific, America/Moncton, Africa/Gaborone, Pacific/Chuuk, Asia/Pyongyang, America/St_Vincent, Asia/Gaza, Etc/Universal, PST8PDT, Atlantic/Faeroe, Asia/Qyzylorda, Canada/Newfoundland, America/Kentucky/Louisville, America/Yakutat, America/Ciudad_Juarez, Asia/Ho_Chi_Minh, Antarctica/Casey, Europe/Copenhagen, Africa/Asmara, Atlantic/Azores, Europe/Vienna, ROK, Pacific/Pitcairn, America/Mazatlan, Australia/Queensland, Pacific/Nauru, Europe/Tirane, Asia/Kolkata, SystemV/MST7, Australia/Canberra, MET, Australia/Broken_Hill, Europe/Riga, America/Dominica, Africa/Abidjan, America/Mendoza, America/Santarem, Kwajalein, America/Asuncion, Asia/Ulan_Bator, NZ, America/Boise, Australia/Currie, EST5EDT, Pacific/Guam, Pacific/Wake, Atlantic/Bermuda, America/Costa_Rica, America/Dawson, Asia/Chongqing, Eire, Europe/Amsterdam, America/Indiana/Knox, America/North_Dakota/Beulah, Africa/Accra, Atlantic/Faroe, Mexico/BajaNorte, America/Maceio, Etc/UCT, Pacific/Apia, GMT0, America/Atka, Pacific/Niue, Australia/Lord_Howe, Europe/Dublin, Pacific/Truk, MST7MDT, America/Monterrey, America/Nassau, America/Jamaica, Asia/Bishkek, America/Atikokan, Atlantic/Stanley, Australia/NSW, US/Hawaii, SystemV/CST6, Indian/Mahe, Asia/Aqtobe, America/Sitka, Asia/Vladivostok, Africa/Libreville, Africa/Maputo, Zulu, America/Kentucky/Monticello, Africa/El_Aaiun, Africa/Ouagadougou, America/Coral_Harbour, Pacific/Marquesas, Brazil/West, America/Aruba, America/North_Dakota/Center, America/Cayman, Asia/Ulaanbaatar, Asia/Baghdad, Europe/San_Marino, America/Indiana/Tell_City, America/Tijuana, Pacific/Saipan, SystemV/YST9, Africa/Douala, America/Chihuahua, America/Ojinaga, Asia/Hovd, America/Anchorage, Chile/EasterIsland, America/Halifax, Antarctica/Rothera, America/Indiana/Indianapolis, US/Mountain, Asia/Damascus, America/Argentina/San_Luis, America/Santiago, Asia/Baku, America/Argentina/Ushuaia, Atlantic/Reykjavik, Africa/Brazzaville, Africa/Porto-Novo, America/La_Paz, Antarctica/DumontDUrville, Asia/Taipei, Antarctica/South_Pole, Asia/Manila, Asia/Bangkok, Africa/Dar_es_Salaam, Poland, Atlantic/Madeira, Antarctica/Palmer, America/Thunder_Bay, Africa/Addis_Ababa, Asia/Yangon, Europe/Uzhgorod, Brazil/DeNoronha, Asia/Ashkhabad, Etc/Zulu, America/Indiana/Marengo, America/Creston, America/Punta_Arenas, America/Mexico_City, Antarctica/Vostok, Asia/Jerusalem, Europe/Andorra, US/Samoa, PRC, Asia/Vientiane, Pacific/Kiritimati, America/Matamoros, America/Blanc-Sablon, Asia/Riyadh, Iceland, Pacific/Pohnpei, Asia/Ujung_Pandang, Atlantic/South_Georgia, Europe/Lisbon, Asia/Harbin, Europe/Oslo, Asia/Novokuznetsk, CST6CDT, Atlantic/Canary, America/Knox_IN, Asia/Kuwait, SystemV/HST10, Pacific/Efate, Africa/Lome, America/Bogota, America/Menominee, America/Adak, Pacific/Norfolk, Europe/Kirov, America/Resolute, Pacific/Kanton, Pacific/Tarawa, Africa/Kampala, Asia/Krasnoyarsk, Greenwich, SystemV/EST5, America/Edmonton, Europe/Podgorica, Australia/South, Canada/Central, Africa/Bujumbura, America/Santo_Domingo, US/Eastern, Europe/Minsk, Pacific/Auckland, Africa/Casablanca, America/Glace_Bay, Canada/Eastern, Asia/Qatar, Europe/Kiev, Singapore, Asia/Magadan, SystemV/PST8, America/Port-au-Prince, Europe/Belfast, America/St_Barthelemy, Asia/Ashgabat, Africa/Luanda, America/Nipigon, Atlantic/Jan_Mayen, Brazil/Acre, Asia/Muscat, Asia/Bahrain, Europe/Vilnius, America/Fortaleza, Etc/GMT0, US/East-Indiana, America/Hermosillo, America/Cancun, Africa/Maseru, Pacific/Kosrae, Africa/Kinshasa, Asia/Kathmandu, Asia/Seoul, Australia/Sydney, America/Lima, Australia/LHI, America/St_Lucia, Europe/Madrid, America/Bahia_Banderas, America/Montserrat, Asia/Brunei, America/Santa_Isabel, Canada/Mountain, America/Cambridge_Bay, Asia/Colombo, Australia/West, Indian/Antananarivo, Australia/Brisbane, Indian/Mayotte, US/Indiana-Starke, Asia/Urumqi, US/Aleutian, Europe/Volgograd, America/Lower_Princes, America/Vancouver, Africa/Blantyre, America/Rio_Branco, America/Danmarkshavn, America/Detroit, America/Thule, Africa/Lusaka, Asia/Hong_Kong, Iran, America/Argentina/La_Rioja, Africa/Dakar, SystemV/CST6CDT, America/Tortola, America/Porto_Velho, Asia/Sakhalin, Etc/GMT+10, America/Scoresbysund, Asia/Kamchatka, Asia/Thimbu, Africa/Harare, Etc/GMT+12, Etc/GMT+11, Navajo, America/Nome, Europe/Tallinn, Turkey, Africa/Khartoum, Africa/Johannesburg, Africa/Bangui, Europe/Belgrade, Jamaica, Africa/Bissau, Asia/Tehran, WET, Europe/Astrakhan, Africa/Juba, America/Campo_Grande, America/Belem, Etc/Greenwich, Asia/Saigon, America/Ensenada, Pacific/Midway, America/Jujuy, Africa/Timbuktu, America/Bahia, America/Goose_Bay, America/Virgin, America/Pangnirtung, Asia/Katmandu, America/Phoenix, Africa/Niamey, America/Whitehorse, Pacific/Noumea, Asia/Tbilisi, Europe/Kyiv, America/Montreal, Asia/Makassar, America/Argentina/San_Juan, Hongkong, UCT, Asia/Nicosia, America/Indiana/Winamac, SystemV/MST7MDT, America/Argentina/ComodRivadavia, America/Boa_Vista, America/Grenada, Asia/Atyrau, Australia/Darwin, Asia/Khandyga, Asia/Kuala_Lumpur, Asia/Famagusta, Asia/Thimphu, Asia/Rangoon, Europe/Bratislava, Asia/Calcutta, America/Argentina/Tucuman, Asia/Kabul, Indian/Cocos, Japan, Pacific/Tongatapu, America/New_York, Etc/GMT-12, Etc/GMT-11, America/Nuuk, Etc/GMT-10, SystemV/YST9YDT, Europe/Ulyanovsk, Etc/GMT-14, Etc/GMT-13, W-SU, America/Merida, EET, America/Rosario, Canada/Saskatchewan, America/St_Kitts, Arctic/Longyearbyen, America/Fort_Nelson, America/Caracas, America/Guadeloupe, Asia/Hebron, Indian/Kerguelen, SystemV/PST8PDT, Africa/Monrovia, Asia/Ust-Nera, Egypt, Asia/Srednekolymsk, America/North_Dakota/New_Salem, Asia/Anadyr, Australia/Melbourne, Asia/Irkutsk, America/Shiprock, America/Winnipeg, Europe/Vatican, Asia/Amman, Etc/UTC, SystemV/AST4ADT, Asia/Tokyo, America/Toronto, Asia/Singapore, Australia/Lindeman, America/Los_Angeles, SystemV/EST5EDT, Pacific/Majuro, America/Argentina/Buenos_Aires, Europe/Nicosia, Pacific/Guadalcanal, Europe/Athens, US/Pacific, Europe/Monaco]
10 
11     // 获取带时区的时间
12     ZoneId zoneId1 = ZoneId.of("America/New_York");
13     ZonedDateTime now = ZonedDateTime.now(zoneId1); // 获取指定时区(美国纽约)的时间
14     System.out.println(now);        // 2024-03-19T03:12:37.262-04:00[America/New_York]
15 
16     // 获取世界标准时间
17     ZonedDateTime now1 = ZonedDateTime.now(Clock.systemUTC());
18     System.out.println(now1);       // 2024-03-19T07:12:37.268Z
19 
20     // 获取系统默认时区对应的时间
21     ZonedDateTime now2 = ZonedDateTime.now();
22     System.out.println(now2);       // 2024-03-19T15:12:37.268+08:00[Asia/Shanghai]
23 
24     // ZonedDateTime转LocalDateTime
25     System.out.println(now2.toLocalDateTime()); // 2024-03-19T15:14:37.157
26     // ZonedDateTime转LocalDate
27     System.out.println(now2.toLocalDate());
28     // ZonedDateTime转LocalTime
29     System.out.println(now2.toLocalTime());
30 
31     // ZonedDateTime包含了全部LocalDateTime、LocalDate、LocalTime获取、修改、增加、减少日期的操作
32     // ZonedDateTime获取年月日时分秒
33     System.out.println(now2.getYear());
34     System.out.println(now2.getMonth());
35     System.out.println(now2.getDayOfMonth());
36     System.out.println(now2.getHour());
37     System.out.println(now2.getMinute());
38     System.out.println(now2.getSecond());
39 }

Instant

 1     public static void main(String[] args) {
 2         Instant now = Instant.now();
 3         System.out.println(now);                    // 2024-03-19T07:28:50.257Z
 4         // 时间戳, 总秒数
 5         System.out.println(now.getEpochSecond());   // 1710833330
 6         // 不到1秒的纳秒数
 7         System.out.println(now.getNano());          // 257000000
 8 
 9         Date date = new Date();
10         // 获取当前毫秒值
11         System.out.println(date.getTime());         // 1710833330299
12     }

DateTimeFormatter

 1 public static void main(String[] args) {
 2     DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
 3 
 4     LocalDateTime now = LocalDateTime.now();
 5     System.out.println(now); // 2024-03-19T15:36:56.256
 6 
 7     // 方式1: 按照指定格式格式化日期
 8     System.out.println(formatter.format(now)); // 2024年03月19日 15:36:56
 9     // 方式2:
10     System.out.println(now.format(formatter)); // 2024年03月19日 15:36:56
11 
12     // 解析指定日期格式, 根据指定的格式化器解析
13     String dateString = "2024年03月19日 15:36:56";
14     LocalDate parseLocalDate = LocalDate.parse(dateString, formatter);
15     LocalTime parseLocalTime = LocalTime.parse(dateString, formatter);
16     LocalDateTime parseLocalDateTime = LocalDateTime.parse(dateString, formatter);
17     System.out.println(parseLocalDate);     // 2024-03-19
18     System.out.println(parseLocalTime);     // 15:36:56
19     System.out.println(parseLocalDateTime); // 2024-03-19T15:36:56
20 }

时间、日期间隔  

  

 

  

  

 1     public static void main(String[] args) {
 2 
 3         LocalDate start = LocalDate.of(2029, 8, 10);
 4         LocalDate end = LocalDate.of(2030, 8, 15);
 5 
 6         // 计算两个日期 年月日的差值
 7         Period period = Period.between(start, end);
 8         System.out.println(period.getYears());  // 1
 9         System.out.println(period.getMonths()); // 0
10         System.out.println(period.getDays());   // 5
11 
12 
13         LocalDateTime from = LocalDateTime.of(2025, 11, 11, 10, 0, 0);
14         LocalDateTime to = LocalDateTime.of(2025, 11, 12, 15, 0, 0);
15 
16         // 计算两个时间对象相差的年月日时分秒等信息 (两个日期间隔多少天、多少小时、多少分钟、多少秒、多少毫秒、多少纳秒)
17         Duration duration = Duration.between(from, to);
18         System.out.println(duration.toDays());      // 1
19         System.out.println(duration.toHours());     // 29 (24+5小时)
20         System.out.println(duration.toMinutes());   // 1740
21         System.out.println(duration.getSeconds());  // 104400
22         System.out.println(duration.toMillis());    // 104400000
23         System.out.println(duration.toNanos());     // 104400000000000
24     }

 

4、运算语法


  分组并进行求和组合运算

  示例代码:

 1 List<String> items =
 2         Arrays.asList("apple", "apple", "banana",
 3                 "apple", "orange", "banana", "papaya");
 4 
 5 Map<String, Long> result =
 6         items.stream().collect(
 7                 Collectors.groupingBy(
 8                         Function.identity(), Collectors.counting()
 9                 )
10         );
11 
12 System.out.println(result);

  运行结果:

    {papaya=1, orange=1, banana=2, apple=3}

  或者针对对象的复杂处理:

1 /* 先按slotId进行分组,再对分好组之后的数据进行求和5  */
6 Map<Long, Long> adNumMap = adSlotDataList.stream().collect(Collectors.groupingBy(AdSlotData::getSlotId, Collectors.summingLong(AdSlotData::getAdNum)));

  List转换成Map

  示例代码:

1 /**
2  * List -> Map
3  * 需要注意的是:
4  * toMap 如果集合对象有重复的key,会报错Duplicate key ....
5  *  apple1,apple12的id都为1。
6  *  可以用 (k1,k2)->k1 来设置,如果有重复的key,则保留key1,舍弃key2
7  */
8 Map<Long, AdSlotData> adPVNumMap = adSlotDataList.stream().collect(Collectors.toMap(AdSlotData::getSlotId, adSlotData -> adSlotData, (k1, k2) -> k1));

5、函数接口


函数式接口新特性

    java8中引入了函数式接口新特性,使用@FunctionalInterface标识,表示有且只有一个抽象方法,但可以有多个非抽象方法。
    无论是否加上注解只要接口只有一个抽象方法,都是函数式接口。    
    示例:
 1 package com.notes.java8.functionInterface;
 2 
 3 /**
 4  * 文件描述 函数式接口:
 5  *      有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
 6  **/
 7 @FunctionalInterface
 8 public interface Hello {
 9 
10     /**
11      * abstract 方法,只能有一个
12      */
13     void hello();
14 
15     /**
16   * 允许定义默认方法
17      */
18     default void hi(){
19         System.out.println("this is default method");
20     }
21 
22     /**
23   * 允许定义静态方法
24      */
25     static void hei() {
26         System.out.println("this is static method");
27     }
28 
29     /**
30   * 允许定义 java.lang.Object 里的 public 方法
31      */
32     @Override
33     boolean equals(Object obj);
34 }

四大基本函数式接口

  Function<T, R>

    根据其中抽象方法的参数列表和返回值类型,可以得知可以在方法中对传入的参数计算和转换,把结果返回    
    接受一个入参T,返回R类型对象,使用apply方法获取方法执行的内容
R apply(T t);

  eg:

1 User user = new User(88, "bb");
2 String name = uft.apply(user);
3 System.out.println(name);
4 
5 
6 /**
7  * Function<T, R> lambda写法
8  */
9 private static Function<User, String> uft = u -> u.getName();

  BiFunction<T, R, U>

    接收两个参数,返回一个结果

  Consumer<T>

    根据其中抽象方法的参数列表和返回值类型,可以得知可以在方法中对传入的参数进行消费
    接受一个参数T,没有返回值,使用accept方法对参数执行操作
void accept(T t);

  eg:

1 User user = new User(88, "bb");
2 
3 uc.accept(user);
4 
5 /**
6   * Consumer<T> lambda写法
7   */
8 private static Consumer<User> uc = u -> System.out.println(u.getName());

  BiConsumer<T, U>

    接口两个参数,没有返回值,也就是消费两个入参

  Supplier<T>

    根据其中抽象方法的参数列表和返回值类型,可以得知可以在方法中创建对象,把创建好的对象返回    
    没有入参,返回T类型结果,使用get方法获取返回结果
T get();

  eg:

1 User user1 = us.get();
2 System.out.println(user1.getName());
3 
4 /**
5   * Supplier<T> lambda写法
6   */
7 private static Supplier<User> us = () -> new User(1, "us");

   Predicate<T>

    根据其中抽象方法的参数列表和返回值类型,可以得知可以在方法中对传入的参数条件判断,返回判断结果
    接受一个入参,返回结果为true或者false,使用test方法进行测试并返回测试结果
boolean test(T t);

  eg:

1 boolean test = up.test(user);
2 System.out.println(test);    
3 
4 /**
5   * Predicate<T>
6   */
7 private static Predicate<User> up = u -> !u.getName().isEmpty(); 

  其他的函数式接口可参见以下接口示意图:

          

  常用的默认方法

    and

    在使用Predicate接口的时候,可能需要进行判断条件的拼接,而and方法就相当于使用&&来拼接两个判断条件

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22);
 5         // Predicate接口中的and方法使用
 6         List<Integer> collect = list.stream().filter(
 7                 new Predicate<Integer>() {
 8                     @Override
 9                     public boolean test(Integer x) {
10                         return x > 10;
11                     }
12                 }.and(new Predicate<Integer>() {
13                     @Override
14                     public boolean test(Integer y) {
15                         return y < 30;
16                     }
17                 })
18         ).collect(Collectors.toList());
19      System.out.println(collect); // [29, 22]
20         // lambda简化
21         List<Integer> collect2 = list.stream().filter(
22                 ((Predicate<Integer>) x -> x > 10).and(y -> y < 30)
23         ).collect(Collectors.toList());
24     }
25 }

    自定义方法

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22);
 5 
 6         handle(list, new IntPredicate() {
 7             @Override
 8             public boolean test(int i) {
 9                 return i > 10;
10             }
11         }, new IntPredicate() {
12             @Override
13             public boolean test(int i) {
14                 return i < 25;
15             }
16         });
17         
18         // lambda简化
19         handle(list, i -> i > 10, i -> i < 25);
20     }
21 
22 
23     public static void handle(List<Integer> list, IntPredicate predicate, IntPredicate predicate2) {
24         for (Integer x : list) {
25             // 打印同时满足两个条件判断的值
26             if (predicate.and(predicate2).test(x)) { // and方法返回的是一个Predicate, 需要调用test方法
27                 System.out.println(x);
28             }
29         }
30     }
31 }

    or

    在使用Predicate接口的时候,可能需要进行判断条件的拼接,而 or 方法就相当于使用 || 来拼接两个判断条件

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22);
 5         // Predicate接口中的or方法使用
 6         List<Integer> collect = list.stream().filter(
 7                 new Predicate<Integer>() {
 8                     @Override
 9                     public boolean test(Integer x) {
10                         return x > 20;
11                     }
12                 }.or(new Predicate<Integer>() {
13                     @Override
14                     public boolean test(Integer y) {
15                         return y > 30;
16                     }
17                 })
18         ).collect(Collectors.toList());
19         System.out.println(collect); // [30, 29, 47, 22]
20 
21         // lambda简化
22         List<Integer> collect2 = list.stream().filter(
23                 ((Predicate<Integer>) x -> x > 20).or(y -> y > 30)
24         ).collect(Collectors.toList());
25         System.out.println(collect2);
26     }
27 }

    negate

    Predicate接口中的方法,相当于在判断前面加了一个!,表示取反的意思

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22);
 5         // 遍历打印元素不大于10,也就是小于10的所有元素
 6         list.stream().filter(new Predicate<Integer>() {
 7             @Override
 8             public boolean test(Integer x) {
 9                 return x>10;
10             }
11         }.negate()).forEach(System.out::println); // 1  2
12         
13         // lambda简化
14         list.stream().filter(((Predicate<Integer>) x -> x > 10).negate()).forEach(System.out::println);
15     }
16 }

6、stream使


 1、流的介绍

  Java8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性    
  而在Java 的集合 API 中,仅仅有极少量的辅助型方法,更多的时候是程序员需要用 Iterator 来遍历集合,完成相关的聚合应用逻辑。    
  所谓聚合操作就类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

2、流的特点

  Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator,用户只要给出需要对其包含的元素执行什么操作,Stream 会隐式地在内部进行遍历,做出相应的数据转换,比如 “过滤掉长度大于 10 的字符串”。而原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作。
  Stream 如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了。和迭代器不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作,Stream 的并行操作依赖于 Java7 中的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。

3、流的操作类型

  中间操作(intermediate )
    一个流可以后面跟随零个或多个 intermediate 操作。其目的是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),仅仅调用到这类方法,并没有真正开始流的遍历。
    对应方法有map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered等

  终止操作(terminal )
    一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用尽了,无法再被操作,这是流的最后一个操作。
    Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果或者一个错误。
    对应方法有forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator

  短路操作(short-circuiting)
    对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的 Stream,但返回一个有限的新 Stream。
    对于一个 terminal 操作,如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果。
    对应方法有anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit

4、流的生成

  流的来源可以是集合,数组,I/O channel, 产生器generator 等。
  基本数值型,目前有三种对应的包装类型 Stream:IntStream、LongStream、DoubleStream。  
  示例:
 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         /*************流的来源*************/
 5         // 1、of方法
 6         //      of(T... values):返回含有多个T元素的Stream
 7         //      of(T t):返回含有一个T元素的Stream
 8         Stream<String> single = Stream.of("a");
 9         Stream<String> multiple = Stream.of("a", "b", "c");
10 
11         // 2、generator方法,返回一个无限长度的Stream,其元素由Supplier接口的提供。
12         Stream<String> generate = Stream.generate(() -> "a");
13         Stream<Double> generateA = Stream.generate(new Supplier<Double>() {
14             @Override
15                 public Double get() {
16                       return java.lang.Math.random();
17             }
18         });
19         //  lambda写法
20         Stream<Double> generateB = Stream.generate(() -> java.lang.Math.random());
21         Stream<Double> generateC = Stream.generate(java.lang.Math::random);
22 
23         // 3、iterate方法,返回的也是一个无限长度的Stream,与generate方法不同的是,其是通过函数f迭代对给指定的元素种子而产生无限连续有序Stream,其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环
24         Stream<Integer> iterate = Stream.iterate(1, item -> item + 2);
25         // 无限流处理
26         iterate.limit(10).forEach(System.out::println);
27 
28         // 4、empty方法返回一个空的顺序Stream,该Stream里面不包含元素项。
29         Stream<Object> empty = Stream.empty();
30 
31         // 5、Collection接口和数组的默认方法
32         String chars[] = new String[] {"a", "b", "c"};
33     Arrays.stream(chars).forEach(System.out::println);
34 
35         List list = Arrays.asList(chars);
36    list.stream().forEach(System.out::println);
37    }
38 }

① 流创建

 1 public class Test {
 2 
 3     public static void main(String[] args) throws Exception {
 4         // 创建流
 5         // 1、单列集合,例如: list set..., 使用 集合对象.stream()
 6         List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
 7         Stream<Integer> stream = list.stream();
 8         // 流的过滤操作
 9         stream.filter(new Predicate<Integer>() {
10             @Override
11             public boolean test(Integer integer) {
12                 return integer>3;
13             }
14         }).forEach(System.out::println); // 终结操作, 遍历流中的每个元素
15 
16         // 2、数组:使用Arrays.stream()或者Stream.of()
17         Integer[] arr = {1,2,3,4,5};
18         // 2.1
19         Stream<Integer> arrStream = Arrays.stream(arr);
20         arrStream.forEach(System.out::println);
21         // 2.2
22         Stream<Integer> integerStream = Stream.of(arr);
23         integerStream.forEach(System.out::println);
24 
25         // 3、双列集合, 转换为单列集合再创建Stream
26         Map<String, Integer> hashMap = new HashMap<>();
27         hashMap.put("a", 1);
28         hashMap.put("b", 2);
29         hashMap.put("c", 3);
30         Set<Map.Entry<String, Integer>> entrySet = hashMap.entrySet();
31         Stream<Map.Entry<String, Integer>> entryStream = entrySet.stream();
32         entryStream.forEach(System.out::println);
33     }
34 }

② 中间操作 

 1 public class Test {
 2 
 3     public static void main(String[] args) throws Exception {
 4         // 中间操作
 5 
 6         // 1、filter, 满足指定条件的数据保留在流中
 7         List<Integer> list = Arrays.asList(1, 2, 2, 3, 3, 4, 5);
 8 
 9         // 需求:将集合中大于3的元素打印
10 
11         // 1.1 匿名内部类方式
12         list.stream().filter(new Predicate<Integer>() {
13             @Override
14             public boolean test(Integer integer) {
15                 return integer > 3;
16             }
17         }).forEach(System.out::println);
18         // 1.1 lambda方式
19         list.stream().filter(x -> x > 3).forEach(System.out::println);
20 
21         // 需求:将集合中元素转换成字符串打印, 如果集合中是对象, 也可以将流中原本每个元素为对象, 提取成对象中的属性, 从而进行操作
22 
23         // 2、map, 类型转换
24         // 2.1 匿名内部类方式
25         list.stream().map(new Function<Integer, String>() {
26             @Override
27             public String apply(Integer integer) {
28                 return String.valueOf(integer);
29             }
30         }).forEach(System.out::println);
31         // 2.2 lambda方式
32         list.stream().map(String::valueOf).forEach(System.out::println);
33 
34         // 3、distinct, 去重, 依赖于Object的equals方法来判断是否相同的对象, 所以如果是自定义类型, 则需要重写equals方法。
35         list.stream().distinct().forEach(System.out::println);
36 
37         // 4、sorted, 排序, 如果sorted()不带参数, 并且流中为自定义的类, 那么这个类需要实现Comparable接口
38         list.stream().distinct().sorted().forEach(System.out::println); // sorted 默认升序
39         list.stream().distinct().sorted(new Comparator<Integer>() {
40             @Override
41             public int compare(Integer t1, Integer t2) {
42                 return t2 - t1; // 返回值小于0, 降序
43             }
44         }).forEach(System.out::println);
45 
46         // 5、limit, 设置流的最大长度, 超出的部分被抛弃
47         list.stream().distinct().sorted().limit(3).forEach(System.out::println);
48 
49         // 6、skip, 跳过流中的前N个元素, 返回剩下的元素
50         list.stream().distinct().sorted().skip(2).forEach(System.out::println);
51 
52         // 7、flatMap
53         // map只能将一个对象转换成另外一个对象作为流中的元素, 而flatMap可以把一个对象转换成多个对象作为流中的元素
54         List<List<Integer>> lists = new ArrayList<>();
55         List<Integer> tempList1 = Arrays.asList(1, 2, 3, 4, 5);
56         List<Integer> tempList2 = Arrays.asList(1, 2, 3, 4, 5);
57         List<Integer> tempList3 = Arrays.asList(1, 2, 3, 4, 5);
58         lists.add(tempList1);
59         lists.add(tempList2);
60         lists.add(tempList3);
61         // 第一个泛型为流中每个元素类型, 第二个泛型为要转换的流的类型
62         // 流中的元素类型由List<Integer> 转变成 Integer
63         lists.stream().flatMap(new Function<List<Integer>, Stream<Integer>>() {
64             @Override
65             public Stream<Integer> apply(List<Integer> integers) {
66                 return integers.stream();
67             }
68         }).forEach(System.out::println);
69     }

③ 终结操作

  1 public class Test {
  2 
  3     public static void main(String[] args) throws Exception {
  4         // 终结操作
  5         List<Integer> list = Arrays.asList(1, 3, 3, 2, 2, 4, 5);
  6 
  7         // 1、forEach, 对流中的元素进行遍历操作
  8         list.stream().distinct().forEach(System.out::println);
  9 
 10         // 2、count, 获取流中的元素的个数
 11         long count = list.stream().distinct().count();
 12 
 13         // 3、max、min 获取流中的最值
 14         // max->匿名内部类方式
 15         Optional<Integer> max = list.stream().distinct().max(new Comparator<Integer>() {
 16             // 比较用来排序的两个参数.根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数.分别返回 -1、0 或 1
 17             // t1 要比较的第一个对象
 18             // t2 要比较的第二个对象
 19             // Compare的返回值反应的是两个参数的权重,返回值为1时,前者(o1)权重大,返回值为-1时,后者(o2)权重大.
 20             // 然后排序结果按照权重(注意这里不是值的大小)由小到大进行排序.即权重默认升序排列
 21             @Override
 22             public int compare(Integer t1, Integer t2) {
 23                 return t1 - t2;
 24             }
 25         });
 26         System.out.println(max.get());
 27         // max->lambda方式
 28         Optional<Integer> max2 = list.stream().distinct().max(Comparator.comparingInt(t -> t));
 29         System.out.println(max2.get());
 30 
 31         // min->匿名内部类方式
 32         Optional<Integer> min = list.stream().distinct().min(new Comparator<Integer>() {
 33             @Override
 34             public int compare(Integer t1, Integer t2) {
 35                 return t1 - t2;
 36             }
 37         });
 38         System.out.println(min.get());
 39         // min->lambda方式
 40         Optional<Integer> min2 = list.stream().distinct().min(Comparator.comparingInt(t -> t));
 41         System.out.println(min2.get());
 42 
 43         // 4、collect, 把流转换成一个集合
 44 
 45         // 4.1将流操作完之后转换成list集合
 46         List<Integer> listStream = list.stream().distinct().collect(Collectors.toList());
 47         // 4.2将流操作完之后转换成set集合
 48         Set<Integer> setStream = list.stream().distinct().collect(Collectors.toSet());
 49         // 4.3将流操作完之后转换成map集合
 50         Map<String, String> mapStream = list.stream().distinct().collect(
 51                 Collectors.toMap(
 52                         // map的key转换方式, 流中的元素为Integer类型, 此处将Integer类型的元素转换成String类型作为map的key
 53                         // 如果流中的元素为对象, 那么可以使用对象的某个属性作为key
 54                         new Function<Integer, String>() {
 55                             @Override
 56                             public String apply(Integer integer) {
 57                                 return String.valueOf(integer);
 58                             }
 59                         },
 60                         // map的value转换方式
 61                         new Function<Integer, String>() {
 62                             @Override
 63                             public String apply(Integer integer) {
 64                                 return String.valueOf(integer);
 65                             }
 66                         }));
 67         // 简写方式2
 68         Map<String, String> mapStream2 = list.stream().distinct().collect(
 69                 Collectors.toMap(item -> String.valueOf(item), item -> String.valueOf(item)));
 70         // 简写方式3
 71         Map<String, String> mapStream3 = list.stream().distinct().collect(Collectors.toMap(String::valueOf, String::valueOf));
 72 
 73 
 74         // 5、anyMatch, 判断流中的元素是否有至少一个符合条件的元素, 结果为boolean类型
 75 
 76         // 判断是否存在大于3的元素
 77         boolean anyMatch = list.stream().distinct().anyMatch(new Predicate<Integer>() {
 78             @Override
 79             public boolean test(Integer integer) {
 80                 return integer > 3;
 81             }
 82         });
 83         // lambda表达式
 84         boolean anyMatch2 = list.stream().distinct().anyMatch(x -> x > 3);
 85 
 86         // 6、allMatch, 判断流中的元素是否全都符合匹配条件, 结果为boolean类型, 如果全都符合结果为true, 否则为false
 87         boolean allMatch = list.stream().distinct().allMatch(new Predicate<Integer>() {
 88             @Override
 89             public boolean test(Integer x) {
 90                 return x < 5;
 91             }
 92         });
 93         // lambda表达式
 94         boolean allMatch2 = list.stream().distinct().allMatch(x -> x < 5);
 95 
 96         // 7、noneMatch, 判断流中的元素是否全都不符合匹配条件, 如果都不符合结果为true, 否则为false
 97         boolean noneMatch = list.stream().distinct().noneMatch(new Predicate<Integer>() {
 98             @Override
 99             public boolean test(Integer x) {
100                 return x > 6;
101             }
102         });
103         boolean noneMatch2 = list.stream().distinct().noneMatch(x -> x > 6);
104 
105         // 8、findAny, 获取流中的任意一个元素, 该方法没有办法保证获取的一定是流中的第一个元素
106         Optional<Integer> findAny = list.stream().distinct().filter(x -> x > 10).findAny();
107         findAny.ifPresent(System.out::println); // 如果获取的元素不为空, 则打印
108 
109         // 9、findFirst, 获取流中的第一个元素
110         Optional<Integer> findFirst = list.stream().distinct().findFirst();
111         findFirst.ifPresent(System.out::println);
112 
113         // 10、reduce, 归并, 对流中的数据按照指定的计算方式计算出一个结果
114 
115 
116         // 先修改流中的元素类型, 然后进行reduce处理
117         // 第一个参数为计算的初始值, 第二个参数为数据处理的操作
118         String reduce = list.stream().map(String::valueOf).reduce("0", new BinaryOperator<String>() {
119             // result为每一次操作的初值, 第一次的时候为"", 也就是reduce的第一个参数的值
120             // element为遍历流中的每一个元素, 拿到这个元素进行操作, 然后返回操作的结果
121             // 这里的操作就是拿到初值, 然后将遍历的每一个元素与初值进行字符串拼接, 然后又赋值给result, 作为下一次遍历的初值
122             @Override
123             public String apply(String result, String element) {
124                 return result + "-" + element; // 将流中的元素进行字符串拼接操作
125             }
126         });
127         System.out.println(reduce);
128 
129         // 需求:使用reduce计算流中的最大值
130         Integer maxReduce = list.stream().distinct().reduce(Integer.MIN_VALUE, new BinaryOperator<Integer>() {
131             @Override
132             public Integer apply(Integer result, Integer item) {
133                 return Integer.max(result, item);
134             }
135         });
136         System.out.println(maxReduce);
137         // lambda简化
138         Integer maxReduce2 = list.stream().distinct().reduce(Integer.MIN_VALUE, Integer::max);
139 
140         // 需求:使用reduce计算流中的最小值
141         Integer minReduce = list.stream().distinct().reduce(Integer.MAX_VALUE, new BinaryOperator<Integer>() {
142             @Override
143             public Integer apply(Integer result, Integer item) {
144                 return Integer.min(result, item);
145             }
146         });
147         System.out.println(maxReduce);
148         // lambda简化
149         Integer minReduce2 = list.stream().distinct().reduce(Integer.MAX_VALUE, Integer::min);
150         System.out.println(minReduce2);
151 
152         // 需求:使用reduce计算集合的和
153         List<String> sumList = Arrays.asList("1", "44", "33", "33", "22", "10");
154         Integer sumReduce = sumList.stream().map(Integer::parseInt).reduce(0, new BinaryOperator<Integer>() {
155             // result 初始值为reduce的第一个参数0, 每一次循环计算的结果会赋值给result
156             // item 流中的元素, 会循环拿到每一个元素
157             @Override
158             public Integer apply(Integer result, Integer item) {
159                 return result + item;
160             }
161         });
162         System.out.println(sumReduce);
163         // lambda简化
164         Integer sumReduce2 = sumList.stream().map(Integer::parseInt).reduce(0, Integer::sum);
165         System.out.println(sumReduce2);
166         // reduce单个参数的使用方式, 其实就是将流中的第一个元素作为计算结果(result)的初始值
167         Optional<Integer> sumReduce3 = sumList.stream().map(Integer::parseInt).reduce(new BinaryOperator<Integer>() {
168             @Override
169             public Integer apply(Integer result, Integer item) {
170                 return Integer.sum(result, item);
171             }
172         });
173         sumReduce3.ifPresent(System.out::println);
174     }

  注意事项: 

  • 惰性求值(如果没有终结操作,没有中间操作是不会得到执行的)
  • 流是一次性的(一旦一个流对象经过一个终结操作后,这个流就不能再被使用)
  • 不会影响原数据(在流中可以多数据做很多处理,但是正常情况下是不会影响原来集合中的元素的)

5、流的相关使用

  当你熟悉使用Lambda表达式时,就可以很方便的进行流相关的操作了,提高代码的编写效率和程序的可读性。

  1 public class Test {
  2 
  3     public static void main(String[] args) throws {
  4         /*************流的操作*************/
  5         //concat 将两个Stream连接在一起,合成一个Stream。若两个输入的Stream都时排序的,则新Stream也是排序的;若输入的Stream中任何一个是并行的,则新的Stream也是并行的;若关闭新的Stream时,原两个输入的Stream都将执行关闭处理。
  6         Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5)).forEach(integer -> System.out.print(integer + "  "));
  7 
  8         //distinct 去除掉原Stream中重复的元素,生成的新Stream中没有没有重复的元素。
  9         Stream.of(1, 1, 3, 4, 3).distinct().forEach(System.out::println);
 10 
 11         //filter 对原Stream按照指定条件过滤,过滤出满足条件的元素。
 12         Stream.of(1, 1, 3, 4, 3).filter(x -> x > 2).forEach(System.out::println);
 13 
 14         //map方法将对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。
 15         //为了提高处理效率,官方已封装好了,三种变形:mapToDouble,mapToInt,mapToLong,将原Stream中的数据类型,转换为double,int或者long类型。
 16         Stream.of("a", "b", "c").map(item -> item.toUpperCase()) // .map(String::toUpperCase)
 17             .forEach(System.out::println);
 18 
 19         // flatMap方法与map方法类似,都是将原Stream中的每一个元素通过转换函数转换,
 20         // 不同的是,该换转函数的对象是一个Stream,也不会再创建一个新的Stream,而是将原Stream的元素取代为转换的Stream。
 21         List<User> users = UserUtil.getUsers(6);
 22         users.stream().flatMap(s -> s.getInterests().stream()).forEach(System.out::println);
 23 
 24         Stream.of("a", "b", "c").flatMap(s -> Stream.of(s.toUpperCase()));
 25 
 26         //peek 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例)
 27         Stream.of("a", "b", "c")
 28             //优先执行
 29             .peek(s -> System.out.println("peek:" + s)).forEach(System.out::println);
 30 
 31         //skip 将过滤掉原Stream中的前N个元素,返回剩下的元素所组成的新Stream。
 32         // 如果原Stream的元素个数大于N,将返回原Stream的后的元素所组成的新Stream;
 33         // 如果原Stream的元素个数小于或等于N,将返回一个空Stream。
 34         Stream.of("a", "b", "c").skip(2).forEach(System.out::println);
 35 
 36         // sorted方法将对原Stream进行排序,返回一个有序列的新Stream。sorterd有两种变体sorted(),sorted(Comparator),
 37         // 前者将默认使用Object.equals(Object)进行排序,而后者接受一个自定义排序规则函数(Comparator),可自定义进行排序。
 38         Stream.of(5, 6, 3, 9, 1).sorted().forEach(System.out::println);
 39 
 40         System.out.println("+++++++++++++++++++++++++++++++");
 41         Stream.of(5, 6, 3, 9, 1).sorted(new Comparator<Integer>() {
 42             @Override
 43             public int compare(Integer o1, Integer o2) {
 44                 //asc
 45                 return o1 - o2;
 46             }
 47         }).forEach(System.out::println);
 48 
 49         Stream.of(5, 6, 3, 9, 1)
 50             //desc
 51             .sorted(((o1, o2) -> o2 - o1)).forEach(System.out::println);
 52         System.out.println("+++++++++++++++++++++++++++++++");
 53 
 54         // count 将返回Stream中元素的个数。
 55         long count = Stream.of(1, 2, 3, 4, 5).count();
 56         System.out.println("count:" + count);
 57 
 58         // forEach 用于遍历Stream中的所元素,避免了使用for循环,让代码更简洁,逻辑更清晰。
 59         Stream.of("a", "b", "c").forEach(System.out::println);
 60 
 61         // forEachOrdered 与forEach类似,都是遍历Stream中的所有元素,
 62         // 不同的是,如果该Stream预先设定了顺序,会按照预先设定的顺序执行(Stream是无序的),默认为元素插入的顺序。
 63         Stream.of(5, 2, 1, 4, 3).forEachOrdered(integer -> System.out.println("integer:" + integer));
 64 
 65         // max 根据指定的Comparator,返回一个Optional,该Optional中的value值就是Stream中最大的元素。
 66         Optional<Integer> max = Stream.of(5, 2, 2, 3, 4, 8).max((o1, o2) -> o2 - o1);
 67         //        Optional<Integer> max3 = Stream.of(1, 2, 3, 4, 5)
 68         //                .max(Comparator.comparingInt(x -> x));
 69         Optional<Integer> max3 = Stream.of(1, 2, 3, 4, 5).max((o1, o2) -> o1 - o2);
 70         int max2 = Stream.of(1, 2, 3, 4, 5).mapToInt(x -> x).max().getAsInt();
 71         System.out.println("max = " + max.get() + "  max2 = " + max2 + "  max3 = " + max3.orElse(-1));
 72 
 73         UserUtil.getUsers(6).stream().sorted(Comparator.comparing(User::getName).thenComparing(User::getId)).forEach(u -> System.out.println(u.getName()));
 74 
 75         // min 根据指定的Comparator,返回一个Optional,该Optional中的value值就是Stream中最小的元素。
 76         Optional<Integer> min = Stream.of(1, 2, 3, 4, 5).min((o1, o2) -> o1 - o2);
 77         System.out.println("min:" + min.get());
 78 
 79         System.out.println("*********************reduce********************");
 80         // reduce
 81         //  1、reduce((sum, item) -> { ... }); //返回Optional,因为可能存在为空的情况,
 82         //  2、reduce(0, (sum, item) -> { ... }); /返回对应类型,不存在为空的情况
 83         //无初始值,第一个参数为stream的第一个元素,第二个参数为stream的第二个元素,计算的结果赋值给下一轮计算的sum
 84         Optional<Integer> optional = Stream.of(1, 2, 3, 4, 5).reduce((sum, item) -> {
 85             System.out.println("sum before:" + sum);
 86             System.out.println("item:" + item);
 87             sum = sum + item;
 88             System.out.println("sum after:" + sum);
 90             return sum;
 91 
 92             // return Integer.sum(sum, item);
 93         });
 94         //等效
 95         Optional<Integer> optional1 = Stream.of(1, 2, 3, 4, 5).reduce((sum, item) -> Integer.sum(sum, item));
 96         //等效
 97         Optional<Integer> optional2 = Stream.of(1, 2, 3, 4, 5).reduce(Integer::sum);
 98         System.out.println("integer = " + optional.orElse(-1));
 99         System.out.println("*****************************************");
100         //给定初始值,第一个参数为初始值,第二个参数为stream的第一个元素,计算的结果赋值给下一轮计算的sum
101         Integer reduce = Stream.of(1, 2, 3, 4, 5).reduce(5, (sum, item) -> {
102             System.out.println("sum2 before:" + sum);
103             System.out.println("item:" + item);
104             sum = sum + item;
105             System.out.println("sum2 after:" + sum);
106             return sum;
107         });
108         //等效
109         Integer reduce2 = Stream.of(1, 2, 3, 4, 5).reduce(0, (sum, item) -> Integer.sum(sum, item));
110         //等效
111         Integer reduce3 = Stream.of(1, 2, 3, 4, 5).reduce(0, Integer::sum);
112         System.out.println("reduce = " + reduce);
113 
114         System.out.println("*********************collect********************");
115         List<Integer> toList = Stream.of(1, 2, 3, 4).collect(Collectors.toList());
116         List<Integer> toList2 = Stream.of(1, 2, 3, 4).collect(Collectors.toCollection(ArrayList::new));
117         System.out.println("toList: " + toList);
118 
119         Set<Integer> toSet = Stream.of(1, 2, 3, 4).collect(Collectors.toSet());
120         Set<Integer> toSet2 = Stream.of(1, 2, 3, 4).collect(Collectors.toCollection(() -> new TreeSet()));
121         System.out.println("toSet: " + toSet);
122 
123         //(value1, value2) -> value1  用前面的value覆盖后面的value,保持不变
124         List<User> userList = UserUtil.getUsers(5);
125         userList.add(new User(2, "fjw"));
126         Map<Integer,String> toMap = userList.stream().collect(Collectors.toMap(User::getId, User::getName, (value1, value2) -> value1));
127         System.out.println("(value1, value2) -> value1");
128         toMap.forEach((k, v) -> System.out.println(k + "-" + v));
129 
130         // 对value值进行了限定不能为null,否则抛出空指针异常
131         //        userList.add(new User(3, null));
132         //(value1, value2) -> value2  用后面的value覆盖前面的value
133         Map<Integer,String> toMap2 = userList.stream().collect(Collectors.toMap(User::getId, User::getName, (value1, value2) -> value2));
134         System.out.println("(value1, value2) -> value2");
135         toMap2.forEach((k, v) -> System.out.println(k + "-" + v));
136 
137         // 解决value值为null方式
138         userList.add(new User(4, null));
139         Map<Integer,String> toMap3 = userList.stream().collect(HashMap::new, (m, u) -> m.put(u.getId(), u.getName()), HashMap::putAll);
140         toMap3.forEach((k, v) -> System.out.println(k + "-" + v));
141 
142         Optional<Integer> maxBy = Stream.of(1, 2, 3, 4).collect(Collectors.maxBy(Comparator.comparingInt(o -> o)));
143         System.out.println("maxBy:" + maxBy.get());
144   // 统计流中元素的数量, 等价于 Long counting = Stream.of(1, 2, 3, 4).count();
145         Long counting = Stream.of(1, 2, 3, 4).collect(Collectors.counting());
146         System.out.println("counting:" + counting);
147 
148         // 分割数据块
149         Map<Boolean,List<Integer>> partitioningBy = Stream.of(1, 2, 3, 4, 5).collect(Collectors.partitioningBy(item -> item > 3));
150         // partitioningBy : {false=[1, 2, 3], true=[4, 5]}
151         System.out.println("partitioningBy : " + partitioningBy);
152        // 根据分组条件进行分组, 然后统计每个分组的数量
153         Map<Boolean,Long> collect = Stream.of(1, 2, 3, 4).collect(Collectors.partitioningBy(item -> item > 3, Collectors.counting()));
154         System.out.println("collect: " + collect); // {false=3, true=1}
155 
156         // 数据分组
157         Map<Boolean,List<Integer>> groupingBy = Stream.of(1, 2, 3, 4, 5).collect(Collectors.groupingBy(item -> item > 3));
158         // partitioningBy : {false=[1, 2, 3], true=[4, 5]}
159         System.out.println("groupingBy : " + groupingBy);
160 
161         // 字符串
162         String joining = Stream.of("a", "b", "c", "d").collect(Collectors.joining(","));
163         System.out.println(joining); // a,b,c,d
164 
165         String joining2 = Stream.of("a", "b", "c", "d").collect(Collectors.joining(",", "[", "]"));
166         System.out.println(joining2);  // [a,b,c,d]
167         // 根据id分组
168         Map<Integer,List<User>> collect3 = userList.stream().collect(Collectors.groupingBy(User::getId));
169         // 将id映射为id集合  List<Integer> collect1 = users.stream().map(User::getId).collect(Collectors.toList());
170         List<Integer> collect1 = userList.stream().collect(Collectors.mapping(User::getId, Collectors.toList())); // [1, 2, 3]
171         // 根据id分组后将name映射成集合
172         Map<Integer, List<String>> collect2 = userList.stream().collect(Collectors.groupingBy(User::getId, Collectors.mapping(User::getName, Collectors.toList())));
173         System.out.println(collect2); //  {1=[zhangsan], 2=[lisi], 3=[wangwu], 4=[zhangsan]}       id -> name176     }
177 }

6、stream基本数据类型优化

  在stream的方法很多都使用了泛型,所以涉及到的参数和返回值都是引用类型。  
  即使操作的都是整数,但是实际用到的都是它们的包装类,JDK5中引入的自动装箱和自动拆箱让我们使用包装类的时候就好像使用基本数据类型一样方便,但是拆箱装箱是会消耗时间的,虽然消耗的时间少,但是大量的数据不断的重复拆箱装箱,这个时间就不能无视它了。  
  例如:mapToInt、mapToLong、mapToDouble、flatMapToInt、flatMapToDouble等  将流程的包装类型先转换成基本类型,然后进行计算
 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22);
 5         // 通过mapToInt将流中的数据在转换的时候,包装类型转为基本类型,后续的处理也都是基本数据类型,减少装箱拆箱带来的耗时
 6         list.stream().mapToInt(x -> x + 10)
 7                 .filter(x -> x > 25)
 8                 .map(x -> x + 3)
 9                 .forEach(System.out::println);
10     }
11 }

7、并行流

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         List<Integer> list = Arrays.asList(1, 2, 30, 29, 47, 22, 11, 14, 13, 55, 92, 53);
 5         // 串行计算
 6         Optional<Integer> reduce = list.stream().filter(x -> x > 10).reduce(Integer::sum);
 7         reduce.ifPresent(System.out::println); // 366
 8 
 9         // 并行计算
10         Optional<Integer> reduce2 = list.stream().parallel().filter(x -> x > 10).reduce(Integer::sum);
11         reduce2.ifPresent(System.out::println); // 366
12     }
13 }

 

base64

  java 1.8中引入了Base64,不在需要引入第三方库就可以使用base64了。

                

  在需要用到base64进行加密解密的时候就可以使用了

1 String text = "base64 in java8 lib";
2 //编码
3 String encode = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8));
4 System.out.println(encode);
5 
6 //解码
7 String decode = new String(Base64.getDecoder().decode(encode), StandardCharsets.UTF_8);
8 System.out.println(decode);
 
posted @ 2022-01-15 10:53  为你编程  阅读(6)  评论(0)    收藏  举报