安云网 - AnYun.ORG | 专注于网络信息收集、网络数据分享、网络安全研究、网络各种猎奇八卦。
当前位置: 安云网 > 技术关注 > 编程茶楼 > JAVA > Java 8 的新特性和改进总览

Java 8 的新特性和改进总览

时间:2015-02-18来源: 作者:点击:
Java 8 的新特性和改进总览

  这篇文章是对Java 8中即将到来的改进做一个面向开发者的综合性的总结,JDK的这一特性将会在2013年9月份发布。

//本文来自安云网

Java 8 的新特性和改进总览 //内容来自AnYun.ORG

  在写这篇文章的时候,Java 8的开发工作仍然在紧张有序的进行中,语言特新和API仍然有可能改变,我会尽我最大的努力保持这份文档跟得到Java 8的改动。 //内容来自AnYun.ORG

  Java 8的预览版,也就是 “Project Lambda”,现在可以从java.net下载到。

//本文来自安云网

  我使用了IntelliJ的预览版做我的IDE,在我看来他是目前支持java 8特性最好的一个IDE,你可以从这里下载到. //内容来自安云网

  由于我没有找到Oracle发布的Java 8的官方文档,所以目前Java 8的文档还只有本地版本,等Oracle公开文档的时候,我将会重新链接到官方文档。

//本文来自安云网

  接口改善 //内容来自安云网

  现在接口里已经完全可以定义静态方法了. 举一个比较普遍的例子就是在java类库中, 对于一些接口如Foo, 都会有一个有静态方法的工具类Foos 来生成或者配合Foo对象实例来使用. 既然静态方法可以存在于接口当中, 那么大多数情况下 Foos工具类完全可以使用接口中的公共方法来代理 (或者将Foos置成package-private). //内容来自AnYun.ORG

  除此之外更重要的就是, Java 8中接口可以定义默认的方法了.举个例子,一个for-each循环的方法就可以加入到java.lang.Iterable中: //内容来自安云网

public default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action); for (T t : this) {
        action.accept(t);
    }
} //本文来自安云网 

  在过去,java类库的接口中添加方法基本上是不可能的. 在接口中添加方法意味着破坏了实现了这个接口的代码. 但是现在, 只要能够提供一个正确明智的默认的方法的实现, java类库的维护者就可以在接口中添加方法.

//内容来自安云网

  Java 8中, 大量的默认方法已经被添加到核心的JDK接口中了. 稍候我会详细介绍它们.

//内容来自AnYun.ORG

  为什么不能用默认方法来重载equals,hashCode和toString?

//内容来自AnYun.ORG

  接口不能提供对Object类的任何方法的默认实现。特别是,这意味着从接口里不能提供对equals,hashCode或toString的默认实现。

//本文来自安云网

  这刚看起来挺奇怪的,但考虑到一些接口实际上是在文档里定义他们的equals行为的。List接口就是一个例子了。因此,为什么不允许这样呢?

//内容来自AnYun.ORG

  Brian Goetz在这个问题上的冗长的回复里给出了4个原因。我这里只说其中一个,因为那个已经足够说服我了: //内容来自AnYun.ORG

  它会变得更困难来推导什么时候该调用默认的方法。现在它变得很简单了:如果一个类实现了一个方法,那总是优先于默认的实现的。一旦所有接口的实例都是Object的子类,所有接口实例都已经有对equals/hashCode/toString的非默认实现。因此,一个在接口上这些的默认版本都是没用的,它也不会被编译。

//内容来自安云网

  要看更多的话,看下由Brian Goetz写的解释: 对“允许默认方法来重载Object的方法”的回复 //本文来自安云网

  函数式接口

//内容来自AnYun.ORG

  Java 8 引入的一个核心概念是函数式接口。如果一个接口定义个唯一一个抽象方法,那么这个接口就成为函数式接口。比如,java.lang.Runnable就是一个函数式接口,因为它只顶一个一个抽象方法:

//本文来自安云网

public abstract void run(); //内容来自AnYun.ORG 

  留意到“abstract”修饰词在这里是隐含的,因为这个方法缺少方法体。为了表示一个函数式接口,并非想这段代码一样一定需要“abstract”关键字。

