Skip to content

JDK8新特性

新增特性

新增什么特性

java
在JDK8版本中 引入了很多新的内容 分为

新的语法
新的功能
新的底层实现

集合相关

java
1、ArrayList 被设计为了懒加载的模式,
    即初始化无参构造,只维护一个空列表,当我们第一次添加元素,才将数组初始化为10。

2、HashMap加入了红黑树数据结构

接口相关

java
1、接口中可以书写普通方法:使用default关键字修饰,加在返回值类型之前,访问修饰符之后。
    
2、接口中可以书写静态方法
java
package com.atguigu.test1;

public interface A {
    public default void m1(){

    }

    public static void m2(){
        
    }

}

函数式接口

函数式接口:即一个接口中只有一个抽象方法,这样的接口称之为SAM接口(Single Abstract Method );

这样的接口可以使用注解@FunctionalInterface修饰则称之为函数式接口;

函数式编程属于一种编程思想,就像面向过程,面向对象,等等都属于编程思想;

函数式编程的代表语言是Haskell,更强调函数(方法)可以实现什么操作,执行了什么功能,不注重是哪个角色调用了这个函数(方法),侧重于面向过程,不是面向对象(Java早期设计);

使用函数式编程 即表示前提必须为函数式接口;

只要一个接口中只有一个抽象方法,即可称之为函数式接口

java
package com.interfacePart.Test01;

public interface Usb {
    // 接口内 默认为 全局静态变量 即 public static final 修饰
    int A = 100;
    // 接口中的方法默认都为全局抽象方法 即不管是否书写均使用public abstract修饰
    void connect();
}

lambda表达式

lambda表达式的使用

java
package com.atguigu.test1;

/**
 *  回顾匿名内部类  :即我们可以”直接new“接口 或者抽象类 相当于创建一个匿名内部类
 *  使用了lambda表达式以后 之前匿名内部类书写格式混乱的问题 可以得到解决
 *  前提:lambda表达式只能用于函数式接口
 *
 *  写法越简洁 前期越难理解 后期使用越方便
 */
public class B {
    public static void main(String[] args) {
        // 之前的写法,使用匿名内部类的方式
        C c1 = new C() {
            @Override
            public void m1() {
                System.out.println("匿名内部类的方式重写m1方法");
            }
        };
		// 调用执行
        c1.m1();

        // 无参 无返回值 只有一条语句
        C c2 = ()-> System.out.println("lambda表达式的方式重写m1方法");
		// 调用执行
        c2.m1();

        // 有一个参数 无返回值 只有一条语句,参数不用写类型声明,接口中已定义。
        D d1 = (a)-> System.out.println("lambda表达式方式重写D接口m1方法" + a);
        d1.m1(100);
        
        // 有两个个参数 无返回值 只有一条语句
        E e1 = (a,b)-> System.out.println("lambda表达式方式重写E接口m1方法" + a + b);
        e1.m1(123, "abc");

        // 有两个参数 有返回值 只有一条语句,不用写 return
        F f1 = (a,b)-> a + b;
        System.out.println(f1.m1(10, 20));

        // 有一个参数 有返回值 有多条语句,需要写一个大括号,有返回值,需要写return。
        F f2 = (a,b)->{
            System.out.println(a + b);
            return a + b;
        };

    }
}

interface F{
    int m1(int a,int b);
}
interface E{
    void m1(int a,String b);
}
interface D{
    void m1(int a);
}
interface C{
    void m1();
}

方法引用格式

以后使用Lambda表达式的形式,90%的场景是直接在方法体中直接调用一个方法,而不是在方法体中写逻辑,调用方法的技术也叫作方法引用。

原理:找一个方法,符合接口中声明的变量类型,返回值类型,作为函数式接口中的抽象方法的方法体,叫作方法引用。

在lambda表达式的基础上,使用其他方法的方法体,作为lambda表达式抽象方法的方法体;

js
规则:
被引用的方法体,原本的方法返回值,形参列表,必须和函数式接口中的抽象方法的返回值,形参列表保持一致。

固定格式:

构造方法引用 类名 :: new;
静态方法引用 类名 :: 方法名;
实例方法引用 对象名 :: 方法名;

✨ 方法引用的示例:

1、构造方法引用:

java
class Student{
    private String name;
    private int age;

