Java新特性一:Lambda 表达式、函数式接口、 :: 操作符、Optional 类、Stream流、Base64编码

本文深入探讨了Java 8中的函数式编程特性,包括函数式接口、lambda表达式、方法引用、构造器引用及Stream API的使用,展示了如何利用这些特性提高代码的简洁性和可读性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Lambda 表达式

1.1 概述

  • Lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。
@Test
public void myTest() {
    Integer[] ints = {8, 4, 7, 2, 9, 5};
    List<Integer> list = Arrays.asList(ints);

    //之前的排序
    list.sort(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2-o1;
        }
    });
    System.out.println(list); // [9, 8, 7, 5, 4, 2]


    //使用Lambda表达式
    list.sort((o1,o2)->(o1-o2));
    System.out.println(list); // [2, 4, 5, 7, 8, 9]
}

1.2 语法

(params) -> expression
或
(params) -> { statements; }

1.3 特征

  • 一个参数可以不用定义圆括号,无参或多个参数必须定义圆括号。
  • 如果主体只有一个语句,可以不需要使用大括号,编译器会自动返回值;如果主体有多个语句,就必须使用大括号,并且需要指定一个返回值。

1.4 示例

// 1. 不需要参数,返回值为 5  
() -> 5

// 2. 不需要参数,返回值为 5  
() -> { return 5; }

// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y 

1.5 变量作用域

  • 在 lambda 表达式中可以捕获外围作用域中变量的值。
  • 在 lambda 表达式的体与嵌套块有相同的作用域,所以在 lambda 表达式中声明与一个局部变量同名的参数或局部变量是不合法的。
  • 在 lambda 表达式中,只能引用 值不会改变的变量。
  • 在 lambda 表达式中捕获的变量必须实际上是最终变量 (effectively final)。
  • 在一个 lambda 表达式中使用 this 关键字时,是指创建这个 lambda 表达式的方法的 this 参数。
public void countDown(long num){
    // num++; 错误操作
    Runnable runnable = () -> {
        // num++; 错误操作
        // long num = 2; 错误操作
        System.out.println(num) ;
    };
    new Thread(runnable).start();
}

2. 函数式接口

2.1 定义

  • 函数式接口(functional interface):有且只有一个抽象方法的接口就是函数式接口。
  • 函数式接口中可以定义 default、static、private 关键字修饰的非抽象方法。
  • @FunctionalInterface 注解用来标记此接口为函数式接口,标记后编译器就会检查此接口是否符合函数式接口的定义,如果不符合就会编译出错。如果能保证你的接口有且只有一个抽象方法,也可以不加这个注解。
/**
 * 自定义一个有参数无返回值的函数式接口
 */
interface MyFunInterface {
    void abc(String s);
}

@Test
public void myTest() {
    MyFunInterface mf = (x) -> System.out.println(x);
    mf.abc("aabbcc"); // aabbcc
}

2.2 JDK8四种核心的函数式接口

2.2.1 供给性接口:Supplier

  • 此接口不接收参数,但有返回值。抽象方法是 get 。
  • 源码:
@FunctionalInterface
public interface Supplier<T> {

    T get();
}
  • 示例:
不接收参数,但有返回值:

Supplier<Long> supplier = () -> System.currentTimeMillis();
Long ctm = supplier.get();

2.2.2 消费性接口:Consumer

  • 此接口接收一个参数t,但没有返回值。抽象方法是 accept 。
  • 源码:
@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}
  • 示例:
接收一个参数,但没有返回值:

Consumer<String> consumer = str -> System.out.println(str);
consumer.accept("123"); // 123


默认方法 andThen() 可以链式调用,将多个对象组合在一起,然后按照顺序执行:

Consumer<String> cs1 = str1 -> "123".equals(str1);
Consumer<String> cs2 = str2 -> System.out.println(str2);
cs1.andThen(cs2).accept("123"); // 123

2.2.3 功能性接口:Function

  • 此接口接收一个功能参数t,并返回一个功能结果R。抽象方法是 apply 。
  • 源码:
@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}
  • 示例:
接收一个参数,并返回一个结果:

Function<String,Integer> function = str -> Integer.parseInt(str);
int result = function.apply("123");


默认方法 andThen() 可以链式调用,将多个对象组合在一起,然后按照顺序执行,第1个对象执行的结果作为第二个对象的参数:

Function<String,Integer> ft1 = t1 -> Integer.parseInt(t1);
Function<Integer,Integer> ft2 = t2 -> t2 * 100;
System.out.println(ft1.andThen(ft2).apply("123")); // 12300

2.2.4 断言性接口:Predicate

  • 此接口接收一个参数t,如果输入参数符合断言则返回true,否则false 。抽象方法是 test 。
  • 源码:
@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

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

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}
  • 示例:
接收一个参数,如果输入参数符合断言则返回true,否则falsePredicate<String> predicate = str -> Integer.parseInt(str) > 123;
boolean result = predicate.test("123");

3. :: 操作符

