Java 8于2014年3月18日发布,在阅读相关文章后,在本教程中,我们将通过示例研究 Java 8功能。
以上是 Java 8 的新特性的列举,接下来展示一些代码片段,来更好理解这些新特性
package com.taotao.springboot;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
public class IterableForEachExample {
    public static void main(String[] args) {
        List<Integer> ints = new ArrayList<>();
        for (int i = 0; i < 10; i++) ints.add(i);
        //迭代器遍历
        Iterator<Integer> iterator = ints.iterator();
        while (iterator.hasNext()) {
            Integer i = iterator.next();
            System.out.println("Iterator Value::" + i);
        }
        //使用forEach方法遍历,这里使用了匿名内部类
        ints.forEach(new Consumer<Integer>() {
            public void accept(Integer t) {
                System.out.println("forEach anonymous class Value::" + t);
            }
        });
        //通过实现Consumer接口的子类遍历,业务处理从遍历逻辑中抽取出来
        MyConsumer action = new MyConsumer();
        ints.forEach(action);
    }
}
//实现Conumer接口
class MyConsumer implements Consumer<Integer> {
    public void accept(Integer t) {
        System.out.println("Consumer impl Value::" + t);
    }
}
通过上面这个例子,看出使用 forEach()方法代码行数会增加,但是它将遍历逻辑与业务处理逻辑分离,有助于我们将关注点放在业务逻辑的处理上。
在上面的例子中,如果有点进去看 forEach()方法,你会发现它是在接口 Iterable 中定义的。但是我们知道接口不能有方法体。从 Java 8开始,接口已改进,允许有默认的实现方法。我们可以在接口中使用关 default 和 static 关键字来创建方法实现。以下是 forEach()的定义。
我们知道在 Java 中不能同时继承多个类,因为这样做会引发 Diamond 问题 ,接口允许有默认方法实现后,接口变得与抽象类非常类似,现在可以利用接口解决这个问题。我们定义两个接口 FunctionInterface1 和FunctionInterface2,以及一个它两的实现类 FunctionInterfaceImpl
package com.taotao.springboot;
@FunctionalInterface
public interface FunctionInterface1 {
    void hello1(String str);
    default void eat(String str){
        System.out.println("I1 eatting::"+str);
    }
    static void print(String str){
        System.out.println("Printing "+str);
    }
    //如果默认方法重写了 Object 类中方法,编译不能通过
//    default String toString(){
//        return "";
//    }
}
在上面代码中,我们知道接口中的默认实现方法不能与Object类中的方法签名相同
package com.taotao.springboot;
@FunctionalInterface
public interface FunctionInterface2 {
    void hello2(String str);
    default void eat(String str){
        System.out.println("I2 eatting::"+str);
    }
}
注意,FunctionInterface1 与 FunctionInterface2 都有一个默认的实现方法 eat()。在这种情况下,我们看看它两的实现类 FunctionInterfaceImpl 会不会有要注意的点。
package com.taotao.springboot;
public class FunctionInterfaceImpl implements FunctionInterface1, FunctionInterface2 {
    @Override
    public void hello1(String str) {
        System.out.println("hello1 " + str);
    }
    //如果不实现 eat() 方法,编译不通过
    @Override
    public void eat(String str) {
        System.out.println("functionInterfaceImpl eatting " + str);
    }
    @Override
    public void hello2(String str) {
        System.out.println("hello2 " + str);
    }
    public static void main(String[] args) {
        FunctionInterface1 interface1 = new FunctionInterfaceImpl();
        interface1.hello1("world");
        interface1.eat("apple");
        FunctionInterface2 interface2 = new FunctionInterfaceImpl();
        interface2.eat("banana");
        interface2.hello2("world");
    }
}
注意,FunctionInterfaceImpl 必须实现 eat()方法。运行 main 方法,打印输出如下:
hello1 world
functionInterfaceImpl eatting apple
functionInterfaceImpl eatting banana
hello2 world
可见,Java 8 在 Collection API 中大量使用默认和静态方法,以便我们的代码保持向后兼容。
同时从 functionInterfaceImpl 的例子可以看出,如果一个类同时实现了两个及以上接口,而这些接口中都有一个签名相同的方法,那么,这个类必须实现这个方法,否则将不能通过编译。此时,接口中的默认实现方法已经不起作用了。如果类只是实现一个接口,则没有这个问题
这也就解释了之前提到的 接口中的默认实现方法不能与Object类中的方法签名相同 。因为Object是所有类的超类(父类),因此如果接口中具有equals(),hashCode()默认方法,那这些默认方法也将不起作用。
如果您仔细观察上述接口代码,则会注意到 @FunctionalInterface 注解。函数式接口是 Java 8中引入的新概念。有且只有一个抽象方法的接口就是函数式接口。@FunctionalInterface 注解不是函数式接口定义的必要条件,它是一个避免在函数式接口中意外添加其他抽象方法的注解。java.lang.Runnable具有单个抽象方法run(),是函数式接口一个很好的例子。
函数式接口的主要优点之一是可以使用 lambda 表达式实例化它们。我们可以用匿名内部类实例化一个接口,但是代码看起来很庞大。
//匿名内部类实现Runnable
Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("My Runnable");
            }
        };
