品茗Java8新特性

看了下openjdk官网 http://openjdk.java.net/projects/jdk/14/ jdk14今年就要发布稳定版本了,连java8都没有系统的学习过。那就来学习下java8吧,这是一个长期维护的版本。这里主要来学习java8的一些新特性。对应demo可以下载链接:

https://github.com/wenqy/java-study

接口默认方法

使用default关键字,为接口声明添加非抽象的方法实现接口默认方法

  1. public interface DefaultInterfaceMethod {
  2.     double calc(int a);
  3.     /**
  4.      * 接口使用 <b>default</b>关键字,添加非抽象方法实现(扩展方法)
  5.      * @param a
  6.      * @return
  7.      * @author wenqy
  8.      * @date 2020年1月16日 上午9:55:20
  9.      */
  10.     default double sqrt(int a) {
  11.         return Math.sqrt(a);
  12.     }
  13. }

然后实现匿名内部类,并调用默认方法

  1. DefaultInterfaceMethod interfaceMethod = new DefaultInterfaceMethod() {
  2.     @Override
  3.     public double calc(int a) {
  4.         return sqrt(a * 100);
  5.     }
  6. };
  7. System.out.println(“calc:” + interfaceMethod.calc(100)); // calc:100.0
  8. System.out.println(“sqrt:” + interfaceMethod.sqrt(16)); // sqrt:4.0

Lambda表达式

来看下排序的例子,静态工具方法Collections.sort接受一个list,和一个Comparator接口作为输入参数,java8之前需要一个匿名对象,java8可以用Lambda表达式变得简洁

  1.     /**
  2.      * Java8之前方法(匿名对象)实现排序
  3.      * @param names
  4.      * @author wenqy
  5.      * @date 2020年1月16日 上午10:07:45
  6.      */
  7.     private static void sortByBefore8Method(List<String> names) {
  8.         Collections.sort(names, new Comparator<String>() {
  9.             @Override
  10.             public int compare(String a, String b) {
  11.                 return b.compareTo(a);
  12.             }
  13.         });
  14.     }
  15.     /**
  16.      * 用Lambda表达式排序,Java编译器能够自动识别参数类型
  17.      * @param names
  18.      * @author wenqy
  19.      * @date 2020年1月16日 上午10:09:40
  20.      */
  21.     private static void sortByLambda(List<String> names) {
  22. //      可以这样写
  23. //      Collections.sort(names, (String a, String b) -> {
  24. //          return b.compareTo(a);
  25. //      });
  26. //      也可以这样写
  27. //      Collections.sort(names, (String a, String b) -> b.compareTo(a));
  28. //      还可以这样写
  29.         Collections.sort(names, (a, b) -> b.compareTo(a));
  30.     }

Java编译器能够自动识别参数的类型,所以可以省略掉类型不写。

函数式接口

每一个lambda都能够通过一个特定的接口,与一个给定的类型进行匹配。一个所谓的函数式接口必须要有且仅有一个抽象方法声明。每个与之对应的lambda表达式必须要与抽象方法的声明相匹配。由于默认方法不是抽象的,因此你可以在你的函数式接口里任意添加默认方法。

任意只包含一个抽象方法的接口,我们都可以用来做成lambda表达式。为了让你定义的接口满足要求,你应当在接口前加上@FunctionalInterface 标注。编译器会注意到这个标注,如果你的接口中定义了第二个抽象方法的话,编译器会抛出异常。

  1. /**
  2.  * 
  3.  * 每一个lambda都能够通过一个特定的接口,与一个给定的类型进行匹配。
  4.  * 一个所谓的函数式接口必须要有且仅有一个抽象方法声明。
  5.  * 每个与之对应的lambda表达式必须要与抽象方法的声明相匹配。
  6.  * 
  7.  * @FunctionalInterface 标注。
  8.  *  编译器会注意到这个标注,如果接口中定义了第二个抽象方法的话,编译器会抛出异常
  9.  * 
  10.  * @version V5.0
  11.  * @author wenqy
  12.  * @date   2020年1月16日
  13.  */
  14. @FunctionalInterface
  15. public interface Converter<F, T> {
  16.     /**
  17.      * 类型转换  F->T
  18.      * @param from
  19.      * @return
  20.      * @author wenqy
  21.      * @date 2020年1月16日 上午10:22:32
  22.      */
  23.     T convert(F from);
  24. //  T mapping(F from);
  25. }

方法和构造函数引用

