一小时上手Java 8新特性

本文摘译自 https://www.journaldev.com/2389/java-8-features-with-examples,并做了适当增补。

Iterable 接口里的 forEach() 方法

Java 8 在 java.lang.Iterable 接口里引入了forEach方法,所以我们可以只关注业务逻辑(不需要关注迭代细节)。

public class ForEach {
public static void main(String[] args) {
ArrayList<Integer> myList = new ArrayList<>(); for (int i = 0; i < 10; i++) {
myList.add(i);
} myList.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(".." + integer);
}
});
}
} class MyConsumer implements Consumer<Integer> {
@Override
public void accept(Integer integer) { }
}

接口中的default和static方法

如果仔细查看forEach例子中的细节的话,会注意到forEach方法是定义在Iterable接口内的;但是我们知道接口并不能拥有方法体。

Java 8增强了接口的功能,可以使用default关键字来创建拥有方法实现的接口。Iterable接口的forEach方法实现如下:

    /**
* Performs the given action for each element of the {@code Iterable}
* until all elements have been processed or the action throws an
* exception. Unless otherwise specified by the implementing class,
* actions are performed in the order of iteration (if an iteration order
* is specified). Exceptions thrown by the action are relayed to the
* caller.
*
* @implSpec
* <p>The default implementation behaves as if:
* <pre>{@code
* for (T t : this)
* action.accept(t);
* }</pre>
*
* @param action The action to be performed for each element
* @throws NullPointerException if the specified action is null
* @since 1.8
*/
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}

我们知道Java是不支持类的多继承的,但是由于现在接口和抽象类更像了,我们可以使用接口来实现。如果一个类实现的多个接口里,有相同的default方法,那么它必须要实现自己的该方法。

接口里的static method (静态方法),可以通过接口名来调用,但是不能被重写。

Java 8在集合API里重度使用了default和static方法。

函数性接口和Lambda表达式

函数性接口是Java 8中引入的新概念:一个只有一个抽象方法的接口,被称为函数性接口。我们并不需要显示使用@FunctionalInterface 注解来声明一个接口成为函数性接口。@FunctionalInterface注解和@Override注解类似,用来帮助发现错误的操作。@FunctionalInterface注解用来避免意外向接口里添加额外方法。

Runnable接口内只有一个run方法,是函数性接口的一个很好的例子。

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

使用函数性接口的一个重要好处是我们可以使用Lamba表达式来实例化一个接口。

  • 使用匿名类实例化一个接口,但是代码看起来很笨重:

    Runnable r = new Runnable() {
    @Override
    public void run() {
    System.out.println("My Runnable");
    }
    };
  • 使用Lamba表达式,来提供方法参数和业务逻辑:

    Runnable r = () -> {
    System.out.println("My Runnable");
    };
  • 如果实现里只有一行语句的话,我们甚至可以省掉花括号:

    Runnable r = () -> System.out.println("My Runnable");

所以Lambda表达式意味着可以更简单的创建函数性接口的匿名类,但是Lambda表达式在运行时并没有任何额外的好处。如果不介意多写一点代码的,建议谨慎使用Lamba表达式。

Lamba表达式是个大话题,此处不展开细说。

批量操作集合数据的 Java Stream API

Java.util.stream 被添加到Java 8中,用来在集合上执行filter/map/reduce等操作。Stream API支持线性和并行执行。

filter

filter用来使用指定条件过滤集合。

示例代码:过滤出 > 90 的元素。

import java.util.ArrayList;
import java.util.stream.Stream; public class StreamAPI {
public static void main(String[] args) {
ArrayList<Integer> myList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
myList.add(i);
} //parallel stream
Stream<Integer> parallelStream = myList.parallelStream(); //sequential stream
Stream<Integer> sequentialStream = myList.stream(); Stream<Integer> highNums = parallelStream.filter(p -> p > 90);
highNums.forEach(p -> System.out.println("High Nums parallel=" + p)); Stream<Integer> sequentialHighNums = sequentialStream.filter(p -> p > 90);
sequentialHighNums.forEach(p -> System.out.println("High Nums sequential=" + p));
}
}

结果

