JDK8新特性总结

Lambda表达式

首先什么是lambda表达式?
lambda表达式其实就是函数式接口,形式类似于(s)->{doSomething;}
其次什么是函数式接口?
函数式接口就是在接口中只存在一个方法的接口,例子如下:

package com.bdcloud.dao;

@FunctionalInterface
public interface MyFunctionInterface {
     void myMethod();
}

结合上述函数式接口,使用lambda的例子如下:

package com.bdcloud.dao.impl;

import com.bdcloud.dao.MyFunctionInterface;

public class DemoMyFunctionInterface {

    private static void doSomething(MyFunctionInterface myFunctionInterface){
        myFunctionInterface.myMethod(); //调用自定义的函数接口方式
    }

    public static void main(String[] args) {
        //传统方式写法
        doSomething(new MyFunctionInterface() {
            @Override
            public void myMethod() {
                System.out.println("此方法为匿名函数类");
            }
        });
        //lambda的写法
        doSomething(()->{
            System.out.println("此调用方式为lambda方式");
        });
    }
}

Lambda的延迟特性

有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。

Demo:例如方法调用中参数为字符串拼接结果,故而无论最终是否需要,都需要先拼接再进行函数调用,例子如下:

public class Demo01Logger {
private static void log(int level, String msg) {
	if (level == 1) {
	System.out.println(msg);
	}
}
public static void main(String[] args) {
	String msgA = "Hello";
	String msgB = "World";
	String msgC = "Java";
	log(1, msgA + msgB + msgC);
	}
}

这样,在方法调用传参过程中,则可以使用函数式编程来优化性能,具体实现如下:

@FunctionalInterface
public interface MessageBuilder {
	String buildMessage();
}

public class Demo02LoggerLambda {
private static void log(int level, MessageBuilder builder) {
	if (level == 1) {
		System.out.println(builder.buildMessage());
	}
}
public static void main(String[] args) {
		String msgA = "Hello";
		String msgB = "World";
		String msgC = "Java";
		log(1, ()> msgA + msgB + msgC );
	}
}

这样一来,只有当级别满足要求的时候,才会进行三个字符串的拼接;否则三个字符串将不会进行拼接。

Lambda作为方法的参数或者返回值

Demo如下:

package com.bdcloud.thread;

/***
 * Runable 的run方法,Lambda表达式作为参数
 */
public class DemoRunable {

    private static void startThread(Runnable runnable){

        new Thread(runnable).start();
    }

    public static void main(String[] args) {
        startThread(()->{
            System.out.println("开启一个线程");
        });
    }

}

Lambda作为返回值

package com.bdcloud.thread;

import java.util.Arrays;
import java.util.Comparator;

/***
 * 测试lambda表达式作为方法返回值
 */
public class LambdaReturn {

    private static Comparator<String> newCompare(){

        return (a,b) -> b.length() - a.length();
    }

    public static void main(String[] args) {
        String [] arrs = {"aaa","bbbb","ccccc"};
        System.out.println(Arrays.toString(arrs));
        Arrays.sort(arrs,newCompare());
        System.out.println(Arrays.toString(arrs));

    }
}

JDK中常用的三种函数式接口

第一种 Supplier接口,位于java.util.function包中,该接口只有一个 T get()方法,它的作用是生产一个对象,Demo如下

package com.bdcloud.supplier;
import java.util.function.Supplier;

/***
 * 测试函数式接口-Supplier
 * 生产接口,用于根据泛型生产相对应对象
 */
public class SupplierDemo {

    private static String supplierTest(Supplier<String> sup){
        return sup.get();
    }

    public static void main(String[] args) {
        String test = supplierTest(() -> {
            return "哇哈哈哈";
        });
        System.out.println(test);
    }
}

第二种是Consumer接口,位于java.util.function包中,该接口只有一个accept(T t)方法,用于 消费对象,举个例子,编写一个Consumer的接口消费字符串,并且将字符串拆分成姓名: XXX 性别:X的形式

    package com.bdcloud.consumer;
    
    import java.util.function.Consumer;
    
    /***
     * 测试jdk函数式接口Consumer
     */
    public class ConsumerTest {
    
        /***
         * andThen方法用于顺序调用俩个consumer接口
         * @param consumer1
         * @param consumer2
         * @param str
         */
        private static void fromatString(Consumer<String> consumer1,Consumer<String> consumer2,String str){
            consumer1.andThen(consumer2).accept(str);
        }
    
        public static void main(String[] args) {
            String [] strs= {"照例音,女","刘德华,男"};
    
            for (String temp:
                 strs) {
    
            fromatString((t)->{
                System.out.print("姓名:"+t.split(",")[0]);
            },(t)->{
                System.out.println(" 性别:"+t.split(",")[1]);
            },temp);
            }
        }
    }

第三种 Predicate接口,位于java.util.function包中,boolean test(T t)的方法用于构造一个返回boolean的方法,举2个例子,第一个判断字符串中是否包含’H’ or|and ‘o’,Demo如下:

