JDK8新特性

1. 介绍

JDK 8 是 Java 平台的一次重要更新,引入了许多新特性,主要目标是提升代码的可读性和生产力。主要特性包括:

  • Lambda 表达式:简化函数式编程。
  • Stream API:支持流式数据处理。
  • 接口的默认方法和静态方法:增强接口的灵活性。
  • 新时间与日期 API:解决旧版日期 API 的不足。
  • Optional 类:优雅地处理空值问题。
  • 并行操作的增强:更高效地利用多核处理器。

2. 集合相关

① ArrayList 被设计为懒加载模式,初始化无参构造,只维护一个空列表,当第一次添加元素才将数组初始化为10

② HashMap加入了红黑树数据结构

③ 新方法支持 Stream API

  • 集合框架中添加了 stream()parallelStream() 方法,支持流式处理集合中的数据。
List<String> names = Arrays.asList("张三", "李四", "王五");
names.stream()
     .filter(name -> name.startsWith("张"))
     .forEach(System.out::println);
  • Map 的增强

  • 新增方法如 computeIfAbsentcomputeIfPresentforEach 等:
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.computeIfAbsent("B", key -> 2);  // 如果 key "B" 不存在,则插入 2
map.forEach((k, v) -> System.out.println(k + ": " + v));

3. 接口相关

① 接口中可以书写普通方法,使用 default 关键字修饰,加在返回值类型之前、访问修饰符之后。

② 接口中可以书写静态方法。

默认方法(Default Methods):

  • 在接口中可以定义带有方法体的 default 方法,避免破坏已有接口的实现。
interface Vehicle {
    default void start() {
        System.out.println("Vehicle is starting");
    }
}
class Car implements Vehicle {}
Car car = new Car();
car.start();  // 输出: Vehicle is starting

静态方法

  • 接口中可以定义 static 方法,供所有实现类使用。
interface Utils {
    static void print(String msg) {
        System.out.println(msg);
    }
}
Utils.print("Hello, Java 8!");

4. 函数式接口、编程

函数式接口,称之为SAM接口(Single Abstract Method)是只包含一个抽象方法的接口(可有默认或静态方法),可使用 @FunctionalInterface 注解标识。

@FunctionalInterface
interface Greeting {
    void sayHello(String name);
}

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

函数式编程的代码语言是Haskell
它更强调函数(方法)可以实现什么操作,执行了什么功能,不注重是哪个角色调用了这个函数(方法)

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

5. lambda表达式

lambda表达式即函数式编程的最终实现

匿名内部类 :即我们可以直接new 接口、或者抽象类,相当于创建一个匿名内部类

使用了lambda表达式以后 之前匿名内部类书写格式混乱的问题 可以得到解决

前提:lambda表达式只能用于函数式接口

写法越简洁 前期越难理解 后期使用越方便

定义

  • Lambda 表达式是简洁地实现匿名内部类的一种方式。
  • 格式:(参数列表) -> {方法体}
List<String> names = Arrays.asList("张三", "李四", "王五");
names.forEach(name -> System.out.println(name));

优势

  • 更少的代码冗余,增强可读性。
  • 配合函数式接口使用,代码更灵活。

示例:

package com.lambda.test;

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");

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

        // 有一个参数 有返回值 有多条语句
        F f2 = (a,b)->{
            System.out.println(a + b);
            return a + b;
        };
    }
}
interface C{
    void m1();
}
interface D{
    void m1(int a);
}
interface E{
    void m1(int a,String b);
}
interface F{
    int m1(int a,int b);
}

输出:
 

匿名内部类的方式重写m1方法
lambda表达式的方式重写m1方法
lambda表达式方式重写D接口m1方法100
lambda表达式方式重写E接口m1方法123abc
30

6. 常见函数式接口

JDK提供的函数式接口位于 java.util.function 包中

这些函数式接口可以大致分为四类

  • 断言型接口:Predicate<T>:boolean test(T t) 接收一个参数,返回布尔值。
  • 功能型接口:Function<T, R>:R apply(T t) 接收一个参数,返回一个结果。
  • 消费型接口:Consumer<T>:accept(T t) 接收一个参数,无返回值。
  • 供给型接口:Supplier<T>:T get() 无参数,返回一个结果。

