临时
接口的增强
Java 8 对接口做了进一步的增强。在接口中可以添加使用 default 关键字修饰的非抽象方法。还可以在接口中定义静态方法。如今,接口看上去与抽象类的功能越来越类似了。
默认方法
Java 8 还允许我们给接口添加一个非抽象的方法实现,只需要使用 default 关键字即可,这个特征又叫做扩展方法。在实现该接口时,该默认扩展方法在子类上可以直接使用,它的使用方式类似于抽象类中非抽象成员方法。但扩展方法不能够重载 Object 中的方法。例如:toString、equals、 hashCode 不能在接口中被重载。
例如,下面接口中定义了一个默认方法 count(),该方法可以在子类中直接使用。
public interface DefaultFunInterface {
//定义默认方法 count
default int count(){
return 1;
}
}
public class SubDefaultFunClass implements DefaultFunInterface {
public static void main(String[] args){
//实例化一个子类对象,改子类对象可以直接调用父接口中的默认方法 count
SubDefaultFunClass sub = new SubDefaultFunClass();
sub.count();
}
}
静态方法
在接口中,还允许定义静态的方法。接口中的静态方法可以直接用接口来调用。
例如,下面接口中定义了一个静态方法 find,该方法可以直接用 StaticFunInterface .find() 来调用。
public interface StaticFunInterface {
public static int find(){
return 1;
}
}
public class TestStaticFun {
public static void main(String[] args){
//接口中定义了静态方法 find 直接被调用
StaticFunInterface.fine();
}
}
集合之流式操作
Java 8 引入了流式操作(Stream),通过该操作可以实现对集合(Collection)的并行处理和函数式操作。根据操作返回的结果不同,流式操作分为中间操作和最终操作两种。最终操作返回一特定类型的结果,而中间操作返回流本身,这样就可以将多个操作依次串联起来。根据流的并发性,流又可以分为串行和并行两种。流式操作实现了集合的过滤、排序、映射等功能。
Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。
串行和并行的流
流有串行和并行两种,串行流上的操作是在一个线程中依次完成,而并行流则是在多个线程上同时执行。并行与串行的流可以相互切换:通过 stream.sequential() 返回串行的流,通过 stream.parallel() 返回并行的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。
下面是分别用串行和并行的方式对集合进行排序。
串行排序:
List
for(int i=0;i<1000000;i++){
double d = Math.random()*1000;
list.add(d+"");
}
long start = System.nanoTime();//获取系统开始排序的时间点
int count= (int) ((Stream) list.stream().sequential()).sorted().count();
long end = System.nanoTime();//获取系统结束排序的时间点
long ms = TimeUnit.NANOSECONDS.toMillis(end-start);//得到串行排序所用的时间
System.out.println(ms+”ms”);
并行排序:
List
for(int i=0;i<1000000;i++){
double d = Math.random()*1000;
list.add(d+"");
}
long start = System.nanoTime();//获取系统开始排序的时间点
int count = (int)((Stream) list.stream().parallel()).sorted().count();
long end = System.nanoTime();//获取系统结束排序的时间点
long ms = TimeUnit.NANOSECONDS.toMillis(end-start);//得到并行排序所用的时间
System.out.println(ms+”ms”);
串行输出为 1200ms,并行输出为 800ms。可见,并行排序的时间相比较串行排序时间要少不少。
中间操作
该操作会保持 stream 处于中间状态,允许做进一步的操作。它返回的还是的 Stream,允许更多的链式操作。常见的中间操作有:
filter():对元素进行过滤;
sorted():对元素排序;
map():元素的映射;
distinct():去除重复元素;
subStream():获取子 Stream 等。
例如,下面是对一个字符串集合进行过滤,返回以“s”开头的字符串集合,并将该集合依次打印出来:
list.stream()
.filter((s) -> s.startsWith("s"))
.forEach(System.out::println);
这里的 filter(...) 就是一个中间操作,该中间操作可以链式地应用其他 Stream 操作。
终止操作
该操作必须是流的最后一个操作,一旦被调用,Stream 就到了一个终止状态,而且不能再使用了。常见的终止操作有:
forEach():对每个元素做处理;
toArray():把元素导出到数组;
findFirst():返回第一个匹配的元素;
anyMatch():是否有匹配的元素等。
例如,下面是对一个字符串集合进行过滤,返回以“s”开头的字符串集合,并将该集合依次打印出来:
list.stream() //获取列表的 stream 操作对象
.filter((s) -> s.startsWith("s"))//对这个流做过滤操作
.forEach(System.out::println);
这里的 forEach(...) 就是一个终止操作,该操作之后不能再链式的添加其他操作了。
注解的更新
对于注解,Java 8 主要有两点改进:类型注解和重复注解。
Java 8 的类型注解扩展了注解使用的范围。在该版本之前,注解只能是在声明的地方使用。现在几乎可以为任何东西添加注解:局部变量、类与接口,就连方法的异常也能添加注解。新增的两个注释的程序元素类型 ElementType.TYPE_USE 和 ElementType.TYPE_PARAMETER 用来描述注解的新场合。ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中。而 ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中(例如声明语句、泛型和强制转换语句中的类型)。
对类型注解的支持,增强了通过静态分析工具发现错误的能力。原先只能在运行时发现的问题可以提前在编译的时候被排查出来。Java 8 本身虽然没有自带类型检测的框架,但可以通过使用 Checker Framework 这样的第三方工具,自动检查和确认软件的缺陷,提高生产效率。
例如,下面的代码可以通过编译,但是运行时会报 NullPointerException 的异常。
public class TestAnno {
public static void main(String[] args) {
Object obj = null;
obj.toString();
}
}
为了能在编译期间就自动检查出这类异常,可以通过类型注解结合 Checker Framework 提前排查出来:
import org.checkerframework.checker.nullness.qual.NonNull;
public class TestAnno {
public static void main(String[] args) {
@NonNull Object obj = null;
obj.toString();
}
}
编译时自动检测结果如下:
`C:\workspace\TestJava8\src\TestAnno.java:4: Warning:
(assignment.type.incompatible) $$ 2 $$ null $$ @UnknownInitialization @NonNull Object $$ ( 152, 156 )