前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JDK8 超详细,肝

JDK8 超详细,肝

作者头像
Java_慈祥
发布2024-08-06 14:36:05
1400
发布2024-08-06 14:36:05
举报
文章被收录于专栏:Web前后端、全栈出发

Java8 新特性

  • JDK1.8已经发布很久了,在很多企业中都已经在使用
  • 虽然,JDK是向下兼容的不会新特性也一样可以正常开发,但是作为程序员还是要不断更新新的技术. 不要求啥都会,但要求能看懂!😘

Java 8 是oracle公司于2014年3月发布

  • 是自Java 5 以 来最具革命性的版本
  • Java 8为Java语言: 编译器、类库、开发 工具与JVM带来了大量新特性.

Lambda表达式

函数式编程

  • Lambda表达式,最早是Python 的语法,简洁优美,一行代码就是一个方法~ 但,说实话可读性 并不是很好 第一次看到这个时候我都懵😵了. 为了不被同事嘲讽,连夜学习了JDK8
  • Lambda 表达式,也可称为闭包 Java 8 发布的最重要新特性 闭包: 闭包,就是能够读取其他函数内部变量的函数,例如JS中,只有函数内部的子函数才能读取局部变量 所以:闭包,可以理解成 “定义在一个函数内部的函数“
  • Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中 👍
基本语法:
代码语言:javascript
复制
/**Lambda语法:*/
	(参数列表) -> { 代码块 }

/**说明:*/
/*Lambda表达式是前提基于:函数式接口来实现的: 只包含一个抽象方法的接口,称为函数式接口 */
	左侧:指定了 Lambda 表达式需要的参数列表
	右侧:指定了 Lambda 体,是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能

Lambda实例:

LambdaTest.Java

  • 以 Runnable接口举例: lambda表达式, 就是 匿名实现类 的,另一种优化声明方式:
代码语言:javascript
复制
@Test
public void test1(){
/** JDK8之前,定义Runnable 接口实现多线程.*/
    //1.  类——>实现Runnable接口,创建实例...填入Thread(Runnable r); .start(); 启动线程
    //2.  匿名内部类方式,获取Runnable 接口实例: 创建一个接口实例
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("匿名内部类实现Runnable 接口实例");
        }
    };
    Thread thread1 = new Thread(runnable);
    thread1.start();

/**JDK8之后,定义Runnable Lambda接口实现多线程.*/
    //Lambda
    //  ->左侧: 指定了 Lambda 表达式需要的参数列表, 这里参数列表是空
    //  ->右侧: 指定了 Lambda 体,是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能;
    Runnable runnable2 = () -> { System.out.println("Lambda实现Runnable 接口实例");};
	Thread thread2 = new Thread(runnable2);
    thread2.start();
    
    //Lambda 优化:
    //  lambda形参列表的参数类型可以省略(类型推断), 如果lambda形参列表只有一个参数,  其一对()也可以省略
    //  lambda体应该使用一对{}包裹, 如果lambda体只有一条执行语句,可能是return语句, 可以省略这一对{}和return关键字.
    Runnable runnable3 = () -> System.out.println("Lambda优化实现Runnable 接口实例");
    Thread thread3 = new Thread(runnable3);
    thread3.start();
    
    System.out.println();
    System.out.println("创建的 Runnable接口实例,正常使用!");
}

控制台结果集:

代码语言:javascript
复制
匿名内部类实现Runnable 接口实例
Lambda实现Runnable 接口实例

创建的 Runnable接口实例,正常使用!
Lambda优化实现Runnable 接口实例

练习:LambdaTest.Java

代码语言:javascript
复制
@Test
public void test2(){
    /** 练习: Comparator 定制排序
         *      Comparator接口,也是一个 "函数式接口": 只包含一个抽象方法的接口,称为函数式接口
         */
        /** JDK8之前,定义Runnable 接口实现多线程.*/
        System.out.println("自然排序/定制排序: 比较基本/引用数据类型,A>B=1 A<B=-1 A==B=0");
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
        System.out.println("定制排序1: "+com1.compare(1, 2));


        /**JDK8之后,定义Runnable Lambda接口实现多线程.*/
        //Lambda
        Comparator<Integer> com2 = (Integer o1, Integer o2) -> { return Integer.compare(o1,o2); };
        System.out.println("定制排序2: "+com2.compare(3, 2));

        //Lambda 优化:
        //类型推断: 省略类型
        //如果lambda体只有一条执行语句,可能是return语句, 可以省略这一对{}和return关键字.
        Comparator<Integer> com3 = (o1,o2) ->  Integer.compare(o1,o2);
        System.out.println("定制排序3: "+com3.compare(4, 3));

        /** 方法引用 */
        Comparator<Integer> com4 = Integer :: compare;      //后面介绍
        System.out.println("定制排序4: "+com4.compare(2, 2));
}
代码语言:javascript
复制
自然排序/定制排序: 比较基本/引用数据类型A>B=1 A<B=-1 A==B=0
定制排序1: -1
定制排序2: 1
定制排序3: 1
定制排序4: 0

总结👍:

Lambda表达式:依赖于函数式接口, 是对函数式接口的,另一种:实例化形式~👍 更简洁,难懂🙃

->左侧: 指定了 Lambda 表达式需要的参数列表

  • lambda形参列表的参数类型可以省略 (类型推断)
  • 如果,lambda形参列表只有一个参数, 其一对 ()也可以省略