函数式接口可以通过方法或构造函数引用使代码变得更简洁。Java 8 允许你通过::关键字获取方法或者构造函数的的引用。

  1. static class Something {
  2.     String startsWith(String s) {
  3.         return String.valueOf(s.charAt(0));
  4.     }
  5. }
  6. private static <F, T> void convert(Converter<F,T> converter, F from) {
  7.     T converted = converter.convert(from);
  8.     System.out.println(converted);
  9. }
  10. public static void main(String[] args) {
  11.     Converter<String, Integer> converter1 = Integer::valueOf; // 静态方法引用
  12.     convert(converter1,“123”); // “1”
  13.     Something something = new Something();
  14.     Converter<String, String> converter2 = something::startsWith; // 对象方法引用
  15.     convert(converter2,“Java”); // “J”
  16.     PersonFactory<Person> personFactory = Person::new;  // 构造函数引用(构造参数需一致)
  17.     Person person = personFactory.create(“Peter”“Parker”);
  18.     System.out.println(person); // Person [firstName=Peter, lastName=Parker]
  19. }

Lambda访问范围

对于lambda表达式外部的变量,其访问权限的粒度与匿名对象的方式非常类似。你能够访问局部对应的外部区域的局部final变量,以及成员变量和静态变量。默认方法无法在lambda表达式内部被访问。

我们可以访问lambda表达式外部的final局部变量,但是与匿名对象不同的是,变量num并不需要一定是final,然而,num在编译的时候被隐式地当做final变量来处理。

  1.     static class Lambda {
  2.         static int outerStaticNum;
  3.         int outerNum;
  4.         /**
  5.          * 访问成员变量
  6.          * 
  7.          * @author wenqy
  8.          * @date 2020年1月16日 上午11:15:31
  9.          */
  10.         void accessOuterNum() {
  11.             Converter<Integer, String> converter = (from) -> {
  12.                 outerNum = 23;
  13.                 return String.valueOf(outerNum + from);
  14.             };
  15.             System.out.println(“accessOuterNum:” + converter.convert(2)); // 25
  16. //          outerNum = 3;
  17.         }
  18.         /**
  19.          * 访问静态变量
  20.          * 
  21.          * @author wenqy
  22.          * @date 2020年1月16日 上午11:18:41
  23.          */
  24.         void accessOuterStaticNum() {
  25.             Converter<Integer, String> converter = (from) -> {
  26.                 outerStaticNum = 72;
  27.                 return String.valueOf(outerStaticNum + from);
  28.             };
  29.             System.out.println(“accessOuterStaticNum:” + converter.convert(2)); // 74
  30. //          outerStaticNum = 3;
  31.         }
  32.         /**
  33.          * 访问局部变量
  34.          *      可以访问lambda表达式外部的final局部变量
  35.          *      但是与匿名对象不同的是,变量num并不需要一定是final
  36.          * @author wenqy
  37.          * @date 2020年1月16日 上午11:06:09
  38.          */
  39.         void accessLocalVariable() {
  40. //          final int num = 1;
  41.             int num = 1;
  42.             Converter<Integer, String> stringConverter =
  43.                     (from) -> String.valueOf(from + num);
  44.             System.out.println(“accessLocalVariable:” + stringConverter.convert(2));     // 3
  45. //          num = 3; // 编译的时候被隐式地当做final变量来处理,无法改变值
  46.         }
  47.     }
  48.     public static void main(String[] args) {
  49.         Lambda lambda = new Lambda();
  50.         lambda.accessLocalVariable();
  51.         lambda.accessOuterNum();
  52.         lambda.accessOuterStaticNum();
  53. //      默认方法无法在lambda表达式内部被访问,无法通过编译
  54. //      DefaultInterfaceMethod formula = (a) -> sqrt( a * 100);
  55.     }

内置函数式接口

Java 8 API 还提供了很多新的函数式接口,来降低程序员的工作负担。让我们来看下几个内置的函数式接口

Predicate是一个布尔类型的函数,该函数只有一个输入参数。Predicate接口包含了多种默认方法,用于处理复杂的逻辑动词(and, or,negate)

Function接口接收一个参数,并返回单一的结果。默认方法可以将多个函数串在一起(compse, andThen)

Supplier接口产生一个给定类型的结果。与Function不同的是,Supplier没有输入参数

Consumer代表了在一个输入参数上需要进行的操作

Comparator接口在早期的Java版本中非常著名。Java 8 为这个接口添加了不同的默认方法

