java8新特征
java8新特征
JinJava 8 (又称为 JDK 8 或 JDK1.8) 是 Java 语言开发的一个主要版本。 Java 8
是 oracle 公司于 2014 年 3 月发布,可以看成是自 Java 5 以来最具革命性的版
本。Java 8 为 Java 语言、编译器、类库、开发工具与 JVM 带来了大量新特性
Lambda 表达式
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递
的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代
码。作为一种更紧凑的代码风格,使 Java 的语言表达能力得到了提升
个人感觉类似 js 里的箭头函数
操作符 ->
格式 参数列表 -> 方法体
代码举例
无参,无返回值
@Test
public void test4() {
// 普通格式
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("哈哈哈哈");
}
};
r1.run();
// lambda 表达式
Runnable r2 = () -> {
System.out.println("lambda");
};
r2.run();
}
需要一个参数,无返回值
@Test
public void test5() {
// 普通格式
Consumer<String> consumer1 = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer1.accept("哈哈哈");
// lambda 表达式
Consumer<String> consumer2 = (String s) -> {
System.out.println(s);
};
consumer2.accept("lambda");
/* 基于上面可优化
数据类型可以省略,因为可由编译器推断得出,称为 类型推断
Lambda 若只需要一个参数时,参数的小括号可以省略
*/
Consumer<String> consumer3 = s -> {
System.out.println(s);
};
consumer3.accept("lambda");
}
需要两个或以上的参数,多条执行语句,并且有返回值
@Test
public void test6() {
// 普通格式
Comparator<Integer> comparator1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println("o1: " + o1 + " o2: " + o2);
return o1.compareTo(o2);
}
};
int compare1 = comparator1.compare(3, 6);
System.out.println(compare1);
// lambda 表达式
Comparator<Integer> comparator2 = (o1, o2) -> {
System.out.println("o1: " + o1 + " o2: " + o2);
return o1.compareTo(o2);
};
int compare2 = comparator2.compare(6, 3);
System.out.println(compare2);
}
lambda 表达式只有一条语句 可以省略大括号 返回值即是这一条语句
@Test
public void test7() {
// 优化上面
Runnable r2 = () -> System.out.println("lambda");
Consumer<String> consumer3 = s -> System.out.println(s);
Comparator<Integer> comparator2 = (o1, o2) -> o1.compareTo(o2);
}
函数式接口
包含一个抽象方法的接口,称为函数式接口。当然该接口可以包含其他非抽象方法,通过 Lambda 表达式来创建该接口的对象
接口上使用 @FunctionalInterface 注解,检查它否是一个函数式接口。javadoc 也会包含一条声明,说明这个接口是一个函数式接口
java.util.function
包下定义了 Java 8 的丰富的函数式接口
四大核心函数式接口
函数式接口 | 称谓 | 参数类型 | 用途 |
---|---|---|---|
Consumer |
消费型接口 | T | 对类型为 T 的对象应用操作,包含方法:void accept(T t) |
Supplier |
供给型接口 | 无 | 返回类型为 T 的对象,包含方法:T get() |
Function<T,R> | 函数型接口 | T | 对类型为T的对象应用操作,并返回结果,结果是 R 类型的对象。包含方法:Rapply(T t) |
Predicate |
判断型接口 | T | 确定类型为 T 的对象是否满足某约束,并返回 boolean 值。包含方法:boolean test(T t) |
消费型接口
特点:有形参,但是返回值类型是 void
接口名 | 抽象方法 | 描述 |
---|---|---|
BiConsumer<T,U> | void accept(T t, U u) | 接收两个对象用于完成功能 |
DoubleConsumer | void accept(double value) | 接收一个 double 值 |
IntConsumer | void accept(int value) | 接收一个 int 值 |
ObjLongConsumer | void accept(int value) | 接收一个 对象 和 long 类型 |
还有好几个…Consumer,LongConsumer,ObjDoubleConsumer,ObjIntConsumer 大致和上面一样,见名知意
供给型接口
特点:无参,但是有返回值
接口名 | 抽象方法 | 描述 |
---|---|---|
BooleanSupplier | boolean getAsBoolean() | 返回布尔值 |
DoubleSupplier | double getAsDouble() | 返回 double |
IntSupplier | int getAsInt() | 返回 int |
LongSupplier | long getAsLong() | 返回 long |
函数型接口
特点:既有参数又有返回值
接口名 | 抽象方法 | 描述 |
---|---|---|
UnaryOperator | T apply(T t) | 接收一个 T 类型对象,返回一个 T 类型对象结果 |
DoubleFunction | R apply(double value) | 接收一个 double 值,返回一个 R 类型对象 |
ToIntFunction | int applyAsInt(T value) | 接收一个 T 类型对象,返回一个 int |
IntToDoubleFunction | double applyAsDouble(int value) | 接收一个 int 值,返回一个 double 结果 |
IntUnaryOperator | int applyAsInt(int operand) | 接收一个 int 值,返回一个 int 结果 |
BiFunction<T,U,R> | R apply(T t, U u) | 接收一个 T 类型和一个 U 类型对象,返回一个 R 类型对象结果 |
BinaryOperator | T apply(T t, T u) | 接收两个 T 类型对象,返回一个 T 类型对象结果 |
ToLongBiFunction<T,U> | long applyAsLong(T t, U u) | 接收一个 T 类型和一个 U 类型对象,返回一个 long |
还有更多,根本写不完,大致形如 xxFunction,xxtoxxFunction, xxOperator ToxxFunction等等,见名知意
更多查看 菜鸟教程
判断型接口
特点:有参,但是返回值类型是 boolean 结果
接口名 | 抽象方法 | 描述 |
---|---|---|
BiPredicate<T,U> | boolean test(T t, U u) | 接收两个对象 |
DoublePredicate | boolean test(double value) | 接收一个 double 值 |
IntPredicate | boolean test(int value) | 接收一个 int 值 |
LongPredicate | boolean test(long value) | 接收一个 long 值 |
代码举例
消费型
@Test
public void test8(){
List<String> list = Arrays.asList("1212", "qwe", "wads");
// default void forEach(Consumer<? super T> action)
// 普通方法 匿名内部类
list.forEach(new Consumer<String>() {
@Override
public void accept(String element) {
System.out.println(element);
}
});
// lambda 表达式
list.forEach(s -> System.out.println(s));
}
供给型
public void test9(){
// 普通方法 匿名内部类
Supplier<String> supplier1 = new Supplier() {
@Override
public Object get() {
return "hello world";
}
};
// lambda
Supplier<String> supplier2 = ()->"hello lambda";
System.out.println(supplier1.get());
System.out.println(supplier2.get());
}
判断型
@Test
public void test10(){
List<String> aslist = Arrays.asList("123", "456", "789");
ArrayList<String> list = new ArrayList<>(aslist);
// 去除包含 456 的值 普通方法 匿名内部类
list.removeIf(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("456");
}
});
// lambda 去除包含3
list.removeIf(s->s.contains("3"));
list.forEach(t-> System.out.println(t));
}
函数型
@Test
public void test11(){
//可以实现将一个字符串首字母转为大写的功能
// 普通方法 匿名内部类
Function<String,String> fun = new Function<String, String>() {
@Override
public String apply(String s) {
return s.substring(0,1).toUpperCase() + s.substring(1);
}
};
String world = fun.apply("world");
System.out.println(world);
//使用 Lambda 表达式实现 Function<T,R>接口
Function<String,String> function = s -> s.substring(0,1).toUpperCase() + s.substring(1);
String hello = function.apply("hello");
System.out.println(hello);
}
方法引用与构造器引用
方法引用
Lambda 表达式是可以简化函数式接口的变量或形参赋值的语法。而方法引用
和构造器引用是为了简化 Lambda 表达式的
方法引用是 Lambda 表达式的一个语法糖
方法引用操作符::
,将类(或对象) 与 方法名分隔开来
三种情况
- 对象 :: 实例方法名
- 类 :: 静态方法名
- 类 :: 实例方法名
使用前提
Lambda 体只有一句语句,并且是通过调用一个对象的/类现有的方法来完成的
针对情况一:对象 :: 实例方法名
函数式接口中的抽象方法 a 在被重写时使用了某一个对象的方法b。如果方法 a 的形参列表、返回值类型与方法 b 的形参列表、返回值类型都相同,则我们可以使用方法 b 实现对方法 a 的重写、替换
例如
Consumer 中的 void accept(T t)
PrintStream 中的 void println(T t)
@Test
public void test13() {
// lambda 表达式
Consumer<String> consumer1 = s -> System.out.println(s);
consumer1.accept("hello");
// 方法引用
PrintStream ps = System.out;
Consumer<String> consumer2 = ps::println;
consumer2.accept("world");
}
Supplier 中的 T get()
Employee 中的 String getName()
@Test
public void test14() {
Employee employee = new Employee(1001, "zs", '1', 18, 23445);
// lambda 表达式
Supplier<String> supplier1 = () -> employee.getName();
String name1 = supplier1.get();
System.out.println(name1);
// 方法引用
Supplier<String> supplier2= employee::getName;
String name2 = supplier2.get();
System.out.println(name2);
}
针对情况二:类 :: 静态方法
函数式接口中的抽象方法 a 在被重写时使用了某一个类的静态方法 b。如果方法 a 的形参列表、返回值类型与方法 b 的形参列表、返回值类型都相同,则我们可以使用方法 b 实现对方法 a 的重写、替换
例如
Comparator 中的 int compare(T t1,T t2)
Integer 中的 int compare(T t1,T t2)
@Test
public void test15(){
// lambda 表达式
Comparator<Integer> comparator = (t1,t2)-> Integer.compare(t1,t2);
System.out.println(comparator.compare(3, 6));
// 方法引用
Comparator<Integer> comparator1 =Integer::compareTo;
System.out.println(comparator1.compare(6, 3));
}
Function 中的 R apply(T t)
Math 中的 Long round(Double d)
@Test
public void test16(){
// lambda 表达式
Function<Double,Long> func = t->Math.round(t);
System.out.println(func.apply(12.1));
// 方法引用
Function<Double,Long> func1 = Math::round;
System.out.println(func1.apply(12.8));
}
针对情况三:类 :: 实例方法
函数式接口中的抽象方法 a 在被重写时使用了某一个对象的方法b。如果方法 a 的返回值类型与方法 b 的返回值类型相同,同时方法 a 的形参列表中有 n 个参数,方法 b 的形参列表有 n-1 个参数,且方法 a 的第 1 个参数作为方法 b 的调用者,且方法 a 的后 n-1 参数与方法 b 的 n-1 参数匹配(类型相同或满足多态场景也可以)
Comparator 中的 int compare(T t1,T t2)
String 中的 int t1.compareTo(t2)
@Test
public void test17() {
// lambda 表达式
Comparator<String> comparator = (t1, t2) -> t1.compareTo(t2);
System.out.println(comparator.compare("1", "2"));
// 方法引用
Comparator<String> comparator1 = String::compareTo;
System.out.println(comparator1.compare("2", "1"));
}
BiPredicate 中的 boolean test(T t1, T t2);
String 中的 boolean t1.equals(t2)
@Test
public void test18(){
// lambda 表达式
BiPredicate<String,String> biPredicate = (t1,t2)-> t1.equals(t2);
System.out.println(biPredicate.test("111", "111"));
// 方法引用
BiPredicate<String,String> biPredicate2 = String::equals;
System.out.println(biPredicate2.test("111", "222"));
}
Function 中的 R apply(T t)
Employee 中的 String getName()
@Test
public void test19(){
Employee employee = new Employee(1001, "zs", '1', 18, 23445);
// lambda 表达式
Function<Employee,String> ef = t->t.getName();
System.out.println(ef.apply(employee));
// 方法引用
Function<Employee,String> ef1 = Employee::getName;
System.out.println(ef1.apply(employee));
}
构造器引用
当 Lambda 表达式是创建一个对象,并且满足 Lambda 表达式形参,正好是给创建这个对象的构造器的实参列表,就可以使用构造器引用
格式:类名::new
代码举例
Supplier 中的 T get()
Employee 的空参构造器:Employee()
@Test
public void test20(){
// lambda 表达式
Supplier<Employee> supplier1 = ()-> new Employee();
System.out.println(supplier1.get());
// 方法引用
Supplier<Employee> supplier2=Employee::new;
System.out.println(supplier2.get());
}
BiFunction 中的 R apply(T t,U u)
Employee 的构造器:Employee(int id, String name)
public void test21(){
// lambda 表达式
BiFunction<Integer,String,Employee> biFunction1 = (t1,t2)->new Employee(t1,t2);
System.out.println(biFunction1.apply(1, "zs"));
// 方法引用
BiFunction<Integer,String,Employee> biFunction2 = Employee::new;
System.out.println(biFunction2.apply(2, "ls"));
}
数组构造引用
当 Lambda 表达式是创建一个数组对象,并且满足 Lambda 表达式形参,正好是给创建这个数组对象的长度,就可以数组构造引用
格式:数组类型名::new
代码举例
数组引用
Function 中的 R apply(T t)
@Test
public void test22(){
// lambda 表达式
Function<Integer,String[]> function1 = len -> new String[len];
String[] arr1 = function1.apply(10);
System.out.println(Arrays.toString(arr1));
// 方法引用
Function<Integer,String[]> function2 = String[]::new;
String[] arr2 = function2.apply(6);
System.out.println(Arrays.toString(arr2));
}
Stream API
Stream API ( java.util.stream) 把真正的函数式编程风格引入到 Java 中。这是目前为止
对 Java 类库最好的补充,因为 Stream API 可以极大提供 Java 程序员的生产力,让程
序员写出高效率、干净、简洁的代码
Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列
注意点:
- Stream 不会自己存储数据
- Stream 不会改变源对象,返回一个持有结果的新 Stream
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果
- Stream 一旦执行了终止操作,就不能再调用其它中间操作或终止操作了
创建步骤
- 创建 Stream 一个数据源(如:集合、数组),获取一个流
- 中间操作,处理返回 Stream对象
- 终止操作
- 终止操作 返回值类型就不再是 Stream 了,因此一旦执行终止操作,就结束整个 Stream 操作
创建 stream 实例的方法
集合:Java8 中的 Collection 接口被扩展,提供了两个获取流的方法
- default Stream stream() : 返回一个顺序流
- default Stream parallelStream() : 返回一个并行流
数组:Java8 中的 Arrays 的静态方法 stream() 可以获取数组流
- static Stream stream(T[] array): 返回一个流
- public static IntStream stream(int[] array)
- public static LongStream stream(long[] array)
- public static DoubleStream stream(double[] array)
Stream 的 of():调用 Stream 类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数
- public static Stream of(T… values) : 返回一个流
创建无限流:使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流
- 迭代 public static Stream iterate(final T seed, final UnaryOperator f)
- 生成 public static Stream generate(Supplier s)
代码如下:
@Test
public void test1(){
// 通过集合
List<String> list = Arrays.asList("123", "222", "333");
Stream<String> stream = list.stream();
// 通过数组
String[] arr = {"1","2","3"};
Stream<String> stream1 = Arrays.stream(arr);
System.out.println(stream1);
// 通过 Stream 的 of()
Stream<String> stream2 = Stream.of("1", "2", "3");
// 创建无限流
// 迭代 偶数
// iterate(final T seed, final UnaryOperator f)
Stream<Integer> stream3 = Stream.iterate(0, x -> x + 2);
stream3.limit(10).forEach(System.out::println);
// 生成 随机数
// generate(Supplier<T> s)
Stream<Double> stream4 = Stream.generate(Math::random);
stream4.limit(10).forEach(System.out::println);
}
中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”
常用方法
筛选和切片
方 法 | 描 述 |
---|---|
filter(Predicatep) | 接收 Lambda , 从流中排除某些元素 |
distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流,若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
映射
方 法 | 描述 |
---|---|
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
排序
方 法 | 描述 |
---|---|
sorted() | 产生一个新流,其中按自然顺序排序 |
sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 |
代码举例
@Test
public void test2(){
// 1. 通过 Stream 的 of() 创建流
Stream<String> stream = Stream.of("123", "223", "333","233","256");
// 2 .中间操作 filter 过滤 返回包含2的值的新stream
// filter(Predicate p)
stream = stream.filter(t -> t.contains("2"));
// limit 截断流,使其元素不超过给定数量
// 取 3 个
stream = stream.limit(3);
// ... 可以加更多操作
// 3. 终止操作 例如循环遍历
stream.forEach(System.out::println);
}
@Test
public void test3(){
//希望能够找出前三个最大值,前三名最大的,不重复
Stream.of(11,2,39,4,54,6,2,22,3,3,4,54,54)
// 去除重复
.distinct()
// 排序 int compare(T t1, T t2)
.sorted((t1,t2)->-Integer.compare(t1,t2))
// 取前3
.limit(3)
// 遍历
.forEach(System.out::println);
// 转大写
String[] arr ={"hello","java","mysql","spring"};
Arrays.stream(arr).map(String::toUpperCase).forEach(System.out::println);
}
终止操作
终止操作会从流的中间中间操作(流水线)生成结果,进行了终止操作后,不能再次使用
终止操作的方法
匹配与查找
方法 | 描述 |
---|---|
allMatch | anyMatch | noneMatch (Predicate p) | 检查是否匹配 所有 | 至少一个 | 没有匹配所有 的元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max | min (Comparator c) | 返回流中最 大 | 小 值 |
forEach(Consumer c) | 内部迭代 |
归约
方法 | 描述 |
---|---|
reduce(T identity,BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 T (累积器) |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 Optional |
收集
方法 | 描述 |
---|---|
collect(Collector c) | 将流转换为其他形式。接收一个 Collector 接口的实现,例如将流搜集到 list, map等等 |
Collectors 类
Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例
方法 | 描述 |
---|---|
toList/Set/Collection() | 把流中元素收集到 List / Set / Collection |
counting() | 计算流中元素的个数 |
summingInt() | 对流中元素的整数属性求和 |
averagingInt() | 计算流中元素 Integer 属性的平均值 |
summarizingInt() | 收集流中 Integer 属性的统计值。如:平均值 |
joining() | 连接流中每个字符串 |
max/minBy() | 根据比较器选择最大/小值 |
reducing() | 从一个作为累加器的初始值开始,利用 BinaryOperator 与流中元素逐个结合,从而归约成单个值 |
collectingAndThen() | 包裹另一个收集器,对其结果转换函数 |
groupingBy() | 根据某属性值对流分组,属性为K,结果为 V |
partitioningBy() | 根据 true 或 false 进行分区 |
代码举例
@Test
public void test4(){
// 1. 通过 Stream 的 of() 创建流
Stream<String> stream = Stream.of("123", "233", "222","233","256");
// 2 .中间操作
// limit 截断流,使其元素不超过给定数量
// 取 3 个
stream = stream.limit(3);
// 3. 终止操作 例如 用allMatch 看当前 stream 都是否包含 2
boolean allMatch = stream.allMatch(t -> t.contains("2"));
System.out.println(allMatch); // true
// reduce
// 将 stream 的值从0开始累加
Integer reduce = Stream.of(1,2,4,5,7,8)
.reduce(0, (t1,t2) -> t1+t2);
System.out.println(reduce); // 27
// stream 的值取最大值 三元运算符
Optional<Integer> max = Stream.
of(3, 4, 5, 6, 1, 2, 3).
reduce((t1, t2) -> t1 > t2 ? t1 : t2);
System.out.println(max); //Optional[6]
// collect 利用Collectors创建常见收集器实例
// toList/Set/Collection()
List<Integer> c = Stream.of(1, 2, 3, 4, 5, 6).collect(Collectors.toList());
System.out.println(c); //[1, 2, 3, 4, 5, 6]
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee(1,"java"));
employees.add(new Employee(2,"py"));
employees.add(new Employee(3,"php"));
employees.add(new Employee(4,"c++"));
// 平均值 averagingInt()
Double c1 = employees.stream().collect(Collectors.averagingInt(Employee::getId));
System.out.println(c1); // 2.5
// 统计值 summarizingInt()
IntSummaryStatistics c2 = employees.stream().collect(Collectors.summarizingInt(Employee::getId));
System.out.println(c2); // IntSummaryStatistics{count=4, sum=10, min=1, average=2.500000, max=4}
// joining 连接流每一个字符串
String c3 = employees.stream().map(Employee::getName).collect(Collectors.joining());
System.out.println(c3); //javapyphpc++
// reducing 累加器
Integer c4 = employees
.stream()
.collect(Collectors.reducing(0, Employee::getId, Integer::sum));
System.out.println(c4); //10
}
java9 新增Stream 实例化方法 ofNullable()
Java 8 中 Stream 不能完全为 null,否则会报空指针异常。而 Java 9 中的
ofNullable 方法允许我们创建一个单元素 Stream,可以包含一个非空元素,也
可以创建一个空 Stream
//ofNullable():允许值为 null
Stream<Object> stream1 = Stream.ofNullable(null);
System.out.println(stream1.count());//0
iterator()重载的使用:
// 创建无限流
@Test
public void test5(){
// 旧方法 limit 限制
Stream.iterate(1,i -> i + 1).limit(10).forEach(System.out::println);
// 新 参数 i -> i < 10 限制
Stream.iterate(1,i -> i < 10,i -> i + 1).forEach(System.out::println);
}
新语法特征
try-catch
(JDK7 的新特性)在 try 的后面可以增加一个(),在括号中可以声明流对象并初始化。try 中的代码
执行完毕,会自动把流对象释放,就不用写 finally 了
例如
@Test
public void test02() {
try (
FileWriter fw = new FileWriter("hello.txt");
BufferedWriter bw = new BufferedWriter(fw);
) {
bw.write("hello");
} catch (IOException e) {
e.printStackTrace();
}
}
- 在 try()中声明的资源,无论是否发生异常,无论是否处理异常,都会自动关闭资源对象
- 这些资源实现类必须实现 AutoCloseable 或 Closeable 接口,实现其中的 close() 方法
- 写到 try()中的资源类的变量默认是 final 声明的,不能修改
局部变量类型推断
JDK 10 的新特性 局部变量的类型推断
例如
//1.局部变量的实例化
var list = new ArrayList<String>();
var set = new LinkedHashSet<Integer>();
// for循环
for (var v : list) {
System.out.println(v);
}
for (var i = 0; i < 100; i++) {
System.out.println(i);
}
// 返回值类型含复杂泛型结构
var iterator = set.iterator();
不适用场景:
- 声明一个成员变量,数组变量(静态初始化)
- 方法的返回值类型,参数类型
- 没有初始化的方法内的局部变量声明
- 作为 catch 块中异常类型
- Lambda 表达式,方法引用中函数式接口的类型
注:这不是 JavaScript。var 并不会改变 Java 是一门静态类型语言的事实。编译器负责推断出类型,并把结果写入字节码文件,就好像是开发人员自己敲入类型一样
instanceof
(JDK14) instanceof 模式匹配提供更简便的语法 自动添加强转
例如
public void test6(){
Object obj = new String("hello");
// 旧方法
if(obj instanceof String){
// 强转
String str = (String) obj;
// 使用 String类的方法
System.out.println(str.contains("hello"));
}else System.out.println("type not String");
// 新特征 省去强转换的过程
if(obj instanceof String str){
// 使用 String类的方法
System.out.println(str.contains("hello"));
}else System.out.println("type not String");
}
switch 表达式
(JDK12) 对 switch 声明语句进行扩展,使用
case L ->
来替代以前的break;
,省去了 break 语句,避免了因少写 break 而出错注意:保持兼容性,case 条件语句中依然可以使用字符: ,但是同一个 switch 结构里不能混用
->
和:
,否则编译错误
enum Fruit {
PEAR, APPLE, GRAPE, MANGO, ORANGE, PAPAYA;
}
public void test{
int numberOfLetters;
Fruit fruit = Fruit.APPLE;
// 原写法
switch (fruit) {
case PEAR:
numberOfLetters = 4;
break;
case APPLE:
case GRAPE:
case MANGO:
numberOfLetters = 5;
break;
case ORANGE:
case PAPAYA:
numberOfLetters = 6;
break;
default:
throw new IllegalStateException("No Such Fruit:" + fruit);
}
System.out.println(numberOfLetters);
// java 12 写法
// 将多个 case 合并到一行 整个 switch 作为表达式返回值
numberOfLetters = switch (fruit) {
case PEAR -> 4;
case APPLE, MANGO, GRAPE -> 5;
case ORANGE, PAPAYA -> 6;
default -> throw new IllegalStateException("No Such Fruit: " + fruit);
};
System.out.println(numberOfLetters);
}
JDK13 中引入了 yield 语句,用于返回值
这意味着,switch 表达式(返回值)应该使用 yield,switch 语句(不返回值)应该使用 break
yield 和 return 的区别在于:return 会直接跳出当前循环或者方法,而 yield 只会跳出当前 switch 块
public void test(){
String x = "3";
int i;
// 原写法
switch (x) {
case "1":
i=1;
break;
case "2":
i=2;
break;
default:
i = x.length();
break;
}
System.out.println(i);
// java13 写法
int i = switch (x) {
case "1" -> 1;
case "2" -> 2;
default -> {
yield 3;
}
};
int i = switch (x) {
case "1":
yield 1;
case "2":
yield 2;
default:
yield 3;
};
System.out.println(i);
}
JDK17 的预览特性:switch 的模式匹配
在 switch 上支持 Object 类型,这就等于同时支持多种类型,使用模式匹
配得到具体类型,大大简化了语法量
// 旧写法
public String fm(Object obj){
String formatted = "unknown";
if(obj instanceof Integer i){
formatted= String.format("int %d", i);
} else if (obj instanceof String s) {
formatted= String.format("String %s", s);
}else if (obj instanceof Double d) {
formatted= String.format("Double %f", d);
}
return formatted;
}
// switch 的模式匹配
public String newFm(Object obj){
return switch (obj){
case Integer i ->String.format("int %d", i);
case String s -> String.format("String %s", s);
case Double d-> String.format("Double %f", d);
default -> obj.toString();
};
}
文本块
JDK13 的新特性
使用 “”” 作为文本块的开始符和结束符,在其中就可以放置多行的字符串,不需要进行任何转义。因此,文本块将提高 Java 程序的可读性和可写性
例如
line1
line2
line3
写成 String 字符串
// 原方法
String str = "line1\nline2\nline3\n";
// 使用"""
String str1 = """
line1
line2
line3
""";
例如 JSON 字符串
{
"name":"zs",
"address":"guangdong",
"email":"zs@126.com"
}
String myJson = "{\n" +
"
\"name\":\"Song Hongkang\",\n" +
"
\"address\":\"www.atguigu.com\",\n" +
"
\"email\":\"shkstart@126.com\"\n" +
"}";
// 新特征
String myJson1 = """
{
"name":"Song Hongkang",
"address":"www.atguigu.com",
"email":"shkstart@126.com"
}""";
JDK14 中二次预览特性
\
:取消换行操作
\s
:表示一个空格
record
JDK14 中预览特性:神说要用 record,于是就有了。实现一个简单的数据载体类,为了避免编写:构造函数,访问器,equals(),hashCode () ,toString ()等,Java 14 推出 record
当你用 record 声明一个类时,该类将自动拥有以下功能:
- 获取成员变量的简单方法
- 一个 equals 方法的实现,执行比较时会比较该类的所有成员属性
- 重写 hashCode() 方法
- toString() 方法
- 只有一个构造器
- 可以自己定义静态字段、静态方法、构造器或实例方法
例如
recordv Dog(String name,int age){};
@Test
public void test7(){
Dog dog1 = new Dog("aa", 3);
Dog dog2 = new Dog("bb", 2);
// toString
System.out.println(dog1.toString()); //Dog[name=aa, age=3]
// getName -> name
System.out.println(dog1.name()); // aa
System.out.println(dog2.age()); // 2
}
最终JDK16 中转正特性
record 的设计目标是提供一种将数据建模为数据的好方法。它也不是JavaBeans 的直接替代品另外 JavaBeans 通常是可变的,而记录是不可变的。尽管它们的用途有点像,但记录并不会以某种方式取代 JavaBean
密封类
java 15,引入了 sealed 类,被 sealed 修饰的类可以指定子类。这样这个类就只能被指定的类继承\
具体使用:
- 使用修饰符 sealed,可以将一个类声明为密封类。密封的类使用保留关键字permits 列出可以直接扩展(即 extends)它的类。
- sealed 修饰的类的机制具有传递性,它的子类必须使用指定的关键字进行修饰,且只能是 final、sealed、non-sealed 三者之一
例如
// Circle Rectangle Square 类 可以继承Shape
public abstract sealed class Shape permits Circle, Rectangle, Square{...}
//non-sealed 表示可、以允许任何类继承
public non-sealed class Square extends Shape {...}
JDK17 中转正特性
api 变化
Optional 类
Optional<T>
类(java.util.Optional) 是一个容器类,它可以保存类型 T 的值,代
表这个值存在。或者仅仅保存 null,表示这个值不存在。如果值存在,则
isPresent()方法会返回 true,调用 get()方法会返回该对象
创建 Optional 类对象的方法:
- Optional empty() :用来创建一个空的 Optional 实例
- Optional of(T value) :用来创建一个 Optional 实例,value 必须非空
- Optional ofNullable(T value):用来创建一个Optional 实例,可空,可非空
判断 Optional 容器中是否包含对象:
- isPresent() : 判断 Optional 容器中的值是否存在
- ifPresent(Consumer<? super T> consumer) :判断 Optional 容器中的值是否存在,如果存在,就对它进行 Consumer 指定的操作,如果不存在就不做
获取 Optional 容器的对象:
- T get(): 如果调用对象包含值,返回该值。否则抛异常。T get()与 of(T value)配合使用
- T orElse(T other):与 ofNullable(T value)配合使用,如果Optional 容器中非空,就返回所包装值,如果为空,就用 orElse(T other) other 指定的默认值(备胎)代替
- T orElseThrow(Supplier<? extends X> exceptionSupplier):如果 Optional 容器中非空,就返回所包装值,如果为空,就抛出你指定的异常类型代替原来的 NoSuchElementException
@Test
public void test8(){
Employee employee = new Employee(10, "zs");
Optional<Employee> employee1 = Optional.of(employee);
System.out.println(employee1);
Optional<Object> empty = Optional.empty();
System.out.println(empty);
Optional<Employee> employee2 = Optional.ofNullable(employee);
System.out.println(employee2);
Employee employee3 = employee2.get();
Employee employee4 = employee2.orElse(new Employee());
}
新特征
新增方法 | 描述 | 新增版本 |
---|---|---|
boolean isEmpty() | 判断 value 是否为空 | jdk11 |
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) | value 非空,执行参数 1 功能,如果 value 为空,执行参数 2 功能 | jdk9 |
Optional or(Supplier<? extends Optional<? extends T>> supplier) | value 非空,返回对应的Optional;value 为空,返回形参封装的 Optional | jdk9 |
Stream stream() | value 非空,返回仅包含此 value的 Stream;否则,返回一个空的Stream | jdk9 |
T orElseThrow() | value 非空,返回 value;否则抛异常 NoSuchElementException | jdk10 |
String 存储结构和 API 变更
jdk 9 String 再也不用 char[] 来存储啦,改成了 byte[] 加上编码标记,节约
了一些空间
JDK11 新特性:新增了一系列字符串处理方法
方法 | 描述 |
---|---|
isBlank() | 判断字符串是否为空白 |
strip() | 去除首尾空白 |
stripTrailing() | 去除尾部空格 |
Leading() | 去除首部空格 |
repeat(int n) | 复制n次字符串 |
lines().count() | 行数统计 |
JDK12 新特性:String 实现了 Constable 接口
就是调用 Optional.of 方法返回一个 Optional 类型
private static void testDescribeConstable() {
String name = "Java";
Optional<String> optional = name.describeConstable();
System.out.println(optional.get()); //Java
}
JDK12 新特性:String 新增方法
transform(Function)
var result = "foo".transform(input -> input + " bar").transform(String::toUpperCase)
System.out.println(result); //FOO BAR
其他变化
JDK9:UnderScore(下划线)使用的限制
java 9 中规定_
不再可以单独命名标识符了,如果使用,会报错
GC 方面新特性:
JDK9 以后默认的垃圾回收器是 G1GC
JDK10 : 为 G1 提供并行的 Full GC
JDK11:引入革命性的 ZGC
JDK12:可中断的 G1 Mixed GC
JDK12:增强 G1,自动返回未用堆内存给操作系统
JDK12:Shenandoah GC:低停顿时间的 GC
JDK13:ZGC:将未使用的堆内存归还给操作系统
JDK14:ZGC on macOS 和 windows
JDK15:Shenandoah 垃圾回收算法转正
JDK15:ZGC 功能转正
JDK16:ZGC 并发线程处理
参考资料