IT评测·应用市场-qidao123.com

标题: [新]Java8的新特性 [打印本页]

作者: 大连全瓷种植牙齿制作中心    时间: 2025-1-23 13:52
标题: [新]Java8的新特性
原文首发在我的博客:https://blog.liuzijian.com/post/86955c3b-9635-47a0-890c-f1219a27c269.html
1.Lambda表达式

  lambda表达式是Java8的重要更新,lambda表达式可以用更简洁的代码来创建一个只有一个抽象方法的接口(函数式接口)的实例,从而更简单的创建匿名内部类的对象。
语法和使用

  lambda表达式的基本语法是形参列表(可以省略范例)箭头,以及代码块,比方() -> {},或者(x, y) -> {},如果只有一个参数,那么小括号()可以省略,如果代码块只有一条语句,那么代码块的花括号{}可一并省略,如果代码块内只有一处return,那么return也可一并省略。
例: TreeSet类的构造器需要传进去一个Comparator的匿名类对象进去,来进行排序,所以程序实现了一个匿名内部类来封装处理举动,而且不得不用匿名内部类的语法来封装对象。
  1. @Test
  2. public void test() {
  3.     TreeSet<Integer> treeSet = new TreeSet<>(new Comparator<Integer>() {
  4.         @Override
  5.         public int compare(Integer o1, Integer o2) {
  6.             return Integer.compare(o1, o2);
  7.         }
  8.     });
  9.     treeSet.add(20);
  10.     treeSet.add(78);
  11.     treeSet.add(-98);
  12.     System.out.println(treeSet);
  13. }
复制代码
Comparator接口是一个函数式接口,因此完全可以使用lambda表达式来简化创建匿名内部类对象,因此上面代码可以修改成如许
  1. @Test
  2. public void test() {
  3.     TreeSet<Integer> treeSet = new TreeSet<>((Integer x, Integer y) -> {
  4.         return x.compareTo(y);
  5.     });
  6.     treeSet.add(20);
  7.     treeSet.add(78);
  8.     treeSet.add(-98);
  9.     System.out.println(treeSet);
  10. }
复制代码
进一步简化: 参数范例可以省略,如果代码块只有一条语句,那么代码块的花括号{}可一并省略,如果代码块内只有一处return,那么return也可一并省略
  1. @Test
  2. public void test() {
  3.     TreeSet<Integer> treeSet = new TreeSet<>((x, y) -> x.compareTo(y));
  4.     treeSet.add(20);
  5.     treeSet.add(78);
  6.     treeSet.add(-98);
  7.     System.out.println(treeSet);
  8. }
复制代码
  逻辑与上面代码是完全雷同的,只是不再需要new Xxx() {}这种繁琐的语法,不需要指出重写方法的名字,也不需要给出重写方法的返回值范例,只需要给出重写方法的括号以及括号内的形参变量即可,用lambda表达式的代码块代替掉匿名内部类抽象方法的方法体,lambda表达式在这里就像是一个匿名方法。
方法引用和构造器引用

  前面说过如果花括号只有一条代码,便可以省略花括号,不但云云,还可以使用方法引用和构造器引用,使得lambda表达式变得再简洁一些,方法引用和构造器引用的语法是两个英文冒号::,支持以下使用方式
种类语法说明lambda表达式写法类方法类名::类方法抽象方法全部参数传给该类某个方法作为参数(a,b,...) -> 类名.类方法(a,b,...)特定对象实例方法特定对象::实例方法抽象方法全部参数传给该方法作为参数(a,b,...) -> 特定对象.实例方法(a,b,...)某类对象实例方法类名::实例方法抽象方法第一个参数作为调用者,后面的参数全部传给该方法作为参数(a,b,c,...) -> a.实例方法(b,c,...)构造器类名::new抽象方法全部参数传给该构造器作为参数(a,b,...) -> new 类名(a,b,...)例: 类名::类方法
  1. @FunctionalInterface
  2. interface Convert {
  3.     Integer fun(String s);
  4. }
  5. @Test
  6. public void test8() {
  7.     Convert convert =  from -> Integer.valueOf(from);
  8.     System.out.println(convert.fun("150") + 1);
  9. }
  10. @Test
  11. public void test9() {
  12.     Convert convert = Integer::valueOf;
  13.     System.out.println(convert.fun("150") + 1);
  14. }
复制代码
例: 特定对象::实例方法
  1. @FunctionalInterface
  2. interface Convert {
  3.     Integer fun(String s);
  4. }
  5. @Test
  6. public void test8() {
  7.     Convert convert = from -> "liuzijian.com".indexOf(from);
  8.     System.out.println(convert.fun("zi"));
  9. }
  10. @Test
  11. public void test9() {
  12.     Convert convert = "liuzijian.com"::indexOf;
  13.     System.out.println(convert.fun("zi"));
  14. }
复制代码
例: 类名::实例方法
  1. @FunctionalInterface
  2. interface Fun {
  3.     String test(String a, int b, int c);
  4. }
  5. @Test
  6. public void test8() {
  7.     Fun fun = (a, b, c) -> a.substring(b, c);
  8.     String s = fun.test("abcdefghi", 3, 5);
  9.     System.out.println(s);
  10. }
  11. @Test
  12. public void test9() {
  13.     Fun fun = String::substring;
  14.     String s = fun.test("abcdefghi", 3, 5);
  15.     System.out.println(s);
  16. }
复制代码
例: 类名::new
  1. @FunctionalInterface
  2. interface Fun {
  3.     BigDecimal test(String n);
  4. }
  5. @Test
  6. public void test8() {
  7.     Fun fun = (n) -> new BigDecimal(n);
  8.     BigDecimal b = fun.test("45.64");
  9.     System.out.println(b);
  10. }
  11. @Test
  12. public void test9() {
  13.     Fun fun = BigDecimal::new;
  14.     BigDecimal b = fun.test("45.64");
  15.     System.out.println(b);
  16. }
复制代码
2.函数式接口

  在Java8中,引入了函数式接口的概念,函数式接口是一个只有一个抽象方法的接口,通常用于Lambda表达式和方法引用,函数式接口可以有多个默认方法静态方法,但是必须只有一个抽象方法