① Predicate<T>

  • 功能:接收一个参数,返回一个布尔值。
  • 典型用途:用于条件判断、过滤。
import java.util.function.Predicate;

public class Main {
    public static void main(String[] args) {
        // 判断一个数是否为偶数
        Predicate<Integer> isEven = n -> n % 2 == 0;

        System.out.println(isEven.test(4)); // 输出: true
        System.out.println(isEven.test(5)); // 输出: false
    }
}

② Function<T, R>

  • 功能:接收一个参数,返回一个结果。
  • 典型用途:用于转换操作。
import java.util.function.Function;

public class Main {
    public static void main(String[] args) {
        // 将一个字符串转换为其长度
        Function<String, Integer> stringLength = str -> str.length();

        System.out.println(stringLength.apply("Hello")); // 输出: 5
        System.out.println(stringLength.apply("Java 8")); // 输出: 7
    }
}

③ Consumer<T>

  • 功能:接收一个参数,无返回值。
  • 典型用途:用于执行操作,例如打印、保存等。
import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        // 打印一个字符串
        Consumer<String> print = str -> System.out.println("Value: " + str);

        print.accept("Hello World"); // 输出: Value: Hello World
        print.accept("Java 8");      // 输出: Value: Java 8
    }
}

④ Supplier<T>

  • 功能:无参数,返回一个结果。
  • 典型用途:用于懒加载、生成默认值。
import java.util.function.Supplier;

public class Main {
    public static void main(String[] args) {
        // 生成一个默认的字符串
        Supplier<String> defaultMessage = () -> "Default Message";

        System.out.println(defaultMessage.get()); // 输出: Default Message
    }
}

综合示例

将以上函数式接口组合在一起处理一个简单任务:

import java.util.function.*;

public class Main {
    public static void main(String[] args) {
        Predicate<Integer> isPositive = n -> n > 0; // 判断是否为正数
        Function<Integer, String> toString = n -> "Number: " + n; // 转换为字符串
        Consumer<String> print = str -> System.out.println(str); // 打印结果
        Supplier<Integer> defaultNumber = () -> 42; // 提供默认值

        int number = -5;

        if (isPositive.test(number)) {
            print.accept(toString.apply(number));
        } else {
            print.accept(toString.apply(defaultNumber.get()));
        }
    }
}

运行结果:

Number: 42

6.方法引用

方法引用  :在lambda表达式的基础上使用其他的方法的方法体来作为 lambda表达式(函数式接口中)抽象方法的方法体
具体细节:被引用的方法体和原本的方法之间的返回值、形参列表必须和函数式接口中抽象方法的 返回值、形参列表完全匹配

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

示例:
 

package com.test;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

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"));


    }
}

输出:

100
123
0.28316372927345934
false

7. stream流式编程

定义

  • Stream 是数据集合的高级抽象,可以对集合进行过滤、映射、归约等操作,支持并行处理。

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

Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。

这是目前为止对Java类库最好的补充,因为Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。

也可以使用 Stream API 来并行执行操作

Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

Stream API 提供了一种高效且易于使用的处理数据的方式。

集合讲的是数据,负责存储数据,Stream流讲的是计算,负责处理数据!

注意:

①Stream 自己不会存储元素。

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

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

 

操作步骤: 

Stream 操作流程
  1. 创建流:从数据源(集合、数组、生成器)获取流。
  2. 中间操作:通过链式操作对流进行处理。
  3. 终止操作:触发流的执行并生成最终结果。

① 创建 Stream

通过数据源(如集合、数组等)获取一个流。

方式一:通过集合

Java 8 中,Collection 接口提供了两个默认方法用于获取流:

  • 顺序流default Stream<E> stream() - 按顺序处理。
  • 并行流default Stream<E> parallelStream() - 支持多线程并行处理。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("A", "B", "C");

        // 获取顺序流
        Stream<String> sequentialStream = list.stream();

        // 获取并行流
        Stream<String> parallelStream = list.parallelStream();

        sequentialStream.forEach(System.out::println); // 顺序输出
        parallelStream.forEach(System.out::println);  // 并行输出(可能乱序)
    }
}
方式二:通过数组