Optional不是一个函数式接口,而是一个精巧的工具接口,用来防止NullPointerException产生。他是一个简单的值容器,这个值可以是null,也可以是non-null。考虑到一个方法可能会返回一个non-null的值,也可能返回一个空值。为了不直接返回null,我们在Java 8中就返回一个Optional

  1. /**
  2.      * 布尔类型函数
  3.      * 
  4.      * @author wenqy
  5.      * @date 2020年1月16日 上午11:39:26
  6.      */
  7.     private static void testPredicates() {
  8.         System.out.println(“——>testPredicates——>”);
  9.         Predicate<String> predicate = (s) -> s.length() > 0;
  10.         System.out.println(predicate.test(“foo”)); // true
  11.         System.out.println(predicate.negate().test(“foo”)); // false
  12.         Predicate<Boolean> nonNull = Objects::nonNull;
  13.         Predicate<Boolean> isNull = Objects::isNull;
  14.         System.out.println(nonNull.test(null)); // false
  15.         System.out.println(isNull.test(null)); // true
  16.         Predicate<String> isEmpty = String::isEmpty;
  17.         Predicate<String> isNotEmpty = isEmpty.negate();
  18.         System.out.println(isEmpty.test(“”)); // true
  19.         System.out.println(isNotEmpty.test(“”)); // false
  20.     }
  21.     /**
  22.      * 内置 Function 函数,将多个函数串联
  23.      * 
  24.      * @author wenqy
  25.      * @date 2020年1月16日 上午11:41:35
  26.      */
  27.     private static void testFunctions() {
  28.         System.out.println(“——>testFunctions——>”);
  29.         Function<String, Integer> toInteger = Integer::valueOf;
  30.         Function<String, String> backToString = toInteger.andThen(String::valueOf);
  31.         System.out.println(backToString.apply(“123”));     // “123”
  32.     }
  33.     /**
  34.      * 内置 Supplier 函数
  35.      *  产生一个给定类型的结果
  36.      * @author wenqy
  37.      * @date 2020年1月16日 上午11:52:51
  38.      */
  39.     private static void testSuppliers() {
  40.         System.out.println(“——>testSuppliers——>”);
  41.         Supplier<Person> personSupplier = Person::new;
  42.         Person person = personSupplier.get();   // new Person
  43.         person.setFirstName(“wen”);
  44.         person.setLastName(“qy”);
  45.         System.out.println(person);
  46.     }
  47.     /**
  48.      * 内置 Consumer 函数
  49.      *  输入参数上需要进行操作
  50.      * @author wenqy
  51.      * @date 2020年1月16日 下午1:48:33
  52.      */
  53.     private static void testConsumers() {
  54.         System.out.println(“——>testConsumers——>”);
  55.         Consumer<Person> greeter = (p) -> System.out.println(“Hello, “ + p.getFirstName());
  56.         greeter.accept(new Person(“Luke”“Skywalker”));
  57.     }
  58.     /**
  59.      * 
  60.      * 内置 Comparator 函数
  61.      *      用于比较
  62.      * @author wenqy
  63.      * @date 2020年1月16日 下午1:54:29
  64.      */
  65.     private static void testComparators() {
  66.         System.out.println(“——>testComparators——>”);
  67.         Comparator<Person> comparator = (p1, p2) -> p1.getFirstName().compareTo(p2.getFirstName());
  68.         Person p1 = new Person(“John”“Doe”);
  69.         Person p2 = new Person(“Alice”“Wonderland”);
  70.         System.out.println(comparator.compare(p1, p2)); // > 0
  71.         System.out.println(comparator.reversed().compare(p1, p2)); // < 0
  72.     }
  73.     /**
  74.      * Optional
  75.      *  值容器,用来防止NullPointerException产生
  76.      * @author wenqy
  77.      * @date 2020年1月16日 下午1:58:44
  78.      */
  79.     private static void testOptionals() {
  80.         System.out.println(“——>testOptionals——>”);
  81.         Optional<String> optional = Optional.of(“bam”);
  82.         System.out.println(optional.isPresent()); // true
  83.         System.out.println(optional.get()); // “bam”
  84.         System.out.println(optional.orElse(“fallback”)); // “bam”
  85.         optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // “b”
  86.     }

Stream

