总览
forEach() method in Iterable interface(Iterable接口中的forEach()方法)
default and static methods in Interfaces(接口中的默认和静态方法)
Functional Interfaces and Lambda Expressions(function接口和Lambda表达式)
Java Stream API for Bulk Data Operations on Collections(用于集合上的批量数据操作的Java Stream API)
Java Time API
Collection API improvements
Concurrency API improvements
Java IO improvements
1.forEach() method in Iterable interface(Iterable接口中的forEach()方法) 每当我们需要遍历Collection时,我们需要创建一个Iterator,其目的是迭代,然后我们在循环中为Collection中的每个元素提供业务逻辑。如果没有正确使用迭代器,会抛出异常ConcurrentModificationException。
Java 8在java.lang.Iterable接口中引入了forEach方法,这样在编写代码时我们只关注业务逻辑。 forEach方法将java.util.function.Consumer对象作为参数,因此它有助于将我们的业务逻辑放在我们可以重用的单独位置。让我们通过简单的例子看看每个用法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 List<IntegermyList = new ArrayList<Integer>(); for (int i=0 ; i<10 ; i++) myList.add(i);Iterator<Integeriterator = myList.iterator(); while (iterator.hasNext()) { Integer next = iterator.next(); System.out.println("Iterator Value::" + next); } myList.forEach(new Consumer<Integer>() { public void accept (Integer t) { System.out.println("forEach anonymous class Value::" +t); } }); MyConsumer action = new MyConsumer(); myList.forEach(action); myList.forEach(System.out::println);
2.default and static methods in Interfaces(接口中的默认和静态方法) jdk8之前,interface方法不能有实现,但是从Java 8开始,接口被增强为具有实现方法。我们可以使用default和static关键字来创建具有方法实现的接口。例如Iterable接口中的forEach方法实现是
1 2 3 4 5 6 default void forEach (Consumer<? super Taction) { Objects.requireNonNull(action); for (T t : this ) { action.accept(t); } }
示例代码 创建一个接口
1 2 3 4 5 6 7 8 9 10 11 12 public interface MyInterface { void show () ; default void showA () { System.out.println("我是接口默认方法" ); } static void showB () { System.out.println("我是接口静态方法" ); } }
创建该接口实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class MyClass implements MyInterface { @Override public void show () { System.out.println("我是实现方法" ); } @Override public void showA () { System.out.println("我覆盖了接口的默认方法" ); } }
测试
1 2 3 4 5 6 7 8 9 public class Test { public static void main (String[] args) { MyClass myClass = new MyClass(); myClass.show(); myClass.showA(); MyInterface.showB(); } }
3.Functional Interfaces and Lambda Expressions(function接口和Lambda表达式) 如果你注意到上面的接口代码,你会注意到@FunctionalInterface注释。功能接口是Java 8中引入的新概念。只有一个抽象方法的接口就变成了功能接口 。我们不需要使用@FunctionalInterface注释将接口标记为功能接口。 @FunctionalInterface注释是一种避免在功能界面中意外添加抽象方法的工具。您可以将其视为@Override注释,并且最佳实践是使用它。实例:java8 的runnable run接口,带有一个抽象方法:
1 2 3 4 @FunctionalInterface public interface Runnable { public abstract void run () ; }
功能接口的主要好处之一是可以使用lambda表达式来实例化它们。我们可以使用匿名类实例化一个接口,但代码看起来很笨重。
1 2 3 4 5 6 7 Runnable runnable = new Runnable() { @Override public void run () { System.out.println("My Runnable" ); } };
由于功能接口只有一个方法,因此lambda表达式可以很容易地提供方法实现。我们只需要提供方法参数和业务逻辑。例如,我们可以使用lambda表达式将上面的实现编写为:
1 2 3 4 Runnable runnable1 = () -System.out.println("My Runnable" ); runnable.run(); runnable1.run();
如果在方法实现中有单个语句,我们也不需要花括号。例如,上面的Interface1匿名类可以使用lambda实例化,如下所示:
1 2 Interface1 interface1 = (s) -System.out.println(s); interface1.method1("interface1 method" );
lambda表达式扩展 Java 中的 Lambda 表达式通常使用 (argument) -(body) 语法书写,例如: 1 2 (arg1, arg2...) -> { body } (type1 arg1, type2 arg2...) -> { body }
Lambda 表达式的结构
一个Lambda表达式可以有零个或多个参数
参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)
与(a)
效果相同
所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b)
或 (int a, int b)
或 (String a, int b, float c)
空圆括号代表参数集为空。例如:() -> 42
当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a * a
Lambda表达式的主体可包含零条或多条语句
如果Lambda表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致
如果Lambda表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空
函数式接口扩展 函数式接口是只包含一个抽象方法声明的接口,可以使用@FunctionalInterface
标记
JDK8之前已有的函数式接口
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
新定义的函数式接口 java.util.function中定义了几组类型的函数式接口以及针对基本数据类型的子接口。
Predicate:传入一个参数,返回一个bool结果,方法为boolean test(T t)
Consumer:传入一个参数,无返回值,纯消费。方法为void accept(T t)
Function:传入一个参数,返回一个结果,方法为R apply(T t)
Supplier:无参数传入,返回一个结果,方法为T get()
UnaryOperator:一元操作符,继承Function,传入参数的类型和返回类型相同。
BinaryOperator:二元操作符,传入的两个参数的类型和返回类型相同,继承BiFunction。
【示例】
1 2 3 4 5 6 7 8 9 10 11 12 13 Predicate<Integer> predicate = (i) -> i > 0 ; Consumer<Integer> consumer = (i) -> System.out.println("consumer : " + i); Function<Integer,Boolean> function = (i) -> i > 0 ; Supplier<Integer> supplier = () -> 1 ; UnaryOperator<Integer> unaryOperator = (i) -> i * i; BinaryOperator<Integer> binaryOperator = (i1,i2) -> i1 * i2; System.out.println(predicate.test(10 )); consumer.accept(10 ); System.out.println(function.apply(10 )); System.out.println(supplier.get()); System.out.println(unaryOperator.apply(100 )); System.out.println(binaryOperator.apply(100 ,200 ));
4.Java Stream API for Bulk Data Operations on Collections(用于集合上的批量数据操作的Java Stream API) 示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 List<Integerlist = new ArrayList<>(); for (int i=0 ;i<100 ;i++) { list.add(i); } Stream<Integerstream = list.stream(); Stream<Integerstream1 = list.parallelStream(); Stream<Integerstream2 = Arrays.stream(list.toArray(new Integer[0 ])); Stream<Integerstream3 = Stream.of(list.toArray(new Integer[0 ])); BufferedReader bufferedReader = new BufferedReader(new FileReader(new File("path" ))); Stream<Stringstream4 = bufferedReader.lines(); IntStream stream5 = IntStream.rangeClosed(1 , 100 ); Stream<Pathstream6 = Files.walk(Paths.get("path" ), 100 ); Stream<Integerstream7 = StreamSupport.stream(list.spliterator(), false ); Random random = new Random(); IntStream stream8 = random.ints(); BitSet bitSet = BitSet.valueOf(new long []{1L , 2L , 3L }); IntStream stream9 = bitSet.stream(); Pattern pattern = Pattern.compile("\\d+" ); Stream<Stringstream10 = pattern.splitAsStream("111sda123sda" ); JarFile jarFile = new JarFile("xxx.jar" ); Stream<JarEntrystream11 = jarFile.stream();
5.Java Time API(Java时间API) Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。对日期与时间的操作一直是Java程序员最痛苦的地方之一。标准的 java.util.Date以及后来的java.util.Calendar一点没有改善这种情况(可以这么说,它们一定程度上更加复杂)。
Clock类 它通过指定一个时区,然后就可以获取到当前的时刻,日期与时间。Clock可以替换System.currentTimeMillis()与TimeZone.getDefault()。
1 2 3 4 final Clock clock = Clock.systemUTC();System.out.println(clock.instant()); System.out.println(clock.millis());
下面是程序在控制台上的输出:
1 2 2019 -01 -09T14:52 :50. 111Z1547045570335
LocaleDate与LocalTime LocaleDate只持有ISO-8601格式且无时区信息的日期部分。相应的,LocaleTime只持有ISO-8601格式且无时区信息的时间部分。LocaleDate与LocalTime都可以从Clock中得到。
1 2 3 4 5 6 7 8 9 10 11 12 13 final LocalDate date = LocalDate.now();final LocalDate dateFromClock = LocalDate.now(clock);System.out.println(date); System.out.println(dateFromClock); final LocalTime time = LocalTime.now();final LocalTime timeFromClock = LocalTime.now(clock);System.out.println(time); System.out.println(timeFromClock);
下面是程序在控制台上的输出:
1 2 3 4 2019 -01 -092019 -01 -0922 :52 :50.383 14 :52 :50.383
LocaleDateTime LocaleDateTime把LocaleDate与LocaleTime的功能合并起来,它持有的是ISO-8601格式无时区信息的日期与时间。
1 2 3 4 5 6 final LocalDateTime datetime = LocalDateTime.now();final LocalDateTime datetimeFromClock = LocalDateTime.now(clock);System.out.println(datetime); System.out.println(datetimeFromClock);
下面是程序在控制台上的输出:
1 2 2019 -01 -09T22:55 :05.194 2019 -01 -09T14:55 :05.194
ZonedDateTime 如果你需要特定时区的日期/时间,那么ZonedDateTime是你的选择。它持有ISO-8601格式具具有时区信息的日期与时间。
1 2 3 4 5 6 7 8 final ZonedDateTime zonedDatetime = ZonedDateTime.now();final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now(clock);final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now(ZoneId.of("America/Los_Angeles" ));System.out.println(zonedDatetime); System.out.println(zonedDatetimeFromClock); System.out.println(zonedDatetimeFromZone);
下面是程序在控制台上的输出:
1 2 3 2019 -01 -09T22:56 :34.033 +08:00 [Asia/Shanghai]2019 -01 -09T14:56 :34. 033Z2019 -01 -09T06:56 :34.035 -08:00 [America/Los_Angeles]
Duration 在秒与纳秒级别上的一段时间。Duration使计算两个日期间的不同变的十分简单。
1 2 3 4 5 6 7 final LocalDateTime from = LocalDateTime.of(2018 , Month.APRIL, 16 , 0 , 0 , 0 );final LocalDateTime to = LocalDateTime.of(2019 , Month.APRIL, 16 , 23 , 59 , 59 );final Duration duration = Duration.between(from, to);System.out.println("Duration in days: " + duration.toDays()); System.out.println("Duration in hours: " + duration.toHours());
上面的例子计算了两个日期2018年4月16号与2019年4月16号之间的过程。下面是程序在控制台上的输出:
1 2 Duration in days: 365 Duration in hours: 8783
Collection API improvements(集合API改进) 上面已经展示了forEach()方法和Stream API在集合上的使用。java8的Collection API中添加了一些新方法:
Iterator default method forEachRemaining(Consumer action) 为每个元素执行给定操作,直到所有元素都已处理或操作引发异常。
源码 1 2 3 4 5 6 7 default void forEachRemaining (Consumer<? super E> action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next()); }
示例代码 1 2 3 4 5 6 List<Integer> list = Arrays.asList(1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ); Iterator<Integer> iterator = list.iterator(); Consumer<Integer> consumer = i -> System.out.println("consumer print " + i); iterator.forEachRemaining(consumer);
控制台输出 1 2 3 4 consumer print 1 consumer print 2 consumer print 3 ...
Collection default method removeIf(Predicate filter) 删除满足给定条件的此集合的所有元素。
源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 default boolean removeIf (Predicate<? super E> filter) { Objects.requireNonNull(filter); boolean removed = false ; final Iterator<E> each = iterator(); while (each.hasNext()) { if (filter.test(each.next())) { each.remove(); removed = true ; } } return removed; }
示例代码 1 2 3 4 5 6 7 8 List<Integer> list = new ArrayList<>(); list.add(1 ); list.add(2 ); list.add(3 ); list.add(4 ); Predicate<Integer> predicate = i -> i > 1 ; list.removeIf(predicate); System.out.println("remove if left items : " + list);
控制台输出 1 2 remove if left items : [1 ]
Collection spliterator() 返回Spliterator实例的方法,该实例可用于顺序或并行遍历元素。
源码 1 2 3 4 default Spliterator<E> spliterator () { return Spliterators.spliterator(this , Spliterator.ORDERED); }
示例代码 1 2 3 4 5 6 List<Integer> list = Arrays.asList(1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ); Spliterator<Integer> spliterator = list.spliterator(); Stream<Integer> stream = StreamSupport.stream(spliterator, false ); Stream<Integer> parallelStream = StreamSupport.stream(spliterator, true );
Map replaceAll(), compute(), merge() methods replaceAll() 替换Map中所有Entry的value值,这个值由旧的key和value计算得出,接收参数 (K, V) -> V
源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void replaceAll (BiFunction<? super K, ? super V, ? extends V> function) { Node<K,V>[] tab; if (function == null ) throw new NullPointerException(); if (size > 0 && (tab = table) != null ) { int mc = modCount; for (int i = 0 ; i < tab.length; ++i) { for (Node<K,V> e = tab[i]; e != null ; e = e.next) { e.value = function.apply(e.key, e.value); } } if (modCount != mc) throw new ConcurrentModificationException(); } }
示例代码 1 2 3 4 5 6 7 8 9 Map<String, String> map = new HashMap<>(); map.put("1" , "A" ); map.put("2" , "B" ); map.put("3" , "C" ); map.put("4" , "D" ); map.put("5" , "E" ); map.replaceAll((s, s2) -> s + s2); System.out.println(map);
控制台输出 1 2 {1 =1A, 2 =2B, 3 =3C, 4 =4D , 5 =5E}
compute() 是computeIfPresent
和computeIfAbsent
方法的组合体
computeIfPresent:如果指定的key不存在,则通过指定的K -> V计算出新的值设置为key的值。
computeIfPresent:如果指定的key存在,则根据旧的key和value计算新的值newValue, 如果newValue不为null,则设置key新的值为newValue, 如果newValue为null, 则删除该key的值。
示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Map<String, String> map = new HashMap<>(); map.put("1" , "A" ); map.put("2" , "B" ); map.put("3" , "C" ); map.put("4" , "D" ); map.put("5" , "E" ); map.compute("1" , (k, v) -> v + " computed" ); System.out.println("key存在" + map.get("1" )); map.compute("6" , (k, v) -> "F" ); System.out.println("key不存在" + map.get("6" )); map.compute("1" , (k, v) -> null ); System.out.println("key存在,设置为null " + map.get("1" ));
控制台输出 1 2 3 key存在A computed key不存在F key存在,设置为null null
merge() 如果指定的key不存在,则设置指定的value值,否则根据key的旧的值oldvalue,value计算出新的值newValue, 如果newValue为null, 则删除该key,否则设置key的新值newValue。
示例代码 1 2 3 4 5 6 7 8 9 10 11 12 Map<String, String> map = new HashMap<>(); map.put("1" , "A" ); map.put("2" , "B" ); map.put("3" , "C" ); map.put("4" , "D" ); map.put("5" , "E" ); System.out.println(map.merge("1" , "merge" , (k, v) -> k + v)); System.out.println(map.merge("1" , "merge" , (k, v) -> null )); System.out.println(map.merge("6" , "merge" , (k, v) -> k + v));
控制台输出
具有键冲突的HashMap类的性能改进
Concurrency API improvements(并发API改进) ConcurrentHashMap JDK8提供的并发友好的HashMap
CompletableFuture 提供了非常强大的 Future 的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合 CompletableFuture 的方法。
Executors newWorkStealingPool() 创建持有足够线程的线程池来支持给定的并行级别,并通过使用多个队列,减少竞争,它需要传一个并行级别的参数,如果不传,则被设定为默认的CPU数量。
Java IO improvements(Java IO API的改进) Files.list(Path dir) 返回一个延迟填充的Stream,其中的元素是目录中的条目。
1 2 3 Stream<Path> list = Files.list(new File("C:\\Users\\Administrator\\Desktop" ).toPath()); list.forEach(System.out::println);
Files.lines(Path path) 从文件中读取所有行作为流。
1 2 3 Stream<String> lines = Files.lines(new File("C:\\Users\\Administrator\\Desktop\\new 3.txt" ).toPath()); lines.forEach(System.out::println);
Files.find() 通过搜索以给定起始文件为根的文件树中的文件,返回使用Path延迟填充的Stream。
1 2 3 4 5 Stream<Path> stream = Files.find(new File("C:\\Users\\Administrator\\Desktop" ).toPath(), 1 , (path, basicFileAttributes) -> basicFileAttributes.isDirectory()); stream.forEach(System.out::println);
BufferedReader.lines() 返回一个Stream,其元素是从这个BufferedReader读取的行。
1 2 3 4 BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\Administrator\\Desktop\\new 3.txt" )); Stream<String> stringStream = br.lines(); stringStream.forEach(System.out::println);
参考资源