    public Student(String name) {
        this.name = name;
        System.out.println("单个参数name属性的构造方法");
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("两个参数 name age属性的构造方法");
    }

    public Student() {
        System.out.println("无参构造方法");
    }
}
java
interface A{
    void m1();
}
interface B{
    void m1(String str);
}
interface E<P>{
    void m1(P p);
}
interface D<R,P>{
    R m1(P p);
}
interface C{
    void m1(String str,int num);
}
java
public static void main(String[] args) {
    // lambda表达式 通过接口和直接写方法体
    A a1 = ()-> System.out.println("");
	a1.m1();
    
    // 接口匹配构造方法的无参构造方法体
    A a2 = Student :: new;
    a2.m1();

    // 接口匹配构造方法的有参构造方法体
    B b1 = Student :: new;
    b1.m1("赵四");

    // 接口匹配构造方法的有参构造方法体
    C c1 = Student :: new;
    c1.m1("a", 20);
}

2、静态方法引用:函数式接口匹配类的静态方法的方法体

java
// 思考:哪个方法首先为静态方法
// 并且参数为布尔类型 返回值为String类型的,这样的方法体,在String类中存在
// 例如使用String类中的 valueOf方法
D<String,Boolean> d1 = String :: valueOf;
// 调用接口中的方法执行 valueOf方法体代码
int length = d1.m1(true).length();
System.out.println("length = " + length);

// 使用 Math类中 的 abs  静态方法,作为方法体,匹配D接口
D<Double,Double> d2 = Math :: abs;
System.out.println(d2.m1(20.0));
// 使用 Math类中 的 abs  静态方法,作为方法体,匹配D接口
D<Integer,Float> d3 = Math :: round;
System.out.println(d3.m1(3.5F));

3、实例方法引用:函数式接口匹配类的普通方法的方法体

java
// 定义一个 String 类型对象
String str = "abc";
// 使用 String类型对象的startsWith方法的方法体,匹配D接口
D<Boolean,String> d4 = str :: startsWith;
// 调用方法
System.out.println(d4.m1("def"));

D<Boolean,String> d5 = str :: endsWith;
System.out.println(d5.m1("c"));

JDK函数式接口

JDK中默认提供了常用的4类函数式接口位于 java.util.function,可供日常使用。(不用再写接口了,使用JDK自己提供的)

  • 消费型接口:
java
Consumer <T> accept(T t)  :只接受参数没有返回值;
  • 功能型接口:
java
Function <T,R> R apply(T t) :有参数 有返回值;
  • 供给型接口:
java
Supplier <T>  T get() :没有参数 但是有返回值;
  • 断言型接口:
java
Predicate <T> boolean test(T t) : 有参数有返回值,但是返回值固定为布尔类型;
java
public class TestFunctional {
    public static void main(String[] args) {
        // 使用消费型接口
        Consumer<Integer> consumer = System.out::println;
        // 调用方法
        consumer.accept(100);
        
		// 使用功能性接口
        Function<String,Integer> function = Integer :: parseInt;
        // 调用方法
        System.out.println(function.apply("123"));
        
		// 使用供给型接口
        Supplier<Double> supplier = Math ::random;
        // 调用方法
        System.out.println(supplier.get());
        
		// 使用断言型接口
        Predicate<String> predicate = String :: isEmpty;
		// 调用接口
        System.out.println(predicate.test("abc"));
    }
}

Stream流式编程

Stream 流式编程 是 Java 8 引入的一种 数据处理方式,它可以让你用类似“管道”的方式去操作集合、数组等数据源,从而写出更简洁、可读性更高的代码。用于处理集合,数组等数据。

它的核心理念是:

关注“做什么”,而不是“怎么做”。 你只描述数据的转换、过滤、汇总规则,底层会自动帮你完成迭代、遍历等操作。

基本理解

在 Java 里,Stream 是一个 元素序列,它并不存储数据,而是从数据源(如 Collection、数组、文件、生成器等)中读取数据,然后通过一系列操作(中间操作和终止操作)来处理这些数据。

流特点

  • ①Stream 自己不会存储元素。

  • ②Stream 不会改变源对象。每次处理都会返回一个持有结果的新Stream。

  • ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

