总览

  • 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对象作为参数,因此它有助于将我们的业务逻辑放在我们可以重用的单独位置。让我们通过简单的例子看看每个用法。

List<IntegermyList = new ArrayList<Integer>();
for(int i=0; i<10; i++) myList.add(i); //使用iterator
Iterator<Integeriterator = myList.iterator();
while (iterator.hasNext()) {
Integer next = iterator.next();
System.out.println("Iterator Value::" + next);
} //foreach + 匿名类
myList.forEach(new Consumer<Integer>() { public void accept(Integer t) {
System.out.println("forEach anonymous class Value::"+t);
} }); //使用consumer 接口
MyConsumer action = new MyConsumer();
myList.forEach(action); //使用lambda表达式
myList.forEach(System.out::println);

2.default and static methods in Interfaces(接口中的默认和静态方法)

jdk8之前,interface方法不能有实现,但是从Java 8开始,接口被增强为具有实现方法。我们可以使用default和static关键字来创建具有方法实现的接口。例如Iterable接口中的forEach方法实现是

default void forEach(Consumer<? super Taction) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}

示例代码

创建一个接口

public interface MyInterface {

    void show();

    default void showA() {
System.out.println("我是接口默认方法");
} static void showB() {
System.out.println("我是接口静态方法");
}
}

创建该接口实现类

public class MyClass implements MyInterface {
@Override
public void show() {
System.out.println("我是实现方法");
} //默认方法支持重写,不覆盖则执行接口的默认方法
@Override
public void showA() {
System.out.println("我覆盖了接口的默认方法");
} //静态方法不可以重写
}

测试

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接口,带有一个抽象方法:

@FunctionalInterface
public interface Runnable {
public abstract void run();
}

功能接口的主要好处之一是可以使用lambda表达式来实例化它们。我们可以使用匿名类实例化一个接口,但代码看起来很笨重。

//使用匿名类实例化
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("My Runnable");
}
};

由于功能接口只有一个方法,因此lambda表达式可以很容易地提供方法实现。我们只需要提供方法参数和业务逻辑。例如,我们可以使用lambda表达式将上面的实现编写为:

//使用lambda表达式
Runnable runnable1 = () -System.out.println("My Runnable");
runnable.run();
runnable1.run();

如果在方法实现中有单个语句,我们也不需要花括号。例如,上面的Interface1匿名类可以使用lambda实例化,如下所示:

Interface1 interface1 = (s) -System.out.println(s);
interface1.method1("interface1 method");

lambda表达式扩展

Java 中的 Lambda 表达式通常使用 (argument) -(body) 语法书写,例如:

(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。

【示例】

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)

示例代码

//从 Collection 和数组
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 bufferedReader = new BufferedReader(new FileReader(new File("path")));
Stream<Stringstream4 = bufferedReader.lines(); //静态工厂
IntStream stream5 = IntStream.rangeClosed(1, 100);//生成1-100 的int stream
Stream<Pathstream6 = Files.walk(Paths.get("path"), 100); //自己构建 通过StreamSupport辅助类从spliterator产生流
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()。

// Get the system clock as UTC offset
final Clock clock = Clock.systemUTC();
System.out.println(clock.instant());
System.out.println(clock.millis());

下面是程序在控制台上的输出:

2019-01-09T14:52:50.111Z
1547045570335

LocaleDate与LocalTime

LocaleDate只持有ISO-8601格式且无时区信息的日期部分。相应的,LocaleTime只持有ISO-8601格式且无时区信息的时间部分。LocaleDate与LocalTime都可以从Clock中得到。

// Get the local date and local time
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now(clock); System.out.println(date);
System.out.println(dateFromClock); // Get the local date and local time
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now(clock); System.out.println(time);
System.out.println(timeFromClock);

下面是程序在控制台上的输出:

2019-01-09
2019-01-09
22:52:50.383
14:52:50.383

LocaleDateTime

LocaleDateTime把LocaleDate与LocaleTime的功能合并起来,它持有的是ISO-8601格式无时区信息的日期与时间。

// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now(clock); System.out.println(datetime);
System.out.println(datetimeFromClock);

下面是程序在控制台上的输出:

2019-01-09T22:55:05.194
2019-01-09T14:55:05.194

ZonedDateTime

如果你需要特定时区的日期/时间,那么ZonedDateTime是你的选择。它持有ISO-8601格式具具有时区信息的日期与时间。

// Get the zoned date/time
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);