Java 8 提供 Arrays.stream() 静态方法,直接基于数组创建流。

static <T> Stream<T> stream(T[] array);
import java.util.Arrays;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        String[] array = {"X", "Y", "Z"};

        // 使用 Arrays.stream() 创建流
        Stream<String> stream = Arrays.stream(array);

        stream.forEach(System.out::println); // 输出: X, Y, Z
    }
}
方式三:通过 Stream.of()

使用 Stream.of() 静态方法,通过显式值快速创建一个流。该方法支持可变参数,因此可以传递任意数量的值。

public static <T> Stream<T> of(T... values);
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        // 使用 Stream.of() 创建流
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);

        stream.forEach(System.out::println); // 输出: 1, 2, 3, 4, 5
    }
}
方式四:创建无限流(了解)

可以使用 Stream.iterate()Stream.generate() 创建无限流,通常需要配合限制操作(如 limit())使用。

 Stream.iterate():基于种子和规则生成序列。

public static <T> Stream<T> iterate(T seed, UnaryOperator<T> f);
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        // 使用 iterate() 生成一个递增的无限流
        Stream<Integer> stream = Stream.iterate(1, n -> n + 1);

        // 限制输出前 5 个
        stream.limit(5).forEach(System.out::println); // 输出: 1, 2, 3, 4, 5
    }
}

Stream.generate():基于 Supplier 生成无限流。

public static <T> Stream<T> generate(Supplier<T> s);
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        // 使用 generate() 生成随机数流
        Stream<Double> stream = Stream.generate(Math::random);

        // 限制输出 5 个随机数
        stream.limit(5).forEach(System.out::println);
    }
}

 

② 中间操作

中间操作会返回一个新的流(Stream 类型),不会立即执行,而是延迟到终止操作调用时执行。

常用方法 

方 法描 述
filter(Predicate p)保存符合指定条件的元素
distinct()筛选,通过流所生成元素的equals() 去除重复元素
limit(long maxSize)保留指定个数的前
skip(long n)跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
sorted()产生一个新流,其中按自然顺序排序
map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个

③ 终止操作

终止操作会触发流的执行,并将结果返回或输出。

常用方法

  • 方法描述
    boolean allMatch(Predicate p)检查是否匹配所有元素
    boolean anyMatch(Predicate p)检查是否至少匹配一个元素
    boolean noneMatch(Predicate p)检查是否没有匹配所有元素
    Optional<T> findFirst()返回第一个元素
    long count()返回流中元素总数
    Optional<T> max()返回流中最大值
    Optional<T> min()返回流中最小值
    void forEach(Consumer c)迭代

示例

筛选出10人中年龄在20~30之前姓杨的女性
 

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

class Person {
    String name;
    int age;
    String gender;

    public Person(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + ", gender='" + gender + "'}";
    }
}

public class Main {
    public static void main(String[] args) {
        // 数据源
        List<Person> people = new ArrayList<>();
        people.add(new Person("杨铭", 22, "女"));
        people.add(new Person("张三", 22, "男"));
        people.add(new Person("李四", 28, "男"));
        people.add(new Person("杨梅", 30, "女"));
        people.add(new Person("赵五", 21, "男"));
        people.add(new Person("杨花", 29, "女"));
        people.add(new Person("王六", 35, "男"));
        people.add(new Person("杨洪", 18, "男"));
        people.add(new Person("杨老六", 44, "女"));
        people.add(new Person("孙七", 27, "男"));

        // 使用 Stream API 筛选条件
        List<Person> result = people.stream()
                .filter(person -> person.name.startsWith("杨"))
                .filter(person -> person.age >= 20 && person.age <= 30)
                .filter(person -> "女".equals(person.gender))
                .collect(Collectors.toList()); // 收集结果为列表

        System.out.println("筛选结果:");
        result.forEach(System.out::println);
    }
}

输出:

筛选结果:
Person{name='杨铭', age=22, gender='女'}
Person{name='杨梅', age=30, gender='女'}
Person{name='杨花', age=29, gender='女'}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值