java.util.Stream表示了某一种元素的序列,在这些元素上可以进行各种操作。Stream操作可以是中间操作,也可以是完结操作。完结操作会返回一个某种类型的值,而中间操作会返回流对象本身,并且你可以通过多次调用同一个流操作方法来将操作结果串起来(就像StringBuffer的append方法一样)。Stream是在一个源的基础上创建出来的,例如java.util.Collection中的list或者set(map不能作为Stream的源)。Stream操作往往可以通过顺序或者并行两种方式来执行。Stream并不会改变原有的序列。

  1. List<String> stringCollection = new ArrayList<>();
  2. public StreamsMain() {
  3.     init();
  4. }
  5. private void init() {
  6.     if (stringCollection.isEmpty()) {
  7.         stringCollection.add(“ddd2”);
  8.         stringCollection.add(“aaa2”);
  9.         stringCollection.add(“bbb1”);
  10.         stringCollection.add(“aaa1”);
  11.         stringCollection.add(“bbb3”);
  12.         stringCollection.add(“ccc”);
  13.         stringCollection.add(“bbb2”);
  14.         stringCollection.add(“ddd1”);
  15.     }
  16. }
  17. /**
  18.  * 
  19.  * Filter接受一个predicate接口类型的变量,并将所有流对象中的元素进行过滤。
  20.  * 该操作是一个中间操作,因此它允许我们在返回结果的基础上再进行其他的流操作(forEach)。
  21.  * ForEach接受一个function接口类型的变量,用来执行对每一个元素的操作。
  22.  * ForEach是一个中止操作。它不返回流,所以我们不能再调用其他的流操作。
  23.  * @author wenqy
  24.  * @date 2020年1月16日 下午2:34:58
  25.  */
  26. private void testFilter() {
  27.     System.out.println(“—–>testFilter—–>”);
  28.     stringCollection
  29.         .stream()
  30.         .filter((s) -> s.startsWith(“a”)) // 过滤以“a”开头的集合
  31.         .forEach(System.out::println);
  32. }
  33. /**
  34.  * Sorted是一个中间操作,能够返回一个排过序的流对象的视图。
  35.  * 流对象中的元素会默认按照自然顺序进行排序,除非你自己指定一个Comparator接口来改变排序规则
  36.  * 
  37.  * @author wenqy
  38.  * @date 2020年1月16日 下午2:38:28
  39.  */
  40. private void testSorted() {
  41.     System.out.println(“—–>testSorted—–>”);
  42.     stringCollection
  43.         .stream()
  44.         .sorted() // 自然排序
  45.         .filter((s) -> s.startsWith(“a”)) // 过滤以“a”开头的集合
  46.         .forEach(System.out::println);
  47.     // sorted不会改变原来集合中元素的顺序。原来string集合中的元素顺序是没有改变的。
  48.     System.out.println(stringCollection);
  49. }
  50. /**
  51.  * map是一个对于流对象的中间操作,通过给定的方法,它能够把流对象中的每一个元素对应到另外一个对象上。
  52.  * 下面的例子就演示了如何把每个string都转换成大写的string. 
  53.  * 不但如此,你还可以把每一种对象映射成为其他类型。
  54.  * 对于带泛型结果的流对象,具体的类型还要由传递给map的泛型方法来决定。
  55.  * 
  56.  * @author wenqy
  57.  * @date 2020年1月16日 下午2:43:38
  58.  */
  59. private void testMap() {
  60.     System.out.println(“—–>testMap—–>”);
  61.     stringCollection
  62.         .stream()
  63.         .map(String::toUpperCase) // 每个元素转大写
  64.         .sorted((a, b) -> b.compareTo(a))
  65.         .forEach(System.out::println);
  66.     // sorted不会改变原来集合中元素的顺序。原来string集合中的元素顺序是没有改变的。
  67.     System.out.println(stringCollection);
  68. }
  69. /**
  70.  * 匹配操作有多种不同的类型,都是用来判断某一种规则是否与流对象相互吻合的。
  71.  * 所有的匹配操作都是终结操作,只返回一个boolean类型的结果。
  72.  * 
  73.  * @author wenqy
  74.  * @date 2020年1月16日 下午2:47:48
  75.  */
  76. private void testMatch() {
  77.     System.out.println(“—–>testMatch—–>”);
  78.     boolean anyStartsWithA =
  79.         stringCollection
  80.             .stream()
  81.             .anyMatch((s) -> s.startsWith(“a”));
  82.     System.out.println(anyStartsWithA);      // true
  83.     boolean allStartsWithA =
  84.         stringCollection
  85.             .stream()
  86.             .allMatch((s) -> s.startsWith(“a”));
  87.     System.out.println(allStartsWithA);      // false
  88.     boolean noneStartsWithZ =
  89.         stringCollection
  90.             .stream()
  91.             .noneMatch((s) -> s.startsWith(“z”));
  92.     System.out.println(noneStartsWithZ);      // true
  93. }
  94. /**
  95.  * Count是一个终结操作,它的作用是返回一个数值,用来标识当前流对象中包含的元素数量。
  96.  * 
  97.  * @author wenqy
  98.  * @date 2020年1月16日 下午2:51:40
  99.  */
  100. private void testCount() {
  101.     System.out.println(“—–>testCount—–>”);
  102.     long startsWithB =
  103.         stringCollection
  104.             .stream()
  105.             .filter((s) -> s.startsWith(“b”))
  106.             .count();
  107.     System.out.println(startsWithB);    // 3
  108. }
  109. /**
  110.  * 该操作是一个终结操作,它能够通过某一个方法,对元素进行削减操作。
  111.  * 该操作的结果会放在一个Optional变量里返回。
  112.  * 
  113.  * @author wenqy
  114.  * @date 2020年1月16日 下午2:54:00
  115.  */
  116. private void testReduce() {
  117.     System.out.println(“—–>testReduce—–>”);
  118.     Optional<String> reduced =
  119.         stringCollection
  120.             .stream()
  121.             .sorted()
  122.             .reduce((s1, s2) -> s1 + “#” + s2);
  123.     reduced.ifPresent(System.out::println); // “aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2”
  124. }

并发流Stream

数量量大时,可以使用并行流进行操作来提高运行效率。

  1. //  int max = 1000000; // 大数据量并发才有优势
  2.     int max = 400000;
  3.     List<String> values = new ArrayList<>(max);
  4.     public ParallelStreamsMain() {
  5.         init();
  6.     }
  7.     private void init() {
  8.         for (int i = 0; i < max; i++) {
  9.             UUID uuid = UUID.randomUUID();
  10.             values.add(uuid.toString());
  11.         }
  12.     }
  13.     /**
  14.      * 顺序排序
  15.      * 
  16.      * @author wenqy
  17.      * @date 2020年1月16日 下午3:20:22
  18.      */
  19.     private void sequenceSorted() {
  20.         System.out.println(“——>sequenceSorted—–>”);
  21.         long t0 = System.nanoTime();
  22.         long count = values.stream().sorted().count();
  23.         System.out.println(count);
  24.         long t1 = System.nanoTime();
  25.         long millis = TimeUnit.NANOSECONDS.toMillis(t1 – t0);
  26.         System.out.println(String.format(“sequential sort took: %d ms”, millis));
  27.     }
  28.     /**
  29.      * 并行排序
  30.      * 
  31.      * @author wenqy
  32.      * @date 2020年1月16日 下午3:21:56
  33.      */
  34.     private void parallelSorted() {
  35.         System.out.println(“——>parallelSorted—–>”);
  36.         long t0 = System.nanoTime();
  37.         long count = values.parallelStream().sorted().count();
  38.         System.out.println(count);
  39.         long t1 = System.nanoTime();
  40.         long millis = TimeUnit.NANOSECONDS.toMillis(t1 – t0);
  41.         System.out.println(String.format(“parallel sort took: %d ms”, millis));
  42.     }

Map

map是不支持流操作的。而更新后的map现在则支持多种实用的新方法

  1. Map<Integer, String> map = new HashMap<>();
  2. for (int i = 0; i < 10; i++) {
  3.     map.putIfAbsent(i, “val” + i); // 旧值存在时返回旧值,不进行替换
  4. }
  5. map.forEach((id, val) -> System.out.println(val));
  6. map.computeIfPresent(3, (num, val) -> val + num);
  7. map.get(3);             // val33
  8. map.computeIfPresent(9, (num, val) -> null); // 返回null时,remove(key)
  9. map.containsKey(9);     // false
  10. map.computeIfAbsent(23, num -> “val” + num);
  11. map.containsKey(23);    // true
  12. map.computeIfAbsent(3, num -> “bam”); // 旧值存在时返回旧值,不进行替换
  13. map.get(3);             // val33
  14. map.remove(3“val3”);  // 校验value删除
  15. map.get(3);             // val33
  16. map.remove(3“val33”);
  17. map.get(3);             // null
  18. map.getOrDefault(42“not found”);  // not found 不存在则返回默认值
  19. map.merge(9“val9”, (value, newValue) -> value.concat(newValue)); // 先前key 9 已经删除了,不存在key,则返回 value
  20. System.out.println(map.get(9));             // val9
  21. map.merge(9“concat”, (value, newValue) -> value.concat(newValue)); // 存在key,才进行合并
  22. System.out.println(map.get(9));             // val9concat

时间日期API

Java 8 包含了全新的时间日期API,这些功能都放在了java.time包下。新的时间日期API是基于Joda-Time库开发的,但是也不尽相同。

  1. /**
  2.  * Clock提供了对当前时间和日期的访问功能。Clock是对当前时区敏感的
  3.  * 
  4.  * @author wenqy
  5.  * @date 2020年1月16日 下午4:36:49
  6.  */
  7. private static void testClock() {
  8.     System.out.println(“—–>testClock—–>”);
  9.     Clock clock = Clock.systemDefaultZone();
  10.     long millis = clock.millis();
  11.     System.out.println(millis); // 获取当前毫秒时间
  12.     Instant instant = clock.instant();
  13.     Date date = Date.from(instant);   // java.util.Date
  14.     System.out.println(date); // 获取日期 Thu Jan 16 16:38:01 CST 2020
  15. }
  16. /**
  17.  * 时区类可以用一个ZoneId来表示。
  18.  * 时区类还定义了一个偏移量,用来在当前时刻或某时间与目标时区时间之间进行转换。
  19.  * 
  20.  * @author wenqy
  21.  * @date 2020年1月16日 下午4:40:38
  22.  */
  23. private static void testTimezones() {
  24.     System.out.println(“—–>testTimezones—–>”);
  25.     System.out.println(ZoneId.getAvailableZoneIds());
  26.     // prints all available timezone ids
  27.     ZoneId zone1 = ZoneId.of(“Europe/Berlin”);
  28.     ZoneId zone2 = ZoneId.of(“Brazil/East”);
  29.     ZoneId zone3 = ZoneId.of(“Asia/Shanghai”);
  30.     System.out.println(zone1.getRules());
  31.     System.out.println(zone2.getRules());
  32.     System.out.println(zone3.getRules());
  33. }
  34. /**
  35.  * 本地时间类表示一个没有指定时区的时间
  36.  * 
  37.  * @author wenqy
  38.  * @date 2020年1月16日 下午4:49:11
  39.  */
  40. private static void testLocalTime() {
  41.     System.out.println(“—–>testLocalTime—–>”);
  42.     LocalTime now1 = LocalTime.now(ZoneId.of(“Europe/Berlin”));
  43.     LocalTime now2 = LocalTime.now(ZoneId.of(“Asia/Shanghai”));
  44.     // 比较两个时间
  45.     System.out.println(now1.isBefore(now2));  // true
  46.     long hoursBetween = ChronoUnit.HOURS.between(now1, now2);
  47.     long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);
  48.     // 柏林时区在东一区,上海时区在东八区   柏林比北京时间慢7小时
  49.     System.out.println(hoursBetween);       // 7
  50.     System.out.println(minutesBetween);     // 420
  51.     // 时间字符串解析操作
  52.     LocalTime late = LocalTime.of(235959);
  53.     System.out.println(late);       // 23:59:59
  54.     DateTimeFormatter germanFormatter =
  55.         DateTimeFormatter
  56.             .ofLocalizedTime(FormatStyle.SHORT)
  57.             .withLocale(Locale.GERMAN);
  58.     LocalTime leetTime = LocalTime.parse(“13:37”, germanFormatter);
  59.     System.out.println(leetTime);   // 13:37
  60. }
  61. /**
  62.  * 本地日期
  63.  *  每一次操作都会返回一个新的时间对象
  64.  * 
  65.  * @author wenqy
  66.  * @date 2020年1月16日 下午5:05:52
  67.  */
  68. private static void testLocalDate() {
  69.     System.out.println(“—–>testLocalDate—–>”);
  70.     LocalDate today = LocalDate.now(); // 今天
  71.     LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS); // 明天=今天+1
  72.     LocalDate yesterday = tomorrow.minusDays(2); // 昨天=明天-2
  73.     System.out.println(yesterday);
  74.     LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);
  75.     DayOfWeek dayOfWeek = independenceDay.getDayOfWeek(); // 获取周
  76.     System.out.println(dayOfWeek); // FRIDAY
  77. }
  78. /**
  79.  * 日期-时间
  80.  *  final对象
  81.  * @author wenqy
  82.  * @date 2020年1月16日 下午5:12:37
  83.  */
  84. private static void testLocalDateTime() {
  85.     System.out.println(“—–>testLocalDateTime—–>”);
  86.     LocalDateTime sylvester = LocalDateTime.of(2019, Month.DECEMBER, 31235959);
  87.     DayOfWeek dayOfWeek = sylvester.getDayOfWeek();
  88.     System.out.println(dayOfWeek);      // TUESDAY
  89.     Month month = sylvester.getMonth();
  90.     System.out.println(month);          // DECEMBER
  91.     long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);
  92.     System.out.println(minuteOfDay);    // 1439
  93.     // 加上时区,转日期Date
  94.     Instant instant = sylvester
  95.             .atZone(ZoneId.systemDefault())
  96.             .toInstant();
  97.     Date legacyDate = Date.from(instant);
  98.     System.out.println(legacyDate);     // Tue Dec 31 23:59:59 CST 2019
  99.     // 自定义格式对象
  100.     DateTimeFormatter formatter =
  101.         DateTimeFormatter
  102.             .ofPattern(“yyyy-MM-dd HH:mm:ss”);  // 线程安全,不可变
  103.     LocalDateTime parsed = LocalDateTime.parse(“2020-01-16 17:13:00”, formatter);
  104.     String string = formatter.format(parsed);
  105.     System.out.println(string);     // 2020-01-16 17:13:00
  106. }