定义
  1. @FunctionalInterface
  2. public interface MyPredicate<T> {
  3.    
  4.     boolean fun(T obj);
  5.    
  6.     default void other() {
  7.         System.out.println("hello world");
  8.     }
  9.     static void staticMethod() {
  10.         System.out.println("static method");
  11.     }
  12. }
复制代码
  @FunctionalInterface注解:这是一个可选的注解,它可以资助编译器在编译时检查接口是否符合函数式接口的要求,即是否只有一个抽象方法,如不符合还加这个注解,会导致编译器报错。
使用

编写一个实体类Employee
  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. @ToString
  5. public class Employee {
  6.     private String name;
  7.     private Double salary;
  8.     private Integer age;
  9.     public Employee(Integer age) {
  10.         this.age = age;
  11.     }
  12.     public Employee(Integer age, String name) {
  13.         this.age = age;
  14.         this.name = name;
  15.     }
  16. }
复制代码
新增一个按条件过滤的方法filter,将List作为第一个参数,函数式接口MyPredicate作为第二个参数传进filter()方法,方法体内循环将每个Employee对象一一作为参数传入接口的抽象方法fun()中,并调用,根据抽象方法运行后得到的布尔值判断是否过滤掉。
  1. private List<Employee> filter(List<Employee>employees, MyPredicate<Employee> predicate) {
  2.     List<Employee>list = new ArrayList<>();
  3.     for (Employee e : employees) {
  4.         if (predicate.fun(e)) {
  5.             list.add(e);
  6.         }
  7.     }
  8.     return list;
  9. }
复制代码
声明一个员工集合employees,插入5个对象,然后调用filter()方法,将employees作为第一个参数传入,然后直接new一个实现MyPredicate接口抽象方法的匿名内部类作为第二个参数传入,如许一来,调用时既告诉了目标方法filter()要处理的数据是employees,也一并将数据的具体处理规则obj.getAge() > 16告诉了目标方法,调用同一个方法可以有无数种处理数据的策略,这个现实上就是一种典范的策略模式,现实上Java8已经为我们写好了一种策略模式的函数式接口。
  1. private List<Employee> employees = Arrays.asList(
  2.     new Employee("soo", 8547.322, 17),
  3.     new Employee("lili", 1000D, 15),
  4.     new Employee("王萌", 2154D, 16),
  5.     new Employee("张帆", 8547.322, 22),
  6.     new Employee("goog", 353D, 12)
  7. );
  8. @Test
  9. public void test3() {
  10.     List<Employee>list = filter(employees, new MyPredicate<Employee>() {
  11.         @Override
  12.         public boolean fun(Employee obj) {
  13.             return obj.getAge() > 16;
  14.         }
  15.     });
  16.     System.out.println(list);
  17. }
复制代码
Java8中,通过将策略接口实现简写为Lambda表达式的方式,可以使得语法显得更加简洁
  1. List<Employee>list2 = filter(employees, (e) -> e.getAge() < 16);
复制代码
内置的函数式接口

Java8提供了一些预定义的函数式接口,位于java.util.function包中
编写4个将函数式接口作为参数的方法
  1. private void testConsumer(String str, Consumer<String>consumer) {
  2.     consumer.accept(str);
  3. }
  4. private String testSupplier(Supplier<String>supplier) {
  5.     return supplier.get();
  6. }
  7. private Integer testFunction(String str, Function<String, Integer>function) {
  8.     return function.apply(str);
  9. }
  10. private boolean testPredicate(String str, Predicate<String>predicate) {
  11.     return predicate.test(str);
  12. }
复制代码
分别调用这些方法,按照业务逻辑通过匿名内部类的lambda表达式写法实现函数式接口的抽象方法,作为参数传入
  1. @Test
  2. public void test4() {
  3.     testConsumer("hello lambda", (x) -> System.out.println(x));
  4.     String str = testSupplier(() -> { return "hello world"; });
  5.     System.out.println(str);
  6.     Integer integer = testFunction("66", (x) -> Integer.valueOf(x));
  7.     System.out.println(integer);
  8.     boolean b = testPredicate("hello", (e) -> e.equals("hello"));
  9.     System.out.println(b);
  10. }
复制代码
得到运行效果
  1. hello lambda
  2. hello world
  3. 66
  4. true
复制代码
还可以通过lambda表达式的方法引用和构造器引用将调用修改的更简洁一些
  1. @Test
  2. public void test2() {
  3.     testConsumer("hello lambda", System.out::println);
  4.     Integer integer = testFunction("66", Integer::valueOf);
  5. }
复制代码
3.Stream API

  Stream是Java8引入的一个新特性,是一个数据流,它提供了一种声明性的方法来处理集合、数组等数据源中的数据,可以更简洁、函数式的方式进行数据处理,它不会改变数据源本身,而是返回一个新的Stream或者是最终的效果。
Java8中引进的常见流式API包括:
此中java.util.stream.Stream是个通用的流接口,以外的几种则代表流的元素范例为long,int,double
  Stream操作是延伸实行的,这意味着它们会比及需要效果时在实行,Stream操作可以被链式调用,而且一样平常分为两类操作:中间操作和停止操作
创建Stream

从集合范例的stream()方法创建
  1. List<String> list = new ArrayList<>();
  2. Stream<String> stream = list.stream();
复制代码
从数组创建
  1. Employee[] employees = new Employee[10];
  2. Stream<Employee> employeeStream = Arrays.stream(employees);
复制代码
通过Stream的静态方法创建流
  1. Employee[] employees = new Employee[10];
  2. Stream<Employee> employeeStream1 = Stream.of(employees);
复制代码
迭代创建无穷流,根据种子和消费接口
  1. Stream.iterate(10, (x) -> x + 2)
  2.       .limit(10)
  3.       .forEach(System.out::println);
复制代码
随机数
  1. Stream.generate(Math::random)
  2.         .limit(20)
  3.         .forEach(System.out::println);