->右侧: 指定了 Lambda 体,是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能

  • lambda体应该使用一对{}包裹
  • 如果lambda体只有一条执行语句,可能是return语句, 可以省略这一对{}和return关键字. 省略return时候也要省略 {}

场景:

  • 可以在,将 函数式接口的实例作 自定义方法的入参进行传递,完成一些方法内部方便操作…
  • 直接定义函数式接口,调用内部的方法完成某些操作~

函数式接口:

只包含一个抽象方法的接口,称为函数式接口

  • JDK8.0 可以通过Lambda表达式,来创建该接口的对象~ 所有的 函数式接口 都可以通过Lambda表达式类进行 实例化~

@FunctionalInterface 注解

  • JDK8.0提供一个注解来标识管理: 该注解, 可以检 查它是否是一个函数式接口,同时 Javadoc 也会包含一条声明,说明这个 接口是一个函数式接口.
  • JDK8 Java.util.function包下定义了Java 8 的丰富的函数式接口 为了方便不同情况的,lambda表达式的使用场景~

函数式接口实例:

Runnable接口举例:ctrl+单机 进入源码:

  • 一个接口,只有一个abstract抽象方法 @FunctionalInterface 注解 修饰.

自定义函数式接口:

WsmInterface.Java

代码语言:javascript
复制
/** 注解可以省略,没有影响,注解只是对程序编写的一个提醒标识~
 *  提示你: 这是一个函数式接口! */
@FunctionalInterface
public interface WsmInterface {
    public abstract void show();
}

Java.util.function 包:

JDK8.0 之后专门为了,Lambda 不同场景提供的不同的函数式接口

Java 内置四大核心函数式接口:

函数式接口

参数类型

返回类型

用途

Consumer< T > 消费型接口

T

void

对类型为T的对象应用操作:void accept(T t)

Supplier< T >供给型接口

T

返回类型为T的对象:T get()

Function<T, R>函数型接口

T

R

对类型为T的对象应用操作,并返回是R类型的对象.R apply(T t)

Predicate< T >断定型接口

T

Boolean

确定类型为T的对象是否满足某约束, 返回boolean 值boolean test(T t)

其他接口:

四大核心函数式接口:

LambdaTest2.Java

代码语言:javascript
复制
import org.junit.Test;
import java.util.function.Consumer;

/** 四大核心函数式接口Function */
public class LambdaTest2 {
/** 正常情况下,函数式接口 实例,当作方法参数传递在方法中完成事情~ */

/** 消费型接口 Consumer<T>     void accept(T t) */
    /** ① 声明一个方法传入Consumer<T> 对象实例:  */
    public void con(Double money, Consumer<Double> con){        //<T> 泛型规范了传入的类型~ Double~
        /** ②方法内,使用Consumer<T>类型参数,调用,它对应的方法,还有自己方法内部的一下操作~  */
        System.out.println("con方法调用~");
        con.accept(money);
        System.out.println("");
    };
/** ③实现 */
    /** J8前 */
    @Test
    public void ConsumerTest(){
        //要传入的参数!
        Double dd = 540.0;
        System.out.println("调用 con(Double,Consumer<Double>) 方法");

        //方式一 创建函数式接口的对象,传入接口的实例: (创建方式,匿名内部类~
        Consumer<Double> con1 = new Consumer<Double>() {
            @Override
            public void accept(Double aDouble) {
                System.out.println("接口实例类,参数传递实现: 身上还有"+aDouble+"块钱!");
            }
        };
        //调用方法
        this.con(dd,con1);

        //方式二: 参数匿名内部类实现
        this.con(dd, new Consumer<Double>() {
            @Override
            public void accept(Double adouble) {
                System.out.println("匿名内部类实现: 身上还有"+adouble+"块钱!");
            }
        });
    }

    /** J8后 */
    @Test
    public void ConsumerTest2(){
        //要传入的参数!
        Double dd = 540.0;

        //JDK8 后Lambda表达式,对 参数匿名内部类 的升级
        /** 调用 con(Double,Consumer<Double>) 方法 */
        this.con(dd, (adouble) -> System.out.println("Lambda表达式实现: 身上还有"+adouble+"块钱!") );
    }
}
代码语言:javascript
复制
# ConsumerTest  运行
调用 con(Double,Consumer<Double>) 方法
con方法调用~
接口实例类,参数传递实现: 身上还有540.0块钱!

con方法调用~
匿名内部类实现: 身上还有540.0块钱!

# ConsumerTest2 运行
con方法调用~
Lambda表达式实现: 身上还有540.0块钱!
  • Java.util.function 包下就是,JDK8为了方便用户操作,二提供的一系列的 函数式接口
  • Consumer< T > 就是一种 函数式接口,可以 定义一个方法,使用该类型 Consumer<T> 作为参数进行方法实现… 完成一些操作.

方法/构造器/数组 引用:

一种更加 高级 的Lambda表达式 的表现形式 本质上就是一种Lambda表达式的 “语法糖”🍬

  • 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
  • 要求: 实现接口的抽象方法的 参数列表返回值 类型,必须与方法引用的方法的参数列表和返回值类型保持一致!👍
  • 语法格式: 使用操作符 :: 将类(或对象) 与 方法名分隔开来
  • 三种场景: 对象 :: 实例方法名 类 :: 静态方法名 类 :: 实例方法名