可重复注解

Java 8中的注解是可重复的

  1. /**
  2.  * @Repeatable,Java 8 允许我们对同一类型使用多重注解
  3.  * 
  4.  * @version V5.0
  5.  * @author wenqy
  6.  * @date   2020年1月16日
  7.  */
  8. @Repeatable(Hints.class)
  9. @Retention(RetentionPolicy.RUNTIME) // 需要指定runtime,否则测试会失败
  10. public @interface Hint {
  11.     String value();
  12. }

在Person类添加注解,尝试获取类注解

  1. /**
  2.  * @Hint 可重复注解(新方法)
  3.  * 使用注解容器(老方法): @Hints({@Hint(“hint1”), @Hint(“hint2”)})
  4.  * @version V5.0
  5.  * @author wenqy
  6.  * @date   2020年1月16日
  7.  */
  8. @Hint(“hint1”)
  9. @Hint(“hint2”)
  10. //@Hints({@Hint(“hint1”), @Hint(“hint2”)})
  11. public class Person {
  12.     // …
  13. }
  14. Hint hint = Person.class.getAnnotation(Hint.class);
  15. System.out.println(hint);                   // null
  16. Hints hints1 = Person.class.getAnnotation(Hints.class);
  17. System.out.println(hints1.value().length);  // 2
  18. Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);
  19. System.out.println(hints2.length);          // 2

