jdk8的新特性

  1. 常用函数式接口

有且只有一个抽象方法的接口

可以用@FunctionInterface来检测一下这个接口是不是函数式接口

使用:一般作为方法参数和返回值类型

 Lambda表达式,来作为函数式接口参数的调用就非常简洁

在这里,我们用lambda表达式来替代一个匿名内部类,这样文件在编译的时候,就不会多生成一个不必要的类,减少内存开销

这里,我始终是认为,匿名内部类有点太麻烦,写法太繁琐

  1. 函数式编程

日志:快速定位问题,对项目进行监控与优化

 Lambda特点:延迟加载

 Lambda使用前提:必须存在函数式接口

首先来看一个性能浪费的例子:

private static void log(int level, String msg) {

if (level == 1) {

System.out.println(msg);

}

上面就比如我们有三段log消息要进行拼接传给msg参数,那么这种情况就是,不管怎么样,字符串消息都会先执行拼接,然后取判断相应的等级是不是1,在去决定把它打印出来

如果用lambda就可以很好的解决这种问题,我们让一个函数式接口做参数

private  static void log(int level, MessageBuilder builder) {

if (level == 1) {

System.out.println(builder.buildMessage());

}

}

上面就会在level == 1的时候,然后调用我们采用lambda实现的方法

我们不采用lambda表达式的时候,我们采用匿名内部类的一个处理方法:

/**
 * 于日志字符串接性能浪的分析
 */
public class Demo11 {
    public static void main(String[] args) {
        String msg1 = "信息1";
        String msg2 = "信息2";
        //采用匿名类处,然后函数内部去用一个这样的接口方法
        log(1,new BuildInfo(){

            @Override
            public String ShowInfo() {
                return msg1 + msg2;
            }
        });

    }

    public static void log(int level,BuildInfo build) {
        if(level == 1) {
            System.out.println("日志信息:" + build.ShowInfo());
        }
    }
}

@FunctionalInterface
interface BuildInfo {
    String ShowInfo();
}

上面的操作,就是感觉非常繁琐

//lambda:非常核心的西:1.式接口一存在
// 2.要要接口方法主体->接口中返回什么内就必须给我做出
log(1, () -> msg1 + msg2);

上面就是lambda一看就是很简单的操作

  1. 函数式接口作为方法的返回值

public class MyComparator {

//这里就是说返回一个接口的匿名内部类

private static Comparator<String> newComparator() {

return (a, b) ‐> b.length() a.length();

}

public static void main(String[] args) {

String[] array = { "abc", "ab", "abcd" };

System.out.println(Arrays.toString(array));

Arrays.sort(array, newComparator());

System.out.println(Arrays.toString(array));

}

  1. 下面说一下常用的函数式接口:

主要是在java.util.function中被提供的函数式接口。

下面说一个Supplier接口

接口指定一个泛型,接口泛型是在我们实现这个接口的时候指定的。

当我们没有在实现接口指定,就在实例化对象的时候指定泛型

private static String getString(Supplier<String> function) {

return function.get();

}

public static void main(String[] args) {

String msgA = "Hello";

String msgB = "World";

System.out.println(getString(() ‐> msgA + msgB));

}

下面问题:

使用Supplier接口作为方法的参数类型,通过lambda表达式来求出int数组中的最大值

这里还是来说一个问题,看看下面的代码,为什么num11会给我们报错说这个变量已经存在

因为num11在用表达式的时候,又给我们重新定义了一遍。

下面看看具体代码:

public class Demo12 {
    public static void main(String[] args) {
        Test2 t2 = getTest2();
        int res = t2.add(1,2);
        System.out.println(res);
    }

    //式接口做函数参数
    public static void getTest1(Test1 test1) {
        test1.say();
    }

    //式接口作返回
    public static Test2 getTest2() {
        
       return ((num1, num2) ->{ return num1 + num2;});
    }



}

//式接口1
@FunctionalInterface
interface Test1 {
    void say();
}

//式接口2
@FunctionalInterface
interface Test2 {
    int add(int num1,int num2);
}

/**
 * 利用supplier式接口,求数组中元素最大
 */
public class Test18 {
    public static void main(String[] args) {
        int[] arr = {3, 6, 5, 9, -1, 4};
        int res = getMax(() -> {
            //数组第一元素最大
            int maxValue = arr[0];
            for(int data : arr) {
                if(data > maxValue) {
                    maxValue = data;
                }
            }
            return maxValue;
        });
        System.out.println("最大:" + res);
    }

    public static int getMax(Supplier<Integer> supplier) {
        return supplier.get();
    }
}

  1. Consumer

这个接口就是说:也只有一个函数式的接口

我们上面就可以很明显的看到,有一个接收泛型数据类型的方法,无返回值。

还有一个andThe的一个默认方法,返回的是一个带泛型的Consumer接口

下面就是一个简单的运行结果:

public static void main(String[] args) {
    //调用这个方法
    method(25,(Integer age) -> System.out.println("我的年:" + age));
}

//定义接收consumer接口的一个方法
public static void method(int age, Consumer<Integer> consumer) {
    consumer.accept(age);
}

注意上面就是说用到了一个泛型的数据类型,传入数据类型的时候注意传入的是一个泛型数据

正是因为这个函数式接口给了我们一个泛型数据类型的参数,我们就可以以各种形式来进行这个数据的消费输出。

比如我们传入一个字符串,可以把字符串进行反转之后输出

String name = new StringBuffer(name).reverse().toString();

然后直接对其输出

下面在说一下它的默认方法;andThen

作用:需要两个Consumer接口,可以把两个Consumer接口组合到一起,然后对数据进行消费,接收Consumer接口,然后返回Consumer接口

Consumer<String> con1

Consumer<String> con2

String s= “hello”

con1.accept(s);

Con2.accept(s);

下面用andthen连接两个接口,在进行消费

con1.andThen(con2).accept(s);//本身就返回一个Consumer接口,所以调用accept(s)方法

  1. 下面说一下andThen方法原理:

下面看JDK源代码:

default Consumer<T> andThen(Consumer<? super T> after) {

Objects.requireNonNull(after);

return (T t) ‐> { accept(t); after.accept(t); };

}

return (T t) ‐> { accept(t); after.accept(t); }; 这句话给剖析一下:

自己返回一个lambda表达式,其实也就是实现了内部的一个accept(T t)方法,然后又去调用accept(T t)就可以传入一个值进来

/**
 * 练习格式化打印信息
 * String[] array = { ",", "王五,", "李六,不男不女" };
 * 以姓名第一接口
 * 以性别为第二接口
 */

  1. 下面我们看一下Predicate函数式接口

里面有个and方法,JDK源代码如下:

default Predicate<T> and(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) > test(t) && other.test(t);
}

//检查字符串是否包含两个指定字符串序列
public class Test21 {
    public static void main(String[] args) {
        String str = "Most of the rain in Spain falls on the plains";
        boolean flag = judge(s -> s.contains("ain"),
                s -> s.contains("te"), str);
        System.out.println(flag);
    }

    public static boolean judge(Predicate<String> p1, Predicate<String> p2, String str) {
        return p1.and(p2).test(str);
    }

  1. return p1.and(p2).test(str);
    上面这段话来给剖析一下,这里返回的肯定是一个boolean类型的值。

首先p1.and(p2)->会返回一个Predicate接口对象,而这个对象内部是用lambda表达式来实现的。就是如下:

 return (t) > test(t) && other.test(t);
加粗的部分其实就是实现了内部的函数式接口,但是整体是返回一个对象。

  1. 下面说一下:or方法,既然有and(&&),就会有or

下面看一下JDK源代码:

default Predicate<T> or(Predicate<? super T> other) {

Objects.requireNonNull(other);

return (t) ‐> test(t) || other.test(t);

}

就是有一个条件满足就成立,与and用法一致

  1. 下面看一下negate,这个就是逻辑非(!)

还是去看一下JDK源代码:

default Predicate<T> negate() {

return (t) ‐> !test(t);

}

其实非常好理解,test(t)函数给我们测出来的结果如果为真,就把它变成假,如果为假,就把它变成真。

最后看一道实测题:

/**
 * 通过Predicate接口中and方法,进行对数据的判断,然后放到集合中
 * 集合信息的筛选,放到集合中的信息要求:
 * 1.必须男生
 * 2.姓名三个字
 */
public class Demo16 {
    public static void main(String[] args) {
        String[] arr = {"周杰, ", "李晟,男", "赵丽颖,女", "子成,"};
        //调用judge,过滤在这过滤
        List<String> list = judge(s -> s.split(",")[0].length() == 3,
                s -> s.contains(""),
                arr);
        //把集合中的数据给打印出来
        System.out.println(list);
    }

    //两个条件进行判断,两个接口
    public static List<String> judge(Predicate<String> p1, Predicate<String> p2, String[] targetArr) {
        //字符串进来之后,我们要分割进行字符串的处理
        List<String> list = new ArrayList<String>();
        //进来之后,做一个循环
        for(String res : targetArr) {
            //每次会从数组中拿出一个字符串
            //进行两个test拼接
            if(p1.and(p2).test(res)) {
                //符合条件,数据添加
                list.add(res);
            }
        }
        //做完返回
        return list;
    }
}

  1. 这里说说,function包中的Function接口

Interface Function<T,R>

根据一个数据类型来得到另外一个数据类型

R apply(T t)

这个函数式接口就是比如通过t,我们来得到r

前者T,就是一个前置条件,后者r就是我们想要的结果

比如我们把int类型转换成String类型

  • 下面看里面一个andThen连接的方法

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {

Objects.requireNonNull(after);

return (T t) ‐> after.apply(apply(t));

}

  • 下面实现对一个字符串实现多个函数的操作

String str = “周杰伦,35”;

1. 将字符串截取数字年龄部分,得到字符串;

2. 将上一步的字符串转换成为int类型的数字;

3. 将上一步的int数字累加10,并将结果变为String类型。

核心思想就是:每一步的操作结果都给下一步,作为一个前置条件

public class Demo01Function {

public static void main(String[] args) {

// TODO Auto-generated method stub

/*String str = "周杰伦,35";

String res = formatMethod1(str);

System.out.println("结果是:" + res);*/

String str = "周杰伦,35";

String res = formatMethod1_1(str,(str1) -> {

 String[] arr = str1.split(",");

return Integer.parseInt(arr[1]);

},(num) -> {

return Integer.toString(num + 10);

});

System.out.println(res);

/*String str = "123";

String res = formatMethod2(str, (str1) -> Integer.parseInt(str1),

(num) -> Integer.toString(num + 10));

System.out.println("结果:" + res);*/

}

//第一种方法

public static String formatMethod1(String str) {

String[] arr = str.split(",");

int age = Integer.parseInt(arr[1]);

age = age + 100;

String res = Integer.toString(age);

return res;

}

//第二种方法

public static String formatMethod1_1(String str,Function<String,Integer> fun1,Function<Integer,String> fun2) {

return fun1.andThen(fun2).apply(str);

}

/*

 * 需求:

         把String类型的"123",转换为Inteter类型,把转换后的结果加10

         把增加之后的Integer类型的数据,转换为String类型

 * */

public static String formatMethod2(String str,Function<String,Integer> fun1,Function<Integer,String> fun2) {

return fun1.andThen(fun2).apply(str);

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值