3.1 方法引用

  • 引用类的静态方法格式: 类名::静态方法名,Math::pow 等价于 (x,y) -> Math.pow(x, y)。
  • 引用实例对象的实例方法格式: 实例对象名::实例方法名,student::getName 等价于 () -> student.getName()。
  • 引用类的实例方法格式: 类名::实例方法名,String::compareTo 等价于 (o1,o2) -> o1.compareTo(o2)。

3.2 构造器引用

构造器引用的语法格式:类名::new ,例如,Student::new 是 Student 构造器的一个引用。

示例:

@Test
public void myTest(){
	// 调用无参构造函数
    Supplier<Student> supplier = Student::new;
    System.out.println(supplier.get()); // 学生
	
	// 调用有参构造函数
    Function<String,Student> function = Student::new;
    System.out.println(function.apply("wang")); // wang
}

class Student {
    public String name;
    public Student() {
        this.name = "学生";
    }
    public Student(String name) {
        this.name = name;
    }
    public String toString() {
        return name;
    }
}

4. Optional 类

  • Optional 类是一个可以包含或不包含非null值的容器。如果存在值,则 isPresent() 将返回 true,而 get() 将返回该值。。
  • Optional 类是一个基于值的类;在Optional实例上使用标识敏感的操作(包括引用等于(==),标识哈希码或同步)可能会产生不可预测的结果,应避免使用。
定义:
public final class Optional<T> extends Object  
源码:
public final class Optional<T> {
     // 如果非空,则为该值;如果为null,则表示不存在任何值
    private final T value;

	// 构造函数为私有构造函数
}


API:
static <T> Optional<T> empty() 返回一个空的Optional实例。
示例:
Optional optional = Optional.empty();
System.out.println(optional); // Optional.empty


boolean	equals(Object obj) 表示其他某个对象是否“等于”此Optional。
源码:
Objects.equals(value, other.value) // 实际equals的是Optional实例的value。


static <T> Optional<T> ofNullable(T value) 返回描述指定值的Optional,如果为null,则返回一个空Optional。
示例:
Optional o1 = Optional.ofNullable(new String("a"));
Optional o2 = Optional.ofNullable(null);
System.out.println(o1); // Optional[a]
System.out.println(o2); // Optional.empty


T orElse(T other) 如果存在,返回该值,否则返回other。
示例:
String str = null;
str = Optional.ofNullable(str).orElse("a");
System.out.println(str); // a


T orElseGet(Supplier<? extends T> other) 如果存在,返回该值,否则调用other,然后返回该调用的结果。
示例:
String str = null;
str = Optional.ofNullable(str).orElseGet(() -> "other");
System.out.println(str); // other


String toString() 返回此Optional的字符串表示。
源码:
public String toString() {
    return value != null ? String.format("Optional[%s]", value) : "Optional.empty";
}


Optional<T> filter(Predicate<? super T> predicate) 
根据传入的 predicate 对 Optional 中的值进行过滤,满足条件则返回该 Optional 对象,否则返回一个空的 Optional。
示例:
Optional optional = Optional.ofNullable(1);
Predicate<Integer> predicate= num -> num > 1;
System.out.println(optional.filter(predicate)); // Optional.empty
predicate = num -> num <= 1;
System.out.println(optional.filter(predicate)); // Optional[1]


<U> Optional<U>	map(Function<? super T,? extends U> mapper) 
如果 Optional 存在值,则执行 mapper 映射函数返回结果,否则返回一个空的 Optional。
示例:
String str = null;
Optional optional = Optional.ofNullable(str).map((s) -> "ab");
System.out.println(optional); // Optional.empty
Optional optional2 = Optional.ofNullable("a").map((s) -> "ab");
System.out.println(optional2); // Optional[ab]


int	hashCode() 如果存在值,返回当前值的哈希码值,如果没有值,则返回0(零)。

boolean	isPresent() 如果存在值,则返回true,否则返回falsevoid ifPresent(Consumer<? super T> consumer) 如果存在值,执行 consumer 的具体操作,否则不执行任何操作。
示例:
Optional optional = Optional.ofNullable("a");
optional.ifPresent(System.out::println); // a

5. Stream流

  • 流不会储存数据。
  • 流的操作不会修改其数据源。
  • 流的操作是尽可能的惰性执行。也就是要等到需要结果的时候才会去执行。
API:
default Stream<E> stream() 
返回当前集合中所有元素的顺序流。Collection 接口的方法。

default Stream<E> parallelStream() 
返回当前集合中所有元素的并行流。Collection 接口的方法。


static <T> Stream<T> empty() 返回一个空的顺序流。
static <T> Stream<T> of(T t) 返回包含单个元素的顺序流。
static <T> Stream<T> of(T... values) 返回其元素为指定值的有序顺序流。
示例:
Stream<String> s = Stream.of("a", "b", "c");
Stream<String> ss = Stream.of("a!b!c".split("!"));


Stream<T> filter(Predicate<? super T> predicate) 
返回一个包含当前流中满足 predicate 所有元素的流。

<R> Stream<R> map(Function<? super T,? extends R> mapper) 
返回一个流,它包含将 mapper 应用于当前流中所有元素产生的结果。