//内容来自安云网

  默认方法不是abstract的,所以一个函数式接口里可以定义任意多的默认方法,这取决于你。 //内容来自安云网

  同时,引入了一个新的Annotation:@FunctionalInterface。可以把他它放在一个接口前,表示这个接口是一个函数式接口。加上它的接口不会被编译,除非你设法把它变成一个函数式接口。它有点像@Override,都是声明了一种使用意图,避免你把它用错。 //内容来自AnYun.ORG

  Lambdas //内容来自AnYun.ORG

  一个函数式接口非常有价值的属性就是他们能够用lambdas来实例化。这里有一些lambdas的例子: //内容来自AnYun.ORG

  左边是指定类型的逗号分割的输入列表,右边是带有return的代码块:

//内容来自AnYun.ORG

(int x, int y) -> { return x + y; } //本文来自安云网 

  左边是推导类型的逗号分割的输入列表,右边是返回值:

//内容来自安云网

(x, y) -> x + y //内容来自安云网 

  左边是推导类型的单一参数,右边是一个返回值:

//内容来自AnYun.ORG

x -> x * x //内容来自安云网 

  左边没有输入 (官方名称: “burger arrow”),在右边返回一个值:

//内容来自安云网

() -> x 

//本文来自安云网

  左边是推导类型的单一参数,右边是没返回值的代码块(返回void):

//本文来自安云网

x -> { System.out.println(x); } 
//内容来自AnYun.ORG

  静态方法引用:

//内容来自AnYun.ORG

String::valueOf 

//本文来自安云网

  非静态方法引用:

//内容来自安云网

Object::toString 

//本文来自安云网

  继承的函数引用:

//内容来自AnYun.ORG

x::toString 
//内容来自安云网

  构造函数引用: //本文来自安云网

ArrayList::new //内容来自安云网 

  你可以想出一些函数引用格式作为其他lambda格式的简写。

//内容来自安云网

方法引用  等价的lambda表达式  
String::valueOf   x -> String.valueOf(x)
Object::toString   x -> x.toString()
x::toString   () -> x.toString()
ArrayList::new   () -> new ArrayList<>()

  当然,在Java里方法能被重载。类可以有多个同名但不同参数的方法。这同样对构造方法有效。ArrayList::new能够指向它的3个构造方法中任何一个。决定使用哪个方法是根据在使用的函数式接口。 //内容来自安云网

  一个lambda和给定的函数式接口在“外型”匹配的时候兼容。通过“外型”,我指向输入、输出的类型和声明检查异常。 //本文来自安云网

  给出两个具体有效的例子:

//内容来自安云网

Comparator<String> c = (a, b) -> Integer.compare(a.length(),
                                                 b.length()); //本文来自安云网 

  一个Comparator<String>的compare方法需要输入两个阐述,然后返回一个int。这和lambda右侧的一致,因此这个任务是有效的。

//本文来自安云网

Runnable r = () -> { System.out.println("Running!"); } //内容来自安云网 

  一个Runnable的run方法不需要参数也不会返回值。这和lambda右侧一致,所以任务有效。 //内容来自安云网

  在抽象方法的签名里的受检查异常(如果存在)也很重要。如果函数式接口在它的签名里声明了异常,lambda只能抛出受检查异常。

//本文来自安云网

  捕获和非捕获的Lambda表达式

//本文来自安云网

  当Lambda表达式访问一个定义在Lambda表达式体外的非静态变量或者对象时,这个Lambda表达式称为“捕获的”。比如,下面这个lambda表达式捕捉了变量x:

//内容来自安云网

int x = 5; return y -> x + y; //内容来自安云网 

  为了保证这个lambda表达式声明是正确的,被它捕获的变量必须是“有效final”的。所以要么它们需要用final修饰符号标记,要么保证它们在赋值后不能被改变。 //内容来自安云网

  Lambda表达式是否是捕获的和性能悄然相关。一个非不捕获的lambda通常比捕获的更高效,虽然这一点没有书面的规范说明(据我所知),而且也不能为了程序的正确性指望它做什么,非捕获的lambda只需要计算一次. 然后每次使用到它都会返回一个唯一的实例。而捕获的lambda表达式每次使用时都需要重新计算一次,而且从目前实现来看,它很像实例化一个匿名内部类的实例。 //本文来自安云网

  lambdas不做的事

