自学Java基础知识第十九天

day19---JDK8新特性

1. Lambda表达式

  1. Lambda本质:

   就是一个对象, 指定函数式接口的实现类对象

  1. Lambda表达式使用前提:

   接口必须是函数式接口 : 接口中只有一个抽象方法

   使用注解 @FunctionalInterface 验证和标识接口就是一个函数式接口

  1. Lambda优势:

   大大简化匿名内部类对象实现过程, 从而提高代码的运行效率

  1. Lambda表达式语法结构:

   (参数列表)->{方法体}; // 表达式整体就表示一个函数式接口的实现类对象

  1. 解释Lambda表达式语法:

   因为Lambda表达式只能用于函数式接口的实现, 接口中只有一个抽象方法, 因此Lambda表达式很明确其需要重写方法是哪个

   (参数列表) : 唯一的抽象方法中的参数列表

   -> : 称为Lambda运算符, 也称为箭头运算符

   {方法体} : 表示唯一抽象方法实现过程

  1. Lambda表达式使用的注意事项:

   a : 参数列表: Lambda表达式参数列表中, 只罗列出参数个数和变量名,不需要写参数的数据类型

1) Lambda表达式重写方法, 没有参数, 小括号就是空的 ()

2) Lambda表达式重写方法, 只有一个参数, 小括号可以省略

3) Lambda表达式重写方法, 有多个参数, 按照方法的参数列表, 列出参数名称即可

   b : 方法体:

1) Lambda表达式重写方法体, 只有一句表达式, 那么大括号可以省略

2) Lambda表达式重写方法体, 只有一句代码逻辑, 并且这一句就是方法返回值结果, 那么大括号和return关键字可以同时省掉

代码

package com.ujiuye.lambda;

public class Demo01_Lambda表达式语法结构 {

public static void main(String[] args) {

// 1.匿名内部类对象实现接口Inter1

new Inter1() {

@Override

public void fun() {

System.out.println("匿名内部类对象实现方法fun");

}

}.fun();

// 2. Lambda表达式实现接口Inter1

// (参数列表)->{方法体};// 接口Inter1的实现类对象

Inter1 t = ()->{

System.out.println("Lambda表达式实现方法fun");

};

t.fun();

}

}

@FunctionalInterface

interface Inter1{

public abstract void fun();

}

package com.ujiuye.lambda;

public class Demo02_Lambda表达式使用注意事项 {

public static void main(String[] args) {

        // 1. 匿名内部类对象实现Inter2

new Inter2() {

@Override

public void print(String s) {

System.out.println(s + "----");

}

}.print("hello");

// 2. Lambda表达式实现Inter2

Inter2 i1 = (a)->{

System.out.println(a + "++++");

};

i1.print("world");

// 3. Lambda表达式实现方法只有一个参数, 参数小括号可以省略

Inter2 i2 = y -> {System.out.println(y + "--啦啦啦啦");};

i2.print("123");

// 4. Lambda表达式实现方法有多个参数

Inter3 i3 = (a,b)->{

System.out.println(a + b);

};

i3.getSum(3, 5);// 8

// 5. Lambda表达式实现方法体只有一句代码逻辑, 大括号可以省略

Inter2 i4 = y->System.out.println(y);

i4.print("省略大括号");

// 6. Lambda表达式实现接口Inter4

Inter4 i5 = (x,y)->{

boolean boo = x == y;

System.out.println(boo);

return boo;

};

i5.equal(3, 4);// false

// 7. Lambda表达式实现接口Inter4, 方法带有返回值类型, 大括号省掉

Inter4 i6 = (a,b)->a==b;

System.out.println(i6.equal(3, 3));// true

}

}

@FunctionalInterface

interface Inter2{

public abstract void print(String s);

}

@FunctionalInterface

interface Inter3{

public abstract void getSum(int a, int b);

}

@FunctionalInterface

interface Inter4{

public abstract boolean equal(int a, int b);

}

2. 函数式接口

2.1 函数式接口的概述

  1. 函数式接口介绍:

   函数式接口中只有一个抽象方法, 使用注解@FunctionalInterface 验证和标识接口就是一个函数式接口 , Lambda表达式只能实现函数式接口

  1. 函数式接口存在意义:

   Java代码定义方法功能, 根据方法实现情况, 需要参数列表, Java方法中的参数列表 数据类型  变量名, 数据类型 变量名..., 方法参数都必须带有指定数据类型 , 但是有一些方法实现过程中, 不仅仅需要固定类型参数, 也需要对于数据的处理方式(处理思想)

   举例 : 定义出一个方法功能, 可以操作两个int类型整数, 实际操作过程按照客户实际需要进行

   客户1 : 做两个整数求和

   客户2 : 做两个整数求差

   客户3 : 做两个整数求乘