//lambda 表达式实现Runnable
Runnable runnable = () -> {
            System.out.println("My Runnable");
        };
如果方法实现中只有一条语句,我们也不需要花括号。如下所示:
Runnable runnable = () -> System.out.println("My Runnable");
因此,lambda表达式是轻松创建函数式接口的匿名类的一种方法。使用lambda表达式代码可读性感觉更差,因此要谨慎使用它,不介意编写一些额外的代码行。
java.util.function是添加了带有函数式接口的新程序包,以提供lambda表达式和方法引用的目标类型。
java.util.stream是 Java 8中添加的新程序包,以便对集合执行类似过滤/映射/归约的操作。Stream API 将允许顺序执行和并行执,是最好的功能之一,如果经常处理Collections,而且集合元素很多,我们可以根据某些条件过滤掉它们。
Collection接口已使用 stream()和 parallelStream()默认方法进行了扩展,以获取用于顺序执行和并行执行的 Stream。让我们用一个简单的例子看看它们的用法。
package com.taotao.springboot;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StreamList {
    public static void main(String[] args) {
        List<Integer> myList = new ArrayList<>();
        for (int i = 0; i < 100; i++) myList.add(i);
        //串行流
        Stream<Integer> sequentialStream = myList.stream();
        //并行流
        Stream<Integer> parallelStream = myList.parallelStream();
        //过滤
        Stream<Integer> highNums = parallelStream.filter(p -> p > 90);
        //使用 forEach 遍历
        highNums.forEach(p -> System.out.println("High Nums parallel=" + p));
        Stream<Integer> highNumsSeq = sequentialStream.filter(p -> p > 90);
        highNumsSeq.forEach(p -> System.out.println("High Nums sequential=" + p));
    }
}
运行上述代码的 main 方法,你将看到如下输出:
High Nums parallel=91
High Nums parallel=93
High Nums parallel=96
High Nums parallel=94
High Nums parallel=95
High Nums parallel=92
High Nums parallel=97
High Nums parallel=98
High Nums parallel=99
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
请注意,并行流不按集合元素排列顺序处理,但在处理大量元素的集合时将非常有用。
在 Java中 使用日期,时间和时区一直很困难。Java中没有用于日期和时间的标准方法或API。java.time程序包是 Java 8一个不错的附加功能,它将简化Java中使用时间的过程。
仅查看 Java Time API软件包,我就可以感觉到它非常易于使用。它具有一些子包java.time.format,这些子包提供用于打印和解析日期和时间的类,并java.time.zone提供对时区及其规则的支持。
新的Time API在整月的几个月和一周中的几天中都更喜欢枚举而不是整数常量。将DateTime对象转换为字符串的类是DateTimeFormatter。
我们已经看到了 forEach()方法和用于集合的 Stream API。Collection API还有一些新方法是:
Iterator forEachRemaining(Consumer action)在所有元素都已处理完毕或该动作引发异常之前,对每个剩余元素执行给定操作的默认方法。Collection removeIf(Predicate filter)删除此集合中所有满足特定条件的元素的默认方法。Collection spliterator() 该方法返回Spliterator实例,该实例可用于顺序或并行遍历元素。map replaceAll(),compute(),merge()方法。一些重要的并发API增强功能包括:
ConcurrentHashMap compute(),forEach(),forEachEntry(),forEachKey(),forEachValue(),merge(),reduce()和search()方法。CompletableFuture 可以明确完成(设置其值和状态)。Executors newWorkStealingPool() 使用所有可用处理器作为目标并行度级别创建窃取线程池的方法。一些IO改进包括:
Files.list(Path dir) 返回延迟填充的Stream,其元素是目录中的条目。Files.lines(Path path) 从文件中读取所有行作为流。Files.find() 通过在以给定起始文件为根的文件树中搜索文件,返回通过路径延迟填充的Stream。BufferedReader.lines() 返回一个Stream,其元素是从此BufferedReader中读取的行。一些其他API改进:
jjs 添加命令以调用 Nashorn Engine。jdeps 添加命令以分析类文件这就是带有代码片段的 Java 8 全部新特性。
原文:https://www.cnblogs.com/loveryn/p/13059948.html