<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
返回一个流,它是通过将 mapper 应用于当前流中所有元素产生的结果连接到一起而获得的。
示例:
List<List<String>> twoList = new ArrayList<>();
List<String> ls1 = new ArrayList<>();
ls1.add("a");
ls1.add("b");
twoList.add(ls1);
List<String> ls2 = new ArrayList<>();
ls2.add("c");
ls2.add("d");
twoList.add(ls2);
List<String> list = twoList.stream().flatMap(two -> two.stream().filter(s -> !s.equals("c"))).collect(Collectors.toList());
list.forEach(System.out::print); // abd


void forEach(Consumer<? super T> action) 为此流的每个元素执行一个动作。

Stream<T> limit(long maxSize) 返回由该流前 maxSize 元素组成的流。

Stream<T> skip(long n) 返回由该流除前 n 个元素之外的元素组成的流。

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) 
创建一个延迟串联的流,其元素是第一个流的所有元素,然后是第二个流的所有元素。


Stream<T> distinct() 
返回由当前流中所有不同元素组成的流(根据Object.equals(Object))。
Stream<T> sorted() 
返回由该流的元素组成的流,并根据自然顺序排序。
Stream<T> sorted(Comparator<? super T> comparator) 
返回由该流的元素组成的流,并根据提供的Comparator对其进行排序。
Stream<T> peek(Consumer<? super T> action) 
返回一个流,它与当前流中的元素相同,在获取其中每个元素时,会将其传递给 action。


Optional<T> max(Comparator<? super T> comparator) 
根据提供的Comparator返回此流的最大元素。
Optional<T> min(Comparator<? super T> comparator) 
根据提供的Comparator返回此流的最小元素。
boolean	allMatch(Predicate<? super T> predicate) 
返回此流的所有元素是否与提供的predicate匹配;所有元素匹配成功,返回true
boolean	anyMatch(Predicate<? super T> predicate) 
返回此流的任何元素是否与提供的predicate匹配;任意一个元素匹配成功,返回trueboolean	noneMatch(Predicate<? super T> predicate) 
返回此流中是否没有元素与提供的predicate匹配。


long count() 返回当前流中元素的数量。这是一个终止操作。
Object[] toArray() 返回包含此流元素的数组。
<R,A> R	collect(Collector<? super T,A,R> collector) 
使用收集器对此流的元素执行可变还原操作。

6. Base64、Base64.Decoder、Base64.Encoder

6.1 Base64工具提供的编码器

Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:

  • 基本:使用RFC 4648和RFC 2045表1中指定的“Base64字母”进行编码和解码操作。编码器不添加任何换行符,解码器拒绝包含base64字母之外的字符的数据。
  • URL和文件名安全:使用RFC 4648表2中指定的“URL和文件名安全的Base64字母”进行编码和解码。编码器不添加任何换行符,解码器拒绝包含base64字母之外的字符的数据。
  • MIME:使用RFC 2045表1中指定的“Base64字母”进行编码和解码操作。编码后的输出必须以不超过76个字符的行表示,并使用回车符’\r’和紧跟换行符’\n’作为行分隔符,编码输出的末尾没有行分隔符。在base64字母表中找不到的所有行分隔符或其他字符在解码操作中将被忽略。

6.2 获取Base64编码器或解码器

1、获取Base64编码器
Base64.Encoder encoder = Base64.getEncoder();
Base64.Encoder urlEncoder = Base64.getUrlEncoder();
Base64.Encoder mimeEncoder = Base64.getMimeEncoder();

2、获取Base64解码器
Base64.Decoder decoder = Base64.getDecoder();
Base64.Decoder urlDecoder = Base64.getUrlDecoder();
Base64.Decoder mimeDecoder = Base64.getMimeDecoder();

6.3 Base64编码解码示例

try {
    byte[] bt = "A-Za-z0-9+/_编码".getBytes("utf-8");
    // 编码
    Base64.Encoder encoder = Base64.getEncoder();
    Base64.Encoder urlEncoder = Base64.getUrlEncoder();
    Base64.Encoder mimeEncoder = Base64.getMimeEncoder();

    String encoderStr = encoder.encodeToString(bt); // QS1aYS16MC05Ky9f57yW56CB
    String urlEncoderStr = urlEncoder.encodeToString(bt); // QS1aYS16MC05Ky9f57yW56CB
    String mimeEncoderStr = mimeEncoder.encodeToString(bt); // QS1aYS16MC05Ky9f57yW56CB
    
    
 	// 解码
    Base64.Decoder decoder = Base64.getDecoder();
    Base64.Decoder urlDecoder = Base64.getUrlDecoder();
    Base64.Decoder mimeDecoder = Base64.getMimeDecoder();

    String decoderStr = new String(decoder.decode(encoderStr), "utf-8"); // A-Za-z0-9+/_编码
    String urlDecoderStr = new String(urlDecoder.decode(urlEncoderStr), "utf-8"); // A-Za-z0-9+/_编码
    String mimeDecoderStr = new String(mimeDecoder.decode(mimeEncoderStr), "utf-8"); // A-Za-z0-9+/_编码

} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值