...

 分析 : 目前方法功能, 不仅仅需要两个整数int, 同时需要提供对各两个数据操作的想法,思想

   数据处理思想,方式, 其实就是对应Java中的一个方法功能,但是Java中不支持将方法作为参数传递, 于是将这个方法封装在一个接口中, 将接口作为方法参数传递, 相当于在传递接口中方法, 传递对于数据的处理思想, 因此函数式接口是为了封装某一个方法而存在

2.2 JDK提供常用内置函数式接口

JDK8版本开始, 提供了4个常用函数式接口, 在不同的数据思想操作层面上, 可以直接使用对应的函数式接口, 而不需要自定义, 函数式接口都来自于 java.util.function

  1. 消费型接口:

   Consumer<T>

    void accept(T t) : 提供T类型参数, 在方法中对于参数t进行消费, 消费指对于数据t的处理方式

  1. 供给型接口:

   Supplier<T>

    T get() : get方法就是可以返回一个T类型结果, 不需要任何参数

  1. 函数式接口:

  Function<T,R>

   R apply(T t) : apply方法通过参数t, 获取到结果R

  1. 断言型接口:

  Predicate<T>

   boolean test(T t) : test 方法提供参数t, 验证这个参数是否符合某种规则

2.3 消费型接口

Consumer<T>

    void accept(T t) : 提供T类型参数, 在方法中对于参数t进行消费, 消费指对于数据t的处理方式; 如果定义一个方法功能, 不仅仅需要参数t, 还需要对于参数t类型数据的消费方式, Consumer可以作为方法参数进行传递

案例 : 定义出一个方法功能, 客户预计消费500元现金, 每一个客户对于500元的消费都不同, 将客户的消费方式实现出来

1) 客户1 : 花了500, 买了一把大宝剑

2) 客户2 : 花了500, 买了一堆化妆品

3) 客户3 : 花了500, 买了一双球鞋

4) .... 还有无限的客户有不同种消费方式

代码

package com.ujiuye.function;

import java.util.function.Consumer;

public class Demo01_消费型接口 {

public static void main(String[] args) {

// 客户1 : 花了500, 买了一把大宝剑

int money = 500;

// 调用testConsumer方法, 第二个参数需要一个Consumer接口的实现类

// 使用Lambda表达式作为Consumer接口的实现类对象

Consumer<Integer> con = x->System.out.println("花了"+x+",买了一把大宝剑");

testConsumer(money,con);

// 2.客户2 : 花了500, 买了一堆化妆品

Consumer<Integer> con1 = x->{

if(x >= 500) {

System.out.println("不能消费,超出预算");

}else {

System.out.println("花了"+x+",买了一堆化妆品");

}

};

testConsumer(400,con1);

}

/*案例 : 定义出一个方法功能, 客户预计消费500元现金, 每一个客户对于500元的消费都不同,

将客户的消费方式实现出来

1)客户1 : 花了500, 买了一把大宝剑

2)客户2 : 花了500, 买了一堆化妆品

3)客户3 : 花了500, 买了一双球鞋

4).... 还有无限的客户有不同种消费方式*/

/*

 *  分析 :

 *   1) 设定出第一个参数, 表示客户消费的金额

 *   2) 设定出第二个参数, 需要 给出客户对于金额消费方式, 提供Consumer<T>, T泛型表示需要消费的数据

 *

 * */

public static void testConsumer(int money,Consumer<Integer> con) {

con.accept(money);

}

}

2.4 供给型接口

Supplier<T>

    T get() : get方法就是可以返回一个T类型结果, 不需要任何参数 ; 如果定义一个方法功能时, 不需要参数, 只需要获取到某一个类型的数据, 而获取方式多种多样, 可以将Supplier类型作为方法参数传递, 实际传递的是获取某类型数据的思想

案例 : 定义出一个方法功能, 能给客户创建出一个ArrayList<Integer>类型容器, 容器中装几个数据由客户决定, 容器中承装的数据有什么规律, 由客户决定, 方法主要给客户返回一个符合客户要求的容器