下面是程序在控制台上的输出:

2019-01-09T22:56:34.033+08:00[Asia/Shanghai]
2019-01-09T14:56:34.033Z
2019-01-09T06:56:34.035-08:00[America/Los_Angeles]

Duration

在秒与纳秒级别上的一段时间。Duration使计算两个日期间的不同变的十分简单。

// Get duration between two dates
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号之间的过程。下面是程序在控制台上的输出:

Duration in days: 365
Duration in hours: 8783

Collection API improvements(集合API改进)

上面已经展示了forEach()方法和Stream API在集合上的使用。java8的Collection API中添加了一些新方法:

Iterator default method forEachRemaining(Consumer action)

为每个元素执行给定操作,直到所有元素都已处理或操作引发异常。

源码

default void forEachRemaining(Consumer<? super E> action) {
//传入一个非空消费者
Objects.requireNonNull(action);
//遍历执行消费者函数
while (hasNext())
action.accept(next());
}

示例代码

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将集合中的每个元素消费
iterator.forEachRemaining(consumer);

控制台输出

consumer print 1
consumer print 2
consumer print 3
...

Collection default method removeIf(Predicate filter)

删除满足给定条件的此集合的所有元素。

源码

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;
}

示例代码

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);

控制台输出

//2,3,4满足条件被删除了
remove if left items : [1]

Collection spliterator()

返回Spliterator实例的方法,该实例可用于顺序或并行遍历元素。

源码

//该方法是接口默认方法
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.ORDERED);
}

示例代码

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

源码

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) {
//使用给定的函数替换原来的value值,key不变
e.value = function.apply(e.key, e.value);
}
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}

示例代码

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");
//replaceAll方法
map.replaceAll((s, s2) -> s + s2);
System.out.println(map);

控制台输出

//原来的value由key + value替换掉了
{1=1A, 2=2B, 3=3C, 4=4D, 5=5E}

compute()

computeIfPresentcomputeIfAbsent方法的组合体

  • computeIfPresent:如果指定的key不存在,则通过指定的K -> V计算出新的值设置为key的值。
  • computeIfPresent:如果指定的key存在,则根据旧的key和value计算新的值newValue, 如果newValue不为null,则设置key新的值为newValue, 如果newValue为null, 则删除该key的值。

示例代码

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");
//key存在,根据旧的key和value计算新的值newValue
map.compute("1", (k, v) -> v + " computed");
System.out.println("key存在" + map.get("1"));
//key不存在,通过指定的K -> V计算出新的值设置为key的值
map.compute("6", (k, v) -> "F");
System.out.println("key不存在" + map.get("6"));
//key存在,如果newValue为null, 则删除该key的值
map.compute("1", (k, v) -> null);
System.out.println("key存在,设置为null " + map.get("1"));

控制台输出

key存在A computed
key不存在F
key存在,设置为null null

merge()

如果指定的key不存在,则设置指定的value值,否则根据key的旧的值oldvalue,value计算出新的值newValue, 如果newValue为null, 则删除该key,否则设置key的新值newValue。

示例代码

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");
//存在key为1,输出 Amerge
System.out.println(map.merge("1", "merge", (k, v) -> k + v));
//新值为null,删除key,输出 null
System.out.println(map.merge("1", "merge", (k, v) -> null));
//不存在key为6,输出 "merge"
System.out.println(map.merge("6", "merge", (k, v) -> k + v));

控制台输出

Amerge
null
merge

Performance Improvement for HashMap class with Key Collisions

具有键冲突的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,其中的元素是目录中的条目。

//返回目录下的元素集合流
Stream<Path> list = Files.list(new File("C:\\Users\\Administrator\\Desktop").toPath());
list.forEach(System.out::println);

Files.lines(Path path)

从文件中读取所有行作为流。

//返回文件中的所有行数
Stream<String> lines = Files.lines(new File("C:\\Users\\Administrator\\Desktop\\new 3.txt").toPath());
lines.forEach(System.out::println);

Files.find()

通过搜索以给定起始文件为根的文件树中的文件,返回使用Path延迟填充的Stream。

//返回符合判断条件的Path流
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读取的行。

//返回文件中的所有行数,类似Files.lines()
BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\Administrator\\Desktop\\new 3.txt"));
Stream<String> stringStream = br.lines();
stringStream.forEach(System.out::println);

参考资源