复制代码
通过builder()创建一个int流
  1. @Test
  2. public void test5() {
  3.    
  4.     IntStream intStream = IntStream.builder()
  5.             .add(1)
  6.             .add(2)
  7.             .add(3)
  8.             .add(4).build();
  9.     // 下面的聚集方法每次只能执行一行
  10.     System.out.println(intStream.max().getAsInt());
  11.     //System.out.println(intStream.min().getAsInt());
  12.     //System.out.println(intStream.sum());
  13.     //System.out.println(intStream.count());
  14.     //System.out.println(intStream.average());
  15. }
复制代码
Stream的操作

  Stream的操作包罗中间操作停止操作,在《疯狂Java讲义》一书中,李刚老师也将其称为中间方法和末了方法,中间操作允许流保持打开状态,并允许直接调用后续方法,中间方法返回值是另一个流。停止方法是对流的最终操作,在对某个流实行停止操作后,整个流将不再可用。
常见中间操作
常见停止操作
中间操作返回的是一个新的Stream,而且中间操作是惰性实行的,直到停止操作才触发盘算
下面是用例:
数据:
  1. private List<Employee> employees = Arrays.asList(
  2.     new Employee("soo", 8547.322, 17),
  3.     new Employee("lili", 1000D, 18),
  4.     new Employee("王萌", 2154D, 16),
  5.     new Employee("张帆", 8547.322, 22),
  6.     new Employee("张帆", 8547.322, 22),
  7.     new Employee("张帆", 8547.322, 22),
  8.     new Employee("goog", 353D, 12)
  9. );
  10. private List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
复制代码
例: stream+limit筛选切片,满意e.getAge() > 16条件的对象达到两个时就停止迭代,而不是迭代一遍后返回前两个,进步效率。停止操作forEach()不触发,中间操作filter(),limit()也不会得到实行。
  1. @Test
  2. public void test() {
  3.     employees.stream()
  4.     .filter((e) -> {
  5.         // 中间操作
  6.         System.out.println("中间操作");
  7.         return e.getAge() > 16;
  8.     })
  9.     .limit(2) //中间操作
  10.     .forEach(System.out::println); //终止操作
  11.    
  12. }
复制代码
运行效果:
  1. 中间操作
  2. Employee(name=soo, salary=8547.322, age=17)
  3. 中间操作
  4. Employee(name=lili, salary=1000.0, age=18)
复制代码
例: 跳过流中的前n个元素,与limit相反
  1. @Test
  2. public void test2() {
  3.     employees.stream().skip(2).forEach(System.out::println);
  4. }
复制代码
运行效果
  1. Employee(name=王萌, salary=2154.0, age=16)
  2. Employee(name=张帆, salary=8547.322, age=22)
  3. Employee(name=张帆, salary=8547.322, age=22)
  4. Employee(name=张帆, salary=8547.322, age=22)
  5. Employee(name=goog, salary=353.0, age=12)
复制代码
例: 去重,根据equals,hashCode,本例去重成功的前提是Employee类需要重写equals,hashCode
  1. //Employee类重写equals hashCode
  2. @Override
  3. public boolean equals(Object o) {
  4.     if (this == o) return true;
  5.     if (o == null || getClass() != o.getClass()) return false;
  6.     Employee employee = (Employee) o;
  7.     if (!Objects.equals(name, employee.name)) return false;
  8.     if (!Objects.equals(salary, employee.salary)) return false;
  9.     return Objects.equals(age, employee.age);
  10. }
  11. @Override
  12. public int hashCode() {
  13.     int result = name != null ? name.hashCode() : 0;
  14.     result = 31 * result + (salary != null ? salary.hashCode() : 0);
  15.     result = 31 * result + (age != null ? age.hashCode() : 0);
  16.     return result;
  17. }
复制代码
  1. @Test
  2. public void test3() {
  3.     employees.stream().distinct().forEach(System.out::println);
  4. }
复制代码
运行效果
  1. Employee(name=soo, salary=8547.322, age=17)
  2. Employee(name=lili, salary=1000.0, age=18)
  3. Employee(name=王萌, salary=2154.0, age=16)
  4. Employee(name=张帆, salary=8547.322, age=22)
  5. Employee(name=goog, salary=353.0, age=12)
复制代码
例: flatMap将流中每个值,都转换成另一个流,然后把全部流连接成一个
下面程序先将"aaa"转换成由3个'a'构成的List,再将List转换为Stream,"bbb"和 "ccc"同理,最后将转换成的三个Stream合并为含有9个元素的Stream,再调用结束方法collect()将其变为含有9个元素的List,依次打印输出。
  1. @Test
  2. public void test5() {
  3.     List<String> list = Arrays.asList("aaa", "bbb", "ccc");
  4.    
  5.     Function<String, Stream<Character>> function = (e) -> {
  6.         List<Character> characters = new ArrayList<>();
  7.         for (char c : e.toCharArray()) {
  8.             characters.add(c);
  9.         }
  10.         return characters.stream();
  11.     };
  12.     List<Character> collect = list.stream()
  13.                 .flatMap(function)
  14.                 .collect(Collectors.toList());
  15.     collect.forEach(System.out::println);
  16. }
复制代码
运行效果
  1. a
  2. a
  3. a
  4. b
  5. b
  6. b
  7. c
  8. c
  9. c
复制代码
例: map映射,得到流中的一个元素,处理组成新的流
  1. @Test
  2. public void test4() {
  3.     employees.stream().map((e) -> e.getName()).forEach(System.out::println);
  4. }
复制代码
运行效果
  1. soo
  2. lili
  3. 王萌
  4. 张帆
  5. 张帆
  6. 张帆
  7. goog
复制代码
例: sorted()天然排序
  1. @Test
  2. public void test() {
  3.     list.stream().sorted().forEach(System.out::println);
  4. }
复制代码
运行效果
  1. a
  2. a
  3. a
  4. b
  5. b
  6. b
  7. c
  8. c
  9. cdddeee
复制代码
例: sorted(Comparator c)定制排序
  1. @Test
  2. public void test2() {
  3.     employees.stream()
  4.             .sorted((e1, e2) -> e1.getAge() - e2.getAge())
  5.             .forEach(System.out::println);
  6. }