1) 客户1 : 5个数据, 都是30-80之间的随机数

2) 客户2 : 8个数据, 1-100之间的随机偶数

3) ...

代码

package com.ujiuye.function;

import java.util.ArrayList;

import java.util.Random;

import java.util.function.Supplier;

public class Demo02_供给型接口 {

public static void main(String[] args) {

         // 1) 客户1 : 5个数据, 都是30-80之间的随机数

Supplier<Integer> sup = ()->new Random().nextInt(51)+30;

System.out.println(testSupplier(5,sup));

 

// 2) 客户2 : 8个数据, 1-100之间的随机偶数

Supplier<Integer> sup1 = ()->{

Random ran = new Random();

while(true) {

int number = ran.nextInt(100)+1;

if(number % 2 == 0) {// 偶数

return number;

}

}

};

System.out.println(testSupplier(8,sup1));

}

 

/*案例 : 定义出一个方法功能, 能给客户创建出一个ArrayList<Integer>类型容器,

容器中装几个数据由客户决定, 容器中承装的数据有什么规律是什么, 由客户决定,

方法主要给客户返回一个符合客户要求的容器

1)客户1 : 5个数据, 都是30-80之间的随机数

2)客户2 : 8个数据, 1-100之间的随机偶数

3)...*/

 

/*

 *  分析 :

 *  1. 设计出第一个参数, int n, n就表示容器中装数据个数

 *  2.设计出第二个参数, Supplier<Integer>

 *      Integer get()

 * */

 

public static ArrayList<Integer> testSupplier(int n,Supplier<Integer> sup){

ArrayList<Integer> list = new ArrayList<>();

for(int i = 1; i <= n; i++) {

// 需要获取一个Integer类型数据

list.add(sup.get());

}

return list;

}

}

 

 

 

2.5 函数型接口

Function<T,R>

   R apply(T t) : 如果定义一个方法功能时, 目前有参数T t, 需要根据t获取到另外一个类型结果R, 那么Function可以作为方法参数传递

   Function andThen(Function fun1) :

   Function<T,R> fun

   Function<R,W> fun1

   fun.andThen(fun1) : 需求目前具有T数据, 目标数据W, 可是转换过程中, 直接将T类型转换成W比较困难, 于是具有T,先转换成R类型, 再由R转换成W类型, andThen方法功能, 将方法调用者fun的结果R, 作为参数fun1的第一个泛型尽量连续的转换计算

 

 

 案例1 : 定义一个方法功能, 根据整数x,计算出对应整数y, x数据由客户给出, y数据的计算方式根据客户要求决定

1) 客户1 : yx2

2) 客户2 : yx相等

3) 客户3 : yx的平方

4) ...

 

 案例2 : 定义一个方法功能, 根据字符串x,计算出对应整数y, x数据由客户给出, y数据的计算方式根据客户要求决定

5) 客户4 : x”6” , 计算出x转换成整数后, 2倍结果

6) 客户5 : x”-2”, 计算出x转换成整数后, +1的结果

...

 

代码

package com.ujiuye.function;

import java.util.function.Function;

public class Demo03_函数型接口 {

public static void main(String[] args) {

// 客户1 : yx2

Function<Integer,Integer> fun = x->x*2;

System.out.println(testFunction(5,fun));// 10

 

// 客户2 : yx相等

Function<Integer,Integer> fun1 = x->x;

System.out.println(testFunction(99,fun1));// 99

 

// 客户3 : yx的平方

Function<Integer,Integer> fun2 = x->x*x;

System.out.println(testFunction(3,fun2));// 9

 

// 客户4 : x为”6, 计算出x转换成整数后, 2倍结果

Function<String,Integer> fun4 = x->Integer.parseInt(x);

Function<Integer,Integer> fun5 = x->x*2;

System.out.println(testFunction1("6",fun4,fun5));// 12

 

// 客户5 : x为”-2, 计算出x转换成整数后, +1的结果

Function<Integer,Integer> fun6 = x->x+1;

System.out.println(testFunction1("-2",fun4,fun6));// -1

}

 

/*案例 : 定义一个方法功能, 根据整数x,计算出对应整数y, x数据由客户给出,

y数据的计算方式根据客户要求决定

1)客户1 : yx2

2)客户2 : yx相等

3)客户3 : yx的平方

4)...*/

 

/*

 *   分析 :

 *   1. 设计出一个参数, 就表示目前拥有整数x

 *   2.设计出第二个参数,  Function<Integer,Integer>

 *     Integer apply(Integer)

 * */

 

public static int testFunction(int x, Function<Integer,Integer> fun) {

return fun.apply(x);

}

 

 

/*案例2 : 定义一个方法功能, 根据字符串x,计算出对应整数y, x数据由客户给出,

y数据的计算方式根据客户要求决定

5)客户4 : x为”6, 计算出x转换成整数后, 2倍结果

6)客户5 : x为”-2, 计算出x转换成整数后, +1的结果

...*/

 

public static int testFunction1(String x,Function<String,Integer> fun,

Function<Integer,Integer> fun1) {

    return fun.andThen(fun1).apply(x);

}

}

 

 