Java8新特性一览表的更多相关文章

  1. java8新特性全面解析

    在Java Code Geeks上有大量的关于Java 8 的教程了,像玩转Java 8--lambda与并发,Java 8 Date Time API 教程: LocalDateTime和在Java ...

  2. Java8新特性

    Java8新特性 Java8主要的新特性涵盖:函数式接口.Lambda 表达式.集合的流式操作.注解的更新.安全性的增强.IO\NIO 的改进.完善的全球化功能等. 1.函数式接口 Java 8 引入 ...

  3. Java系列 - 用Java8新特性进行Java开发太爽了

    本人博客文章网址:https://www.peretang.com/using-java8s-new-features-to-coding-is-awesome/ 前言 从开始写博客到现在已经过去3个 ...

  4. Java8 新特性之Stream----java.util.stream

    这个包主要提供元素的streams函数操作,比如对collections的map,reduce. 例如: int sum = widgets.stream() .filter(b -> b.ge ...

  5. 这可能是史上最好的 Java8 新特性 Stream 流教程

    本文翻译自 https://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/ 作者: @Winterbe 欢迎关注个人微信公众 ...

  6. Java8新特性之二:方法引用

    上一节介绍了Java8新特性中的Lambda表达式,本小节继续讲解Java8的新特性之二:方法引用.方法引用其实也离不开Lambda表达式. 1.方法引用的使用场景 我们用Lambda表达式来实现匿名 ...

  7. Java8新特性(一)_interface中的static方法和default方法

    什么要单独写个Java8新特性,一个原因是我目前所在的公司用的是jdk8,并且框架中用了大量的Java8的新特性,如上篇文章写到的stream方法进行过滤map集合.stream方法就是接口Colle ...

  8. Java8 新特性 | 如何风骚走位防止空指针异常

    文章整理翻译自 https://winterbe.com/posts/2015/03/15/avoid-null-checks-in-java/ 文章首发于个人网站: https://www.exce ...

  9. 干货 | Java8 新特性教程

    本教程翻译整理自 https://github.com/winterbe/java8-tutorial 本教程首发自个人网站: https://www.exception.site/java8/jav ...

随机推荐

  1. java 利用POI 读取Execel数据的真实行数

    java 利用poi 读execel文件的操作,读取总的数据行数一般是通过调用 sheet.getLastRowNum() ;可是这样有时候会出现一些问题,例如,当其中一行的数据的确都为空,可是其原本 ...

  2. 微信小程序中的图表构建

    第一 html中的代码 <view class="container"> <canvas canvas-id="lineCanvas" bin ...

  3. 2020牛客寒假算法基础集训营4 E:最小表达式

    E:最小表达式 考察点 : 贪心,高精度 坑点 : 高精度一定不要写错,一定一定不要写错 剩下的就是细节问题 侃侃 : 1.字符串长度达到 5e5,如果要涉及到加法,乘法,普通的肯定会爆 long l ...

  4. 《Head first设计模式》之工厂模式

    工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个.工厂方法让类把实例化推迟到了子类. 预定披萨 假设你有一个披萨店,预定披萨的代码可能是这么写的: Pizza orderPizz ...

  5. Sparc V8

    Sparc V8指令 在sparc V8手册中p83(Table A-1 Mapping of Synthetic Instructions to SPARC Instructions)有合成指令sy ...

  6. day13 JS Dom

    js两种存在形式 1:文件 2:块 放到body标签底部 防止加载js超时页面反应慢的问题 声明变量 name = "sb"; //全局变量 var age=18; //局部变量 ...

  7. [jQuery]jQuery和DOM对象互换(四)

    DOM 和 jQuery 相互转换 DOM 转jQuery $(DOM对象) # (1)直接获取 $('video'); # (2)转换 $(DOM对象) var myVideo = document ...

  8. 谈下slot-scope

    Vue里有个slot插槽的概念,常用的一般是命名的slot和默认的slot, 这里谈下slot-scope,Vue2.6后改成v-slot slot-scope场景是父组件用子组件的数据,但是样式自己 ...

  9. linux系统初装

    一.linux系统安装 VMware workstation是一个虚拟机软件,它的主要作用是在原有操作系统(windows或linux)下,虚拟出一台电脑,你可以在这台虚拟电脑上安装不同的操作系统,进 ...

  10. mysql 5.7.28 中GROUP BY报错问题 SELECT list is not in GROUP BY clause and contains no

    ----mysql 5.7.28 中GROUP BY报错问题 SELECT list is not in GROUP BY clause and contains no------ 解决方案: sel ...