Java8新特性一览表
总览
- 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()
是computeIfPresent
和computeIfAbsent
方法的组合体
- 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新特性一览表的更多相关文章
- java8新特性全面解析
在Java Code Geeks上有大量的关于Java 8 的教程了,像玩转Java 8--lambda与并发,Java 8 Date Time API 教程: LocalDateTime和在Java ...
- Java8新特性
Java8新特性 Java8主要的新特性涵盖:函数式接口.Lambda 表达式.集合的流式操作.注解的更新.安全性的增强.IO\NIO 的改进.完善的全球化功能等. 1.函数式接口 Java 8 引入 ...
- Java系列 - 用Java8新特性进行Java开发太爽了
本人博客文章网址:https://www.peretang.com/using-java8s-new-features-to-coding-is-awesome/ 前言 从开始写博客到现在已经过去3个 ...
- Java8 新特性之Stream----java.util.stream
这个包主要提供元素的streams函数操作,比如对collections的map,reduce. 例如: int sum = widgets.stream() .filter(b -> b.ge ...
- 这可能是史上最好的 Java8 新特性 Stream 流教程
本文翻译自 https://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/ 作者: @Winterbe 欢迎关注个人微信公众 ...
- Java8新特性之二:方法引用
上一节介绍了Java8新特性中的Lambda表达式,本小节继续讲解Java8的新特性之二:方法引用.方法引用其实也离不开Lambda表达式. 1.方法引用的使用场景 我们用Lambda表达式来实现匿名 ...
- Java8新特性(一)_interface中的static方法和default方法
什么要单独写个Java8新特性,一个原因是我目前所在的公司用的是jdk8,并且框架中用了大量的Java8的新特性,如上篇文章写到的stream方法进行过滤map集合.stream方法就是接口Colle ...
- Java8 新特性 | 如何风骚走位防止空指针异常
文章整理翻译自 https://winterbe.com/posts/2015/03/15/avoid-null-checks-in-java/ 文章首发于个人网站: https://www.exce ...
- 干货 | Java8 新特性教程
本教程翻译整理自 https://github.com/winterbe/java8-tutorial 本教程首发自个人网站: https://www.exception.site/java8/jav ...
随机推荐
- java 利用POI 读取Execel数据的真实行数
java 利用poi 读execel文件的操作,读取总的数据行数一般是通过调用 sheet.getLastRowNum() ;可是这样有时候会出现一些问题,例如,当其中一行的数据的确都为空,可是其原本 ...
- 微信小程序中的图表构建
第一 html中的代码 <view class="container"> <canvas canvas-id="lineCanvas" bin ...
- 2020牛客寒假算法基础集训营4 E:最小表达式
E:最小表达式 考察点 : 贪心,高精度 坑点 : 高精度一定不要写错,一定一定不要写错 剩下的就是细节问题 侃侃 : 1.字符串长度达到 5e5,如果要涉及到加法,乘法,普通的肯定会爆 long l ...
- 《Head first设计模式》之工厂模式
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个.工厂方法让类把实例化推迟到了子类. 预定披萨 假设你有一个披萨店,预定披萨的代码可能是这么写的: Pizza orderPizz ...
- Sparc V8
Sparc V8指令 在sparc V8手册中p83(Table A-1 Mapping of Synthetic Instructions to SPARC Instructions)有合成指令sy ...
- day13 JS Dom
js两种存在形式 1:文件 2:块 放到body标签底部 防止加载js超时页面反应慢的问题 声明变量 name = "sb"; //全局变量 var age=18; //局部变量 ...
- [jQuery]jQuery和DOM对象互换(四)
DOM 和 jQuery 相互转换 DOM 转jQuery $(DOM对象) # (1)直接获取 $('video'); # (2)转换 $(DOM对象) var myVideo = document ...
- 谈下slot-scope
Vue里有个slot插槽的概念,常用的一般是命名的slot和默认的slot, 这里谈下slot-scope,Vue2.6后改成v-slot slot-scope场景是父组件用子组件的数据,但是样式自己 ...
- linux系统初装
一.linux系统安装 VMware workstation是一个虚拟机软件,它的主要作用是在原有操作系统(windows或linux)下,虚拟出一台电脑,你可以在这台虚拟电脑上安装不同的操作系统,进 ...
- 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 ...