2.6 断言型接口

Predicate<T>

   boolean test(T t) : 如果现在具有类型数据T t, 验证这个t是否规则, 可以将Predicate作为方法参数传递

   有两个默认方法功能:

   Predicate and(Predicate pre) : 表示将方法调用者需要规则与方法参数pre的规则, &&判断

   Predicate or(Predicate pre) : 表示将方法调用者需要规则与方法参数pre的规则, ||判断

 

案例1 :

  定义出一个方法, 需要客户提供一个容器ArrayList<Integer>, 通过客户的意愿, 将容器中符合条件的数据筛选出来, 将筛选出的数据放置在新容器中返回给客户

1) 客户1 : 要求容器中的所有数, 都能被2整除

2) 客户2 : 要求所有的数据都不大于100

3) 客户3 : 要求所有数据都小于100, 并且都是奇数

4) 客户4 : 要求所有数据或者小于100, 或者是偶数

5) ...

代码

package com.ujiuye.function;

import java.util.ArrayList;

import java.util.function.Predicate;

public class Demo04_断言型接口 {

public static void main(String[] args) {

ArrayList<Integer> list = new ArrayList<>();

list.add(12);

list.add(15);

list.add(120);

list.add(111);

        // 1. 客户1 : 要求容器中的所有数, 都能被2整除

Predicate<Integer> pre = x->x % 2 == 0;

System.out.println(testPredicate(list,pre));

// 2. 客户2 : 要求所有的数据都不大于100

Predicate<Integer> pre1 = x->x <= 100;

System.out.println(testPredicate(list,pre1));

// 3.客户3 : 要求所有数据都小于100, 并且都是奇数

// Predicate<Integer> pre2 = x->x < 100 && x % 2 != 0;

Predicate<Integer> pre3 = x->x < 100;

Predicate<Integer> pre4 = x->x % 2 != 0;

System.out.println(testPredicate(list,pre3.and(pre4)));

// 4.客户4 : 要求所有数据或者小于100, 或者是偶数

System.out.println(testPredicate(list,pre3.or(pre)));

}

/*案例 :

  定义出一个方法, 需要客户提供一个容器ArrayList<Integer>,

  通过客户的意愿, 将容器中符合条件的数据筛选出来, 将筛选出的数据放置在新容器中返回给客户

1)客户1 : 要求容器中的所有数, 都能被2整除

2)客户2 : 要求所有的数据都不大于100

3)客户3 : 要求所有数据都小于100, 并且都是奇数

        4)客户4 : 要求所有数据或者小于100, 或者是偶数

...*/

/*

 *  分析 :

 *   1. 设计出第一个参数, ArrayList<Integer>, 就表示客户提供原始集合数据

 *   2. 设计出第二个参数, Predicate<T>

 *      boolean test(T t)

 *

 * */

public static ArrayList<Integer> testPredicate(ArrayList<Integer> list,Predicate<Integer> pre){

 ArrayList<Integer> newList = new ArrayList<>();

 for(Integer i : list) {

if(pre.test(i)) {// 符合规则

newList.add(i);

}

 }

 return newList;

}

}

2.7 方法引用

Lambda表达式 : 作为一个函数式接口实现类对象存在

如果使用Lambda表达式实现一个函数式接口, Lambda(方法体) 已经被某个类型中某个方法实现过了, 那么可以直接引用这个已经实现的方法作为Lambda表达式实现过程

(Lambda表达式实现一个接口时, A方法已经将需要的实现过程完成好了, 不需要再次将重复的代码在Lambda体中再写一遍, 只需要调用A方法即可)

注意 : Lambda表达式参数列表和方法体实现上都需要与A方法保持一致

