Java的演变过程

Java从头到尾都是被设计成为一门面向对象的语言,所以时间长了,它就积累了很多的有用的库。从头开始,他就拥有多线程处理的能力。最重要的是Java里面有两个非常强大非常超前的两个概念:jvm和Java字节码。

Java虚拟机( JVM)及其字节码可能会变得比Java语言本身更重要,而且对于某些应用来说, Java可能会被同样运行在JVM上的竞争对手语言(如Scala或Groovy)取代 。

但是,编程语言生态系统的气候正在变化。程序员越来越多地要处理所谓的大数据(数百万兆甚至更多字节的数据集),并希望利用多核计算机或计算集群来有效地处理。意味着需要使用并行处理——Java以前对此并不支持。

Java 8对于程序员的主要好处在于它提供了更多的编程工具和概念,能以更快,更重要的是能以更为简洁、更易于维护的方式解决新的或现有的编程问题。

语言需要不断改进以跟进硬件的更新或满足程序员的期待 。

要坚持下去, Java必须通过增加新功能来改进,而且只有新功能被人使用,变化才有意义。所以,使用Java 8,你就是在保护你作为Java程序员的职业生涯。

Java8的函数式编程

在未使用函数式编程的时候,我们从一个篮子中取出来一个绿色的苹果,需要编写这样的一个函数:

public static List filterGreenApples(List inventory){List result = new ArrayList<>();for (Apple apple: inventory){if ("green".equals(apple.getColor())) {result.add(apple);}}return result;
}

当我们要再筛选出重量大于50g的苹果的时候,需要重新写一个方法:

public static List filterHeavyApples(List inventory){List result = new ArrayList<>();for (Apple apple: inventory){if (apple.getWeight() > 150) {result.add(apple);}}return result;}

这样子比较麻烦,所以Java8新增了函数式编程的语法,再遇到这种函数的时候,我们可以这样写:

public static boolean isGreenApple(Apple apple) {return "green".equals(apple.getColor());
}
public static boolean isHeavyApple(Apple apple) {return apple.getWeight() > 150;
}
static List filterApples(List inventory, Predicate p) {List result = new ArrayList<>();for (Apple apple: inventory){if (p.test(apple)) {result.add(apple);}}return result;
}

要使用这个函数,我们可以这么调用它:

filterApples(inventory, Apple::isGreenApple);
//或者
filterApples(inventory, Apple::isHeavyApple);

我们发现,在传递方法作为参数的时候,需要先定义一个谓词(方法),这样的话,也是比较麻烦的一件事情。所以Java8的新语法引入了lambda(匿名函数)表达式来防止这种不够简单的做法:

filterApples(inventory, (Apple a) -> "green".equals(a.getColor()) );
//或者
filterApples(inventory, (Apple a) -> a.getWeight() > 150 );
//甚至
filterApples(inventory, (Apple a) -> a.getWeight() < 80 || "brown".equals(a.getColor()) );
//再甚至,省去定义filterApples方法,直接使用库方法filter
filter(inventory, (Apple a) -> a.getWeight() > 150 );

通过这种写法,我们的代码优雅(少)了很多,看起来也很直观。但是,当传递的方法比较复杂,代码比较多时,我们尽量还是定义一个名字比较直观的方法作为参数传进来的比较好。(ps:作为一个优秀的攻城狮,要保证我们的代码质量)

你可能以为对Java8的函数编程特性就此结束了,但是,很遗憾,最优秀的还在最后面:流Streams。通过函数式编程结合Streams的使用,我们可以充分利用好CPU的多核特性,完成对资源的充分利用,它有一套函数式程序员熟悉的、类似于filter的操作,比如map、 reduce,还有我们接下来要讨论的在Collections和Streams之间做转换的方法。

Java8的Streams

Java8中的Streams可以帮助我们更好的对集合进行处理。比如我们要从一个列表中筛选金额较高的交易,然后按照货币进行分组。在Java8之前,我们可能需要这么写:

Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>();
for(Transaction transaction:transactions){if(transaction.getPrice()>1000){Currency currency=transaction.getCurrency();List<Transaction> transactionsForCurrency=transactionsByCurrencies.get(currency);if(transactionsForCurrency==null){transactionsForCurrency=new ArrayList<>();transactionsByCurrencies.put(currency,transactionsForCurrency);}transactionsForCurrency.add(transaction);}
}

这段代码有很多套路的写法,好几个if判断导致我们没有办法直观的理解这段代码是做什么的。如果使用streams后,我们可以这样写:

Map<Currency, List<Transaction>> transactionsByCurrencies =transactions.stream().filter((Transaction t) -> t.getPrice() > 1000).collect(groupingBy(Transaction::getCurrency));