复制代码
运行效果
  1. Employee(name=goog, salary=353.0, age=12)
  2. Employee(name=王萌, salary=2154.0, age=16)
  3. Employee(name=soo, salary=8547.322, age=17)
  4. Employee(name=lili, salary=1000.0, age=18)
  5. Employee(name=张帆, salary=8547.322, age=22)
  6. Employee(name=张帆, salary=8547.322, age=22)
  7. Employee(name=张帆, salary=8547.322, age=22)
复制代码
例: xxxMatch,findXXX,count(),max(),min()
  1. @Test
  2. public void test3() {
  3.     boolean b = employees.stream().allMatch((e) -> e.getAge() > 10);
  4.     System.out.println(b);
  5.     b = employees.stream().anyMatch((e) -> e.getAge() > 100);
  6.     System.out.println(b);
  7.     b = employees.stream().noneMatch((e) -> e.getAge() > 100);
  8.     System.out.println(b);
  9.     Optional<Employee> first = employees.stream().findFirst();
  10.     System.out.println(first.get());
  11.     Optional<Employee> any = employees.stream().findAny();
  12.     System.out.println(any.get());
  13.     long count = employees.stream().count();
  14.     System.out.println(count);
  15.     Optional<Employee> max = employees.stream()
  16.             .max(Comparator.comparingInt(Employee::getAge));
  17.     System.out.println(max.get());
  18.     Optional<Integer> maxAge = employees.stream()
  19.             .map(Employee::getAge)
  20.             .max(Integer::compare);
  21.     System.out.println(maxAge.get());
  22. }
复制代码
运行效果
  1. true
  2. false
  3. true
  4. Employee(name=soo, salary=8547.322, age=17)
  5. Employee(name=soo, salary=8547.322, age=17)
  6. 7
  7. Employee(name=张帆, salary=8547.322, age=22)
  8. 22
复制代码
例: reduce() 将流中元素反复结合,得到新值,先将起始值作为x,从流中取出一个值作为y
  1. @Test
  2. public void test() {
  3.     List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
  4.     Integer sum = list.stream().reduce(0, Integer::sum);
  5.     System.out.println(sum);
  6.    
  7.     Optional<Double> reduce = employees.stream().map(Employee::getSalary)
  8.             .reduce(Double::sum);
  9.     System.out.println(reduce.get());
  10. }
复制代码
运行效果
  1. 45
  2. 37696.288
复制代码
例: .collect(Collectors.toList()) .collect(Collectors.toCollection()) 收集为集合
  1. @Test
  2. public void test2() {
  3.     List<String> names = employees.stream()
  4.             .map(Employee::getName)
  5.             .collect(Collectors.toList());
  6.             //.collect(Collectors.toCollection(LinkedList::new))
  7.    
  8.     names.forEach(System.out::println);
  9. }
复制代码
运行效果
  1. soo
  2. lili
  3. 王萌
  4. 张帆
  5. 张帆
  6. 张帆
  7. goog
复制代码
例: collect(Collectors.averagingDouble()) 求平均值
  1. @Test
  2. public void test5() {
  3.     Double avg = employees.stream()
  4.             .collect(Collectors.averagingDouble(Employee::getSalary));
  5.     System.out.println(avg);
  6. }
复制代码
例: collect(Collectors.joining()) 用雷同的内容连接多个字符串,非常适合SQL等参数拼接场景
  1. @Test
  2. public void test() {
  3.     String collect = list.stream().collect(Collectors.joining(","));
  4.     System.out.println(collect);
  5. }
复制代码
运行效果
  1. aaa,bbb,ccc,ddd,eee
复制代码
例: 收集为Map Collectors.groupingBy()
将雷同航司和票号的票和行李的代价加在一起
  1. public class TestGroupBy {
  2.    
  3.     private List<Detail> details = new ArrayList<>();
  4.    
  5.     @Before
  6.     public void mock() {
  7.         details.add(new Detail(1, "001", "123456789", new BigDecimal("120.00")));
  8.         details.add(new Detail(2, "001", "123456789", new BigDecimal("99.32")));
  9.         
  10.         details.add(new Detail(3, "003", "333222111", new BigDecimal("27.32")));
  11.         details.add(new Detail(4, "003", "333222111", new BigDecimal("36.00")));
  12.         details.add(new Detail(5, "003", "123456789", new BigDecimal("48.32")));
  13.         
  14.         details.add(new Detail(6, "101", "123456789", new BigDecimal("53.32")));
  15.         details.add(new Detail(7, "101", "123456789", new BigDecimal("10.32")));
  16.         details.add(new Detail(8, "102", "333222111", new BigDecimal("3.32")));
  17.         details.add(new Detail(9, "103", "123456789", new BigDecimal("9.00")));
  18.         details.add(new Detail(10, "103", "123456789", new BigDecimal("12.12")));
  19.         
  20.     }
  21.    
  22.     @Test
  23.     public void test() {
  24.         Map<String, List<Detail>> groupByAir = details.parallelStream().collect(Collectors.groupingBy(Detail::getAir));
  25.         groupByAir.forEach((air, sameAirs) -> {
  26.             Map<String, List<Detail>> groupByDoc = sameAirs.parallelStream().collect(Collectors.groupingBy(Detail::getDocument));
  27.             groupByDoc.forEach((doc, sameDocs) -> {
  28.                 Optional<BigDecimal> reduce = sameDocs.parallelStream().map(Detail::getPrice).reduce(BigDecimal::add);
  29.                 reduce.ifPresent(e -> {
  30.                     System.out.println(air + " "+ doc + " " + e);
  31.                 });
  32.             });
  33.         });
  34.     }
  35.    
  36.     @Data
  37.     @AllArgsConstructor
  38.     public static class Detail {
  39.         /**
  40.          * ID
  41.          */
  42.         private Integer id;
  43.         /**
  44.          *航司编码
  45.          */
  46.         private String air;
  47.         /**
  48.          *票号
  49.          */
  50.         private String document;
  51.         /**
  52.          *机票价格
  53.          */
  54.         private BigDecimal price;
  55.     }
  56.    
  57. }