High Nums parallel=93
High Nums parallel=94
High Nums parallel=95
High Nums parallel=98
High Nums parallel=91
High Nums parallel=96
High Nums parallel=92
High Nums parallel=99
High Nums parallel=97
High Nums sequential=91
High Nums sequential=92
High Nums sequential=93
High Nums sequential=94
High Nums sequential=95
High Nums sequential=96
High Nums sequential=97
High Nums sequential=98
High Nums sequential=99

可以看到filter Lambda表达式p -> p > 90,过滤出了大于90的元素。并且可以看到parallelStream()输出的结果是乱序的(由并发执行引起),而stream()是按照原顺序输出的。

map

map则是在每个元素上应用给定的函数做操作。

示例代码:如果元素大有5,则增加100。

import java.util.ArrayList;
import java.util.stream.Stream; public class StreamAPI {
public static void main(String[] args) {
ArrayList<Integer> myList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
myList.add(i);
} //parallel stream
Stream<Integer> parallelStream = myList.parallelStream(); //sequential stream
Stream<Integer> sequentialStream = myList.stream(); Stream<Integer> mapNums = parallelStream.map((p) -> {
if (p > 5) {
return p + 100;
} else {
return p;
}
});
mapNums.forEach(p -> System.out.println("High Nums parallel=" + p)); Stream<Integer> sequentialMapNums = sequentialStream.map((p) -> {
if (p > 5) {
return p + 100;
} else {
return p;
}
});
sequentialMapNums.forEach(p -> System.out.println("High Nums sequential=" + p));
}
}

结果

High Nums parallel=106
High Nums parallel=5
High Nums parallel=1
High Nums parallel=0
High Nums parallel=107
High Nums parallel=108
High Nums parallel=4
High Nums parallel=109
High Nums parallel=3
High Nums parallel=2
High Nums sequential=0
High Nums sequential=1
High Nums sequential=2
High Nums sequential=3
High Nums sequential=4
High Nums sequential=5
High Nums sequential=106
High Nums sequential=107
High Nums sequential=108
High Nums sequential=109

reduce

reduce是相对复杂的一个函数,他用来对集合元素做一些聚合操作,如求和、计数、最大值、最小值等等。

package com.alibaba.toolkit.demo.java8;

import java.util.ArrayList;
import java.util.Optional;
import java.util.stream.Stream; public class StreamAPI {
public static void main(String[] args) {
ArrayList<Integer> myList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
myList.add(i);
} //parallel stream
Stream<Integer> parallelStream = myList.parallelStream(); //sequential stream
Stream<Integer> sequentialStream = myList.stream(); Optional<Integer> sumResult = parallelStream.reduce((sum, i) -> {
return sum + i;
});
System.out.println("sumResult: " + sumResult.get()); Optional<Integer> sequentialSumResult = sequentialStream.reduce((sum, i) -> {
return sum + i;
});
System.out.println("sequentialSumResult: " + sequentialSumResult.get());
}
}

结果

sumResult: 45
sequentialSumResult: 45

reduce方法的第一个参数是当前的中间结果,第二个参数是当前遍历到的元素。

如果写完整的话,代码为

Optional<Integer> sequentialSumResult = stream.reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer sum, Integer i) {
return sum + i;
}
});

Java Time API

之前版本的Java,使用Date, Time, 和Time Zones都很繁琐,Joda-Time等库变得很流行。Java 8在java.time包中提供了对时间流式操作的新API。Java 8的Time API很大程度上也参考了Joda-Time。

import java.time.LocalDate;
import java.time.Month; public class TimeAPI {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
System.out.println("Current Date=" + today); today = LocalDate.of(2019, Month.MARCH, 31);
System.out.println("today=" + today); LocalDate tomorrow = today.plusDays(1);
System.out.println("tomorrow=" + tomorrow); LocalDate nextMonth = today.plusMonths(1);
System.out.println("nextMonth=" + nextMonth);
}
}

结果

Current Date=2019-03-31
today=2019-03-31
tomorrow=2019-04-01
nextMonth=2019-04-30

其他

Java 8 还增强了Collection API,Concurrency API,Java IO,以及众多的微小改进。篇幅所限,不一一展开了。