package com.bdcloud.predicate;

import java.util.function.Predicate;

/***
 * 编程式接口-Predicate,返回一个boolean值
 */
public class PredicateDemo {

    /***
     * 测试test方法
     * @param predicate
     * @param value
     * @return
     */
    public static boolean method(Predicate<String> predicate,String value){
        return predicate.test(value);
    }


    /***
     * 测试and方法
     * @param one
     * @param two
     */
    public static void testAnd(Predicate<String> one,Predicate<String> two){
        boolean helloWorld = one.and(two).test("HelloWorld");
        System.out.println("是否包含相关字符串"+helloWorld);
    }

    /***
     * 测试Or方法
     * @param one
     * @param two
     */
    public static void testOr(Predicate<String> one,Predicate<String>two){
        boolean helloWorld = one.or(two).test("helloWorld");
        System.out.println("测试字符串包含"+helloWorld);
    }

    /***
     * 测试
     * @param one
     */
    public static void testNegate(Predicate<String>one){
        System.out.println("测试亦或操作="+one.negate().test("HHHHHHHH"));
    }


    public static void main(String[] args) {
        boolean method = method((value) -> {
            return value.length() > 5;
        }, "AAAAAAA");
        System.out.println(method);

        //测试And方法
        testAnd((s)->{return  s.contains("H");},(s)->{return s.contains("o");});

        //测试Or方法
        testOr((s)->{return s.contains("H");},(s)->{return s.contains("o");});

        testNegate((s)->{return s.length()>5;});
    }
}

第二个筛选字符串数组中中名字长度为3且为女性的字符串;Demo如下

package com.bdcloud.predicate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/***
 * 做一个字符串筛选过滤
 */
public class PredicateDemo2 {

    private static void filter(Predicate<String> one,Predicate<String>two,String[] arr){

        List<String> result = new ArrayList<>();
        for (String temp:
             arr) {
            if (one.and(two).test(temp)) {
                result.add(temp);
            }
        }

        System.out.println(result.toString());
    }

    public static void main(String[] args) {
        String[] arrs = {"古力娜扎,女","黄晓明,男","丰绅殷德,女"};

        filter((s)->{return s.split(",")[0].length() == 4;},(s)->{return s.split(",")[1].equals("女");},arrs);
    }
}

最后一个Function接口,位于java.util.function包中,内部方法 R apply(T t),用于接收一个类型的对象,转换为另一种类型的对象,举个例子,将一个字符串转换成Int并且自增1并返回,代码如下:

package com.bdcloud.function;

import java.util.function.Function;

/***
 * 测试函数式编程-Function接口
 */
public class FunctionDemo {

    /***
     * 测试将String转换为int
     * @param function
     */
    private static void changeNum(Function<String,Integer> function){
        Integer apply = function.apply("100");
        System.out.println("转换后="+apply);
    }

    /***
     * 测试andThen
     * @param function
     * @param two
     */
    private static void changeNumAndAdd(Function<String,Integer>function,Function<Integer,Integer>two){
        Integer apply = function.andThen(two).apply("100");
        System.out.println("结果="+apply);
    }

    public static void main(String[] args) {
        changeNum((s)->{return Integer.valueOf(s);});

        changeNumAndAdd((s)->{return Integer.valueOf(s);},(s)->{return s++;});
    }
}

Stream流

随着jdk8函数式编程的引入,引入stream流的概念用于解决传统集合的弊端,过滤都是重型加载匹配,且不方便代码修改变更

Stream的一些特性

  • 为函数式编程而生。对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。
  • 惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
  • 可消费性。stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。

类似于Stream的filter操作可以使用lambda传递真实处理逻辑

package com.bdcloud.stream;

import org.omg.PortableInterceptor.INACTIVE;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

/***
 * 使用Stream-API进行测试
 */
public class StreamDemo {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("lisi");
        list.add("yuanqianqian");
        list.add("zhangfeng");

        list.stream().filter(name -> name.startsWith("l")).filter(name-> name.length()==11).forEach(name-> System.out.println(name));

        //map方法调用,将字符串转换为int
        Stream<String> stringStream = Stream.of("1", "2", "3", "4");
        stringStream.map((s->Integer.valueOf(s))).forEach((s-> System.out.println(s)));

        //count函数
        Stream<String> stringStream2 = Stream.of("1", "2", "3", "4");
        System.out.println(stringStream2.count());

        //只取用前n个。limit
        Stream<String> stringStream3 = Stream.of("1", "2", "3", "4");
        stringStream3.limit(3).forEach(s-> System.out.println(s));

        //skip。跳过前几个
        Stream<String> stringStream4 = Stream.of("1", "2", "3", "4");
        stringStream4.skip(1).forEach(s-> System.out.println(s));