复制代码
运行效果
  1. 001 123456789 219.32
  2. 101 123456789 63.64
  3. 102 333222111 3.32
  4. 003 333222111 63.32
  5. 003 123456789 48.32
  6. 103 123456789 21.12
复制代码
例: peek() 实时打印调试看流处理的每一步里面的元素是什么样的
  1. @Test
  2. public void test6() {
  3.     List<String> names = Arrays.asList("liuzijian", "liutongtong", "zhaoying", "wangwendi");
  4.     names.stream()
  5.             .filter(name -> name.startsWith("liu"))
  6.             .peek(name -> System.out.println("过滤后: " + name))
  7.             .map(String::toUpperCase)
  8.             .peek(name -> System.out.println("变成大写后: " + name))
  9.             .collect(Collectors.toList());
  10. }
复制代码
运行效果
  1. 过滤后: liuzijian
  2. 变成大写后: LIUZIJIAN
  3. 过滤后: liutongtong
  4. 变成大写后: LIUTONGTONG
复制代码
并行流和串行流

  在Java8中,流可以分为并行流和串行流,这两者的主要区别在于数据处理的方式。
  Java8的stream()默认是串行流,即数据按顺序一个一个处理,可以通过parallel()方法将串行流转换为并行流,或者直接在流创建时使用parallelStream()
  并行流底层是基于Java的ForkJoinPool实现的,这个池管理多个线程来并行处理数据,流的元素会被拆分成多个子使命并分配到不同的线程中处理,最后将效果合并。
  并行流本身并不保证顺序。但是,在某些操作中,比如Collectors.joining(),它会保证合并效果的顺序,这通过收集器的设计来实现。
例: 并行流遍历打印
  1. @Test
  2. public void test() {
  3.     list.parallelStream().forEach(System.out::println);
  4. }
复制代码
运行效果
  1. ccc
  2. eee
  3. ddd
  4. bbb
  5. aaa
复制代码
例: 并行流多线程将0加到100
LongStream.rangeClosed(0, 100000000000L)创建了从0到100000000000L之间全部整数的流,然后reduce()会先将流分成多个子流,每个子流盘算局部的和,在不同的线程中进行,每个线程分别盘算一部分和,盘算完成后,再将各个子使命盘算的效果合并,得到盘算效果932356074711512064
  1. public static void main(String[] args) {
  2.     long reduce = LongStream.rangeClosed(0, 100000000000L)
  3.             .parallel() // 转换为并行流,底层是fork-join
  4.             .reduce(0, Long::sum);
  5.     System.out.println(reduce);
  6. }
复制代码
以上就是Java8 StreamAPI的全部内容。
4.接口的默认方法

  Java8前的接口,只能有两个成员,全局静态常量和抽象方法,Java8引入了接口的默认方法和静态方法作为新特性,它们的引入是为了增强接口的功能,特别是在接口的扩展性和灵活性方面。
  接口中的默认方法,使用default修饰符修饰,可以带有实现,实现类可以直接继续使用,实现类可以选择重写默认方法,也可以直接使用。
  接口中的静态方法只能通过接口名调用,不能通过接口的实现类或实例调用,为接口提供相关的工具性功能,而不需要依赖具体的实现类,静态方法不会被实现类继续,也不能被实现类重写。
接口的默认方法和静态方法

编写一个接口test.testinterface.MyInterface,拥有两个默认方法test(),hello()和一个静态方法helloworld()
  1. package test.testinterface;
  2. public interface MyInterface {
  3.    
  4.     default String test() {
  5.         System.out.println("default");
  6.         return "default";
  7.     }
  8.     default void hello() {
  9.         System.out.println("my interface");
  10.     }
  11.    
  12.     static void helloworld() {
  13.         System.out.println("hello java8!!!");
  14.     }
  15. }
复制代码
编写一个类test.testinterface.SubClass,实现接口MyInterface
  1. package test.testinterface;
  2. public class SubClass  implements MyInterface {
  3.     public static void main(String[] args) {
  4.         SubClass subClass = new SubClass();
  5.   
  6.         subClass.hello();
  7.         MyInterface.helloworld();
  8.     }
  9. }
复制代码
不实现接口里面的hello()方法也可以直接调用默认方法hello(),而且可以通过接口名直接调用接口的静态方法helloworld(),程序输出:
  1. my interface
  2. hello java8!!!
复制代码
方法辩说

编写另一个接口test.testinterface.OtherInterface,并实现一个默认方法hello
  1. package test.testinterface;
  2. public interface OtherInterface {
  3.     default void hello() {
  4.         System.out.println("other interface");
  5.     }
  6. }
复制代码
令类test.testinterface.SubClass再实现一个接口OtherInterface,该接口含有和接口MyInterface一样定义的default方法hello(),就产生了接口辩说,当实现的多个接口中有雷同签名的默认方法时,子类必须显式重写辩说的方法hello(),最终程序输出效果:"sub hello!"
  1. package test.testinterface;
  2. public class SubClass implements MyInterface, OtherInterface {
  3.     /**
  4.      * 多实现方法冲突,实现类必须实现
  5.      **/
  6.     @Override
  7.     public void hello() {
  8.         System.out.println("sub hello!");
  9.     }
  10.     public static void main(String[] args) {
  11.         SubClass subClass = new SubClass();
  12.         subClass.hello();
  13.     }
  14. }
复制代码
类优先

编写一个类test.testinterface.MyClass,里面有一个方法String test(),并让SubClass类继续它,并实行subClass.test();,得到输出效果:"class",但是SubClass实现的接口MyInterface里面也有个方法String test(),却没有被实行,而是实行了类里面的方法,说明类优先,如果类或其父类中已经提供了方法实现,则优先使用类的实现,而不是接口的默认方法。
  1. package test.testinterface;
  2. public class MyClass  {
  3.     public String test() {
  4.         System.out.println("class");
  5.         return "class";
  6.     }
  7. }