在理解了streams的用法以后,这段代码的意思就会非常容易读懂。当然,streams并不只是改变了代码的呈现方式,在底层,streams api处理数据的方式与传统的单线程代码并不相同。在之前的代码中,我们会用到for-each来对集合进行迭代,这种方法也叫外部迭代;而在有了streams api之后,根本用不着操心循环的事情,数据处理完全是在库内部进行的,这种方法也叫内部迭代。

在进行内部迭代的时候,我们需要知道,streams api会自动利用cpu的多核特性,充分利用CPU的计算资源进行计算。要使用多核,那就涉及到了并行编程。在以往的代码中,并行编程是一件不容易的事情,因为线程间会涉及到共享变量的同时访问和更新,如果没有协调好,数据可能会被意外改变。代码如下:

public class AddTest implements Runnable {private static int i = 0;public static void main(String[] args) {ExecutorService exec = Executors.newCachedThreadPool();AddTest test = new AddTest();for (int i = 0; i < 10; i++) {exec.execute(test);}exec.shutdown();}@Overridepublic void run() {i++;Thread.yield();System.out.println(i);}
}

Java 8也用Stream API( java.util.stream)解决了这两个问题:集合处理时的套路和晦涩,以及难以利用多核。这样设计的第一个原因是,有许多反复出现的数据处理模式,类似于前一节所说的filterApples或SQL等数据库查询语言里熟悉的操作,如果在库中有这些就会很方便:根据标准筛选数据(比如较重的苹果), 提取数据(例如抽取列表中每个苹果的重量字段),或给数据分组(例如,将一个数字列表分组,奇数和偶数分别列表)等。第二个原因是,这类操作常常可以并行化。例如,如图1-6所示,在两个CPU上筛选列表,可以让一个CPU处理列表的前一半,第二个CPU处理后一半,这称为分支步骤(1)。 CPU随后对各自的半个列表做筛选(2)。最后(3),一个CPU会把两个结果合并起来( Google搜索这么快就与此紧密相关,当然他们用的CPU远远不止两个了)。 -<<Java8实战>>

下面我们体验一下如何利用Stream和Lambda表达式顺序或并行的从一个列表里筛选比较重的苹果:

//顺序处理:
List<Apple> heavyApples = inventory.stream().filter((Apple a) -> a.getWeight() > 150).collect(toList());
//并行处理:
List<Apple> heavyApples = inventory.parallelStream().filter((Apple a) -> a.getWeight() > 150).collect(toList());

可以看到,使用stream可以大大的减少代码量,增加代码的可读性,方便的控制并发编程。好处多多。

如何利用Java8中的默认方法功能

Java 8中加入默认方法主要是为了支持库设计师,让他们能够写出更容易改进的接口。 我们都知道,Java8之前,如果在接口里面定义了一个方法,那么实现这个接口的方法就必须实现该接口的所有方法,否则,编译就不会通过。而要实现上面的List中的stream和parallelStream方法,就需要在List或者它的父类Conllection中实现这些方法。如果按照之前的需要在实现类中实现接口的所有方法,那么这将会成为我们这些程序员的噩梦。

让我们放心的是,Java8引入了默认方法:以后在接口中可以直接写出来方法体作为默认的方法了,实现这个接口的类不用必须实现已经实现默认方法的方法了。例如: 在Java 8里,你现在可以直接对List调用sort方法。它是用Java 8 List接口中如下所示的默认方法实现的,它会调用Collections.sort静态方法:

default void sort(Comparator<? super E> c) {Collections.sort(this, c);
}

这意味着List的任何实体类都不需要显式实现sort,而在以前的Java版本中,除非提供了sort的实现,否则这些实体类在重新编译时都会失败。 因为这会导致多重继承的问题,所以java8中才用了一些限制来避免出现类似于C++中的菱形继承问题。

其他使用JVM的编程语言对比

与Java运行在jvm虚拟机上一样,Scala语言也是运行在jvm上的,它在有些方面有赶超java的趋势。

现在我们知道了java8中新引进来的思想有:将方法和lambda作为一等值;以及在没有可变共享状态时,函数或方法可以有效、安全的并行执行。这两个思想在Stream里面都用到了。

另外,对null的处理,Java8中引入了optional类来进行处理。另外Java8也引入了Scala中的模式匹配的概念。

总结一下:
1、 请记住语言生态系统的思想,以及语言面临的“要么改变,要么衰亡”的压力
2、 函数是一等值;记得方法如何作为函数式值来传递,还有Lambda是怎样写的
3、 Java 8中Streams的概念使得Collections的许多方面得以推广,让代码更为易读,并允许并行处理流元素
4、 可以在接口中使用默认方法,在实现类没有实现方法时提供方法内容
5、 其他来自函数式编程的有趣思想,包括处理null和使用模式匹配