总结:👍

实现接口的抽象方法的 参数列表返回值 类型, **须与方法引用的方法的参数列表和返回值类型保持一致!👍

代码语言:javascript
复制
也可以理解,方法引用就是更加,简化了Lambda的操作,如果有一个类已经实现了 “函数式接口的方法”`

假设有一个函数式接口A
	函数式接口A
		内部方法 af();	

lambda表达式创建 A 的实例
	A a = () -> { 方法内部的操作... }
	
如果 函数式接口 内部的方法,已经有一个B类bf()对其进行了实现,则可以在方法内部直接通过: B对象.bf(); 一行完成方法的调用
	A a = () -> { 
    	B b = new B();
    	b.bf();
    }
方法引用 简化升级: 
	B b = new B();		创建B类的 对象;
	A a = b::bf;		放法引用的 对象::实例方法名 引用;	 省略方法的参数列表...		

注意:

  • 实现接口的抽象方法的 参数列表返回值 类型,必须与方法引用的方法的参数列表和返回值类型保持一致!👍 这样: 对象/类 :: 方法名 后面不需要跟着 (参数列表) ,因为函数式接口的方法, 和 实现类方法的 “参数列表一致可以省略...”

对象 :: 实例方法名

com.wsm.met 包下:

A 接口

代码语言:javascript
复制
/** 自定义函数式: */
@FunctionalInterface
public interface A {
    //上面实例是一个 无参, 这里定义一个有参的方法();
    public void af(int i);
}

B类 实现

代码语言:javascript
复制
/** 自定义类,实现函数式接口 */
public class B {
    //方法参数列表,与函数式接口相同~
    public void bf(int i){
        System.out.println("对象::实例方法引用 参数列表i="+i);
    }
}

MethodTest类 方法引用测试类

代码语言:javascript
复制
import org.junit.Test;
/** 方法引用测试类 */
public class MethodTest {
/** JDK1.8之前创建A接口实例: */
    @Test
    public void Test(){
        A a = new A() {
            @Override
            public void af(int i) {
                System.out.println("JDK8之前实现接口~ 参数列表i="+i);
            }
        };

        a.af(1);
    }

/** Lambda表达式创建A接口实例: */
    @Test
    public void Test2(){
        //Lambda表示式实现A接口:
        A a =  i ->  System.out.println("Lambda表达式实现接口 参数列表i="+i);

        int i = 2;
        a.af(i);
    }

/** 对象 :: 非静态方法 */
    /** 升级Lambda表达式: 方法引用 */
    @Test
    public void Test3(){
    //① 创建B  类对象;
        B b = new B();
    //② 方法引用:
        A a = b::bf;
    //③ 调用方法~
        a.af(3);
        /** 因为 af(i) 和 bf(i) 方法实现`af实现的操作bf已经完成了` 返回值 参数列表相同~  */
        /** 则满足方法引用,直接使用! */
    }
}

cmd

代码语言:javascript
复制
Test
JDK8之前实现接口~ 参数列表i=1

Test2
Lambda表达式实现接口 参数列表i=2

Test3
对象::实例方法引用 参数列表i=3

类 :: 静态方法名

对象 :: 实例方法名 Demo测试扩展~

B类 扩展

代码语言:javascript
复制
//类的静态方法, bsf(int i);
public static void bsf(int i){
    System.out.println("类::static静态方法引用 参数列表i="+i);
}

MethodTest类 扩展

代码语言:javascript
复制
/** 类 :: 静态方法 */
@Test
public void Test4(){
    System.out.println("Test4");
    //① 方法引用:
    A a = B::bsf;           //直接通过 类::匹配的静态方法()~
    //② 调用方法~
    a.af(4);
}

cmd

代码语言:javascript
复制
Test4
类::static静态方法引用 参数列表i=4
总结👍
  • 对象 :: 非静态方法类 :: 静态方法
  • 实现都类似, 一个通过 对象.实例方法~ 一个 通过类.静态方法 而,类.实例方法 有点不同~

类 :: 实例方法名

MethodTest.Java

代码语言:javascript
复制
/** 类 :: 实例方法  (有难度) */
    /** 以Comparator 方法举例: int comapre(T t1,T t2) 方法 */
    /** String中的int t1.compareTo(t2) */
    @Test
    public void Test5(){
        System.out.println("Test5");
        /** lambda表达式实现 */
        Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
        System.out.println("lambda比较两个字符串大小~"+com1.compare("abc","abd"));

        Comparator<String> com2 = String :: compareTo;
        System.out.println("方法引用: 类 :: 实例方法 比较两个字符串大小~"+com2.compare("abc","abd"));

        /**
         * 如果:
         *  A 函数式接口的的实现来源于~
         *    af(T1,T2);   T1类.方法(T2); 实现,则属于 类::实例方法;  的方法引用~
         * */
    }

cmd

代码语言:javascript
复制
Test5
lambda比较两个字符串大小~-1
方法引用: 类 :: 实例方法 比较两个字符串大小~-1

类::实例方法 的方法引用, 需要通过, 函数式接口的方法(T1,T2) 参数列表: T1类型.实例方法(T2参数); 完成“函数式接口的实现!”

构造器引用

B.Java 扩展

代码语言:javascript
复制
public String name = "default";

public B(){
    System.out.println("B() 无参构造");
}

public B(String name) {
    System.out.println("B(name,age) 有参构造");
    this.name = name;
}
//省略get/set

ConstructorRefTest.Java

代码语言:javascript
复制
/** 构造器引用 */
    //JDK8提供的函数式接口 Supplier中的T get()  返回一个 T 类型对象
    //B 类的空参构造器: B()
    public void Test(){
        /** JDK8之前 */
        Supplier<B> supB = new Supplier<B>() {
            @Override
            public B get() {
              return new B();
            };
        };
        System.out.println("JDK8之前");
        System.out.println("无参默认创建的对象: supB.get().getName();"+supB.get().getName());

        /** Lambda构造器引用 */
        Supplier<B>  supB1 = () -> new B();
        System.out.println("Lambda创建的对象: supB.get().getName();"+supB1.get().getName());

        Supplier<B>  supB2 = B :: new;
        System.out.println("构造器引用创建的对象: supB.get().getName();"+supB2.get().getName());
    }

/** 构造器引用2.0 */
    //Function中的R apply(T t);   Function<T, R>函数式接口,对类型为T的对象应用操作,并返回是R类型的对象
    @Test
    public void Test2(){
        System.out.println("Lambda创建的对象:");
        Function<String ,B> func1 = name -> new B(name);
        System.out.println("Lambda创建的对象: supB.get().getName()= "+func1.apply("wsm1").getName());

        System.out.println("*******\n");

        System.out.println("构造器引用创建的对象:");
        Function<String ,B> func2 = B :: new;           //参数自动匹配~
        System.out.println("Lambda创建的对象: supB.get().getName()= "+func2.apply("wsm2").getName());
    }

CMD

代码语言:javascript
复制
Test
JDK8之前
B() 无参构造
无参默认创建的对象: supB.get().getName();default
B() 无参构造
Lambda创建的对象: supB.get().getName();default
B() 无参构造
构造器引用创建的对象: supB.get().getName();default


Test2
Lambda创建的对象:
B(name,age) 有参构造
Lambda创建的对象: supB.get().getName()= wsm1
*******

构造器引用创建的对象:
B(name,age) 有参构造
Lambda创建的对象: supB.get().getName()= wsm2
总结:👍
  • 构造器引用,就是 与函数式接口相结合,自动与函数式接口中方法兼容
  • 可以把构造器引用赋值给定义的方法

注意:

  • 要求构造器参数列表要与接口中抽象 方法的参数列表一致! 且方法的返回值即为构造器对应类的对象

格式: ClassName::new

数组引用:

与构造器引用类似 不详细介绍了...

代码语言:javascript
复制
@Test
public void Test3(){
    Function<Integer,String[]> func1 = length -> new String[length];
    String[] arr1 = func1.apply(5);
    System.out.println(Arrays.toString(arr1));

    System.out.println("*******************");

    Function<Integer,String[]> func2 = String[] :: new;
    String[] arr2 = func2.apply(10);
    System.out.println(Arrays.toString(arr2));
}

Stream API

java.util.stream包下

Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则 是 Stream API

  • Stream API 把真正的函数式编程风格引入到Java中 这是目前为止对Java类库最好的补充
  • Stream API可以极大提供Java程 序员的生产力,让程序员写出高效率、干净、简洁的代码.
  • Stream 是 Java8 中处理集合的关键抽象概念 它可以指定你希望对集合进 行的操作,可以执行非常复杂的查找、过滤和映射数据等操作 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询,Stream API 提供了一种 高效且易于使用的处理数据的方式

为什么要使用Stream API:

  • 实际开发中,项目中多数数据源都来自于Mysql,Oracle等 关系性数据库
  • 而对于 Redsi MongDB 非关系性数据库 并不能提供,复杂性查询操作:过滤 分组 计算... 而这些NoSQL的数据就需要 Java层面去处理 很麻烦🙃

Stream 和 Collection 集合的区别:

  • Collection 是一种静态的内存数据结构 基于内存的存储数据的空间
  • 而 Stream 是有关计算的CPU计算~

Stream 的操作三个步骤

创建 Stream

  • 一个数据源如:集合、数组,获取一个流 ①Stream 自己不会存储元素 ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行

中间操作

  • 一个中间操作链,对数据源的数据进行处理

终止操作(终端操作)

  • 一旦执行终止操作,就执行中间操作链,并产生结果。之后的Stream对象,不会再被使用

创建 Stream

Emp.Java 自定义操作实体类:

代码语言:javascript
复制
/** 自定义一个实体类: */
public class Emp {
    private int id;
    private String name;
    private int age;
    private double salary;
    //无参构造
    public Emp() {    }
    //有参构造
    public Emp(int id, String name, int age, double salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    //自定义操作数据集, `模拟从 Redis 数据库获取到的数据集合!`
    public static List<Emp> getEmployees(){
        List<Emp> list = new ArrayList<>();

        list.add(new Emp(1001, "马化腾", 34, 6000.38));
        list.add(new Emp(1001, "马化腾", 34, 6000.38));
        list.add(new Emp(1002, "马云", 12, 9876.12));
        list.add(new Emp(1002, "马云", 12, 9876.12));
        list.add(new Emp(1003, "刘强东", 33, 3000.82));
        list.add(new Emp(1004, "雷军", 26, 7657.37));
        list.add(new Emp(1005, "李彦宏", 65, 5555.32));
        list.add(new Emp(1006, "比尔盖茨", 42, 9500.43));
        list.add(new Emp(1007, "任正非", 26, 4333.32));
        list.add(new Emp(1008, "扎克伯格", 35, 2500.32));
        return list;
    }
    //重新toString();
    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
    //省略get/set...
}    

StreamTest.Java 测试类:创建Stream

代码语言:javascript
复制
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/** 强大的Stream Api */
public class StreamTest {
/** 创建 Stream */
    @Test
    public void test1(){
        /** 方式一: 通过集合: 集合.stream/parallelStream(); 返回一个stream顺序流/并行流~ */
        List<Emp> emps = Emp.getEmployees();

        //default Stream<E> stream() : 返回一个顺序流
        Stream<Emp> stream = emps.stream();
        //default Stream<E> parallelStream() : 返回一个并行流
        Stream<Emp> parallelStream = emps.parallelStream();
        /** Java8 Collection接口添加了新的方法 stream()、parallelStream()、forEach()和removeIf()... */

        /** 方式二: 通过数组: Arrays.stream(数组); 返回对应的Stream流对象! */
        int[] arr = new int[]{1,2,3,4,5,6};
        //调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流
        IntStream intstream = Arrays.stream(arr);              /** 基本数据类型返回,对应的 xxxstream  Stream流对象~ */
        Emp e1 = new Emp(1001,"Tom",34, 6000.38);
        Emp e2 = new Emp(1002,"Jerry",34, 6000.38);
        Emp[] arr1 = new Emp[]{e1,e2};
        Stream<Emp> empStream = Arrays.stream(arr1);                /** 自定类型数组,返回 Stream<自定义类型> 的Sream类型对象! */

        /** 方式三: 通过Stream的of() */
        Stream<Integer> stream3 = Stream.of(1, 2, 3, 4, 5, 6);      /** 用的少了解即可~ */
    }

    /** 方式四: 创建无限流: 通过Stream类的静态方法 iterate()迭代 generate()生成 */
    public void test2(){
        /** 两种方式: 迭代流 生成流 */
//      迭代
//      public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
        //参数 T , UnaryOperator函数式接口(内部方法: 传入参数T类型,返回T类型结果,正好用于对T的每次迭代的改变~)

        //Stream.iterate(0, t -> t + 2).forEach(System.out::println);
        //  注释原因: 迭代流会无限的迭代下去 +2 +2 +2...
        //  .limit(10) 之取前十个数据
        //  .forEach() 结束操作! 结束时候做的事情~

        //遍历前10个偶数
        Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);

//      生成
//      public static<T> Stream<T> generate(Supplier<T> s)
        //参数 Supplier函数式接口对象, 内部方法, 返回一个T类型对象... 可以根据后一个规则无限的来生成一些数据~
        Stream.generate(Math::random).limit(10).forEach(System.out::println);       /** 生成10个小于 1 随机数! */
    }
}
总结:

创建Stream 一共有四种方式:

  • 集合.stream/parallelStream(); 返回一个stream顺序流/并行流~ 顺序流:使用主线程,单线程,顺序执行~ 并行流: .parallel()可以将其修改成并行流,内部以多线程并行执行任务的方式执行~
  • 通过数组: Arrays.stream(数组); 返回对应的Stream流对象! 基本数据类型返回,对应的 xxxstream Stream流对象~ 自定类型数组,返回 Stream<自定义类型> 的Stream类型对象!
  • 通过Stream的of() 使用的少,类似于数组创建Stream流~
  • 创建无限流: 通过Stream类的静态方法 iterate()迭代 generate()生成

中间操作

创建完Stream流对象之后,就可以通过 流对象S.xx().xx().xx() 各种的中间操作,完成对 流种数据的计算: 筛选 切片 映射 排序…等操作

  • 中间操作, 是多个方法, 每个方法可以对流中的数据进行筛选计算~
  • 多个方法可以像链条一样 拼接操作~
中间操作,方法描述

方 法( );

描 述:

筛选与切片

filter(Predicate p)

接收 Lambda , 从流中排除某些元素,传入一共 函数式接口 (方法参数,传入一个 T 返回Boolean结果)

distinct()

筛选,对流中元素进行 hashCode() 和 equals() 去除重复元素.

limit(long maxSize)

截断流,使其元素不超过给定数量

skip(long n)

跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一 个空流。与 limit(n) 互补

映 射

map(Function f)

接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素

flatMap(Function f)

接收一个函数作为参数,将流中的每个值都换成另 一个流,然后把所有流连接成一个流

mapToInt(ToIntFunction f)

接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 IntStream

mapToLong(ToLongFunctionf)

接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 LongStream

mapToDouble(ToDoubleFunction f)

接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 DoubleStream

排 序

sorted()

产生一个新流,其中按自然顺序排序

sorted(Comparator com)

产生一个新流,其中按比较器顺序排序

示例: Demo

StreamTest2.Java

代码语言:javascript
复制
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

/** Stream中间操作
 *      创建完Stream流对象之后,就可以通过 流对象S.xx().xx().xx() 各种的中间操作,完成对 流种数据的计算: 筛选 切片 映射 排序...等操作
 *      中间操作, 是多个方法, 每个方法可以对流中的数据进行筛选计算~
 *      多个方法可以像链条一样 拼接操作~
 * */
public class StreamTest2 {
    /** 筛选与切片 */
    @Test
    public void test1(){
        System.out.println("筛选与切片");
        //获取 emp 集合~
        List<Emp> employees = Emp.getEmployees();
        //JDK8 Collection接口新增,foreach(); 方法: 遍历结果集操作~
        employees.forEach(System.out::println);

        //集合创建Stream 流~
        Stream<Emp> stream = employees.stream();
        System.out.println("\nfilter: 从流中排除某些元素");
        System.out.println("练习: 查询员工表中薪资大于7000的员工信息");
        stream.filter(e->e.getSalary()>7000).forEach(System.out::println);  //filter(Predicate<T>); lambda表达式,每次传入一个流中对象,返回一共 Boolean结果,过滤掉false数据!

        /** 注意一共Stream 使用之后就不可以在次使用,流已经关闭了... 但创建流的,集合依然存在~ */
//        stream.filter(e->e.getSalary()>7000).forEach(System.out::println);  //异常:stream has already been operated upon or closed 需要重新创建一共新的流~

        System.out.println("\ndistinct:筛选,对流中元素进行 hashCode() 和 equals() 去除重复元素");
        employees.stream().distinct().forEach(System.out::println);

        System.out.println("\nlimit(n)——截断流,使其元素不超过给定数量(获取前几个元素~)");
        employees.stream().limit(3).forEach(System.out::println);

        System.out.println("\nskip(n) —— 跳过元素(跳过前几个元素不要~)");
        employees.stream().skip(3).forEach(System.out::println);
    }

    /** **映 射** */
    @Test
    public void test2(){
        System.out.println("映射");
        System.out.println("\nmap(Function f): ——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素");
        List<String> strlist = Arrays.asList("aa", "bb", "cc", "dd");
        strlist.stream().map(str -> str.toUpperCase()).forEach(System.out::println);   //遍历每一个元素,对元素进行二次操作~

        System.out.println("\n过年了,给所有员工工资+1000");
        //获取 emp 集合~
        List<Emp> employees = Emp.getEmployees();
        System.out.println("遍历一遍集合:");
        employees.stream().forEach(System.out::println);
        System.out.println("\n加薪: map( 内部函数式接口方法,要求传入什么参数类型,则返回什么类型~ ); 顺便去重 distinct()");
        employees.stream().map(emp -> {emp.setSalary(emp.getSalary()+1000.0); return emp; }).distinct().forEach(System.out::println);

        System.out.println("\nflatMap(f); 是map(f)的高级版~");
        System.out.println("1.Map(f) 实现遍历每一个strlist 的字符");
        Stream<Stream<Character>> streamStream = strlist.stream().map(StreamTest2::fromStringToStream);
        streamStream.forEach(s ->{
            s.forEach(System.out::println);
        });
        System.out.println("2.flatMap(f) 实现遍历每一个strlist 的字符");
        Stream<Character> characterStream = strlist.stream().flatMap(StreamTest2::fromStringToStream);
        characterStream.forEach(System.out::println);
        System.out.println("flatMap 会将,内部的每一个元素进行操作,如果是Stream元素也会重新拆开执行~");

        System.out.println("\n下面的映射,就不详细介绍了: mapToInt(T) mapToLong(T) mapToDouble(T) 传入泛型T 返回对应的类似数据~");
    }

    /** 将字符串中的多个字符构成的集合转换为对应的Stream的实例 */
    public static Stream<Character> fromStringToStream(String str){
        ArrayList<Character> list = new ArrayList<>();
        for(Character c : str.toCharArray()){
            list.add(c);
        }
        return list.stream();
    }

    /** flatMap 和 Map :就类似于,数组里面套数组 集合里面套集合 遍历数组和集合的所有元素~*/
    @Test
    public void flat(){
        ArrayList list1 = new ArrayList();
        list1.add(1);
        list1.add(2);
        list1.add(3);

        ArrayList list2 = new ArrayList();
        list2.add(4);
        list2.add(5);
        list2.add(6);
        /** map 就相当于是 add(集合) */
//        list1.add(list2);
        /** flatMap 就相当于是 addAll(集合) 将集合拆分,对每个单独的元素进行操作~*/
//        list1.addAll(list2);
        System.out.println(list1);
    }


    /** 排序 */
    @Test
    public void test3(){
        System.out.println("sorted()——自然排序");
        System.out.println("注意,如果是自定义类型需要,实现Comparable接口,int 默认实现了Comparable");
        List<Integer> list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7);
        list.stream().sorted().forEach(System.out::println);

        System.out.println("sorted(Comparator com)——定制排序");
        list.stream().sorted((e1,e2)->-e1.compareTo(e2)).forEach(System.out::println);
        /**
         * 排序 A 比较 B
         *  返回 0  A相等B
         *  返回 -1 A小于B
         *  返回 1  A大于B
         * */
    }
}

终止操作(终端操作)

每个Stream流都是有三个步骤:创建 中间操作 终止操作

  • 一共Stream一旦调用了终止操作 就表示改Strema 对象,关闭了, 就不可以在进行操作~
  • 同样,一共没有调用 终止操作 的Stream 是不会结束的, 一直占用系统资源~
  • 终端操作会从流的流水线生成结果,其结果可以是任何不是流的值,例 如:List、Integer 流进行了终止操作后,不能再次使用

方法

描述

匹配与查找

allMatch(Predicate p)

检查是否匹配所有元素

anyMatch(Predicate p)

检查是否至少匹配一个元素

noneMatch(Predicate p)

检查是否没有匹配所有元素

findFirst()

返回第一个元素

findAny()

返回当前流中的任意元素

count()

返回流中元素总数

max(Comparator c)

返回流中最大值

min(Comparator c)

返回流中最小值

forEach(Consumer c)

内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代)Stream API 使用内部迭 代——它帮你把迭代做了)

归 约

reduce(T iden, BinaryOperator b)

可以将流中元素反复结合起来,得到一 个值,返回 T

reduce(BinaryOperator b)

可以将流中元素反复结合起来,得到一 个值,返回 Optional< T >

收 集collect⭐

collect(Collector c)

将流转换为其他形式。接收一个 Collector 接口的实现,用于给Stream中元素做汇总 的方法

collect(Collector c)

Collector 接口中方法的实现决定了如何对流执行收集的操作:

  • 如收集到 List、Set、 Map
  • Collectors 实用类提供了很多静态方法 可以方便地创建常见收集器实例, 具体方法与实例如下表:

方法

返回类型

作用

示例:

toList

List< T >

把流中元素收集到List

List emps= list.stream().collect(Collectors.toList());

toSet

Set< T >

把流中元素收集到Set

Set emps= list.stream().collect(Collectors.toSet());

toCollection

Collection< T >

把流中元素收集到创建的集合

Collection emps =list.stream().collect(Collectors.toCollection(ArrayList::new));

counting

Long

计算流中元素的个数

long count = list.stream().collect(Collectors.counting());

summingInt

Integer

对流中元素的整数属性求和

int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));

averagingInt

averagingInt

计算流中元素Integer属性的平均值

double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));

summarizingInt

IntSummaryStatistics

收集流中Integer属性的统计值如:平 均值

int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));

代码语言:javascript
复制
方法				返回类型               作用                                   示例:

joining 			String 				连接流中每个字符串
String str= list.stream().map(Employee::getName).collect(Collectors.joining());

maxBy 				Optional<T> 		根据比较器选择最大值
Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));

minBy 				Optional<T> 		根据比较器选择最小值
Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));

reducing 			归约产生的类型			从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));

collectingAndThen 	转换函数返回的类型 	   包裹另一个收集器,对其结果转换函数
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));

groupingBy 			Map<K, List<T>> 	  根据某属性值对流分组,属性为K,结果为V
Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));

partitioningBy 		Map<Boolean, List<T>> 根据true或false进行分区
Map<Boolean,List<Emp>> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));
示例:Demo
代码语言:javascript
复制
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/** 终止操作 */
public class StreamTest3 {

    @Test
    public void test(){
        System.out.println("匹配与查找\n");
        List<Emp> employees = Emp.getEmployees();
//        allMatch(Predicate p)——检查是否匹配所有元素。
//          练习:是否所有的员工的年龄都大于18
        boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
        System.out.println(allMatch);

//        anyMatch(Predicate p)——检查是否至少匹配一个元素。
//         练习:是否存在员工的工资大于 10000
        boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
        System.out.println(anyMatch);

//        noneMatch(Predicate p)——检查是否没有匹配的元素。
//          练习:是否存在员工姓“雷”
        boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
        System.out.println(noneMatch);
//        findFirst——返回第一个元素
        Optional<Emp> employee = employees.stream().findFirst();
        System.out.println(employee);
//        findAny——返回当前流中的任意元素
        Optional<Emp> employee1 = employees.parallelStream().findAny();
        System.out.println(employee1);
    }

    @Test
    public void test2(){
        List<Emp> employees = Emp.getEmployees();
        // count——返回流中元素的总个数
        long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
        System.out.println(count);
//        max(Comparator c)——返回流中最大值
//        练习:返回最高的工资:
        Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
        Optional<Double> maxSalary = salaryStream.max(Double::compare);
        System.out.println(maxSalary);
//        min(Comparator c)——返回流中最小值
//        练习:返回最低工资的员工
        Optional<Emp> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(employee);
        System.out.println();
//        forEach(Consumer c)——内部迭代
        employees.stream().forEach(System.out::println);

        //使用集合的遍历操作
        employees.forEach(System.out::println);
    }

    //2-归约
    @Test
    public void test3(){
//        reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T
//        练习1:计算1-10的自然数的和
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        Integer sum = list.stream().reduce(0, Integer::sum);
        System.out.println(sum);


//        reduce(BinaryOperator) —— 函数式接口对象——>可以将流中元素反复结合起来,得到一个值,返回 Optional<T>
//        练习2:计算公司所有员工工资的总和
        List<Emp> employees = Emp.getEmployees();
        Stream<Double> salaryStream = employees.stream().map(Emp::getSalary);
        Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2);
        System.out.println(sumMoney.get());

    }

    //3-收集
    @Test
    public void test4(){
//        collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
//        练习1:查找工资大于6000的员工,结果返回为一个List或Set
        List<Emp> employees = Emp.getEmployees();
        List<Emp> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
        employeeList.forEach(System.out::println);

        System.out.println();
        Set<Emp> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
        employeeSet.forEach(System.out::println);
    }

}

Optional 类

Java应用中最常见的bug就是空值异常

  • 在Java 8之前,Google Guava引入了Optionals类来解决NullPointerException从而避免源码被各种**null**检查污染,以便开发者写出更加整洁的代码
  • Java 8也将Optional加入了官方库

Optional 类(java.util.Optional) 是一个容器类

  • 就是对一共对象,的一共包装~ 保证调用对象之后不会产生 空指针 它可以保存类型T的值,代表这个值存在,或者仅仅保存null,表示这个值不存在

常用方法:

创建Optional类对象的方法

  • Optional.of(T t) : 创建一个 Optional 实例,t必须非空
  • Optional.empty() : 创建一个空的 Optional 实例
  • Optional.ofNullable(T t):t可以为null

判断Optional容器中是否包含对象

  • boolean isPresent() : 判断是否包含对象
  • void ifPresent(Consumer consumer) :如果有值,就执行Consumer 接口的实现代码,并且该值会作为参数传给它

获取Optional容器的对象

  • T get(): 如果调用对象包含值,返回该值,否则抛异常
  • T orElse(T other) :如果有值则将其返回,否则返回指定的other对象
  • T orElseGet(Supplier other) :如果有值则将其返回,否则返回由 Supplier接口实现提供的对象
  • T orElseThrow(Supplier exceptionSupplier) :如果有值则将其返 回,否则抛出由Supplier接口实现提供的异常

接口默认方法

这个最为简单,可以简单的理解现在的接口中方法可以定默认实现

这样做到了像以前一样的抽象方法实现接口的默认实现,也方便了我们不在需要像以前一样做抽象的模板模式

代码语言:javascript
复制
interface A{
	defalut void method1(){
		method2(); //默认实现
	}
	void method2();
}

java8接口中除了default method,还提供定义(并实现)静态方法

代码语言:javascript
复制
interface B{
	static String method(){
		return "xxx";
	}
}

注解的影响:

本人的注解学习~

新日期API

本人常用类学习

其它新增:

Base64 加解密:

Java 8将Base64 加入到JDK库中 样不需要使用第三方库就可以进行Base64编码

代码语言:javascript
复制
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/** Base64 加解密 */
public class Base {
    public static void main(String[] args) {
        final String text = "Java慈祥,yyds!";

        final String encoded = Base64.getEncoder().encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
        System.out.println("加密:"+encoded);

        final String decoded = new String(Base64.getDecoder().decode( encoded ),StandardCharsets.UTF_8 );
        System.out.println("解密:"+decoded);
    }
}

MD5 加解密:

尽然提到加密就顺便题一下MD5 加密:

  • MD5的全称是Message-Digest Algorithm信息-摘要算法
  • MD5其实不算是加密算法,而是一种信息的摘要,它的特性是不可逆的 除了暴力破解 一般逆序算法是得不到结果的一个个实验暴力循环~

举个例子:

  • 1+99=100 **MD5接到的字符是1和99 然后通过自己的算法最后生成100 **但知道结果是100却很难推测出是通过1+99得来的
  • 再比如 一本书的每一页取一个字,最后通过计算得出一个MD5码 但却很难通过这个MD5码去推测出这本书的内容…

MD5加密的特点主要有以下几点:

  • 针对不同长度待加密的数据、字符串等等,其都可以返回一个固定长度的MD5加密字符串
  • 其加密过程几乎不可逆,除非维护一个庞大的Key-Value数据库来进行碰撞破解,否则几乎无法解开
  • 对于一个固定的字符串。数字等等,MD5加密后的字符串是固定的,也就是说不管MD5加密多少次,都是同样的结果

java.security.MessageDigest

代码语言:javascript
复制
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/** Md5加密 */
public class Md5 {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        // 生成一个MD5加密计算摘要
        MessageDigest md = MessageDigest.getInstance("MD5");

        md.update("Java.慈祥,yyds".getBytes());
        //digest()最后确定返回md5 hash值,返回值为8位字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
        //BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
        //一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方)
        System.out.println(new BigInteger(1, md.digest()).toString(16));
    }
}

MD5 简单的字符串加密之后可以在线解密,复杂的话解不出来的 在线解密

UUID 🐶

UUID 是Java1.5 就新增的~

  • UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的

UUID由以下几部分的组合:

  1. 当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同
  2. 时钟序列
  3. 全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得
代码语言:javascript
复制
import java.util.UUID;

String uuid = UUID.randomUUID().toString();
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java8 新特性
    • Lambda表达式
      • 基本语法:
    • Lambda实例:
      • 总结👍:
      • 函数式接口:
        • 函数式接口实例:
          • 自定义函数式接口:
            • Java.util.function 包:
              • 四大核心函数式接口:
          • 方法/构造器/数组 引用:
            • 对象 :: 实例方法名
              • 类 :: 静态方法名
                • 总结👍
              • 类 :: 实例方法名
                • 构造器引用
                  • 总结:👍
                • 数组引用:
                • Stream API
                  • Stream 的操作三个步骤
                    • 创建 Stream
                      • 总结:
                    • 中间操作
                      • 中间操作,方法描述
                      • 示例: Demo
                    • 终止操作(终端操作)
                      • collect(Collector c)
                      • 示例:Demo
                  • Optional 类
                    • 常用方法:
                    • 接口默认方法
                    • 注解的影响:
                    • 新日期API
                    • 其它新增:
                      • Base64 加解密:
                        • MD5 加解密:
                          • UUID 🐶
                          相关产品与服务
                          容器服务
                          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档