处理流程

  1. 创建流(从数据源生成 Stream)
  2. 中间操作(对数据进行加工,如 filtermapsorted
  3. 终止操作(触发执行,如 collectforEachcount

中间操作是惰性执行的,只有在终止操作时才会真正遍历数据。

Stream接口

流接口

构造方法

java
无,使用静态方法创建
static interface  Stream.Builder<T>

方法

generate()

java
作用:创建无限流

参数:
    
返回值:
    
示例:

中间操作方法

java
作用:中间操作

参数:
    
返回值:
    
示例:

终止操作方法

java
作用:终止操作

参数:
    
返回值:
    
示例:

创建流

创建流方式1:使用Collect类中的Stream()方法,创建流对象。

javascript
default Stream<E> stream()

作用:返回以此集合作为源的顺序 Stream 

参数:无

返回值:Stream流对象,这个流对象可以读取到集合中的所有元素

特点:这个流对象,本身是不排序的,集合中的顺序是什么,在流对象中还是什么。

示例:
List<Student> list = new ArrayList<Student>();
Stream<Student> stream = list.stream();

创建流方式2:使用Arrays类中stream()方法。

java
static <T> Stream <T> stream(T[] array)

作用:返回顺序T与指定的数组作为源(静态方法)

参数:T类型的数组

返回值:Stream流对象

特点:这个流对象,本身是不排序的,集合中的顺序是什么,在流对象中还是什么。

示例:
IntStream stream2 = Arrays.stream(new int[]{1,2,3,4,5})

创建流方式3:使用Stream类中静态方法of(),通过显示值创建一个流,可以接收任意数量的参数。

java
static <T> Stream <T> of(T... values) 

作用:返回其元素是指定值的顺序排序流(静态方法)

参数:T类型的值

返回值:Stream流对象

示例:
// 方式3
Stream<String> stream2 = Stream.of("v", "t", "b");
System.out.println("stream2 = " + stream2);

创建流方式4:创建一个无限流:可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流

java
public static<T> Stream<T> generate(Supplier<T> s)
    
作用:返回无限顺序无序流,其中每个元素由提供的 Supplier (静态方法)

参数:供给型函数式接口对象

返回值:Stream流对象

示例:
// 生成一个无限流,里边的内容是随机数,限制5个,每个都打印出来。
Stream.generate(Math :: random).limit(5).forEach(System.out :: println)

中间操作

方法作用
filter(Predicate)按条件过滤
map(Function)元素映射转换
flatMap(Function)扁平化映射
sorted() / sorted(Comparator)排序
distinct()去重
limit(n)取前 n 个
skip(n)跳过前 n 个

终止操作

方法作用
boolean allMatch(Predicate p)检查是否匹配所有元素
boolean anyMatch**(**Predicate p)检查是否至少匹配一个元素
boolean noneMatch(Predicate p)检查是否没有匹配所有元素
Optional T indFirst()返回第一个元素
long count()返回流中元素总数
Optional T max()返回流中最大值
Optional T min()返回流中最小值
void forEach(Consumer c)迭代遍历
findAny()查找元素
collect(Collectors.toList())转集合

示例

java
import java.util.*;
import java.util.stream.*;

public class StreamDemo {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("apple", "banana", "pear", "orange", "apple");

        // 流式编程:筛选 + 转大写 + 去重 + 排序 + 收集
        List<String> result = list.stream()
                .filter(s -> s.length() > 4)      // 过滤长度大于4的
                .map(String::toUpperCase)         // 转大写
                .distinct()                       // 去重
                .sorted()                         // 排序
                .collect(Collectors.toList());    // 收集成 List

        System.out.println(result);
    }
}

中间操作使用示例:

java
package com.newJDK.TestLambda.Stream;

import org.junit.Test;

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

public class TestStream {
    // 创建一个数组集合
    static List<Student> list = new ArrayList<Student>();
    static {
        // 向集合中添加元素
        Student stu1 = new Student("hanser",18,'女');
        Student stu2 = new Student("yousa",19,'女');
        Student stu3 = new Student("cat",20,'女');
        Student stu4 = new Student("Akie",20,'女');
        Student stu5 = new Student("senere",20,'女');
        Student stu6 = new Student("senere",20,'女');
        list.add(stu1);
        list.add(stu2);
        list.add(stu3);
        list.add(stu4);
        list.add(stu5);
        list.add(stu6);
    }
    
    // filter使用
    @Test
    public void m1(){
        // 创建 流对象
        Stream<Student> stream = list.stream();
        // filter的使用
        stream.filter((stu)->stu.getName().equals("hanser")).forEach(System.out::println);
    }
    
    // map的使用:对每个元素进行处理
    @Test
    public void m2(){
        // 创建 流对象
        Stream<String> stream = Stream.of("abc", "edf");
        // filter的使用
        stream.map((str)->str.toUpperCase()).forEach(System.out::println);
    }
    // 去重
    @Test
    public void m3(){
        // 创建 流对象
        Stream<Student> stream = list.stream();
        // 需要在 Student类中重写 equals方法和hashCode方法
        stream.distinct().forEach(System.out::println);
    }
    @Test
    // 限制条数
    public void m4(){
        // 创建 流对象
        Stream<Student> stream = list.stream();
        stream.limit(3).forEach(System.out::println);
    }
    // 跳过
    @Test
    public void m5(){
        // 创建 流对象
        Stream<Student> stream = list.stream();
        stream.skip(1).forEach(System.out::println);
    }
    
    // 扁平化映射:
    @Test
    public void m6(){
        // 创建 流对象
        Stream<String> stream = Stream.of("Hanser", "is", "MyAngle");
        // 遍历到流对象中的每个元素
        stream.flatMap(TestStream::buildstrToStream).forEach(System.out::println);

    }
    // 定义一个方法处理逻辑:传入一个string字符串,返回一个流对象
    public static Stream<Character> buildstrToStream(String str){
        // 字符串转为字符数组
        char[] chars = str.toCharArray();
        // 新建一个集合
        List<Character> list = new ArrayList<>();
        // 把字符数组的每一个字符添加到集合中
        for (char aChar : chars) {
            list.add(aChar);
        }
        // 返回一个流对象
        return list.stream();
    }
    
    // 排序
    @Test
    public void m7(){
        // 创建 流对象
        Stream<Student> stream = list.stream();
        // Student类中实现了Comparable接口
        stream.sorted().forEach(System.out::println);
    }
    
    // 排序
    @Test
    public void m7(){
        // 创建 流对象
        Stream<Student> stream = list.stream();
        // Student类中实现了Comparable接口
        stream.sorted(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getAge() - o2.getAge();
            }
        }).forEach(System.out::println);
    }
    
}
java
package com.newJDK.TestLambda.Stream;

