你还在使用JDK7,今天阿粉带你来了解一下JDK8,不得不说,真香!

前几天阿粉还在和同事抱怨,说现在 JDK 都已经11,12了,结果自己还在用 JDK 7,于是就发生了下面一幕。

1
2
3
4
5
6
7
8
9
10
11
12
阿粉:老宫阿(宫保鸡丁),你说现在 JDK 都已经出到11了,你说对咱们是不是太不友好了,我都快学不动了。

老宫:先别说出到几了,你们现在开发用的是啥?

阿粉:我们还是用的7呀!

老宫:你竟然还在用7,那你知道什么是 Lambda 么?

阿粉:Lambda?听过,但是没用过呀,但是据说挺好用的,但是我觉得学起来好麻烦,我想静静!

老宫:你是不知道呀,JDK8 里面可不仅仅是 Lambda,还有好多新特性呢!

于是故事开始了,在老宫(宫保鸡丁)的讲述下,阿粉也不得不重视起来了,阿粉也要开始学习 JDK8 了,不然再往后,都直接被淘汰了!

1. Java8 核心新特性讲解

Java 8的核心新特性:Lambda(匿名函数)、流、默认方法。

自1998年JDK 1.0(Java 1.0)发布以来,Java已经受到了学生、项目经理和程序员等一大 批活跃用户的欢迎。你看从学生,到项目经理,到程序员,这么多的角色都对 Java 情有独钟,看来不学是真的不行了。

Java 8对硬件也有影响:平常我们用的 CPU 都是多核的——你的笔记本电脑或台式机上的处理器可能有四个 CPU 内核,甚至更多。但是,绝大多数现有的 Java 程序都只使用其中一个内核,其他三个都闲着,或只是用一小部分的处理能力来运行操作系统或杀毒程序。 在Java 8之前,专家们可能会告诉你,必须利用线程才能使用多个内核。问题是,线程用 起来很难,也容易出现错误。

从 Java 的演变路径来看,它一直致力于让并发编程更容易、 出错更少。Java 1.0里有线程和锁,甚至有一个内存模型——这是当时的最佳做法,但事实证明,不具备专门知识的项目团队很难可靠地使用这些基本模型。Java 5添加了工业级 的构建模块,如线程池和并发集合。Java 7添加了分支/合并(fork/join)框架,使得并行 变得更实用,但仍然很困难。

而Java 8对并行有了一个更简单的新思路,不过你仍要遵循一些规则,这些规则阿粉会在后边给大家讲。

2. Lambda表达式

2.1 什么是Lambda表达式

阿粉在研究 Java 8的时候第一件事就是研究的 Lambda 表达式是怎么书写的。

Lambda表达式是带有参数变量的表达式,是一段可以传递的代码,可以被一次或多次执行,是一种精简的字面写法,其实就是把匿名内部类中“一定”要做的工作省略掉,然后由JVM通过推导把简化的表达式还原。

先给大家来个传统排序。

排序结果:

阿粉再来带大家来看看使用 Lambda 表达式的方式:

其实这是 Lambda 表达式的简单的使用,Lambda表达式里面还有很多应用。比如说使用 Lambda表达式来实现多线程。

1
2
3
4
5
6
7
8
9
public class TestClass {
    public static void main(String[] args) {
        new Thread(()-> {
            for(int i=0;i<20;i++) {
                System.out.println("阿粉最帅");
            }
        }).start();
    }
}

其实你看到上面的 Lambda表达式 是不是感觉很简单,但是简单是简单,阿粉要问大家一个问题了? 大家什么时候才适合使用 Lambda表达式呢?

2.2 什么时候使用 Lambda表达式

  • 需要显示创建函数式接口对象的地方,都可以使用

  • 实际上函数式接口的转换是Lambda表达式唯一能做的事情

  • 用于替换以前广泛使用的内部匿名类,各种回调

3. 使用函数式接口

函数式接口定义且只定义了一个抽象方法。函数式接口很有用,因为抽象方法的签名可以描述Lambda表达式的签名。函数式接口的抽象方法的签名 称为函数描述符。所以为了应用不同的Lambda表达式,你需要一套能够描述常见函数描 述符的函数式接口。Java API中已经有了几个函数式接口,比如说 Comparable、Runnable和Callable。

Predicate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

 public static <T> List<T> filter(List<T> list, Predicate<T> p) {
        List<T> results = new ArrayList<>();
        for (T s : list) {
            if (p.test(s)) {
                results.add(s);
            }
        }
        return results;
    }

Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty(); 

List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);

java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛 型T对象,并返回一个boolean。现在就可以直接使用了。 在我们需要表示一个涉及类型T的布尔表达式时,就可以使用这个接口。

Consumer

1
2
3
4
5
6
7
8
9
public static <T> void forEach(List<T> list, Consumer<T> c) {
        for (T i : list) {
            c.accept(i);
        }
    }

    forEach(Arrays.asList(1,2,3,4,5), (
    Integer i)->System.out.println(i) ←─Lambda是Consumer中accept方法的实现 );

java.util.function.Consumer定义了一个名叫accept的抽象方法,它接受泛型T 的对象,没有返回(void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用这个接口。比如,你可以用它来创建一个forEach方法,接受一个Integers的列 表,并对其中每个元素执行操作。在上面的代码中,你就可以使用这个forEach方法,并配合Lambda来打印列表中的所有元素。

至于剩下的 Function 阿粉就不给大家说了。

Java 8中的常用的函数式接口

今天阿粉就先给大家说这些关于 Java8 的特性,阿粉在接下来的文章中会继续讲述 Java8 的新特性呦。

Java Geek Tech wechat
欢迎订阅 Java 极客技术,这里分享关于 Java 的一切。