JDK 1.5-19 各版本新特性
JDK 1.5新特性
2004-9-30(Tiger)老虎
泛型
增强循环,可以使用迭代方式
自动装箱与自动拆箱
类型安全的枚举
可变参数
底层就是一个数组,根据参数个数不同,会创建不同长度的数组
在传递时可以直接传递数组(传递数组后就不能再传递其余可变参数)
静态引入
元数据(注解)
Instrumentation
JDK 1.6新特性
2006-12-11 Mustang(野马)
支持脚本语言
JDBC 4.0API
Java Compiler API
可插拔注解
增加对Native PKI(Public Key Infrastructure),Java GSS(Generic Security Service)
Kerberos和LDAP(Lightweight Directory Access Protocol)支持
继承Web Services
JDK 7新特性
2011-07-28 Dolphin(海豚)
1、switch支持字符串变量
public static String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) {
String typeOfDay;
switch (dayOfWeekArg) {
case "Monday":
typeOfDay = "Start of work week";
break;
case "Tuesday":
case "Wednesday":
case "Thursday":
typeOfDay = "Midweek";
break;
case "Friday":
typeOfDay = "End of work week";
break;
case "Saturday":
case "Sunday":
typeOfDay = "Weekend";
break;
default:
throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg);
}
return typeOfDay;
}
2、泛型实例化类型自动推断
ArrayList<String> list = new ArrayList<>();
3、新的整数字面表达方式 "ob"前缀 和 "_"连接符
1)表示二进制字面值的前缀ob
以下三个值相同
byte b1 = ob00100001;
byte b2 = 0x21;
byte b3 = 33;
2)字面常量数字的下划线.用下划线连接整数提升其可读性,自身无含义,不可用在数字的起始和末尾
Java编码语言对给数值型的字面值加下划线有严格的规定。如上所述,你只能在数字之间用下划线。你不能用把一个数字用下划线开头,或者已下划线结尾。这里有一些其它的不能在数值型字面值上用下划线的地方:
在数字的开始或结尾
对浮点型数字的小数点附件
F或L下标的前面
该数值型字面值是字符串类型的时候
float pi1 = 3_.1415F; // 无效的; 不能在小数点之前有下划线
float pi2 = 3._1415F; // 无效的; 不能在小数点之后有下划线
long socialSecurityNumber1=999_99_9999_L;//无效的,不能在L下标之前加下划线
int a1 = _52; // 这是一个下划线开头的标识符,不是个数字
int a2 = 5_2; // 有效
int a3 = 52_; // 无效的,不能以下划线结尾
int a4 = 5_______2; // 有效的
int a5 = 0_x52; // 无效,不能在0x之间有下划线
int a6 = 0x_52; // 无效的,不能在数字开头有下划线
int a7 = 0x5_2; // 有效的 (16进制数字)
int a8 = 0x52_; // 无效的,不能以下划线结尾
int a9 = 0_52; // 有效的(8进制数)
int a10 = 05_2; // 有效的(8进制数)
int a11 = 052_; // 无效的,不能以下划线结尾
4、在单个catch块中捕获多个异常,以及用升级版的类型检查重新抛出异常
catch代码块得到了升级,用以在单个catch块中处理多个异常,如果你要捕获多个异常并且它们包含相似的代码,使用这一特性会减少代码重复
try {
//xxx
} catch (AException | BException e) {
e.printStackTrace();
}
这种情况下ex变量是final的,缺点是异常处理细粒度降低
5、try-with-resources语句
try-with-resources语句是一个声明一个或多个资源的try语句,一个资源作为一个对象,必须在程序结束后关闭
try-with-resources语句确保在语句的最后每个资源都被关闭,任何实现了Java.lang.AutoCloseable和java.io.Closeable的对象都可以使用try-with-resource来实现异常处理和关闭资源
try-with-resource可以声明多个资源(声明语句之间用分好分割,最后一个可忽略分号)
try (流创建等需要关闭的资源打开操作,多个之间用;隔开) {操作} catch(Exception 这里可加catch可不加) {}
try ( InputStream is = new FileInputStream("xx");
OutputStream os = new FileOutputStream("xx")
) {
//xxx
//不用关闭is、os,了,JVM帮你关闭流
}
引入JavaNIO.2开发包
null值的自动处理
JDK 1.8
2014-03-18
Lambda表达式
Lambda 是一个匿名函数,我们可以把Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
格式:
-> :lambda操作符 或 箭头操作符
->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表)
->右边:lambda体 (其实就是重写的抽象方法的方法体)