Java8-为什么选择java8相关推荐

  1. Java8 HashMap详解

    文章推荐 精选java等全套学习资源 精选java电子图书资源 精选大数据学习资源 java项目练习精选 ###Java8 HashMap Java8 对 HashMap 进行了一些修改,最大的不同就 ...

  2. 那些年,我们追过的java8

    那些年,我们追过的java8 9月份java9就要发布了,在8月的最后一天决定回顾一下java8那些惊天动地的变化,加深理解,共同进步. 我们都知道java与c++,c不同是一个为面向对象而生的语言, ...

  3. java8 guava_Guavate:桥接Guava和Java8的微型库

    java8 guava Java8很棒,并向JDK添加了一些有用的抽象,这些抽象通过Google出色的Guava commons库在Java社区中得到了普及. 小组讨论表明, 不久将有一个需要Java ...

  4. java8返回单个号码_如何在单个API中支持Java 6、8、9

    java8返回单个号码 借助jOOQ 3.7,我们终于添加了对Java 8功能的正式支持. 这为许多不错的改进打开了大门,例如: 创建结果流 try (Stream<Record2<Str ...

  5. java8的新特性_Java8的

    java8的新特性 Java8没有安排释放,直到2014年3月,但早期发行版本已经可用了一段时间. 一些最有趣的新功能是: 流 功能接口 默认方法 Lambdas Java时间 流 新的java.ut ...

  6. 《Java8实战》笔记汇总

    <Java8实战>笔记(01):为什么要关心Java8 <Java8实战>笔记(02):通过行为参数传递代码 <Java8实战>笔记(03):Lambda表达式 & ...

  7. 【JAVA8】Map新方法,别再重复造车轮了

    文章目录 getOrDefault forEach compute computeIfAbsent computeIfPresent merge putIfAbsent remove(key,valu ...

  8. 【Java8 环境安装】Java1.8JDK环境安装jdk-8u361-windows-x64

    Java1.8环境安装 一.JDK的下载 首先进入Oracle官网下载JDK1.8安装包 https://www.oracle.com/ 依次点击Resources>Java Downloads ...

  9. java8的时期和时间

    文章目录 旧版日期时间的问题 新版日期时间介绍 Java.util.date java.sql.date SimpleDateFormatter calendar java8日期 DateTimeFo ...

  10. java 8 函数式接口_必看:通俗易懂地告诉你什么是java8中的“函数式接口”

    花10分钟认真的看完一篇文章,或许会有意想不到的收获 java8发布已经好几年了,相信很多小伙伴都使用过java8,java8这版本带来了很多新特性,其中一个就是"函数式接口",今 ...

最新文章

  1. 【逆序对】Ultra - Quicksort
  2. 用于安装python第三方库的工具是_Python第三方库安装
  3. zynq中mgtx应用_基于ZYNQ的UCOS移植(TCP通讯)
  4. tf.parse_single_example
  5. frida需要Java基础吗_Android Hook工具之Frida 基础使用
  6. linux命令里的xz是干嘛的,linux xz命令详解
  7. cocos2d JS 鼠标响应事件
  8. 建站基础知识之CSS 究竟什么来头?
  9. Linux 系统故障修复和修复技巧
  10. 从玄学走向科学:在字节跳动广告投放这么干
  11. 身体健康是第一生产力 --- 我看央视主持人李咏早逝
  12. 音频转换成mp3,音频转mp3格式
  13. (63)计数器设计(递增计数器)
  14. UCOSIII总结------消息队列(6)
  15. java bouncycastle_BouncyCastle
  16. Hinton介绍胶囊理论的论文
  17. 操作系统MSXML组件版本过低,导致启动失败的原因
  18. 经典力学(动力学)——牛顿定律
  19. 微信小程序的爱心点赞
  20. 520 | 用一幅爱的地图表白他/她

热门文章

  1. LWIP协议 | 理论基础知识解析
  2. 空间相关性分析:空间权重矩阵
  3. Debian Qualcomm Atheros QCA61x4 Wireless Network Adapter - 网卡驱动
  4. 基于单片机24V直流无刷电机电动车控制器设计
  5. 三十款国外IES灯光经典素材整理i
  6. sap 界面创建凭证_在sap系统设置纸张打印格式(针式打印机)
  7. SQL语法提示工具SQL Prompt教程——控制代码建议框何时弹出
  8. Threejs初级教程
  9. 21_多易教育之《yiee数据运营系统》用户画像-模型标签流失预测篇兼退拒风险概率预测篇
  10. 有备无患:避免文件丢失的可行方案