import java.util.Objects;

public class Student implements Comparable<Student>{
    private String name;
    private int age;
    private char sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public Student(String name, int age, char sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public Student() {

    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                '}';
    }

    // 重写equals方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && sex == student.sex && Objects.equals(name, student.name);
    }
    // 重写hashCode方法
    @Override
    public int hashCode() {
        return Objects.hash(name, age, sex);
    }

    // 重写比较方法
    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}

终止操作使用示例:

java
// 判断是否全部匹配条件
@Test
public void m8(){
    // 创建 流对象
    Stream<Student> stream = list.stream();
    boolean res = stream.filter(stu -> stu.getAge() > 20).allMatch(stu -> stu.getSex() == '女');
    System.out.println("结果为"+res);
}

// 判断是否有一个匹配条件
@Test
public void m9(){
    // 创建 流对象
    Stream<Student> stream = list.stream();
    boolean res = stream.filter(stu -> stu.getAge() > 20).anyMatch(stu -> stu.getSex() == '女');
    System.out.println("结果为"+res);
}

// 返回流中元素数量
@Test
public void m10(){
    // 创建 流对象
    Stream<Student> stream = list.stream();
    long count = stream.count();
    System.out.println("结果为"+count);
}

// 返回第一个元素
@Test
public void m11(){
    // 创建 流对象
    Stream<Student> stream = list.stream();
    Optional<Student> first = stream.findFirst();
    System.out.println("结果为"+first);
}

// 返回流中最大的元素
@Test
public void m12(){
    // 创建 流对象
    Stream<Student> stream = list.stream();
    Optional<Student> maxOne = stream.max(new Comparator<Student>() {
        @Override
        public int compare(Student o1, Student o2) {
            return o1.getAge() - o2.getAge();
        }
    });
    System.out.println("结果为"+maxOne);
}