方法引用:

1) 函数式接口名 变量 = 类对象 :: 方法名;

2) 函数式接口名 变量 = 类名 :: 方法名;

代码

package com.ujiuye.function;

public class Demo05_方法引用 {

public static void main(String[] args) {

        // 1. 使用Lambda表达式实现函数式接口My

My my = s->System.out.println(s);

my.print("hello");

// 2. 使用方法引用实现函数式接口

My my1 = System.out :: println;

my1.print("123-------");

// 3.  使用方法引用实现函数式接口

// My my2 = s->System.out.println(Integer.parseInt(s)+1);

My my2 = new Print1() :: print;

my2.print("115");// 116

// 4. 使用方法引用实现函数式接口

My my3 = Print2 :: print2;

my3.print("10");// 15

}

}

class Print2{

public static void print2(String s) {

System.out.println(Integer.parseInt(s)+5);

}

}

class Print1{

public void print(String s) {

System.out.println(Integer.parseInt(s)+1);

}

}

@FunctionalInterface

interface My{

public abstract void print(String s);

}

3. StreamAPI

  1. JDK8版本, Collection单列集合顶层父接口中, 添加一个方法功能stream()

1) stream() : 表示获取到当前集合的流资源, 返回值类型Stream接口, 证明stream方法返回Stream的一个实现类对象

2) Stream<T>接口 : 表示流资源, 主要是可以对于单列集合进行操作,访问,筛选相关操作, Stream中的泛型数据就是当前操作集合中的数据类型

  1. Stream中常用方法:

1) filter(Predicate<T> pre) : 功能将集合中的每一个数据通过参数pre给出的规则进行筛选, 返回值类型Stream<T> , 因此方法调用完毕还可以链式调用

2) forEach(Consumer<T> con) : 功能遍历Stream中的操作的集合中数据, 参数需要消费型接口, 一边遍历还可以对遍历出元素进行消费性操作

3) count() : 表示获取到流资源中操作的元素个数, 返回值类型long

4) skip(long n) : 表示跳过流资源中的前n个数据, 之后的数据继续进行操作, 返回值类型Stream<T> , 因此方法调用完毕还可以链式调用

5) limit(long n) : 表示保留流资源中前n个数据,之后不操作,返回值类型Stream<T> , 因此方法调用完毕还可以链式调用

6) map(Function<T,R> fun) : 将流资源中所有的T类型数据转换成R类型数据,返回值类型Stream<T> , 因此方法调用完毕还可以链式调用

案例 : 定义出一个ArrayList<String>, 集合中存储人物的姓名

1) 将集合中所有姓张的, 名字有3个字的人名获取到, 并遍历

2) 跳过前三个人名, 剩下的所有姓张的,名字有2个字的人名获取到, 并遍历

3) 只留取集合中的前2个人名, 筛选出姓张的人名, 并遍历

代码

package com.ujiuye.stream;

import java.util.ArrayList;

public class StreamDemo {

public static void main(String[] args) {

// 案例 : 定义出一个ArrayList<String>, 集合中存储人物的姓名

       //1. 定义出一个带有人物名字集合

ArrayList<String> list = new ArrayList<>();

list.add("张无忌");

list.add("6");

list.add("赵敏");

list.add("周芷若");

list.add("小昭");

list.add("张三丰");

list.add("张成");

// 1)将集合中所有姓张的, 名字有3个字的人名获取到, 并遍历

// filter(Predicate<T> pre) : 功能将集合中的每一个数据通过参数pre给出的规则进行筛选

list.stream().filter(x->x.startsWith("")&&x.length()==3).forEach(

System.out :: println);

System.out.println(list.stream().count());// 7

// 2)跳过前三个人名, 剩下的所有姓张的,名字有2个字的人名获取到, 并遍历

list.stream().skip(3).filter(x->x.startsWith("")&&x.length()==2).forEach(

x->System.out.println(x));

// 3) 只留取集合中的前2个人名, 筛选出姓张的人名, 并遍历

list.stream().limit(2).filter(x->x.startsWith("")).forEach(System.out :: println);

// 4) map(Function<T,R> fun) : 将流资源中所有的T类型数据转换成R类型数据,返回值类型Stream<T> , 因此方法调用完毕还可以链式调用

list.stream().map(x->x+1).forEach(System.out :: print);

}

}

原文地址:https://www.cnblogs.com/masterhxh/p/13629056.html