复制代码
  1. package test.testinterface;
  2. public class SubClass extends MyClass implements MyInterface, OtherInterface {
  3.     // 多实现方法冲突,实现类必须实现
  4.     @Override
  5.     public void hello() {
  6.         System.out.println("sub hello!");
  7.     }
  8.     public static void main(String[] args) {
  9.         SubClass subClass = new SubClass();
  10.         // 类优先原则, 继承类的方法
  11.         subClass.test();
  12.     }
  13. }
复制代码
5.新的日期和时间API (java.time)

旧API的线程安全题目

  旧的日期时间工具类java.text.SimpleDateFormat存在线程安全题目,比方SimpleDateFormat线程不安全,内部依赖一个Calendar实例来解析和格式化日期,而Calendar是线程不安全的,多线程格式化会并发更新Calendar状态会导致出现异常。
以下代码使用100个线程并发调用一个format对象进行日期解析操作,会导致出现错误。
  1. package test.time;
  2. import java.text.SimpleDateFormat;
  3. public class Test1 {
  4.     public static void main(String[] args)  {
  5.         
  6.         SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
  7.         Runnable r = new Runnable() {
  8.             @Override
  9.             public void run() {
  10.                 try {
  11.                     System.out.println(format.parse("20191231"));
  12.                 } catch (Exception e) {
  13.                     throw new RuntimeException(e);
  14.                 }
  15.             }
  16.         };
  17.         for (int i=0; i<100; i++) {
  18.             new Thread(r, "t"+i).start();
  19.         }
  20.     }
  21. }
复制代码
如果值不存在,可以抛出自定义异常
  1. package test.time;
  2. import java.text.SimpleDateFormat;
  3. public class Test1 {
  4.     public static void main(String[] args)  {
  5.         
  6.         SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
  7.         Runnable r = new Runnable() {
  8.             @Override
  9.             public void run() {
  10.                 synchronized (format) {
  11.                     try {
  12.                         System.out.println(format.parse("20191231"));
  13.                     } catch (Exception e) {
  14.                         throw new RuntimeException(e);
  15.                     }
  16.                 }
  17.             }
  18.         };
  19.         for (int i=0; i<100; i++) {
  20.             new Thread(r, "t"+i).start();
  21.         }
  22.     }
  23. }
复制代码
转换

map() 如果有值进行处理,并返回处理后的Optional对象,否则返回Optional.empty()
空值,不实行输出
  1. package test.time;
  2. import java.text.SimpleDateFormat;
  3. public class Test1 {
  4.     public static void main(String[] args)  {
  5.         Runnable r = new Runnable() {
  6.             @Override
  7.             public void run() {
  8.                 try {
  9.                     System.out.println(new SimpleDateFormat("yyyyMMdd").parse("20191231"));
  10.                 } catch (Exception e) {
  11.                     throw new RuntimeException(e);
  12.                 }
  13.             }
  14.         };
  15.         for (int i=0; i<100; i++) {
  16.             new Thread(r, "t"+i).start();
  17.         }
  18.     }
  19. }
复制代码
非空,处理后返回新的Optional,输出:HELLO WORLD
  1. package test.time;
  2. import java.text.SimpleDateFormat;
  3. import java.util.concurrent.ExecutorService;
  4. import java.util.concurrent.Executors;
  5. public class Test1 {
  6.    
  7.     public static void main(String[] args)  {
  8.         ExecutorService executorService = Executors.newFixedThreadPool(10);
  9.         ThreadLocal<SimpleDateFormat> threadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd"));
  10.         Runnable runnable = new Runnable() {
  11.             @Override
  12.             public void run() {
  13.                 try {
  14.                     System.out.println(threadLocal.get().parse("20191231"));
  15.                 }
  16.                 catch (Exception e) {
  17.                     throw new RuntimeException(e);
  18.                 }
  19.             }
  20.         };
  21.         for (int i=0; i<100; i++) {
  22.             executorService.submit(runnable, "t"+i);
  23.         }
  24.         executorService.shutdown();
  25.     }
  26. }
