JDK8 新增功能 ( Java 语言新特性 )

2021-07-13  本文已影响0人  张云飞Vir

1. 背景

JDK8 是一个主要功能版本。本文档总结了其中的特性和增强功能。

说明:用更少的简洁文字和易读代码来表达。

2. Java 语言新特性

2.1 默认接口方法

在接口里可以写一个 “ 默认方法 ”,它可以在 不强制必须实现这个方法,从而很方便的往现存接口中添加新的方法。

package com.zhangyf.javademo.stage1;

/**
 * 示例:接口中写"默认方法"
 */
interface IDemo {
    void hello(); // 这个方法需要 "被实现"。

    // 默认方法,不需要  "被实现"。
    default void hi() {
        System.out.println("hi,zyf");
    }
}

class Demo1 implements IDemo {

    // 实现类仅仅 "实现 hello 方法就可。
    // 默认方法 hi 无需实现了。
    @Override
    public void hello() {
        System.out.println("hello,zyf");
    }
}

// 执行示例
class Stage1 {

    public static void main(String[] args) {
        Demo1 demo1 = new Demo1();
        demo1.hello();
        demo1.hi();
    }

}

“ 默认方法 ” 功能也称为扩展方法。适用于“ 为现有的类扩展新增新方法功能的场景 ”

2.2 lambda 表达式

/**
 * 示例:Lambda 表达式
 */
public class Stage2 {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("banana", "apple", "mike", "xenia");
        // 使用 Lambda 时:很简洁。
        Collections.sort(names, (a, b) -> a.compareTo(b));
        System.out.println("排序后:" + names);
    }
}

再看下,当不用 Lambda ,传统写法,很长。

        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });

java 编译器知道参数类型,因此您也可以省略来写。

2.3 函数式接口

单方法接口(称为函数式接口)

函数式接口必须只包含一个抽象方法声明,为了防止意外的添加第二方法而导致错误,可以用 @FunctionalInterface 注解来加强声明定义,声明后编译器会帮你做检查符合函数式接口。

函数式接口 配合 Lambda 表达式可以更简洁地表达。

// 定义一个 " 函数式接口 ",它必须只有一个方法。
@FunctionalInterface
interface MyConverter<FROM, TO> {
    TO convert(FROM from);
}

class MainClass3 {

    public static void main(String[] args) {
        //使用 lambda 实现了一个 "函数式接口": 将 字符串 转换成 整数
        MyConverter<String, Integer> myConvert = (from) -> Integer.valueOf(from);
        //使用 "函数式接口"
        int intPara = myConvert.convert("1234");

        System.out.println(intPara);
    }

}

2.4 方法引用 ( 通过 :: 和方法名称 )

Java 8 允许您通过::关键字传递方法的引用。上面的例子进一步简化:

@FunctionalInterface
interface MyConverter1<FROM, TO> {
    TO convert(FROM from);
}

class MainClass4 {
    public static void main(String[] args) {
        MyConverter1<String, Integer> myConvert = Integer::valueOf;
        int intPara = myConvert.convert("1234");
        System.out.println(intPara);
    }
}

上面代码中的 Integer::valueOf 意思是 引用 Integer.valueOf 方法,可以作为参数传递使用了。

2.5 "方法引用" 也可以引用构造方法

class User {
    String name;
    int age;

    // 构造方法1:无参
    public User() {
    }

    // 构造方法2:两个参数
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return name + "_" + age;
    }
}

// 一个工厂。注意是个 接口, 不是实现类
interface UserFactory {
    User create(String name, int age);
}

// 调用者
class MainClass5 {
    public static void main(String[] args) {
        // 通过方法引用了 User类的构造方法,好像 "粘" 在了一起。
        UserFactory userFactory = User::new;
        // 编译器通过推断,识别到 "这是两个参数的构造方法"
        User user = userFactory.create("zyf", 30);
        System.out.println(user);
    }
}

2.6 接口里可写“静态方法”

interface InterfaceDemo6 {
    static void createEntity() {
        System.out.println("createEntity....");
    }
}

// 调用者
class MainClass6 {
    public static void main(String[] args) {
        InterfaceDemo6.createEntity();
    }
}

2.7 可重复注解(一个注解可在方法里写多次)

以前在同一个地方不能多次使用同一个注解。Java 8引入了重复注解的概念,允许在同一个地方多次使用同一个注解。

@interface Hints {
    Hint[] value();
}

@Repeatable(Hints.class)
@interface Hint {
    String value();
}

@Hint("hint1")
@Hint("hint2")
class Person {
    ...
}

3. 扩展:内置的函数接口

JDK 1.8 API 包含许多内置 函数式接口(功能接口),这些接口经过 @FunctionalInterface 注解后 扩展已可以对 Lambda 支持。

3.1 谓词(Predicates)

根据给定的参数计算该谓词, 返回一个 boolean 结果。方法原型:

    boolean test(T t);