// 返回流中最小的元素
    @Test
    public void m12(){
        // 创建 流对象
        Stream<Student> stream = list.stream();
        Optional<Student> maxOne = stream.min(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getAge() - o2.getAge();
            }
        });
        System.out.println("结果为"+maxOne);
    }

Optional类

到目前为止,空指针异常是导致Java应用程序失败的最常见原因。

以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。

以前写代码时每次都需要判断对象是否为空,现在使用Optional类对空指针的检查更佳简洁一点。

简化我们对空指针判断的代码。

方法

of(Object obj):静态方法

java
作用:根据传入对象获取一个Optional对象,此对象不能为null

参数:对象
    
返回值:Optional对象
    
示例:
public void m1(){
    Optional<String> op1 = Optional.of("Hanser");
    Optional<String> op2 = Optional.of(null);
    System.out.println("Optional对象:" + op1); // Optional[Hanser]
    System.out.println("Optional对象:" + op2); // 抛异常
}

empty():静态方法

java
作用:包装一个保存有null的Optional对象

参数:无
    
返回值:Optional对象
    
示例:
// Optional类中的empty方法
@Test
public void m2(){
    Optional<String> op1 = Optional.empty();
    System.out.println("Optional对象:" + op1); // Optional.empty
}

ofNullable(Object obj):静态方法

java
作用:根据传入对象获取一个Optional对象,此对象可以为null

参数:对象或null
    
返回值:Optional对象
    
示例:

// Optional类中的ofNullable(Object obj)
@Test
public void m3(){
    Optional<String> op1 = Optional.ofNullable("123");
    Optional<String> op2 = Optional.ofNullable(null);
    System.out.println("Optional对象:" + op1);
    System.out.println("Optional对象:" + op2);
}

get():Optional实例方法

java
作用:获取Optional中保存的对象,如果为null,则报空指针异常

参数:空
    
返回值:对象
    
示例:
@Test
public void m4(){
    Optional<String> op1 = Optional.ofNullable("123");
    String value = op1.get();
    System.out.println("value = " + value);
}

isPresent():Optional实例方法

java
作用:表示判断Optional是否为null,为null结果为false,不为null结果为true

参数:空
    
返回值:布尔值
    
示例:
// isPresent
@Test
public void m5(){
    Optional<String> op1 = Optional.ofNullable(null);
    boolean res = op1.isPresent();
    System.out.println("value = " + res); // false
}

ifPresent(Consumer ? super T consumer):Optional实例方法

java
作用:如果Optional对象中的对象不为null,则消费此对象,否则不消费

参数:消费型接口对象
    
返回值:无
    
示例:
// isPresent
@Test
public void m5(){
    Optional<String> op1 = Optional.ofNullable(null);
    // 消费型接口,有参,返回值无
    op1.ifPresent(System.out::println);
}

orElse(T other):Optional实例方法

java
作用:如果当前Optional对象中保存对象为null,则使用传入对象

参数:对象
    
返回值:无
    
示例:
// isPresent
@Test
public void m6(){
    Optional<String> op1 = Optional.ofNullable(null);
    String value = op1.orElse("Akie");
    System.out.println("value = " + value);
}

orElseGet(Supplier ? extends T other):Optional实例方法

java
作用:如果当前Optional对象中为null,则获取传入的另外一个对象,否则不获取

参数:供给型对象
    
返回值:无
    
示例:
    
// isPresent
@Test
public void m7(){
    Optional<String> op1 = Optional.ofNullable(null);
    String str = "Warma Kawaii";
    // Supplier 不接受任何参数,只会返回一个值
    String value = op1.orElseGet(str :: toString);
    // String value1 = op1.orElseGet(str -> str.toString()); // 错误的
    String value1 = op1.orElseGet(() -> str.toString()); // 正确的
    System.out.println("value = " + value);
    System.out.println("value = " + value1);
}

orElseThrow(Supplier ? extends X exceptionSupplier):实例方法

java
作用:如果当前Optional对象中为null则抛出异常,否则不抛出

参数:供给型对象
    
返回值:Throwable异常类型
    
示例:
    
// isPresent
@Test
public void m8(){
    Optional<String> op1 = Optional.ofNullable(null);
    String res = op1.orElseThrow(() -> new RuntimeException("运行时异常"));
    System.out.println("res = " + res);
}