/**
* Lambda表达式的使用
*
* 1.举例: (o1,o2) -> Integer.compare(o1,o2);
* 2.格式:
* -> :lambda操作符 或 箭头操作符
* ->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表)
* ->右边:lambda体 (其实就是重写的抽象方法的方法体)
*
* 3. Lambda表达式的使用:(分为6种情况介绍)
*
* 总结:
* ->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略
* ->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字
*
* 4.Lambda表达式的本质:作为函数式接口的实例
*
* 5. 如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解,
* 这样做可以检查它是否是一个函数式接口。
*
* 6. 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
*
* @author 物竞天择适者生存
*
*/
public class LambdaTest {
//语法格式一:无参,无返回值
@Test
public void test1(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
r1.run();
System.out.println("***********************");
Runnable r2 = () -> {
System.out.println("我爱北京故宫");
};
r2.run();
}
//语法格式二:Lambda 需要一个参数,但是没有返回值。
@Test
public void test2(){
Consumer <String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("谎言和誓言的区别是什么?");
System.out.println("*******************");
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一个是听得人当真了,一个是说的人当真了");
}
//语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
@Test
public void test3(){
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一个是听得人当真了,一个是说的人当真了");
System.out.println("*******************");
Consumer<String> con2 = (s) -> {
System.out.println(s);
};
con2.accept("一个是听得人当真了,一个是说的人当真了");
}
@Test
public void test4(){
ArrayList <String> list = new ArrayList<>();//类型推断
int[] arr = {1,2,3};//类型推断
}
//语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
@Test
public void test5(){
Consumer<String> con1 = (s) -> {
System.out.println(s);
};
con1.accept("一个是听得人当真了,一个是说的人当真了");
System.out.println("*******************");
Consumer<String> con2 = s -> {
System.out.println(s);
};
con2.accept("一个是听得人当真了,一个是说的人当真了");
}
//语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test6(){
Comparator <Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(com1.compare(12,21));
System.out.println("*****************************");
Comparator<Integer> com2 = (o1,o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(12,6));
}
//语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
@Test
public void test7(){
Comparator<Integer> com1 = (o1,o2) -> {
return o1.compareTo(o2);
};
System.out.println(com1.compare(12,6));
System.out.println("*****************************");
Comparator <Integer> com2 = (o1, o2) -> o1.compareTo(o2);
System.out.println(com2.compare(12,21));
}
@Test
public void test8(){
Consumer <String> con1 = s -> {
System.out.println(s);
};
con1.accept("一个是听得人当真了,一个是说的人当真了");
System.out.println("*****************************");
Consumer<String> con2 = s -> System.out.println(s);
con2.accept("一个是听得人当真了,一个是说的人当真了");
}
}
方法引用
方法引用提供了非常有用的语法,可以直接引用已有的Java类或对象(实例)的方法或构造器.
与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码
默认方法
默认方法就是一个在接口里面有了一个实现的方法
interface A{
/*
*默认方法的语法
*/
访问修饰符 default 返回值类型 方法名(){
System.out.println("Hello,我是默认方法");
}
}
//默认方法不需要重写 当然如果你需求不一样也可以进行重写
class B implements A{
}
接口静态方法
//接口中的静态方法是不能被实现类重写的,调用的话只能通过接口类型来调用:A.静态方法()
//所以在进行扩展时可以把不允许被子类重写的方法写成静态方法
interface A{
//只能通过接口类型调用
访问修饰符 static 返回值类型 方法名(){
方法体
}
}
函数式(Functional)接口
我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口
/**
* 自定义函数式接口
* @author shkstart
* @create 2019 下午 2:20
*/
@FunctionalInterface
public interface MyInterface {
void method1();
}
新工具
新的编译工具 如:Nashorn引擎 jjs 类依赖分析器jdeps
Stream API
新添加的Stream API(java.utils.stream)把真正的函数式编程风格引入到Java中
Stream 的操作三个步骤
创建 Stream
一个数据源(如:集合、数组),获取一个
中间操作
一个中间操作链,对数据源的数据进行处理
终止操作 终端操作
一旦执行 终止操作, 就 执行中间操作链 ,并产生结果 。之后,不会再被使用

