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
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
网友评论