String

java8添加了对字符数据流的流式操作

  1. System.out.println(String.join(“:”“foobar”“foo”“bar”)); // foobar:foo:bar
  2.         // 指定分割符,将字符串拼接
  3.         System.out.println(
  4.                 “foobar:foo:bar”
  5.                 .chars() // 字符数据流
  6.                 .distinct()
  7.                 .mapToObj(c -> String.valueOf((char)c))
  8.                 .sorted()
  9.                 .collect(Collectors.joining())
  10.                 ); // :abfor
  11.         System.out.println(
  12.                 Pattern.compile(“:”)
  13.                 .splitAsStream(“foobar:foo:bar”)
  14.                 .filter(s -> s.contains(“bar”))
  15.                 .sorted()
  16.                 .collect(Collectors.joining(“:”))
  17.             ); // bar:foobar
  18.         System.out.println(
  19.                 Stream.of(“bob@gmail.com”“alice@hotmail.com”)
  20.                     .filter(Pattern.compile(“.*@gmail\\.com”).asPredicate())
  21.                     .count()
  22.             ); // 1

数值处理

Java8添加了对无符号数的额外支持。新增了一些方法来处理数值溢出

  1. System.out.println(Integer.MAX_VALUE);      // 2147483647
  2.         System.out.println(Integer.MAX_VALUE + 1);  // -2147483648
  3.         long maxUnsignedInt = (1l << 32) – 1// 
  4.         String string = String.valueOf(maxUnsignedInt);
  5.         int unsignedInt = Integer.parseUnsignedInt(string, 10); // 无符号转换
  6.         String string2 = Integer.toUnsignedString(unsignedInt, 10);
  7.         System.out.println(string2);
  8.         try {
  9.             Integer.parseInt(string, 10);
  10.         } catch (NumberFormatException e) {
  11.             System.err.println(“could not parse signed int of “ + maxUnsignedInt);
  12.         }
  13.         try {
  14.             Math.addExact(Integer.MAX_VALUE, 1);
  15.         }
  16.         catch (ArithmeticException e) {
  17.             System.err.println(e.getMessage());
  18.             // => integer overflow
  19.         }
  20.         try {
  21.             Math.toIntExact(Long.MAX_VALUE);
  22.         }
  23.         catch (ArithmeticException e) {
  24.             System.err.println(e.getMessage());
  25.             // => integer overflow
  26.         }