        //组合 concat
        Stream<String> stream1 = Stream.of("张三丰");
        Stream<String> stream2 = Stream.of("张无忌");
        Stream<String> concat = Stream.concat(stream1, stream2);
        System.out.println(concat.count());
    }
}

使用Stream的综合例子

package com.bdcloud.stream;

import com.bdcloud.bean.Person;

import java.util.ArrayList;
import java.util.stream.Stream;

/**
 * 综合测试Stream方法
 * 1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
 * 2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
 * 3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
 * 4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
 * 5. 将两个队伍合并为一个队伍;存储到一个新集合中。
 * 6. 根据姓名创建 Person 对象;存储到一个新集合中。
 * 7. 打印整个队伍的Person对象信息。
 */
public class DemoArrayListNames {
    public static void main(String[] args) {
        //第一支队伍
        ArrayList<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("石破天");
        one.add("石中玉");
        one.add("老子");
        one.add("庄子");
        one.add("洪七公");
        //第二支队伍
        ArrayList<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯赵四");
        two.add("张天爱");
        two.add("张二狗");

        Stream<String> stream1 = one.stream().filter(s -> s.length() == 3).limit(3);
        Stream<String> stream2 = two.stream().filter(s -> s.startsWith("张")).skip(2);

        Stream<String> concat = Stream.concat(stream1, stream2);
        concat.map(s-> new Person(s)).forEach(s-> System.out.println(s));
    }
}

方法引用

方法引用出现的初衷就是为了简化lambda表达式,lambda表达式传入的是一个解决方案,若这种解决方案在其他地方已经定义,那是否需要重复定义,所以就出现了方法引用,引用方式 对象::方法名

@FunctionalInterface
public interface Printable {
	void print(String str);
}

在 Printable 接口当中唯一的抽象方法 print 接收一个字符串参数,目的就是为了打印显示它。那么通过Lambda来使用它的代码很简单:

public class Demo01PrintSimple {
private static void printString(Printable data) {
		data.print("Hello, World!");
}
public static void main(String[] args) {
		printString(s ‐> System.out.println(s));
	}
}

若使用方法引用简化后,书写格式是这样的

public class Demo02PrintRef {
private static void printString(Printable data) {
		data.print("Hello, World!");
}
public static void main(String[] args) {
		printString(System.out::println);
	}
}

通过对象名引用成员方法

若一个类中实现了对一个字符串转变大写并输出,类定义如下:

public class MethodRefObject {
public void printUpperCase(String str) {
	System.out.println(str.toUpperCase());
   }
}

函数式接口定义如下:

@FunctionalInterface
public interface Printable {
	void print(String str);
}

若此时想使用MethodRefObject中的printUpperCase作为该函数式接口的lambda实现,则使用对象名引用,使用代码如下:

public class Demo04MethodRef {
private static void printString(Printable lambda) {
	lambda.print("Hello");
  }
public static void main(String[] args) {
	MethodRefObject obj = new MethodRefObject();
	printString(obj::printUpperCase);
  }
}

通过类名引用静态成员方法

@FunctionalInterface
public interface Calcable {
	int calc(int num);
}

第一种使用lambda实现

public class Demo05Lambda {
private static void method(int num, Calcable lambda) {
	System.out.println(lambda.calc(num));
  }
public static void main(String[] args) {
	method(10, n ‐> Math.abs(n));
  }
}

第二种使用Math类的abs方法引用实现

public class Demo06Lambda {
private static void method(int num, Calcable lambda) {
	System.out.println(lambda.calc(num));
  }
public static void main(String[] args) {
	method(10, Math::abs);
  }
}

通过super/this引用成员方法
定义函数式接口如下:

@FunctionalInterface
public interface Greetable {
	void greet();
}

父类Human的定义如下:

public class Human {
public void sayHello() {
	System.out.println("Hello!");
  }
}

子类的lambda调用与实现

public class Man extends Human {
	@Override
	public void sayHello() {
		System.out.println("大家好,我是Man!");
	}
	//定义方法method,参数传递Greetable接口
	public void method(Greetable g){
		g.greet();
	}
	public void show(){
	//调用method方法,使用Lambda表达式
	method(()>{
		//创建Human对象,调用sayHello方法
		new Human().sayHello();
	});
	//简化Lambda
	method(()>new Human().sayHello());
	//使用super关键字代替父类对象
	method(()>super.sayHello());
	}
}

若使用super的对象引用如下:

method(super::sayHello);

通过类的构造引用
函数式接口定义如下:

public interface PersonBuilder {
	Person buildPerson(String name);
}

类的实现如下:

public class Person {
	private String name;
	public Person(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

普通lambda的实现与调用

public class Demo09Lambda {
public static void printName(String name, PersonBuilder builder) {
	System.out.println(builder.buildPerson(name).getName());
  }
public static void main(String[] args) {
	printName("赵丽颖", name ‐> new Person(name));
  }
}

使用方法引用改造lambda

printName("赵丽颖", Person::new);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值