Stream的创建
创建Stream 方式一:通过集合
Java8中的 Collection 接口被扩展,提供了两个获取流的方法
default Stream<E> stream() : 返回一个顺序流
default Stream<E> parallelStream() : 返回一个并行流
创建Stream 方式二:通过数组
Java8中的 Arrays 的静态方法 stream() 可以获取数组流:
static <T> Stream<T> stream(T[] array): 返回一个流
重载形式,能够处理对应基本类型的数组:
public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)
创建Stream 方式三:通过Stream的of()
可以调用Stream 类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
public static<T> Stream<T> of(T... values) : 返回一个流
创建Stream 方式四:创建无限流
可以使用静态方法Stream.iterate() 和 Stream.generate(),创建无限流。
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)//迭代
public static<T> Stream<T> generate(Supplier<T> s)//生成
/**
* 1. Stream关注的是对数据的运算,与CPU打交道
* 集合关注的是数据的存储,与内存打交道
*
* 2.
* ①Stream 自己不会存储元素。
* ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
* ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
*
* 3.Stream 执行流程
* ① Stream的实例化
* ② 一系列的中间操作(过滤、映射、...)
* ③ 终止操作
*
* 4.说明:
* 4.1 一个中间操作链,对数据源的数据进行处理
* 4.2 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
*
*
* 测试Stream的实例化
*
* @author shkstart
* @create 2019 下午 4:25
*/
public class StreamAPITest {
//创建Stream方式一:通过集合
@Test
public void test01(){
List <Employee> employees = EmployeeData.getEmployees();
// default Stream<E> stream() : 返回一个顺序流
Stream <Employee> stream = employees.stream();
// default Stream<E> parallelStream() : 返回一个并行流
Stream <Employee> parallelStream = employees.parallelStream();
}
//创建Stream方式二:通过数组
@Test
public void test02(){
int[] arr = new int[]{1,2,3,4,5};
//调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流
IntStream stream = Arrays.stream(arr);
Employee employee1 = new Employee(1001, "刘备", 25, 15000);
Employee employee2 = new Employee(1002, "关羽", 31, 12000);
Employee[] arr2 = {employee1, employee2};
Stream <Employee> stream1 = Arrays.stream(arr2);
}
//创建Stream方式三:通过Stream的of()
@Test
public void test03(){
Stream <Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
}
//创建Stream 方式四:创建无限流
@Test
public void test04(){
//迭代
// public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
//遍历前10个偶数
Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);
//生成
// public static<T> Stream<T> generate(Supplier<T> s)
//生成10个随机数
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
Stream的中间操作
多个中间操作 可以连接起来形成一个 流水线 ,除非流水线上触发终止操作,否则 中间操作不会执行任何的处理 !而在 终止操作时一次性全部处理,称为“惰性求值” 。
1-筛选与切片
方法 描述
filter(Predicate p):接收Lambda ,从流中排除某些元素
distinct() :筛选,通过流所生成元素的hashCode() 和equals() 去除重复元素
limit(long maxSize) :截断流,使其元素不超过给定数量
skip(long n) :跳过元素,返回一个扔掉了前n 个元素的流。若流中元素不足n 个,则返回一个空流。与limit(n)互补
2-映 射
方法 描述
map(Function f) :接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f) :接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream。
mapToInt(ToIntFunction f) :接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream。
mapToLong(ToLongFunction f) :接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream。
flatMap(Function f) :接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
3-排序
方法 描述
sorted() :产生一个新流,其中按自然顺序排序
sorted(Comparator com) :产生一个新流,其中按比较器顺序排序
代码演示
/**
* 测试Stream的中间操作
*/
public class StreamAPITest2 {
//筛选与切片
@Test
public void test01(){
List <Employee> list = EmployeeData.getEmployees();
Stream <Employee> stream = list.stream();
//filter(Predicate p)——接收 Lambda , 从流中排除某些元素。
//练习:查询员工表中薪资大于7000的员工信息
stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
System.out.println();
// limit(n)-截断流,使其元素
list.stream().limit(3).forEach(System.out::println);
System.out.println();
// skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
list.stream().skip(3).forEach(System.out::println);
System.out.println();
// distinct()——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",41,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",42,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.stream().distinct().forEach(System.out::println);
}
@Test
public void test02(){
// map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
List <String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(str->str.toUpperCase()).forEach(System.out::println);
// 练习1:获取员工姓名长度大于3的员工的姓名。
List <Employee> employees = EmployeeData.getEmployees();
Stream <String> nameStream = employees.stream().map(Employee::getName);
nameStream.filter(name->name.length() > 3).forEach(System.out::println);
System.out.println();
// 练习2.map下面使用fromStringToStream
//list.stream()会生成一个stream<string>,但是里面的每一个string 又会通过fromStringToStream再生成stream,最终的结果就是steam<stream>
Stream <Stream <Character>> streamStream = list.stream().map(StreamAPITest2::fromStringToStream);
streamStream.forEach(s->s.forEach(System.out::println));//遍历时候需要双层for循环
System.out.println();
//练习3 flatMap下面使用fromStringToStream
// flatMap(Function f)——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
Stream <Character> characterStream = list.stream().flatMap(StreamAPITest2::fromStringToStream);
characterStream.forEach(System.out::println);
//map和flatMap的区别:
// map: 类似于add(),如果新增加的元素是一个stream的话,则是stream里面又有一个stream,即Stream<Stream>
// faltMap: 类似于addAll(),如果新增加的是一个stream,则会把stream拆散,加入到大的stream中,即Stream
}
//将字符串中的多个字符构成的集合转换成对应的Strean的实例
public static Stream<Character> fromStringToStream(String str){
List <Character> list = new ArrayList <>();
for (char c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
@Test
public void test3(){
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(2);
list1.add(3);
ArrayList list2 = new ArrayList();
list2.add(4);
list2.add(5);
list2.add(6);
list1.add(list2);// 如果添加的项是一个集合,则把该集合当成一个元素 [1, 2, 3, [4, 5, 6]]
list1.addAll(list2);//如果添加的项是一个集合,则把集合拆分后再加入到list1中 [1, 2, 3, 4, 5, 6]
System.out.println(list1);
}
//3-排序
@Test
public void test04(){
// sorted()——自然排序
List <Integer> list = Arrays.asList(12, 43, 65, 34, 89, -5, 26);
list.stream().sorted().forEach(System.out::println);
//抛异常,原因:Employee没有实现Comparable接口
// List <Employee> employees = EmployeeData.getEmployees();
// employees.stream().sorted().forEach(System.out::println);
// sorted(Comparator com)——定制排序
List <Employee> employees = EmployeeData.getEmployees();
employees.stream().sorted((e1,e2)->{
//按照年龄升序,工资降序排列
int ageValue = Integer.compare(e1.getAge(), e2.getAge());
return ageValue != 0 ?ageValue : -Double.compare(e1.getSalary(),e2.getSalary());
}).forEach(System.out::println);
}
}
Stream的终止操作:匹配与查找
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void 。
流进行了终止操作后,不能再次使用。
1-匹配与查找
方法 描述
allMatch(Predicate p) :检查是否匹配所有元素
anyMatch(Predicate p) :检查是否至少匹配一个元素
noneMatch(Predicate p) :检查是否没有匹配所有元素
findFirst() : 返回第一个元素
findAny() :返回当前流中的任意元素
count() :返回流中元素总数
max(Comparator c) :返回流中最大值
min(Comparator c) :返回流中最小值
forEach(Consumer c) :内部迭代(使用Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
2-归约
方法 描述
reduce(T iden, BinaryOperator b) :可以将流中元素反复结合起来,得到一个值。返回T
reduce(BinaryOperator b) :可以将流中元素反复结合起来,得到一个值。返回Optional
备注:map 和reduce 的连接通常称为map-reduce 模式,因Google 用它来进行网络搜索而出名。
3-收集
方法 描述
collect(Collector c) :将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
Collector接口中方法的实现决定了如何对流执行收集的操作 如收集到 List 、 Set 、Map。
另外,Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例
具体方法与实例如下表

/**
* @author lxy
* @version 1.0
* @Description 测试Stream的终止操作
* @date 2022/7/21 12:59
*/
public class StreamAPITest3 {
//1-匹配与查找
@Test
public void test01(){
List <Employee> employees = EmployeeData.getEmployees();
// allMatch(Predicate p)——检查是否匹配所有元素。
// 练习:是否所有的员工的年龄都大于18
boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println(allMatch);
// anyMatch(Predicate p)——检查是否至少匹配一个元素。
// 练习:是否存在员工的工资大于 10000
boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 8000);
System.out.println(anyMatch);
// noneMatch(Predicate p)——检查是否没有匹配的元素。
// 练习:是否存在员工姓“雷”
boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
System.out.println(noneMatch);
// findFirst——返回第一个元素
Optional <Employee> employee = employees.stream().findFirst();
System.out.println(employee);
// findAny——返回当前流中的任意元素
Optional <Employee> employee1 = employees.stream().findAny();
System.out.println(employee1);
}
//2-匹配与查找2
@Test
public void test02(){
List <Employee> employees = EmployeeData.getEmployees();
// count-返回流中元素的总个数
long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
System.out.println(count);
// max(Comparator c)——返回流中最大值
// 练习:返回最高的工资:
Optional <Double> maxSalary = employees.stream().map(e -> e.getSalary()).max((d1, d2) -> Double.compare(d1, d2));
System.out.println(maxSalary);
// min(Comparator c)——返回流中最小值
// 练习:返回最低工资的员工
Optional <Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(employee);
System.out.println();
// forEach(Consumer c)——内部迭代
employees.stream().forEach(System.out::println);
System.out.println();
//使用集合的遍历操作
employees.forEach(System.out::println);
}
//3-归约
@Test
public void test3(){
// reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T
// 练习1:计算1-10的自然数的和
List <Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(0, Integer::sum);//实际上就是 传入俩值得到结果,再作为新值继续往后加
System.out.println(sum);
// reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
// 练习2:计算公司所有员工工资的总和
List <Employee> employees = EmployeeData.getEmployees();
Stream <Double> salaryStream = employees.stream().map(Employee::getSalary);
// Optional <Double> sumMoney = salaryStream.reduce(Double::sum);
Optional <Double> sumMoney = salaryStream.reduce((d1,d2)-> Double.sum(d1,d2));
System.out.println(sumMoney.get());
}
//4-收集
@Test
public void test04(){
// collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
// 练习1:查找工资大于6000的员工,结果返回为一个List或Set
List <Employee> employees = EmployeeData.getEmployees();
List <Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out::println);
System.out.println();
Set <Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
employeeSet.forEach(System.out::println);
}
}
Data Time API
Java 8 在java.time包下推出了一组全新的时间日期API,涵盖了日期、时间、日期时间、时区、时刻、间隔、时钟等
新的java.time包下的所有类都是不可变类型而且线程安全的,解决了在此之前日期时间中存在的线程安全、横跨多包、使用复杂等诸多问题
1、时间和日期
// 本地时间
LocalTime lt = LocalTime.now();
// 本地日期
LocalDate ld = LocalDate.now();
// 本地日期时间
LocalDateTime ldt = LocalDateTime.now();
// 创建一个指定的时间
LocalDateTime ldt = LocalDateTime.of(2012, 2, 12, 12, 12, 12);
// 日期时间转日期或时间
LocalDate ld = ldt.toLocalDate();
LocalTime lt = ldt.toLocalTime();
2、字符串互转
// 格式化模版
DateTimeFormatter DATETIME19 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 时间转字符串
String dtStr = DATETIME19.format(LocalDateTime.now());
// 字符串转时间
LocalDateTime ldt = LocalDateTime.parse(dtStr, DATETIME19);
3、时间运算
//获取指定单位的值
int year = ldt.getYear();
int day = ldt.getDayOfMonth();
int week = ldt.getDayOfWeek().getValue();
int hour = ldt.getHour();
// 指定时间单位的值
LocalDateTime ldt2 = ldt1.withDayOfMonth(10).withYear(2020); // 返回的是一个新的对象,需要接收
// 在现有时间上做加法
LocalDateTime ldt2 = ldt1.plusYears(2).plusMonths(-2);
// 在现有时间上做减法
LocalDateTime ldt2 = LocalDateTime.now().minus(-2, ChronoUnit.MONTHS).minusDays(3)
// 获取一天的开始或结束
LocalDateTime ldtStart = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
LocalDateTime ldtEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
// 时间是否在指定时间之前
boolean isBefore = ldt1.isBefore(ldt2);
// 时间是否在指定时间之后
boolean isAfter = ldt1.isAfter(ldt2);
// 比较两个日期是否相等 重写的equals可以直接比较
boolean equality = ld1.equals(ld2);
// 比较是否是周期性日期,比如 生日 节假日 账单日 等
MonthDay holiday = MonthDay.of(5, 1); // 五一
boolean equality = holiday.equals(MonthDay.from(LocalDateTime.now())); // 今天是否是五一
4、间隔计算
LocalDateTime ldt1 = LocalDateTime.of(2012, 2, 12, 12, 12, 12);
LocalDateTime ldt2 = LocalDateTime.of(2015, 5, 15, 15, 15, 15);
// 时间的间隔 Duration 表示时分秒的时间量(累计向上单位的差,即计算实际的总共的差)
Duration duration = Duration.between(ldt1, ldt2);
long durnMill = duration.toMillis(); // 计算毫秒差
long durnMin = duration.toMinutes(); // 计算分钟差
long durnHour = duration.toHours(); // 计算小时差
long durnDay = duration.toDays(); // 计算天数差
// 日期的间隔 Period 表示年月日的时间量(只计算当前单位的差,不累计向上单位的差距)
Period period = Period.between(ldt1.toLocalDate(), ldt2.toLocalDate());
long perdDay = period.getDays(); // 只计算当前差,不累计年月差带来的天数差
long perdMonth = period.getMonths(); // 只计算当前差,不累计年数差带来的月数差
long perdYear = period.getYears();
// 计算实际总间隔天数的第二种方法
long diffEehDay =ldt1.toLocalDate().toEpochDay() - ldt2.toLocalDate().toEpochDay();
// 计算指定时间单位之差
long diffChrDay =ChronoUnit.DAYS.between(ldt1, ldt2); // 日期单位之差
long diffChrMin =ChronoUnit.MINUTES.between(ldt1, ldt2); // 分钟单位之差
5、时间戳、瞬时点、Date、本地时间、转换
// 时间戳
long timestamp = System.currentTimeMillis();
// 瞬时点
Instant instant = Instant.now();
// Date
Date date = new Date();
// 时间戳 转 瞬时点
Instant instant = Instant.ofEpochMilli(timestamp);
// 瞬时点 转 时间戳
long timestamp = instant.toEpochMilli();
// Date 转 瞬时点
Instant instant = date.toInstant();
// 瞬时点 转 Date
Date date = Date.from(instant);
// 瞬时点 转 本地时间
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
// 本地时间 转 时间戳
long timestamp = ldt.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
long timestamp = ldt.toInstant(ZoneOffset.of("+08:00")).toEpochMilli();
long timestamp = ldt.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
6、时区时间
// 时区ID的集合
Set<String> zoneSet = ZoneId.getAvailableZoneIds();
// 默认时区
ZoneId zoneId = ZoneId.systemDefault();
// 时区时间
LocalDateTime cur = LocalDateTime.now(); // 本地默认时间 2019-04-29T14:45:07.156
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("America/Los_Angeles")); // 时区当前时间 2019-04-28T23:45:07.156
OffsetDateTime odt = OffsetDateTime.now(ZoneId.of("America/Los_Angeles")); // 带偏移量时间 2019-04-28T23:45:07.156-07:00
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("America/Los_Angeles")); // 带时区的时间 2019-04-28T23:45:07.156-07:00[America/Los_Angeles]
LocalDateTime ldto = odt.toLocalDateTime(); // 转本地类时间 2019-04-28T23:45:07.156
LocalDateTime ldtz = zdt.toLocalDateTime(); // 转本地类时间 2019-04-28T23:45:07.156
// 时钟 类似时间戳
Clock clockDefault = Clock.systemDefaultZone(); //系统默认
Clock clockUtc = Clock.systemUTC(); // UTC
Clock c1ockZone = Clock.system(ZoneId.of("+08:00")); //指定时区
Clock clockRegion = Clock.system(ZoneId.of("Asia/Shanghai")); //指定区域
long timestamp = clockDefault.millis(); //获取时间戳,等于System.currentTimeMillis()
Optional类
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用null 表示一个值不存在,现在Optional 可以更好的表达这个概念。并且可以避免空指针异常。
Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional提供很多有用的方法,这样我们就不用显式进行空值检测
创建Optional类对象的方法:
Optional.of(T t): 创建一个Optional 实例,t必须非空;
Optional.empty() : 创建一个空的Optional 实例
Optional.ofNullable(T t):t可以为null
判断Optional容器中是否包含对象:
boolean isPresent() : 判断是否包含对象
void ifPresent(Consumer<? super T> consumer) :如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。
获取Optional容器的对象:
T get(): 如果调用对象包含值,返回该值,否则抛异常
T orElse(T other) :如果有值则将其返回,否则返回指定的other对象。
T orElseGet(Supplier<? extends T> other) :如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
T orElseThrow(Supplier<? extends X> exceptionSupplier) :如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。
Optional的应用举例
/**
* @author shkstart
* @create 2019 下午 7:23
*/
public class Girl {
private String name;
@Override
public String toString() {
return "Girl{" +
"name='" + name + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Girl() {
}
public Girl(String name) {
this.name = name;
}
}
- 创建一个Boy类|
/**
* @author shkstart
* @create 2019 下午 7:22
*/
public class Boy {
private Girl girl;
@Override
public String toString() {
return "Boy{" +
"girl=" + girl +
'}';
}
public Girl getGirl() {
return girl;
}
public void setGirl(Girl girl) {
this.girl = girl;
}
public Boy() {
}
public Boy(Girl girl) {
this.girl = girl;
}
}
- 创建测试类
/**
* Optional类:为了在程序中避免出现空指针异常而创建的。
*
* 常用的方法:ofNullable(T t)
* orElse(T t)
* @author lxy
* @version 1.0
* @Description
* @date 2022/7/21 14:53
*/
public class OptionalTest {
//传统的空指针异常以及解决办法
@Test
public void test1(){
Boy boy = new Boy();
boy = null;
// String girlName = getGirlName(boy);
String girlName = getGirlName1(boy);
System.out.println(girlName);
}
public String getGirlName(Boy boy){
return boy.getGirl().getName();
}
//优化以后的getGirlName():
public String getGirlName1(Boy boy){
if(boy != null){
Girl girl = boy.getGirl();
if(girl != null){
return girl.getName();
}
}
return null;
}
//使用Optional解决空指针异常
@Test
public void test2(){
/*
Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):t可以为null
*/
Girl girl = new Girl();
girl = null; // NullPointerException
//of(T t):保证t是非空的
Optional <Girl> optionalGirl = Optional.of(girl);
System.out.println(optionalGirl);
//ofNullable(T t):t可以为null
Optional <Girl> optionalGirl1 = Optional.ofNullable(girl);
System.out.println(optionalGirl1);
//orElse(T t1):如果单前的Optional内部封装的t是非空的,则返回内部的t.
//如果内部的t是空的,则返回orElse()方法中的参数t1.
Girl girl1 = optionalGirl1.orElse(new Girl("赵丽颖"));
System.out.println(girl1);
}
@Test
public void test3(){
Boy boy = null;
boy = new Boy();
boy = new Boy(new Girl("苍老师"));
String girlName = getGirlName2(boy);
System.out.println(girlName);
}
//使用Optional类的getGirlName()
public String getGirlName2(Boy boy){
Optional <Boy> boyOptional = Optional.ofNullable(boy);
Boy boy1 = boyOptional.orElse(new Boy(new Girl("迪丽热巴")));
Girl girl = boy1.getGirl();
Optional <Girl> girlOptional = Optional.ofNullable(girl);
Girl girl1 = girlOptional.orElse(new Girl("古丽扎纳"));
return girl1.getName();
}
}

Nashorn,JavaScript引擎
Java8提供了一个新的Nashorn JavaScript引擎,它允许我们在JVM上运行特定的JavaScript应用
JDK 9新特性:
2017-9-22
模块系统
模块是一个包的容器,Java9最大的变化之一是引入了模块系统(Jigsaw项目)
REPL(JShell)
交互式编程环境
在cmd中输入JShell 可以帮你自动创建类
以及运行一些简单的方法
HTTP2客户端
HTTP/2标准是HTTP协议的最新版本,新的HTTPClient API支持WebSocket和HTTP2流以及服务器推送特性
改进的Javadoc
Javadoc现在支持在API文档中的进行搜索,另外Javadoc的输出现在符合兼容HTML5标准
多版本兼容Jar包
多版本兼容Jar功能能让你创建仅在特定版本的Java环境中运行库程序时选择使用的class版本
集合工厂方法
List,Set和Map接口中,新的静态工厂方法可以创建这些集合的不可变实例
私有接口方法
在接口中使用private私有方法.我们可以使用private访问修饰符在接口中编写私有方法
进程API
改进的API来控制和管理操作系统进程.改进java.lang.ProcessHandle及其嵌套接口info来让开发者逃离时常因为要获取一个本地进程的PID而不得不适用本地代码的窘境
改进的Stream API
改进的Stream API添加了一些便利的方法,使流处理更容易,并使用收集器编写复杂的查询
改进 try-with-resources
如果你已经有一个资源是final或等效于final变量,您可以在try-with-resources语句中使用该变量,而无需再try-with-resources语句中声明一个新变量
改进的弃用注解@Deprecated
可以标记Java API的状态 可以表示被标记的API将会被移除,或者已被破坏
改进钻石操作符(Diamond Operator)
匿名类可以使用钻石操作符(Diamond Operator)
改进Optional类
java.util.Optional添加了很多新的有用方法,Optional可以直接转为stream
多分辨率图像API
定义多分辨率图像API,开发者可以很容易的操作和展示不同分辨率的图像了
改进的CompletableFuture API
CompletableFuture类的异步机制可以在ProcessHandle.onExit方法退出时执行操作
轻量级的JSON API
内置了一个轻量级的JSON API
响应式流(Reactive Streams)API
Java9中引入了新的响应式流API来支持Java9中的响应式编程
模块化
可定制JRE:更小的运行时镜像
更确定的模块依赖关系:避免Jar Hell问题
与 OSGI 的比较
模块的定义:
模块是一个命名的,自我描述的代码和数据的集合
模块的代码被组织成多个包,每个包中包含Java类和接口
模块的数据则包括资源文件和其他静态信息
模块声明文件
模块声明文件:module-info.java
新的关键字:module 声明一个模块
模块名称的规则 与Java包的命名规则相似
Java 10
2018-3-21
JEP286,var局部变量类型推断
JEP296,将原来用Mercurial管理的众多JDK仓库代码,合并到一个仓库中,简化开发和管理过程
JEP304,统一的垃圾回收接口
JEP307,G1垃圾回收器的并行完整垃圾回收,实现并行性来改善最坏情况下的延迟
JEP310,应用程序类数据(AppCDS)共享,通过跨进共享通用类元数据来减少内存占用空间,和减少启动时间
JEP312,ThreadLocal握手交互.在不进入到全局JVM安全点(Safepoint)的情况下,对线程执行回调,优化可以只停止单个线程,而不是停全部线程或一个都不停
JEP313,移除JDK中附带的javah工具,可以使用javac -h代替
JEP314,使用附加的Unicode语言标记扩展
JEP317,能将堆内存占用分配给用户指定的备用内存设备
JEP317,使用Graal基于Java的编译器,可以预先把Java代码编译成本地代码来提升效能
JEP318,在OpenJDK中提供一组默认的根证书颁发机构证书,开源目前Oracle提供的JavaSE的根证书,这样OpenJDK对开发人员使用起来更方便
JEP322,基于时间定义的发布版本,即上述提到的发布周期,版本号为...分别是大版本,中间版本,升级包和补丁版本
Java 11
2018-09-25
181:Nest_Based访问控制
309:动态类文件常量
315:改善Aarch64 intrinsic
318:无操作垃圾收集器ZGC
320:消除Java EE和CORBA模块
321:HTTP客户端(标准)
323:局部变量的语法?参数
324:Curve25519和Curve448关键协议
327:Unicode 10
328:飞行记录器
329:ChaCha20和Poly1305加密算法
330:发射一列纵队源代码程序
331:低开销堆分析
332:传输层安全性(Transport Layer Security,TLS)1.3
333:动作,一个可伸缩的低延迟垃圾收集器(实验)
335:反对Nashorn JavaScript引擎
336:反对Pack200工具和API
Java12
2019-3-19
在Java12中,switch可以有返回值 并且使用->直接
break可以携带返回值,如果需要break返回值,则需每一个case后都有返回值或者抛出一个异常
int num = switch(value) {
case 1 ->1;
case 2 ->2;
case 3,4 ->3;
case 5: break 5;
case 6 -> {System.out.println("hello world");//没有返回则抛出异常}
}
Java13
2019-9-17
Java13中,switch支持如下写法
int date = switch (day) {
case MONDAY, FRIDAY, SUNDAY : yield 6;
case TUESDAY : yield 7;
case THURSDAY, SATURDAY : yield 8;
case WEDNESDAY : yield 9;
default : yield 1; // default条件是必须的
};
支持文本块 使用三个"""
起始的"""结尾必须换行
String text = """
hello world
! """
文本块是在13中是预览功能,使用前需要先启用
手动编译:
javac --release 13 --enable-preview ...
手动运行
java --enable-preview ...
Java14
2020-3-17
改进的switch表达式在14中获得了完全的支持
instanceof支持模式匹配(语言特性 预览特性)
使用instanceof和instanceof里的对象不用强转了
NullPointerException(JVM特性)
14的这个异常更加详细,精确到某个属性
Record(预览功能)
java14提供了解决get,set,toString等代码冗余,
Record会提供equals,hashCode,toString方法的实体,可以重构如下
public record Clazz(int a,double b,String c) {}
通过record,可以自动地得到equals,hashCode,toString的实现,还有构造器和getter方法
文本块中加入 \ 可以不换行
Java15
2020-9-15
文本块转正
ZGC垃圾回收器转正
Shenandoah转正
移除 Nashorn JavaScript Engine(JDK脚本执行引擎)
在JDK11标记过期,15完全移除
CharSequence新增isEmpty默认方法
Java16
2021-3-16
向量API(孵化)
提供了jdk.incubator.vector来用于矢量计算
启用c++ 14 语言特性
从Mercurial迁移到Git
迁移到GitHub(将OpenJDK源码的版本控制迁移到github上)
ZGC 并发线程堆栈处理
Unix-Domain 套接字通道
为socket channel和server-socket channel api增加Unix-domain(AF_UNIX)套接字支持。
Alpine Linux Port
Elastic Metaspace
Windows/AArch64 Port
Foreign Linker API(孵化)
提供jdk.incubator.foreign来简化native code的调用
基于值的类的警告
提供注解 @jdk.internal.ValueBased 来用于标注作为value-based的类
将原始包装类指定为基于值的类,并弃用它们的构造函数以便删除,并提示新的弃用警告。提供关于在Java平台中任何基于值类的实例上进行同步的警告。
打包工具
在JDK 16中转正,从 jdk.incubator.jpackage 转为 jdk.jpackage
用于打包自包含的Java应用程序
外部内存访问API(第三次孵化)
引入一个API,允许Java程序安全有效地访问Java堆之外的外部内存
为 instanceof 进行模式匹配
在JDK 16中转正,可以如下使用
Object o = new String("");
if (o instanceof String str) {
System.out.println(str.concat("模式匹配"));
}
Records
在JDK 16中转正
对内部API进行更多封装,鼓励开发者从使用内部的方法迁移到标准API
Sealed Classes
Java17(LTS)
2021-9-14
发行说明: https://www.oracle.com/java/technologies/javase/17-relnote-issues.html
Sealed Classes(密封类)
已将密封类添加到Java语言中。密封类和接口限制其他类或接口可以扩展或实现它们。
密封类由JEP360提出,并在JDK15中作为预览特性提供。JEP 397再次提出了这些建议,并进行了改进
Pattern Matching for switch (Preview)
使用开关表达式和语句的模式匹配以及模式语言的扩展来增强Java编程语言。
通过将模式匹配扩展到switch,可以针对多个模式对表达式进行测试,每个模式都有一个特定的操作,因此可以简洁而安全地表达复杂的面向数据的查询。
New macOS Rendering Pipeline(新的MacOS渲染管道)
Swing API用于渲染的Java 2D API现在可以使用新的Apple Metal加速渲染API for macOS。
这在默认情况下目前是禁用的,因此渲染仍然使用OpenGL API,虽然Apple不推荐使用这些API,但它们仍然可用并受支持。
要启用Metal,应用程序应通过设置系统属性指定其用途:-Dsun.java2d.metal=true
Metal或OpenGL的使用对应用程序是透明的,因为这是内部实现的差异,对JavaAPI没有影响。
金属管道需要macOS 10.14.x或更高版本。在早期版本上设置它的尝试将被忽略。
New API for Accessing Large Icons
JDK 17中提供了一种新方法
javax.swing.filechooser.FileSystemView.getSystemIcon(File,int,int)
它可以在可能的情况下访问更高质量的图标。
它在Windows平台上完全实现;但是,在其他平台上的结果可能会有所不同,稍后将进行增强。例如,通过使用以下代码:
FileSystemView fsv = FileSystemView.getFileSystemView();
Icon icon = fsv.getSystemIcon(new File("application.exe"), 64, 64);
JLabel label = new JLabel(icon);
用户可以为“application.exe”文件获取更高质量的图标。
此图标适用于创建可在高DPI环境中更好缩放的标签。
DatagramSocket Can Be Used Directly to Join Multicast Groups
在此版本中,java.net.DatagramSocket已更新,以添加对加入多播组的支持。
它现在定义了joinGroup和leaveGroup方法来加入和离开多播组。
java.net.DatagramSocket的类级API文档已经更新,以解释如何配置普通DatagramSocket并使用它加入和退出多播组。
此更改意味着DatagramSocket API可以用于多播应用程序,而无需使用传统的java.net.MulticastSocket API。
MulticastSocket API与以前一样工作,尽管它的大多数方法都不推荐使用。
有关这一变化的基本原理的更多信息,请参见CSR JDK-8260667。
Add support for UserDefinedFileAttributeView on macOS(在macOS上添加对UserDefinedFileAttributeView的支持)
macOS上的文件系统提供程序实现已在此版本中更新,以支持扩展属性。
现在可以使用java.nio.file.attribute.UserDefinedFileAttributeView API获取文件扩展属性的视图。
以前的JDK版本不支持此(可选)视图。
Enhanced Pseudo-Random Number Generators(增强型伪随机数发生器)
Provide new interface types and implementations for pseudorandom number generators (PRNGs), including jumpable PRNGs and an additional class of splittable PRNG algorithms (LXM).
为伪随机数生成器(PRNG)提供新的接口类型和实现,包括可跳线PRNG和附加的可拆分PRNG算法(LXM)。
Modernization of Ideal Graph Visualizer(理想图形可视化仪的现代化)
Source Details in Error Messages(错误消息中的源详细信息)
New Page for "New API" and Improved "Deprecated" Page(“新API”的新页面和改进的“弃用”页面)
Foreign Function & Memory API (Incubator)(外部函数和内存API(孵化器))
Console Charset API(控制台字符集API)
已更新java.io.Console以定义一个新方法,该方法返回控制台的字符集。
返回的字符集可能不同于从Charset.defaultCharset()方法返回的字符集。
例如,它返回IBM437,而Charset.defaultCharset()在windows上返回windows-1252(en-US)。有关更多详细信息,请参阅CSR。
Flight Recorder Event for Deserialization(用于反序列化的飞行记录器事件)
Implement Context-Specific Deserialization Filters(实现特定于上下文的反序列化过滤器)
弃用 Applet API 以进行删除
它基本上无关紧要,因为所有 Web 浏览器供应商都已取消对 Java 浏览器插件的支持或宣布了这样做的计划。
Java 9 中的JEP 289先前已弃用 Applet API,但并未将其删除。
弃用安全管理器以进行删除
安全管理器和与其相关的 API 已被弃用,并将在未来版本中删除。
为确保开发人员和用户知道安全管理器已被弃用,如果通过java -Djava.security.manager.
如果通过System::setSecurityManagerAPI动态启用安全管理器,Java 运行时还会在运行时发出警告。无法禁用这些警告。
弃用 Kerberos 中的 3DES 和 RC4
弃用套接字实现工厂机制
static void ServerSocket.setSocketFactory?(SocketImplFactory fac)
static void Socket.setSocketImplFactory?(SocketImplFactory fac)
static void DatagramSocket.setDatagramSocketImplFactory?(DatagramSocketImplFactory fac)
以下用于设置系统范围套接字实现工厂的静态方法已被弃用:
弃用 JVM TI 堆函数 1.0
Java18
2022-03-22
发行说明:https://www.oracle.com/java/technologies/javase/18-relnote-issues.html
[用于核心库改进和更新]
默认编码UTF-8(java.nio.charsets)
从 JDK 18 开始,UTF-8 是 Java SE API 的默认字符集.
依赖于默认字符集的 API 现在在所有 JDK 实现中表现一致,并且独立于用户的操作系统、语言环境和配置
简单的 Web 服务器(java.net)
jwebserver, 一个用于启动最小静态 Web 服务器的命令行工具, 已经被引入.
该工具和随附的 API 位于 jdk.httpserver 模块的 com.sun.net.httpserver 包中,旨在用于原型设计、临时编码和测试,尤其是在教育环境中。
使用方法句柄重新实现核心反射(java.lang:reflect)
使用方法句柄重新实现核心反射。依赖于现有实现的高度实现特定和未记录方面的代码可能会受到影响。可能出现的问题包括:
检查内部生成的反射类(例如 的子类MagicAccessorImpl)的代码不再有效,必须更新。
尝试破坏封装并将 的私有 finalmodifiers字段Method的值更改为Field与Constructor基础成员不同的类的代码可能会导致运行时错误。必须更新此类代码。
为了减轻这种兼容性风险,您可以启用旧实现作为解决方法
方法是使用 -Djdk.reflect.useDirectMethodHandle=false.
我们将在未来的版本中删除旧的核心反射实现。-Djdk.reflect.useDirectMethodHandle=false解决方法将在此时停止工作。
互联网地址解析 SPI(java.net)
为主机名和地址解析引入服务提供者接口 (SPI),以便 java.net.InetAddress 可以使用平台内置解析器以外的解析器。这个新的 SPI 允许替换操作系统的本地解析器,该解析器通常配置为使用本地主机文件和域名系统 (DNS) 的组合。
[用于工具改进]
Java API文档中的代码片段(javadoc)
为 JavaDoc 的标准 Doclet 添加了一个 @snippet 标记,以简化在 API 文档中包含示例源代码。
[用于预览和孵化器]
Vector API(第三个孵化器)(核心库)
引入一个 API 来表达向量计算,该计算可以在运行时可靠地编译为支持的 CPU 架构上的最佳向量指令,从而实现优于等效标量计算的性能。
外部函数和内存 API(第二个孵化器)(核心库)
引入一个 API,Java 程序可以通过该 API 与 Java 运行时之外的代码和数据进行互操作。通过有效地调用外部函数(即 JVM 之外的代码)和安全地访问外部内存(即不受 JVM 管理的内存),API 使 Java 程序能够调用本机库并处理本机数据,而不会出现脆弱性和危险。 JNI。
[规范/语言]
switch 的模式匹配(第二次预览)
通过对 switch 表达式和语句的模式匹配以及对模式语言的扩展来增强 Java 编程语言。将模式匹配扩展到 switch 允许针对多个模式测试表达式,每个模式都有特定的操作,因此可以简洁安全地表达复杂的面向数据的查询。
浙公网安备 33010602011771号