一小时上手Java 8新特性的更多相关文章

  1. Java 8新特性解读

    (四)Java 8 相关知识 关于 Java 8 中新知识点,面试官会让你说说 Java 8 你了解多少,下面分享一我收集的 Java 8 新增的知识点的内容,前排申明引用自:Java8新特性及使用 ...

  2. Java 8 新特性——实践篇

    Java 8 新特性--实践篇 参考 Java8新特性 重要更新:Lambda 表达式和Stream API Lambda 表达式 Lambda 表达式引入之前: 举个场景例子:当我们要对一个班级里的 ...

  3. Java 8新特性-4 方法引用

    对于引用来说我们一般都是用在对象,而对象引用的特点是:不同的引用对象可以操作同一块内容! Java 8的方法引用定义了四种格式: 引用静态方法     ClassName :: staticMetho ...

  4. Spring 4支持的Java 8新特性一览

    有众多新特性和函数库的Java 8发布之后,Spring 4.x已经支持其中的大部分.有些Java 8的新特性对Spring无影响,可以直接使用,但另有些新特性需要Spring的支持.本文将带您浏览S ...

  5. java 8 新特性

    最近在IDEA的️驱使下,看了点java8的东西,链接贴一下,,,,, 1.Java 8新特性概述2.Java 8中的 Stream API 详解[3.Java 8新特性终极指南] 简单的使用看完新特 ...

  6. Java 8 新特性终极版

    声明:本文翻译自Java 8 Features Tutorial – The ULTIMATE Guide,翻译过程中发现并发编程网已经有同学翻译过了:Java 8 特性 – 终极手册,我还是坚持自己 ...

  7. Java 8新特性前瞻

    快端午小长假了,要上线的项目差不多完结了,终于有时间可以坐下来写篇博客了. 这是篇对我看到的java 8新特性的一些总结,也是自己学习过程的总结. 几乎可以说java 8是目前为止,自2004年jav ...

  8. Java 8新特性探究(八)精简的JRE详解

    http://www.importnew.com/14926.html     首页 所有文章 资讯 Web 架构 基础技术 书籍 教程 Java小组 工具资源 - 导航条 - 首页 所有文章 资讯 ...

  9. Java 8 新特性1-函数式接口

    Java 8 新特性1-函数式接口 (原) Lambda表达式基本结构: (param1,param2,param3) -> {代码块} 例1: package com.demo.jdk8; i ...

随机推荐

  1. Java Spring Boot VS .NetCore (四)数据库操作 Spring Data JPA vs EFCore

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  2. Java Spring Boot VS .NetCore (五)MyBatis vs EFCore

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  3. RN页面获取组件位置和大小的方法

    在RN的页面布局和操作中,有时需要获取元素的大小和位置信息,本文从网上抄袭了几个常用方法,以备不时之需. 首先是获取设备屏幕的宽高 import {Dimensions} from 'react-na ...

  4. eclipse查看一个方法被谁引用(调用)的快捷键四种方式

    1.(首推)双击选中该方法,Ctrl+Alt+H 如果你想知道一个类的方法到底被那些其他的类调用,那么请选中这个方法名,然后按“Ctrl+Alt+H”, Eclipse就会显示出这个方法被哪些方法调用 ...

  5. UOJ#42. 【清华集训2014】Sum 类欧几里德算法

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ42.html 题解 首先我们把式子改写一下: $$(-1)^{\lfloor a\rfloor} \\=1 ...

  6. H5测试与PC端测试不同的点

    1.通过H5网页(非手机的返回功能)的返回功能可以返回,不会出现无法返回的情况. 2.横屏竖屏相互切换,能自适应,并且布局不会乱掉: 3.为能在不同分辨率的手机上能更好的展示,建议采用响应式设计(如: ...

  7. CF 960 G

    难受的1b,怎么会这样 先去学写一发 NTT 大概说一下斯特林数

  8. git Disconnected:No supported authentication methods available问题解决

    在本地克隆gitlab上的项目,报如下错误:Disconnected:No supported authentication methods available(server sent:publick ...

  9. 使用Eclipse中的SVN提交代码遇到的问题

    问题: Previous operation has not finished; run 'cleanup' if it was interrupted svn: Commit failed (det ...

  10. 更改checkbox样式css

    checkbox {  width: 20px; height: 20px;  background-color: #d6bfa6; border: #d6bfa6; -webkit-border-r ...