示例:

        // 谓词 Predicate
        Predicate<String> n1 = (s) -> s.length() > 0;
        boolean foo = n1.test("foo"); // true
        Predicate n2 = Objects::isNull;
        Predicate<String> n3 = String::isEmpty;

3.2 Functions

它可以引用一个 指定 一个参数,和返回值 的方法。
方法原型:

R apply(T t);

示例:

     // 功能 Function
        Function<String, Integer> f1 = Integer::valueOf;
        Integer interge1 = f1.apply("33432");

3.3 提供者 Supplier

它可以引用一个 构造方法。用于 返回一个对象
方法原型:

    T get();

示例:

// 提供者 Supplier ,它可以引用一个 构造方法
        Supplier supplier1 = Object::new;
        supplier1.get();

3.4 Consumers 消费者

Consumers, 它可以引用 "一个参数",无返回值的方法
方法原型:

    void accept(T t);

示例:

        Consumer<String> consumer1 = (str) -> System.out.println(str);
        Consumer<String> consumer2 = System.out::println;
        consumer2.accept("hello");

3.5 比较器 Comparator

方法原型:

    int compare(T o1, T o2);

示例:

Comparator<String> comparator1 = (p1, p2) -> p1.compareTo(p2);
comparator1.compare("P1","p3");

3.6 “可选的” Optional

Optional, 它不是一个 函数式接口。它 用于辅助判断 空值

        Optional<String> optional1 = Optional.of("some");
        optional1.isPresent(); // 有值,则 true
        optional1.get();

4. 流:Stream

Steam API极大得简化了集合操作,非常地强大。

// 准备演示数据
List<String> lst = new ArrayList<>();
lst.add("s1");
lst.add("s99");
lst.add("s3");
lst.add("ccc");
lst.add("zzz");
lst.add("111");

System.out.println("-------- 示例:filter ---------");
// filter 过滤器:接收一个表达式判断,返回boolean值,决定是否过滤掉
lst.stream().filter((it) -> it.startsWith("s")).forEach(System.out::println);

System.out.println("-------- 示例:sorted ---------");
// sorted:排序
lst.stream().sorted().forEach(System.out::println);

System.out.println("-------- 示例:map ---------");
// map:对子元素 map 映射后,成为新的集合
lst.stream().map(String::toUpperCase).forEach(System.out::println);

System.out.println("-------- 示例:匹配 ---------");
// anyMatch: 只要有一个 匹配成功
boolean result1 = lst.stream().anyMatch((s) -> s.startsWith("c"));
System.out.println(result1);// true
// allMatch: 都匹配成功,则为 true
boolean result2 = lst.stream().allMatch((s) -> s.startsWith("c"));
System.out.println(result2);// fasle
// noneMatch : 都不匹配,则为 true
boolean result3 = lst.stream().noneMatch((s) -> s.startsWith("$"));
System.out.println(result3);//

System.out.println("-------- 示例:count ---------");
// count: 计算总数。 先过滤 s 开头的,再计算个总数
long count1 = lst.stream().filter((p) -> p.startsWith("s")).count();
System.out.println("count1=" + count1);//

System.out.println("-------- 示例:reduce ---------");
// reduce: 归纳。 传入两个参数,返回一个结果
Optional<String> optional = lst.stream().sorted().reduce((t1, t2) -> t1 + "," + t2);
System.out.println("optional=" + optional);//

stream 也支持并行处理
参考我的另一篇文章:https://www.jianshu.com/p/941fc03867ff

5.扩展:日期 API

Java 8 在包下包含一个全新的日期和时间的API。

// 获得 系统的默认时区对应的 时钟对象
Clock clock = Clock.systemDefaultZone();
// 获得毫秒值
long millis = clock.millis();
System.out.println("millis: " + millis);

// 本地时间
LocalTime now1 = LocalTime.now();
System.out.println("now1: " + now1); // 23:34:46.112

// 本地时间
LocalDate now2 = LocalDate.now();
System.out.println("now2: " + now2); // 2021-07-13

// 本地时间
LocalDateTime now3 = LocalDateTime.now();
System.out.println("now2: " + now3); // 2021-07-13T23:34:46.113

// 持续时间
LocalDateTime start = LocalDateTime.now();
Thread.sleep(1234);
LocalDateTime end = LocalDateTime.now();
// 两个时间之间的差
Duration duration = Duration.between(start, end);
long mill = duration.toMillis();
System.out.println("mill: " + mill);

我的代码示例见:https://github.com/vir56k/java_demo/tree/master/java_new_feature_demo

6.参考:

更多新特性请阅读:https://www.oracle.com/java/technologies/javase/8-whats-new.html

Java 语言更新
https://docs.oracle.com/en/java/javase/15/language/java-language-changes.html#GUID-6459681C-6881-45D8-B0DB-395D1BD6DB9B

https://docs.oracle.com/javase/tutorial/java/index.html

https://docs.oracle.com/en/java/javase/15/language/local-variable-type-inference.html

https://www.oracle.com/java/technologies/javase/8-whats-new.html

上一篇下一篇

猜你喜欢

热点阅读