JAVA8初探-让方法参数具备行为能力并引入Lambda表达式
关于JAVA8学习的意义先来贴一下某网站上的对它的简单介绍:“Java 8可谓Java语言历史上变化最大的一个版本,其承诺要调整Java编程向着函数式风格迈进,这有助于编写出更为简洁、表达力更强,并且在很多情况下能够利用并行硬件的代码。Java 8所带来的函数式编程特性使得Java从此屹立于函数式编程的世界,这是Java这门“古老的”编程语言自从发布以来变化最大的一个版本,在很多地方从根本上改变了我们编码的方式,使得Java也可以像Scala、Python、JavaScript等函数式编程语言一样通过更为精炼、表现力更强的函数式编程手法来编写代码。我认为,Java 8作为Java历史上变化最为激进的一个版本,值得每一个有志向的Java开发者好好研究并落实到自己的日常编码工作中。”,而其实在实际工作中基本上本地的JDK环境都已经升级到了JAVA8版本了,但是!!确对JAVA8中的东东一点都不了解,当然也谈不上去在实际工作中去使用JAVA8的东东,基本还是用JDK5那些标准的API去写代码,既然都已经是JAVA8环境了,而且又有这么大的变化,为何不拥抱一下它使之可以为我们实际工作所用呢,所以接下来慢慢来学习使用它。
在学习之前,先介绍一本很好的JAVA8的教程:Java 8 in Action,如下:
而学习以它为向导一点点去学,打开目录可以看到:
而从第二章开始学,也就是这次要学的:Passing code with behavior parameterization 【让方法参数具备行为能力】
下面就用代码来进行参考学习,开发工具这里不用eclipse,而是用intellij idea,因为我们的Android Studio也是来自于它,而intellij idea是专为JAVA而生的,也是被如今广泛使用的,顺便也享用一下它的开发体验,首先先去官网下载:https://www.jetbrains.com/:
貌似当时学习kotlin也是上了这个官网,点击进去之后就可以到下载页面了:
看一下这两个版本的区别:
可见旗舰版功能是最多最全的,但是是收费滴,不过貌似网上有一些破解教程【http://blog.csdn.net/nishiwodebocai21/article/details/71359619,其中我试用了博客中的方法一】,具体安装这里就不多说了,安装好之后,新建工程:
然后按操作进行创建项目既可,创建一个空项目之后的目录如下:
接下来就可以正式编写我们的代码了,还是按java8 in action的书上的例子来,如书上开篇所说:
然后书上就以"在农厂工作,需要对水果进行过滤统计,并且怎么很轻松的应对农场主给出的各种不同的统计需求"为例,下面就围绕着书的例子一步步的来引起JAVA8的东东,如下:
所以下面自己动手照着来做一下,光看不练假把式,所以新建一个Apple类:
/** * 苹果实体 */ public class Apple { private String color; private long weight; public Apple(String color, long weight) { this.color = color; this.weight = weight; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public long getWeight() { return weight; } public void setWeight(long weight) { this.weight = weight; } @Override public String toString() { return "Apple{" + "color='" + color + '\'' + ", weight=" + weight + '}'; } }
再来建一个类用来实现苹果的筛选,比较简单直接给出,不多解释:
/** * 苹果的筛选 */ public class FilterApple { public static List<Apple> filterGreenApples(List<Apple> apples) { List<Apple> result = new ArrayList<>(); for (Apple apple : apples) { if ("green".equals(apple.getColor())) result.add(apple); } return result; } public static void main(String[] args) { List<Apple> apples = Arrays.asList(new Apple("green", 150), new Apple("yellow", 120), new Apple("green", 170)); List<Apple> greenApples = filterGreenApples(apples); System.out.println(greenApples); } }
编译运行:
这种无脑的代码不用去在意其实现,而在于推导JAVA8东东的过程,所以继续:这时农场主又来新需求了,需要筛选出红颜色的苹果,怎么做呢?
如果不用大脑想的方式则再写一个找红苹果的方法既可,伪代码类似于:
但是显然这种方式应对万变的需求不是特别好,如果还要找其它条件的苹果那还得不断增加方法,比如灵活的方式应该如书中所提到:
所以修改我们的代码如下:
编译运行:
那此时农场主要提需求了:"筛选出重量在150g以上的所有苹果",那怎么实现这种需求呢?简单,再搞个方法不就得了,如书中所示:
但是这时农场主的需求又变了:"找出在150g以上的所有红色的苹果",也就是条件既有重量,也有颜色,那这时我们代码又可以修改为:
很明显面对农场主的这些需求目前的这种代码实现方式应对有点吃力了,那有没有一种更加幽雅更加灵活的实现方式呢?当然有:利用策略模式实现,书中也提到了,这里就不贴书上的例子了,直接写我们的代码:
编译运行:
如果是其它条件则新建子类去继承接口去定义就好了,但是!!书中作者说这种方式不太好,代码量大,而且不够简洁,要更简洁其实用匿名内部类的写法可能更加精练,如书中所说:
但是书中又谈到了使用匿名类部类的不好:占空间、代码容易混淆:
但是,其实还有一个重要为啥要使用Lambda表达式的原因就是: 使用Lambda表达式会比使用上面匿名内部类或子类继承的方式更节省空间,因为java8在JVM上的空间是发生了具体在变化的,如何可以证明呢?下面用jdk自带的jstat命令去查看一下JVM的内存结构,对比一下java7和java8之间的区别,具体如下:
先查看一下JAVA7的JVM结构:
再查看一下JAVA8的JVM结构:
对比一下:
貌似上面的对比也体会不到使用Lambda表达式的代码更省空间呀,这时JAVA8的开篇暂且放一放它的研究,总之为啥要引入Lambda表达式有个重要原因就是:更省空间!
【提示】:Jstat是JDK自带的一个轻量级小工具。全称“Java Virtual Machine statistics monitoring tool”,它位于java的bin目录下,主要利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。可见,Jstat是轻量级的、专门针对JVM的工具,非常适用。jstat工具特别强大,有众多的可选项,详细查看堆内各个部分的使用量,以及加载类的数量。使用时,需加上查看进程的进程id,和所选参数。关于这些命令先不用太过纠结,等未来有时间再来探究下,目前主要是通过这个实验来感性的认知一下JAVA8上内存结构是发生了变化,并且使用Lambda表达式会更省空间就足够了。
那既然已经引入了Lambda表达式了,那倒底这长啥样呢?下面初步去使用下它,还是以之前的苹果的过滤的小案列去使用,如下:
/** * 苹果的筛选 */ public class FilterApple { public interface AppleFilter { boolean filter(Apple apple); } //比如要找出大于150g的所有绿苹果,定义一个相应的策略 public static class GreenAnd150WeightFilter implements AppleFilter { @Override public boolean filter(Apple apple) { return "green".equals(apple.getColor()) && apple.getWeight() > 150; } } public static List<Apple> filterApples(List<Apple> apples, AppleFilter appleFilter) { List<Apple> result = new ArrayList<>(); for (Apple apple : apples) { if (appleFilter.filter(apple)) result.add(apple); } return result; } public static void main(String[] args) { List<Apple> apples = Arrays.asList(new Apple("green", 150), new Apple("yellow", 120), new Apple("green", 170)); List<Apple> greenApples = filterApples(apples, new GreenAnd150WeightFilter()); System.out.println(greenApples); } }
下面开始,首先先介绍一个JAVA8的概念:就是在一个接口中如果只有一个方法,其实就是一个Functional,这时就可以用Annotation来标注它:
也就是说它就可以用Lambda表达式去作业的,这里就用Lambda表达式来查询所有绿颜色的苹果,如下:
编译运行:
语法是不是比较奇怪,等于第一个括号里的是接口方法中的参数,其实还可以简化:
另外还可以简化:
而细心观察一下IDE,左侧有个提示:
再回到我们的接口,在阐述使用Lambda表达式时说到只有当接口中只有一个方法时才是用Lambda表达式可以对它进行作业的,那如果我再声明一个方法会怎样?
所以说@FunctionalInterface这个注解是说明该接口是Functional的,可以使用Lambda表达式去使用它,另外难道只有存在一个方法的接口才能使用这个注解么?其实不然,default的方法和static方法也可以定义在这个接口中,如下:
【注意】:居然在JAVA8中可以在一个接口中定义static和default的方法实现,好强大,貌似以前版本的接口只能有声明~
另外,对于一个具有Functional的接口如果不写这个Annotation也是可以的,只是代码可读性上没有加上好。
其实对于JDK8中的一些系统类也有声明成Functional的,这里以线程中的Runnable接口为例,看下它在JAVA8中的原码:
所以说可以有Lambda表达式来写写我们的线程代码,说干就干,先用传统的方式来启动一个线程:
那如果用Lambda如何写呢?
编译运行:
是不是Lambda表达式写起来更加简洁,语法上可能是有些怪异,肯定是有一个熟悉适应的过程,其实对于之前我们JDK中使用的一些系统接口很多都加上了Functional的行为,再看一个:
所以Lambda表达式是java8的很重要的特性,这片是初步先感受了下它的神奇,接下来会仔细一点点去深入的,加油!