//内容来自AnYun.ORG

  你应该记住,有一些lambdas不提供的特性。为了Java 8它们被考虑到了,但是没有被包括进去,由于简化以及时间限制的原因。

//内容来自安云网

  Non-final* 变量捕获 - 如果一个变量被赋予新的数值,它将不能被用于lambda之中。”final”关键字不是必需的,但变量必须是“有效final”的(前面讨论过)。这个代码不会被编译: //内容来自安云网

int count = 0;
List<String> strings = Arrays.asList("a", "b", "c");
strings.forEach(s -> {
    count++; // error: can't modify the value of count }); 
//内容来自AnYun.ORG

  例外的透明度 - 如果一个已检测的例外可能从lambda内部抛出,功能性的接口也必须声明已检测例外可以被抛出。这种例外不会散布到其包含的方法。这个代码不会被编译:

//本文来自安云网

void appendAll(Iterable<String> values, Appendable out) throws IOException { // doesn't help with the error values.forEach(s -> {
        out.append(s); // error: can't throw IOException here // Consumer.accept(T) doesn't allow it });
} 
//内容来自AnYun.ORG

  有绕过这个的办法,你能定义自己的功能性接口,扩展Consumer的同时通过像RuntimeException之类抛出 IOException。我试图用代码写出来,但发现它令人困惑是否值得。

//本文来自安云网

  控制流程 (break, early return) -在上面的 forEach例子中,传统的继续方式有可能通过在lambda之内放置 ”return;”来实现。但是,没有办法中断循环或者从lambda中通过包含方法的结果返回一个数值。例如:

//内容来自AnYun.ORG

final String secret = "foo"; boolean containsSecret(Iterable<String> values) {
    values.forEach(s -> { if (secret.equals(s)) {
            ??? // want to end the loop and return true, but can't }
    });
} //内容来自安云网 

  进一步阅读关于这些问题的资料,看看这篇Brian Goetz写的说明:在 Block<T>中响应“已验证例外”

//内容来自安云网

  为什么抽象类不能通过利用lambda实例化

//本文来自安云网

  抽象类,哪怕只声明了一个抽象方法,也不能使用lambda来实例化。 //内容来自AnYun.ORG

  下面有两个类 Ordering 和 CacheLoader的例子,都带有一个抽象方法,摘自于Guava 库。那岂不是很高兴能够声明它们的实例,像这样使用lambda表达式?

//内容来自AnYun.ORG

Ordering<String> order = (a, b) -> ...;

CacheLoader<String, String> loader = (key) -> ...; 

//内容来自AnYun.ORG

  这样做引发的最常见的争论就是会增加阅读lambda的难度。以这种方式实例化一段抽象类将导致隐藏代码的执行:抽象类的构造方法。 //本文来自安云网

  另一个原因是,它抛出了lambda表达式可能的优化。在未来,它可能是这种情况,lambda表达式都不会计算到对象实例。放任用户用lambda来声明抽象类将妨碍像这样的优化。 //本文来自安云网

  此外,有一个简单地解决方法。事实上,上述两个摘自Guava 库的实例类已经证明了这种方法。增加工厂方法将lambda转换成实例。 //本文来自安云网

Ordering<String> order = Ordering.from((a, b) -> ...);
CacheLoader<String, String> loader = CacheLoader.from((key) -> ...); 

//内容来自AnYun.ORG

  要深入阅读,请参看由 Brian Goetz所做的说明: response to “Allow lambdas to implement abstract classes”//内容来自安云网

  java.util.function //内容来自AnYun.ORG

  包概要:java.util.function //内容来自AnYun.ORG

  作为Comparator 和Runnable早期的证明,在JDK中已经定义的接口恰巧作为函数接口而与lambdas表达式兼容。同样方式可以在你自己的代码中定义任何函数接口或第三方库。 //本文来自安云网

  但有特定形式的函数接口,且广泛的,通用的,在之前的JD卡中并不存在。大量的接口被添加到新的java.util.function 包中。下面是其中的一些: //内容来自安云网

  • Function<T, R> -T作为输入,返回的R作为输出
  • Predicate<T> -T作为输入,返回的boolean值作为输出
  • Consumer<T> - T作为输入,执行某种动作但没有返回值
  • Supplier<T> - 没有任何输入,返回T
  • BinaryOperator<T> -两个T作为输入,返回一个T作为输出,对于“reduce”操作很有用

  这些最原始的特征同样存在。他们以int,long和double的方式提供。例如: //内容来自AnYun.ORG

  • IntConsumer -以int作为输入,执行某种动作,没有返回值

  这里存在性能上的一些原因,主要释在输入或输出的时候避免装箱和拆箱操作。

//内容来自AnYun.ORG

  java.util.stream

//内容来自安云网

  包汇总: java.util.stream

//内容来自安云网

  新的java.util.stream包提供了“支持在流上的函数式风格的值操作”(引用javadoc)的工具。可能活动一个流的最常见方法是从一个collection获取:

//内容来自安云网

Stream<T> stream = collection.stream(); //内容来自安云网 

  一个流就像一个地带器。这些值“流过”(模拟水流)然后他们离开。一个流可以只被遍历一次,然后被丢弃。流也可以无限使用。

//本文来自安云网

  流能够是 串行的 或者 并行的。 它们可以使用其中一种方式开始,然后切换到另外的一种方式,使用stream.sequential()或stream.parallel()来达到这种切换。串行流在一个线程上连续操作。而并行流就可能一次出现在多个线程上。 //内容来自AnYun.ORG

  所以,你想用一个流来干什么?这里是在javadoc包里给出的例子:

//本文来自安云网

int sumOfWeights = blocks.stream().filter(b -> b.getColor() == RED)
                                  .mapToInt(b -> b.getWeight())
                                  .sum(); 
//内容来自AnYun.ORG

  注意:上面的代码使用了一个原始的流,以及一个只能用在原始流上的sum()方法。下面马上就会有更多关于原始流的细节。 //内容来自AnYun.ORG

  流提供了流畅的API,可以进行数据转换和对结果执行某些操作。流操作既可以是“中间的”也可以是“末端的”。 //内容来自安云网

  •  -中间的操作保持流打开状态,并允许后续的操作。上面例子中的filter和map方法就是中间的操作。这些操作的返回数据类型是流;它们返回当前的流以便串联更多的操作。
  • 末端的 - 末端的操作必须是对流的最终操作。当一个末端操作被调用,流被“消耗”并且不再可用。上面例子中的sum方法就是一个末端的操作。

  通常,处理一个流涉及了这些步骤: //内容来自安云网

  1. 从某个源头获得一个流。
  2. 执行一个或更多的中间的操作。
  3. 执行一个末端的操作。

  可能你想在一个方法中执行所有那些步骤。那样的话,你就要知道源头和流的属性,而且要可以保证它被正确的使用。你可能不想接受任意的Stream<T>实例作为你的方法的输入,因为它们可能具有你难以处理的特性,比如并行的或无限的。 //内容来自安云网

  有几个更普通的关于流操作的特性需要考虑:

//本文来自安云网

  • 有状态的 - 有状态的操作给流增加了一些新的属性,比如元素的唯一性,或者元素的最大数量,或者保证元素以排序的方式被处理。这些典型的要比无状态的中间操作代价大。
  • 短路 - 短路操作潜在的允许对流的操作尽早停止,而不去检查所有的元素。这是对无限流的一个特殊设计的属性;如果对流的操作没有短路,那么代码可能永远也不会终止。

  对每个Sttream方法这里有一些简短的,一般的描述。查阅javadoc获取更详尽的解释。下面给出了每个操作的重载形式的链接。

//内容来自安云网

  中间的操作: //内容来自AnYun.ORG

  • filter 1 - 排除所有与断言不匹配的元素。
  • map 1 2 3 4 - 通过Function对元素执行一对一的转换。
  • flatMap 1 2 3 4 5 - 通过FlatMapper将每个元素转变为无或更多的元素。
  • peek 1 - 对每个遇到的元素执行一些操作。主要对调试很有用。
  • distinct 1 - 根据.equals行为排除所有重复的元素。这是一个有状态的操作。
  • sorted 1 2 - 确保流中的元素在后续的操作中,按照比较器(Comparator)决定的顺序访问。这是一个有状态的操作。
  • limit 1 - 保证后续的操作所能看到的最大数量的元素。这是一个有状态的短路的操作。
  • substream 1 2 - 确保后续的操作只能看到一个范围的(根据index)元素。像不能用于流的String.substring一样。也有两种形式,一种有一个开始索引,一种有一个结束索引。二者都是有状态的操作,有一个结束索引的形式也是一个短路的操作。

  末端的操作: //内容来自AnYun.ORG

  • forEach 1 - 对流中的每个元素执行一些操作。
  • toArray 1 2 - 将流中的元素倾倒入一个数组。
  • reduce 1 2 3 - 通过一个二进制操作将流中的元素合并到一起。
  • collect 1 2 - 将流中的元素倾倒入某些容器,例如一个Collection或Map.
  • min 1 - 根据一个比较器找到流中元素的最小值。
  • max 1 -根据一个比较器找到流中元素的最大值。
  • count 1 - 计算流中元素的数量。
  • anyMatch 1 - 判断流中是否至少有一个元素匹配断言。这是一个短路的操作。
  • allMatch 1 - 判断流中是否每一个元素都匹配断言。这是一个短路的操作。
  • noneMatch 1 - 判断流中是否没有一个元素匹配断言。这是一个短路的操作。
  • findFirst 1 - 查找流中的第一个元素。这是一个短路的操作。
  • findAny 1 - 查找流中的任意元素,可能对某些流要比findFirst代价低。这是一个短路的操作。

  如 javadocs中提到的 , 中间的操作是延迟的(lazy)。只有末端的操作会立即开始流中元素的处理。在那个时刻,不管包含了多少中间的操作,元素会在一个传递中处理(通常,但并不总是)。(有状态的操作如sorted() 和distinct()可能需要对元素的二次传送。) //内容来自AnYun.ORG

  流试图尽可能做很少的工作。有一些细微优化,如当可以判定元素已经有序的时候,省略一个sorted()操作。在包含limit(x) 或 substream(x,y)的操作中,有些时候对一些不会决定结果的元素,流可以避免执行中间的map操作。在这里我不准备实现公平判断;它通过许多细微的但却很重要的方法表现得很聪明,而且它仍在进步。 //内容来自安云网

  回到并行流的概念,重要的是要意识到并行不是毫无代价的。从性能的立场它不是无代价的,你不能简单的将顺序流替换为并行流,且不做进一步思考就期望得到相同的结果。在你能(或者应该)并行化一个流以前,需要考虑很多特性,关于流、它的操作以及数据的目标方面。例如:访问顺序确实对我有影响吗?我的函数是无状态的吗?我的流有足够大,并且我的操作有足够复杂,这些能使得并行化是值得的吗? //内容来自AnYun.ORG

  有针对int,long和double的专业原始的Stream版本: //内容来自AnYun.ORG

  可以在众多函数中,通过专业原始的map和flatMap函数,在一个stream对象与一个原始stream对象之间来回转换。给几个虚设例子:

//内容来自AnYun.ORG

List<String> strings = Arrays.asList("a", "b", "c");
strings.stream() // 
Stream<String> .mapToInt(String::length) // IntStream .longs() // 
LongStream .mapToDouble(x -> x / 10.0) // DoubleStream .boxed() // 
Stream<Double> .mapToLong(x -> 1L) // LongStream .mapToObj(x -> "") // 
Stream<String> ... 
//内容来自安云网

  原始的stream也为获得关于stream的基础数据统计提供方法,那些stream是指作为数据结构的。你可以发现count, sum, min, max, 以及元素平均值全部是来自于一个终端的操作。

//内容来自AnYun.ORG

  原始类型的剩余部分没有原始版本,因为这需要一个不可接受的JDK数量的膨胀。IntStream, LongStream, 和 DoubleStream被认为非常有用应当被包含进去,其他的数字型原始stream可以由这三个通过扩展的原始转换来表示。 //内容来自AnYun.ORG

  在flatMap操作中使用的 FlatMapper 接口是具有一个抽象方法的功能性接口:

//本文来自安云网

void flattenInto(T element, Consumer<U> sink); //内容来自安云网 

  在一个flatMap操作的上下文中,stream为你提供element和 sink,然后你定义该用element 和 sink做什么。element是指在stream中的当前元素,而sink代表当flatMap操作结束之后在stream中应该显示些什么。例如:

//本文来自安云网

Set<Color> colors = ...;
List<Person> people = ...;
Stream<Color> stream = people.stream().flatMap(
    (Person person, Consumer<Color> sink) -> { // Map each person to the colors they like. for (Color color : colors) { if (person.likesColor(color)) {
                sink.accept(color);
            }
        }
    }); 
//内容来自AnYun.ORG

  注意上面lambda中的参数类型是指定的。在大多数其它上下文中,你可以不需要指定类型,但这里由于FlatMapper的自然特性,编译器需要你帮助判定类型。如果你在使用flatMap又迷惑于它为什么不编译,可能是因为你没有指定类型。

//本文来自安云网

  最令人感到困惑,复杂而且有用的终端stream操作之一是collect。它引入了一个称为Collector的新的非功能性接口。这个接口有些难理解,但幸运的是有一个Collectors工具类可用来产生所有类型的有用的Collectors。例如:

//内容来自AnYun.ORG

List<String> strings = values.stream()
                             .filter(...)
                             .map(...)
                             .collect(Collectors.toList()); //本文来自安云网 

  如果你想将你的stream元素放进一个Collection,Map或String,那么Collectors可能具有你需要的。在javadoc中浏览那个类绝对是值得的。 //内容来自AnYun.ORG

  泛型接口改进 //内容来自安云网

  建议摘要:JEP 101: 通用化目标-Type 接口

//本文来自安云网

  这是一个以前不能做到的,对编译器判定泛型能力的努力改进。在以前版本的Java中有许多情形编译器不能给某个方法计算出泛型,当方法处于嵌套的或串联方法调用这样的上下文的时候,即使有时候对程序员来说它看起来“很明显”。那些情况需要程序员明确的指定一个“类型见证”(type witness)。它是一种通用的特性,但吃惊的是很少有Java程序员知道(我这么说是基于私下的交流并且阅读了一些StackOverflow的问题)。它看起来像这样:

//内容来自AnYun.ORG

// In Java 7: foo(Utility.<Type>bar());
Utility.<Type>foo().bar(); 
//内容来自安云网

  如果没有类型见证,编译器可能会将<Object>替代为泛型,而且如果需要的是一个更具体的类型,代码将编译失败。

//内容来自AnYun.ORG

  Java 8 极大的改进了这个状况。在更多的案例中,它可以基于上下文计算出更多的特定的泛型类型。 //本文来自安云网

// In Java 8: foo(Utility.bar());
Utility.foo().bar(); //内容来自安云网 

  这项工作仍在发展中,所以我不确定建议中列出的例子有多少能真正包含进Java 8。希望是它们全部。 //内容来自AnYun.ORG

  java.time

//内容来自AnYun.ORG

  包概要: java.time

//本文来自安云网

  在Java8中新的 date/timeAPI存在于 java.time包中。如果你熟悉Joda Time,它将很容易掌握。事实上,我认为如此好的设计,以至于从未听说过 Joda Time的人也能很容易的掌握。 //内容来自AnYun.ORG

  几乎在API中的任何东西都是永恒的,包括值类型和格式化 。对于Date域或者处理或处理本地线程日期格式化不必太过担心。 //内容来自AnYun.ORG

  与传统的date/timeAPI的交叉是最小的。有一个清晰的分段:

//内容来自AnYun.ORG

  新API对于像月份和每周的天数,喜欢枚举类型更胜过整形常量。

//本文来自安云网

  那么,那是什么呢?包级别的javadocs 对额外类型的做出了非常好的阐述。我将对一些值得注意的部分做一些简短的纲要。 //本文来自安云网

  非常有用的值类型: //本文来自安云网

  其他有用的类型:

//内容来自AnYun.ORG

  • DateTimeFormatter - 将日期类型转换成字符串类型
  • ChronoUnit - 计算出两点之间的时间量,例如ChronoUnit.DAYS.between(t1, t2)
  • TemporalAdjuster - 例如date.with(TemporalAdjuster.firstDayOfMonth())

  大多数情况下,新的值类型由JDBC提供支持。有一小部分异常,如ZonedDateTime在SQL中没有对应的(类型)。 //内容来自安云网

  集合API附件

//内容来自AnYun.ORG

  实际上接口能够定义默认方法允许了JDK作者加入大量的附件到集合API接口中。默认实现在核心接口里提供,而其他更有效或更好的重载实现被加入到可适用的具体类中。

//本文来自安云网

  这里是新方法的列表:

//内容来自安云网

  同样, Iterator.remove() 现在有一个默认的, 会抛出异常的实现,使得它稍微容易地去定义不可修改的迭代器。

//本文来自安云网

  Collection.stream()和Collection.parallelStream()是流API的主要门户。有其他方法去生成流,但这些在以后会更为长用。 //本文来自安云网

  List.sort(Comparator)的附件有点怪异。以前排序一个ArrayList的方法是:

//内容来自安云网

Collections.sort(list, comparator); //内容来自AnYun.ORG 

  这代码是你在Java7里唯一可选的,非常低效。它会复制list到一个数组里,排序这个数组,然后使用ListIterator来把数组插入到新list的新位置上。

//本文来自安云网

  List.sort(比较器)的默认实现仍然会做这个,但是具体的实现类可以自由的优化。例如,ArrayList.sort在ArrayList内部数组上调用了Arrays.sort。CopyOnWriteArrayList做了同样的事情。

//内容来自安云网

  从这些新方法中获得的不仅仅是性能。它们也具有更多的令人满意的语义。例如, 对Collections.synchronizedList()排序是一个使用了list.sort的原子操作。你可以使用list.forEach对它的所有元素进行迭代,使之成为原子操作。 //本文来自安云网

  Map.computeIfAbsent使得操作类似多重映射的结构变得容易了: //本文来自安云网

// Index strings by length: 
Map<Integer, List<String>> map = new HashMap<>(); for (String s : strings) {
    map.computeIfAbsent(s.length(),
                        key -> new ArrayList<String>())
       .add(s);
} // Although in this case the stream API may be a better choice:
 Map<Integer, List<String>> map = strings.stream()
    .collect(Collectors.groupingBy(String::length)); 

//本文来自安云网

  增加并发API //内容来自AnYun.ORG

  有太多的链接可以点击,因此参看ConcurrentHashMap javadocs文档以获得更多信息。 //内容来自AnYun.ORG

  • ConcurrentHashMap.reduce…
  • ConcurrentHashMap.search…
  • ConcurrentHashMap.forEach…

  ForkJoinPool.commonPool()是处理所有并行流操作的结构。当你 需要的时候,它是一个好而简单的方式去获得一个ForkJoinPool/ExecutorService/Executor对象。ConcurrentHashMap<K, V>完全重写。内部看起来它一点不像是Java7版本。从外部来看几乎相同,除了它有大量批量操作方法:多种形式的减少搜索和forEach。

//内容来自AnYun.ORG

ConcurrentHashMap.newKeySet()提供了一个并发的java.util.Set实现。它基本上是Collections.newSetFromMap(new ConcurrentHashMap<T, Boolean>())的另一种方式的重写。

//内容来自AnYun.ORG

  StampedLock是一种新型锁的实现,很可能在大多数场景都可以替代ReentrantReadWriteLock。当作为一个简单的读写锁的时候,它比RRWL的性能要好。它也为“读优化”提供了API,通过它你获得了一个功能有点弱,但代价很小的读操作锁的版本,执行读操作,然后检查锁是否被一个写操作设定为无效。在Heinz Kabutz汇总的一系列幻灯片中,有更多关于这个类及其性能的细节(在这个系列幻灯片大约一半的地方开始的):“移相器和StampedLock演示//本文来自安云网

  CompletableFuture<T>是Future接口的一个非常棒的实现,它提供了无数执行(和串接)异步任务的方法。它特别依赖功能性的接口;lambdas是值得增加这个类的一个重要原因。如果你正在使用Guava的 Future工具,例如FuturesListenableFuture, 和 SettableFuture,那么你可能会希望校验CompletableFuture能否作为潜在的替代选择。 //内容来自安云网

  IO/NIO API的新增内容 //本文来自安云网

  简单的说,这些API用于从文件和InputStreams获取java.util.stream.Stream对象。不过它们与直接从常规的collection得到的流有些不同,它们引入了两个概念:

//内容来自安云网

  • UncheckedIOException - 当有IO错误时抛出这个异常,不过由于Iterator/Stream的签名中不允许有IOException,所以它只能借助于unchecked异常。
  • CloseableStream - 可以(并且应该)定义在 try-with-resources 语句里面的流。

  反射和annotation的改动

//内容来自安云网

  Annotation允许在更多的地方被使用,例如List<@Nullable String>。受此影响最大的可能是那些静态分析工具,如Sonar和FindBugs。

//内容来自AnYun.ORG

  JSR 308的网站解释了增加这些改动的动机,介绍的不错: “类型Annotation (JSR 308) 和 Checker框架” //本文来自安云网

  Nashorn JavaScript 引擎

//内容来自安云网

  提案的摘要: JEP 174: Nashorn JavaScript 引擎

//本文来自安云网

  我对Nashorn没什么经验,因而我对上面提案所描述的所知甚少。简单的说,它是 Rhino 的接替者。Rhino 显得有些老了,并且有点慢,开发者决定最好还是从头做一个。 //内容来自安云网

  其他新增,涉及java.lang,java.util,和java.sql

//本文来自安云网

  这里可以介绍的太多了,只能挑一些最需要注意的项目。

//本文来自安云网

  ThreadLocal.withInitial(Supplier<T>) 可以在定义thread-local变量时更好的进行初始化。之前你初始化变量时是这样的:

//本文来自安云网

ThreadLocal<List<String>> strings = new ThreadLocal<List<String>>() { @Override protected List<String> initialValue() { return new ArrayList<>();
        }
    }; 
//内容来自AnYun.ORG

  现在则是这样: //本文来自安云网

ThreadLocal<List<String>> strings =
    ThreadLocal.withInital(ArrayList::new); 
//内容来自AnYun.ORG

  stream的API的返回值有一个可选的<T>,就像min/max, findFirst/Any, 以及reduce的某些形式。这样做是因为stream中可能没有任何元素,但是它要提供一个一致的API,既可以处理“一些结果”,也可以处理“没有结果”。你可以提供默认值,抛异常,或者只在有结果的时候执行一些动作。

//内容来自安云网

  它与Guava’s Optional类非常非常的相似。它一点都不像是在Scala里的操作,也不会试图成为之一,有相似的地方纯属巧合。

//内容来自AnYun.ORG

  旁白:Java 8′s Optional和Guava’s Optional最终如此的相似是很有趣的事,尽管荒谬的辩论发生在这两个库。

//内容来自安云网

“FYI…. Optional was the cause of possibly the single greatest conflagration on the internal Java libraries discussion lists ever.”

//本文来自安云网

  Kevin Bourrillion在 response to “Some new Guava classes targeted for release 10″如实写到: //本文来自安云网

“On a purely practical note, the discussions surrounding Optional have exceeded its design budget by several orders of magnitude.”

//内容来自安云网

  Brian Goetz 在  response to “Optional require(s) NonNull”写到。

//内容来自AnYun.ORG

  StringJoinerandString.join(…)来得太晚了。他们来得如此之晚以至于大多数Java开发者已经为字符串联合编写或发现了有用的工具,但这对JDK本身来说很每秒,因为最终自己实现这一点。每个人都会遇到要求字符串连接的情形,我们现在能够通过每个Java开发者(事实上的)即将知道的标准的API来阐明,这也算是一件好事。
ComparatorsandComparator.thenComparing(…)提供了非常优秀的工具,基于链的比较和基于域的比较。像这样: //内容来自安云网

people.sort(Comparators.comparing(Person::getLastName)
                       .thenComparing(Person::getFirstName)); //内容来自安云网 

  这些新增功能提供了良好的,复杂的各种可读的简写。大多数用例由JDK里增加的 ComparisonChain和Ordering工具类来提供服务。对于什么是有价值的,我认为JDK版本比在Guava-ese里功能等效的版本的可读性好了很多。

//内容来自AnYun.ORG

  更多?

//内容来自AnYun.ORG

  其实,还有很多的小问题的修正和一些性能的改进在这篇文章中没有提及,但是那些也是非常的重要的。

//内容来自安云网

  写这篇文章主要是想全面的介绍到Java 8的每个语言层面和API层面的改进,如果有遗漏,那就是个错误。如果你发现了这样的错误请告诉我,你可以通过e-mail联系我或者是在黑客咨询上发布评论。

//本文来自安云网

  英文原文:Everything about Java 8 //内容来自AnYun.ORG

QQ群: WEB开发者官方总群(196171896) 验证消息:Admin10000
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
验证码: 点击我更换图片
相关内容
推荐内容