1、JDK8新特性简介
- 速度更快:算法的优化(比如HashMap)、底层内存结构的优化(将永久区PremGen变成了元空间MetaSpace)
- 代码更少(增加了新的语法Lambda表达式)
- 强大的Stream API
- 偏于并行
- 最大化减少空指针异常 Optional
tips:
永久区属于堆heap(在jdk1.7之前,heap中分为了2个区:垃圾回收区和永久区);而元空间存储在物理内存上
2、函数式编程思想
- 在数学中,函数指的是给定一个输入值,通过一套计算方案,最终给出结果值。 也就是拿什么东西做什么事。
- 面向对象又过分的强调了必须要通过对象的形式来做某事。(重视做事的过程)
- 而函数式编程强调“做什么,而不是以什么形式去做”。(重视做事的结果)
3、Lambda编程基础
3.1函数式接口
在学习Lambda之前,需要先搞清楚函数式接口
函数式接口:适用于函数式编程场景(Lambda表达式),接口中有且仅有一个抽象方法,且需要添加FunctionalInterface注解
示例代码如下:
@FunctionalInterface
interface Conver{
void fun();
}
3.2Lambda表达式格式及说明
Lambda表达式的标准格式为: (参数类型 参数名称) ‐> { 代码语句 }
说明:
- 小括号内的语法与传统方法参数列表一致
- -> 是新引入的语法格式,代表指向动作 ,表示小括号前面的参数被用于后面{}里面的代码语句
- 大括号内的语法与传统方法体要求基本一致,它实际上是对函数式接口里唯一的那个抽象方法的重写
3.3Lambda省略格式
Lambda强调的是“做什么”而不是“怎么做”,所以凡是可以根据上下文推导得知的信息,都可以省略。
省略规则如下:
- 小括号内参数的类型可以省略;
- 如果小括号内有且仅有一个参,则小括号可以省略;
- 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
3.4代码实例
1)无参无返回值
@Test
publuic vode demo01(){
//匿名内部类写法
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("多线程任务1");
}
};
new Thread(task).start();
//Lambda写法
new Thread(()->{System.out.println("多线程任务2");}).start();
//{}里面只有一条语句,可以省略{}和;
new Thread(()->System.out.println("多线程任务3")).start();
}
2)有参有返回值
//有参有返回值
@FunctionalInterface
interface Calculator{
int calc(int a,int b);
}
public class LambdaDemo {
@Test
public void demo02() {
//需求:求两个整数和
//int result = invokeCalc(10,20,(a,b)->{return a+b;});
//可优化为下面代码
int result = invokeCalc(10,20,(a,b)->a+b);
System.out.println(result);
}
//两个int类型进行处理
public int invokeCalc(int a,int b,Calculator c){
return c.calc(a,b);
}
}
3)参数、返回值类型为函数式接口类型
@Test
public void demo03() {
//需求1:执行1个线程任务
startThread(()->System.out.println("多线程"));
//需求2:将数组按照由小到大顺序排序
String[] arr = {"c","a","d","b"};
Arrays.sort(arr,compara());
System.out.println(Arrays.toString(arr));
}
//参数为函数式接口,执行1个线程操作
public void startThread(Runnable target){
new Thread(target).start();
}
//返回值类型为一个函数式接口,返回一个比较器对象
public Comparator<String> compara(){
return (a,b)->a.compareTo(b);
}
3.5Lambda的延迟执行
有些场景代码执行后,结果不一定会使用,这就造成了性能浪费。而Lambda具有延迟执行的特性,就可以很好的解决这个问题。
参考代码如下:
@FunctionalInterface
interface MessageBuilder{
String append();
}
public class LambdaDemo {
@Test
public void demo04() {
String msg1 = "hello";
String msg2 = "world";
//不论level,在调用log方法的时候,后面的参数一定要进行拼接操作
log("2",msg1+msg2);
//只有当level符合条件,才会去调用append方法,才会执行Lambda
newLog("1",()->msg1+msg2);
}
//记录日志
public void log(String level,String msg){
if("1".equals(level)){
System.out.println(msg);
}
}
public void newLog(String level,MessageBuilder mb){
if("1".equals(level)){
System.out.println(mb.append());
}
}
}
4、Lambda编程进阶
公共定义的函数式接口
从jdk1.8开始提供了function包,该包针对于用户有可能做的函数式接口做了一个公共定义
最为核心的有四个接口:
- 功能性接口:Function<T, R>
是对接收一个T类型参数,返回R类型的结果的方法的抽象
抽象方法:R apply(T t)
默认方法:andThen
示例代码如下:
@Test
public void functionTest() {
int num = fun("100",s->Integer.parseInt(s) );//字符串转成int类型
System.out.println(num);
//先将字符串转成int,再*10
int result = method("5", s->Integer.parseInt(s), a->a *= 10);
System.out.println(result);
}
//用于字符串处理
public Integer fun(String s,Function<String,Integer> fun){
return fun.apply(s);
}
//andThen:先执行一次apply操作,然后将第一次执行apply操作后获得的返回值作为参数再执行一次apply操作
public int method(String s, Function<String,Integer> one,Function<Integer,Integer> two){
return one.andThen(two).apply(s);
}
andThen源码如下:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
- 消费型接口:Consumer<T>
对应的方法类型为接收一个参数,没有返回值
抽象方法:void accept(T t)
默认方法:andThen
示例代码如下:
@Test
public void consumerTest(){
shopping(12345, m->System.out.println("花了"+m+"元买了包包"));
//将"hElLo"转成大写输出,其次再转成小写输出
fun("hElLo",s->System.out.println(s.toUpperCase()),
s->System.out.println(s.toLowerCase()));
}
//花钱购物
public void shopping(double money,Consumer<Double> con){
con.accept(money);
}
//andThen:使用提供的参数执行两次操作
public void fun(String str,Consumer<String> one,Consumer<String> two){
one.andThen(two).accept(str);
}
andThen源码如下:
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
- 供给型接口:Supplier<T>
该接口对应的方法类型不接受参数,但是提供一个返回值
抽象方法:T get()
示例代码如下:
@Test
public void supplierTest(){
//获取10个0~99之间的随机数
List<Integer> list = getList(10, ()->new Random().nextInt(100));
System.out.println(list);
}
//获取指定个数的Integer集合
public List<Integer> getList(int count, Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for(int i=0;i<count;i++){
list.add(sup.get());
}
return list;
}
- 断言型接口:Predicate<T>
该接口对应的方法为接收一个参数,返回一个Boolean类型
抽象方法:boolean test(T t)
默认方法:and(与),or(或),negate(非)
示例代码如下:
@Test
public void predicateTest(){
List<String> list = Arrays.asList("Tom","Jerry","Tony");
List<String> nl = filterList(list, s->s.startsWith("T"));//将所有以“T”开头的字符串放入集合中
System.out.println(nl);
//判断一个字符串既包含"H",又包含"O"
boolean bool = andFun(s->s.contains("H"),s->s.contains("O"),"HelloOpen");
System.out.println(bool);
//判断一个字符串中是否含有"H"或者"O"
bool = orFun(s->s.contains("H"),s->s.contains("O"),"HelloOpen");
System.out.println(bool);
//对给定操作进行非判定
bool = negateFun(s->s.length()<5,"helloworld");
System.out.println(bool);
}
//将满足条件的字符串放入集合中
public List<String> filterList(List<String> list, Predicate<String> pre){
List<String> nl = new ArrayList<>();
for(String s:list){
if(pre.test(s)){
nl.add(s);
}
}
return nl;
}
//and
public boolean andFun(Predicate<String> one,Predicate<String> two,String s){
return one.and(two).test(s);
}
//or
public boolean orFun(Predicate<String> one,Predicate<String> two,String s){
return one.or(two).test(s);
}
//negate
public boolean negateFun(Predicate<String> pre,String s){
return pre.negate().test(s);
}
附:一些常用的其它接口