复制代码
使用flatMap()进一步防止空指针异常,如果optional中的值为null,flatMap()直接返回Optional.empty(),否则,它返回一个包罗e.getName()的Optional对象
  1. package test.time;
  2. import org.junit.Test;
  3. import java.time.*;
  4. import java.time.format.DateTimeFormatter;
  5. import java.time.format.FormatStyle;
  6. import java.time.temporal.*;
  7. import java.util.Date;
  8. public class Test4 {
  9.     /**
  10.      * java8 API获取当前时间
  11.      */
  12.     @Test
  13.     public void current() {
  14.         Instant instant = Instant.now();
  15.         LocalDate localDate = LocalDate.now();
  16.         LocalTime localTime = LocalTime.now();
  17.         LocalDateTime localDateTime = LocalDateTime.now();
  18.         ZonedDateTime zonedDateTime = ZonedDateTime.now();
  19.         System.out.println(instant);
  20.         System.out.println(localDate);
  21.         System.out.println(localTime);
  22.         System.out.println(localDateTime);
  23.         System.out.println(zonedDateTime);
  24.     }
  25.    
  26.     /**
  27.      * Instant的常见方法
  28.      */
  29.     @Test
  30.     public void testInstant() {
  31.         //通过Instant获取当前时间戳,格林威治时间
  32.         Instant now = Instant.now();
  33.         System.out.println(now);
  34.         //添加时区,转换为带时区的时间:OffsetDateTime
  35.         OffsetDateTime us = now.atOffset(ZoneOffset.ofHours(-4));
  36.         System.out.println(us);//US
  37.         //设置偏移量
  38.         OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(+8));
  39.         System.out.println(offsetDateTime);//CN
  40.         System.out.println(now.atOffset(ZoneOffset.ofHours(+9)));//JP
  41.         System.out.println(now.atOffset(ZoneOffset.ofHours(+10)));//AU
  42.         //根据给定的Unix时间戳(即自1970年1月1日00:00:00 UTC起的秒数)创建一个Instant对象
  43.         Instant instant = Instant.ofEpochSecond(1);//开始于1970
  44.         System.out.println(instant);
  45.         //设置时区
  46.         ZonedDateTime zonedDateTime = now.atZone(ZoneId.of("GMT+9"));
  47.         LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
  48.         System.out.println(localDateTime);
  49.     }
  50.     /**
  51.      * LocalDateTime LocalDate LocalTime 的常见方法和使用
  52.      */
  53.     @Test
  54.     public void testLocalDateTime() {
  55.         // 获取当前时间
  56.         LocalDateTime now = LocalDateTime.now();
  57.         System.out.println(now);
  58.         //构造时间
  59.         LocalDateTime localDateTime = LocalDateTime.of(2019,8,8,12,23,50);
  60.         System.out.println(localDateTime);
  61.         //从LocalDate和LocalTime构造时间
  62.         System.out.println(LocalDateTime.of(LocalDate.now(), LocalTime.now()));
  63.         // 获取年月日时分秒
  64.         System.out.println(localDateTime.getYear());
  65.         System.out.println(localDateTime.getDayOfYear());
  66.         System.out.println(localDateTime.getDayOfMonth());
  67.         //星期
  68.         DayOfWeek dayOfWeek = localDateTime.getDayOfWeek();
  69.         System.out.println(dayOfWeek);
  70.         //当前时间的纳秒部分,表示这个时间点内的精细时间
  71.         System.out.println(localDateTime.getNano());
  72.         //时间计算
  73.         System.out.println(LocalDateTime.now().plusMonths(2));
  74.         System.out.println(LocalDateTime.now().minusYears(2));
  75.         System.out.println(LocalDateTime.now().plusHours(24));
  76.         System.out.println(LocalDateTime.now().plusNanos(500));
  77.         System.out.println(LocalDateTime.now().plusYears(2).plusMonths(8).plusDays(9));
  78.         // Period.of 用于创建一个表示特定时间间隔的Period对象
  79.         System.out.println(LocalDateTime.now().plus(Period.of(3, 5, 20))); ;
  80.         // ChronoUnit.DECADES代表十年
  81.         System.out.println(LocalDateTime.now().plus(3, ChronoUnit.DECADES)) ;
  82.         // 时间修改
  83.         System.out.println(LocalDateTime.now().withMonth(2));
  84.         System.out.println(LocalDateTime.now().withDayOfMonth(25));
  85.         System.out.println(LocalDateTime.now().withSecond(22));
  86.         System.out.println(LocalDateTime.now().with(ChronoField.DAY_OF_MONTH, 2));
  87.         System.out.println(LocalDateTime.now().with(ChronoField.MONTH_OF_YEAR, 8));
  88.         // LocalDate LocalTime
  89.         System.out.println(LocalDate.of(2020, 1, 19));
  90.         System.out.println(LocalDate.of(2020, Month.AUGUST, 19));
  91.         System.out.println(LocalDate.of(2020, Month.of(12), 19));
  92.         System.out.println(LocalTime.of(20, 0));
  93.         System.out.println(LocalDate.now().withMonth(8));
  94.         System.out.println(LocalDate.of(2020, Month.AUGUST, 19).plusDays(5));
  95.         System.out.println(LocalDate.of(2020, Month.of(12), 19));
  96.         System.out.println( LocalTime.of(20, 0).plusHours(8) );
  97.         // LocalDate的方法,判断当前年份是否为闰年
  98.         System.out.println(LocalDate.now().isLeapYear());
  99.     }
  100.     /**
  101.      * TemporalAdjusters 时间校正器
  102.      */
  103.     @Test
  104.     public void testTemporalAdjusters() {
  105.         // 下一个周四
  106.         LocalDateTime dateTime = LocalDateTime.now();
  107.         dateTime.with(TemporalAdjusters.next(DayOfWeek.THURSDAY));
  108.         System.out.println(dateTime);
  109.         dateTime.with(TemporalAdjusters.previous(DayOfWeek.THURSDAY));
  110.         System.out.println(dateTime);
  111.         dateTime.with(TemporalAdjusters.nextOrSame(DayOfWeek.THURSDAY));
  112.         System.out.println(dateTime);
  113.         dateTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.THURSDAY));
  114.         System.out.println(dateTime);
  115.         System.out.println(LocalDate.now().with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY)));
  116.         // 获取月份第一天
  117.         System.out.println(LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()));
  118.         System.out.println(LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()));
  119.         // 自定义 计算下一个工作日
  120.         LocalDateTime nextWorkDay = LocalDateTime.now().with((e) -> {
  121.             LocalDateTime temp = LocalDateTime.from(e);
  122.             DayOfWeek dayOfWeek = temp.getDayOfWeek();
  123.             if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {
  124.                 return temp.plusDays(3);
  125.             } else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {
  126.                 return temp.plusDays(2);
  127.             } else {
  128.                 return temp.plusDays(1);
  129.             }
  130.         });
  131.         System.out.println(nextWorkDay);
  132.     }
  133.     public void test() {
  134.         System.out.println(Year.now());
  135.         System.out.println(YearMonth.now());
  136.         System.out.println(MonthDay.now());
  137.     }
  138.     /**
  139.      * 计算时间间隔:武汉封了多少天,多少小时
  140.      */
  141.     @Test
  142.     public void testChronoUnit() {
  143.         LocalDateTime from = LocalDateTime.of(2020, Month.JANUARY, 23, 10, 0,0);
  144.         LocalDateTime to = LocalDateTime.of(2020, Month.APRIL, 8, 0, 0,0);
  145.         long days = ChronoUnit.DAYS.between(from, to);
  146.         long hours = ChronoUnit.HOURS.between(from, to);
  147.         System.out.println( days );
  148.         System.out.println( hours );
  149.     }
  150.     /**
  151.      * 使用 TemporalQuery 来计算当前时间与一个指定时间点(2020年1月19日10:00:00)之间的小时差,
  152.      * 并将其作为 long 类型的值返回
  153.      */
  154.     @Test
  155.     public void testTemporalQuery() {
  156.         long l = LocalDateTime.now().query(new TemporalQuery<Long>() {
  157.             @Override
  158.             public Long queryFrom(TemporalAccessor temporal) {
  159.                 LocalDateTime now = LocalDateTime.from(temporal);
  160.                 LocalDateTime from = LocalDateTime.of(2020, Month.JANUARY, 19, 10, 0,0);
  161.                 return ChronoUnit.HOURS.between(from, now);
  162.             }
  163.         });
  164.         System.out.println(l);
  165.     }
  166.     /**
  167.      * Duration类,只能计算时间差异
  168.      */
  169.     @Test
  170.     public void testDurationPeriod() {
  171.         LocalTime start = LocalTime.of(20, 0);
  172.         LocalTime end = LocalTime.of(21, 30);
  173.         // 时间间隔
  174.         Duration between = Duration.between(start, end);
  175.         System.out.println(between.toHours());
  176.         System.out.println(between.toMinutes());
  177.     }
  178.     /**
  179.      * 格式化 DateTimeFormatter
  180.      */
  181.     @Test
  182.     public void testDateTimeFormatter() {
  183.         DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
  184.         System.out.println(LocalDateTime.now().format(formatter));
  185.         LocalDate localDate = LocalDate.parse("2009-12-31", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
  186.         System.out.println(localDate);
  187.         LocalDateTime localDateTime = LocalDateTime.parse("2009-12-31 01:01:02", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
  188.         System.out.println(localDateTime);
  189.         // 2024年12月1日 星期日
  190.         System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)));
  191.         // 2024年12月1日
  192.         System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)));
  193.         // 24-12-1
  194.         System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)));
  195.         // 2024-12-1
  196.         System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)));
  197.     }
  198.     @Test
  199.     public void getAvailableZoneIds() {
  200.         // 当前系统时区
  201.         System.out.println(ZoneId.systemDefault());
  202.         // 打印java8中所有支持时区
  203.         ZoneId.getAvailableZoneIds().forEach(System.out::println);
  204.     }
  205.     /**
  206.      * OffsetDateTime
  207.      */
  208.     @Test
  209.     public void testOffsetDateTime() {
  210.         OffsetDateTime offsetDateTime = new Date().toInstant().atOffset(ZoneOffset.of("-4"));
  211.         System.out.println(offsetDateTime);
  212.         System.out.println(offsetDateTime.toLocalDateTime());
  213.         OffsetDateTime of = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.of("-4"));
  214.         System.out.println(of);
  215.     }
  216.     /**
  217.      * ZonedDateTime
  218.      */
  219.     @Test
  220.     public void testZonedDateTime() {
  221.         // 当前时间转换为东京时间是几时
  222.         ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
  223.         System.out.println(zonedDateTime);
  224.         System.out.println(zonedDateTime.toLocalDateTime());
  225.         ZonedDateTime of = ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("Asia/Tokyo"));
  226.         System.out.println(of);
  227.         // 为当前时间带上时区
  228.         ZonedDateTime tokyo = LocalDateTime.now().atZone(ZoneId.of("Asia/Tokyo"));
  229.         System.out.println(tokyo);
  230.         System.out.println(tokyo.toLocalDateTime());
  231.         // 将一个时区时间转换为同一时刻另一个时区时间
  232.         ZonedDateTime beijing = tokyo.withZoneSameInstant(ZoneId.of("GMT+8"));
  233.         System.out.println(beijing);
  234.         ZonedDateTime usa = LocalDateTime.now()
  235.                 .atZone(ZoneId.systemDefault())
  236.                 .withZoneSameInstant(ZoneId.of("GMT-4"));
  237.         System.out.println(usa);
  238.     }
  239. }