文件处理

java8新增了对文件处理的方法

  1. /**
  2.  * 文件查找
  3.  * @throws IOException
  4.  * @author wenqy
  5.  * @date 2020年1月18日 下午3:02:05
  6.  */
  7. private static void findFiles() throws IOException {
  8.     Path start = Paths.get(“”);
  9.     int maxDepth = 5;
  10.     try (Stream<Path> stream = Files.find(start, maxDepth, (path, attr) ->
  11.             String.valueOf(path).endsWith(“.js”))) {
  12.         String joined = stream
  13.             .sorted()
  14.             .map(String::valueOf)
  15.             .collect(Collectors.joining(“; “));
  16.         System.out.println(“Found: “ + joined);
  17.     }
  18. }
  19. /**
  20.  * 获取文件列表
  21.  * @throws IOException
  22.  * @author wenqy
  23.  * @date 2020年1月18日 下午3:02:26
  24.  */
  25. private static void listFiles() throws IOException {
  26.     try (Stream<Path> stream = Files.list(Paths.get(“”))) {
  27.         String joined = stream
  28.             .map(String::valueOf)
  29.             .filter(path -> !path.startsWith(“.”))
  30.             .sorted()
  31.             .collect(Collectors.joining(“; “));
  32.         System.out.println(“List: “ + joined);
  33.         // 列出了当前工作目录的所有文件,之后将每个路径都映射为它的字符串表示。之后结果被过滤、排序,最后连接为一个字符串
  34.     }
  35. }
  36. /**
  37.  * 文件读写处理
  38.  * @throws IOException
  39.  * @author wenqy
  40.  * @date 2020年1月18日 下午3:02:52
  41.  */
  42. private static void handleFiles() throws IOException {
  43.     List<String> lines = Files.readAllLines(Paths.get(“res/nashorn1.js”)); // 整个文件都会读进内存,不高效。文件越大,所用的堆区也就越大
  44.     lines.add(“print(‘foobar’);”);
  45.     Files.write(Paths.get(“res/nashorn1-modified.js”), lines);
  46.     try (Stream<String> stream = Files.lines(Paths.get(“res/nashorn1.js”))) { // 行读取,高效点
  47.         stream
  48.             .filter(line -> line.contains(“print”))
  49.             .map(String::trim)
  50.             .forEach(System.out::println);
  51.     }
  52.     try (BufferedReader reader = Files.newBufferedReader(Paths.get(“res/nashorn1.js”))) { // BufferedReader 更精细
  53.         System.out.println(reader.readLine());
  54.     }
  55.     Path pathOut = Paths.get(“res/output.js”);
  56.     try (BufferedWriter writer = Files.newBufferedWriter(pathOut)) { // BufferedWriter 写入文件
  57.         writer.write(“print(‘Hello World’);”);
  58.     }
  59.     try (BufferedReader reader = Files.newBufferedReader(Paths.get(“res/nashorn1.js”))) {
  60.         long countPrints = reader
  61.             .lines() // 流式处理
  62.             .filter(line -> line.contains(“print”))
  63.             .count();
  64.         System.out.println(countPrints);
  65.     }
  66. }