复制代码
7.重复注解 (Repeating Annotations)

1.首先创建一个容器注解,这个注解范例包罗一个注解数组,存储多个雷同范例的注解
  1. package test.time;
  2. import org.junit.Test;
  3. import java.sql.Timestamp;
  4. import java.time.*;
  5. import java.util.Calendar;
  6. import java.util.Date;
  7. public class Test5 {
  8.     /**
  9.      * 将 LocalDateTime 和系统默认时区结合,转换为 ZonedDateTime
  10.      * 再将 ZonedDateTime 转换为 Instant,这是一个包含 UTC 时间戳的对象。
  11.      * Date.from():将 Instant 转换为 java.util.Date 对象
  12.      */
  13.     @Test
  14.     public void toDate() {
  15.         LocalDateTime localDateTime = LocalDateTime.now();
  16.         Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
  17.         Date date = Date.from(instant);
  18.         System.out.println(date);
  19.     }
  20.     @Test
  21.     public void toLocalDateTime() {
  22.         Date date = new Date();
  23.         LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
  24.         System.out.println(dateTime);
  25.     }
  26.     /**
  27.      * java.sql.Date 转换 LocalDateTime
  28.      */
  29.     @Test
  30.     public void sqlDate() {
  31.         java.sql.Date date = new java.sql.Date(System.currentTimeMillis());
  32.         LocalDate localDate = date.toLocalDate();
  33.         System.out.println(localDate);
  34.         Timestamp timestamp = new Timestamp(System.currentTimeMillis());
  35.         LocalDateTime localDateTime = timestamp.toLocalDateTime();
  36.         System.out.println(localDateTime);
  37.     }
  38.     /**
  39.      * Calendar 转换 LocalDateTime
  40.      */
  41.     @Test
  42.     public void calendarToLocalDateTime() {
  43.         Calendar calendar = Calendar.getInstance();
  44.         ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(calendar.toInstant(), calendar.getTimeZone().toZoneId());
  45.         System.out.println(zonedDateTime.toLocalDateTime());
  46.     }
  47. }
复制代码
2.定义一个重复注解,并使用@Repeatable标记
  1. Optional<String> optional = Optional.of("Hello, World!");
复制代码
3.测试,通过反射访问方法上的注解,由于MyAnnotation是重复注解,所以一个方法加上多个也不会语法报错,然后提取此中的多个MyAnnotation注解。
  1. Optional<String> optional = Optional.ofNullable(null);
复制代码
8.Nashorn JavaScript引擎

这个不常用,未完待续

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4