避免 Null 检查

java8之前,如果是多层内嵌对象,需要多次判空,引入Optional 类型提高安全性

  1. public static void main(String[] args) {
  2.     Outer outer = new Outer();
  3.     if (outer != null && outer.nested != null && outer.nested.inner != null) {
  4.         System.out.println(outer.nested.inner.foo);
  5.     }
  6.     Optional.of(new Outer())
  7.         .map(Outer::getNested)
  8.         .map(Nested::getInner)
  9.         .map(Inner::getFoo)
  10.         .ifPresent(System.out::println);
  11.     Outer obj = new Outer();
  12.     resolve(() -> obj.getNested().getInner().getFoo())
  13.         .ifPresent(System.out::println);
  14.     // 这两个解决方案可能没有传统 null 检查那么高的性能
  15. }
  16. private static <T> Optional<T> resolve(Supplier<T> resolver) {
  17.     try {
  18.         T result = resolver.get();
  19.         return Optional.ofNullable(result);
  20.     }
  21.     catch (NullPointerException e) {
  22.         return Optional.empty();
  23.     }
  24. }

初识java8新特性,java8语法糖还是挺香的,java8之前方法调用一堆的回调,让人看的眼花缭乱,不整洁。但让人想吐槽的是调试,加难了调试力度,可能是新手错觉。。。

参考

https://github.com/winterbe/java8-tutorial java8教程

https://wizardforcel.gitbooks.io/modern-java/content/ 中文译站

https://github.com/wenqy/java-study

https://github.com/wenqy/java-study

One thought on “品茗Java8新特性

发表评论

电子邮件地址不会被公开。 必填项已用*标注

25 + 6 = ?