Java基础关键_037_Java 常见新特性
目 录
一、新语法
1.JShell
2.try-with-resources
(1)jdk 7 之前
(2)jdk 7 之后
(3)jdk 9 之后
3.局部变量类型判断(不推荐)
4.instanceof 的模式匹配
(1)之前写法
(2)模式匹配增强
5.switch
(1)之前写法
(2)jdk 12 之后
(3)jdk 13 之后
6.文本块
7.Record
8.密封类 / 接口
二、新 API
1.String
(1)存储结构改变
(2)jdk 11 新增方法
(3)jdk 12 新增方法
2.接口支持私有方法
3.标识符命名变化
4.简化编译运行程序
5.创建只读集合
6.Optional
(1)创建 Optional 对象
(2)Optional 类的方法
一、新语法
1.JShell
JShell 是 jdk 9 引入的,像 Python、Scala 等语言之前就有交互式编程环境(REPL)。开发者只需要输入一些代码,就可以在编译前获得反馈。之前版本的 Java,必须创建文件、声明类、提供测试方法。
2.try-with-resources
(1)jdk 7 之前
在之前的章节我们就知道,所有被打开的资源(流、文件、Socket 连接 等) 都需要手动关闭,否则随程序不断运行,会造成严重的资源泄漏。
在 jdk 7 之前,需要在 finally 代码块中完成资源的关闭。
public class TryWithResources {
public static void main(String[] args) {
// jdk 7 之前,创建资源对象,然后调用 close() 方法关闭资源
FileReader fileReader = null;
FileWriter fileWriter = null;
try {
fileReader = new FileReader("D:\\apache-maven-3.6.3\\bin\\m2.conf");
fileWriter = new FileWriter("D:\\m2.conf");
int readCount = 0;
char[] chars = new char[1024];
while ((readCount = fileReader.read()) != -1) {
fileWriter.write(chars, 0, readCount);
}
fileWriter.flush();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (fileReader != null) {
fileReader.close();
}
if (fileWriter != null) {
fileWriter.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
(2)jdk 7 之后
jdk 7 之后,应该使用 try-with-resources 语法自动关闭资源。
使用该语法,需要关闭资源的对象的对应类应该实现 java.lang.AutoCloseable 接口,该接口提供了一个抽象的 close 方法。
public class TryWithResources {
public static void main(String[] args) {
// jdk 7 之后,可以使用try-with-resources语句,自动关闭资源
try (FileReader reader = new FileReader("D:\\apache-maven-3.6.3\\bin\\m2.conf");
FileWriter writer = new FileWriter("D:\\m2.conf")) {
int readCount = 0;
char[] chars = new char[1024];
while ((readCount = reader.read()) != -1) {
writer.write(chars, 0, readCount);
}
writer.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(3)jdk 9 之后
jdk 9 之后,再次进行改进。
public class TryWithResources {
public static void main(String[] args) throws Exception {
// jdk 9 之后,try-with-resources 再次改进
FileReader reader = new FileReader("D:\\apache-maven-3.6.3\\bin\\m2.conf");
FileWriter writer = new FileWriter("D:\\m2.conf");
try (reader; writer) {
int readCount = 0;
char[] chars = new char[1024];
while ((readCount = reader.read()) != -1) {
writer.write(chars, 0, readCount);
}
writer.flush();
}
}
}
3.局部变量类型判断(不推荐)
jdk 10 之后,新增了局部变量类型判断。在方法体或代码块当中,对于可以在编译期确定的类型,可以使用 var 来定义。对于编译期无法确定的类型,仍需写清楚类型。但是不推荐使用。
public class VarType {
public static void main(String[] args) {
/*int i = 1;
double d = 1.0;
String s = "hello";
boolean b = true;*/
var i = 1;
var d = 1.0;
var s = "hello";
var b = true;
System.out.println(i);
System.out.println(d);
System.out.println(s);
System.out.println(b);
}
}
4.instanceof 的模式匹配
jdk 14 新增预览,jdk 16 正式版。通过 instanceof 模式匹配增强,可以直接在模式匹配的括号中声明对应类型的局部变量。
(1)之前写法
public class Person {
}
public class Student extends Person {
public void study() {
System.out.println("study");
}
}
public class InstanceofTest {
public static void main(String[] args) {
Person p = new Student();
if (p instanceof Student) {
Student student = (Student) p;
student.study();
}
}
}
(2)模式匹配增强
public class InstanceofTest {
public static void main(String[] args) {
Person p = new Student();
if (p instanceof Student s) {
s.study();
}
}
}
5.switch
- 之前 switch 表达式:
- 匹配自上而下,没有 break,则后边的 case 语句都会被执行;
- 不同 case 定义的变量名不能相同;
- 不允许在一个 case 后边写多个值;
- 整个 switch 不可以作为表达式的返回值。
- jdk 12 之后的 switch 表达式更加简洁。
(1)之前写法
public class SwitchTest {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("欢迎查询季节");
System.out.print("请输入月份:");
int month = in.nextInt();
switch (month) {
case 3:
case 4:
case 5:
System.out.println("春季");
break;
case 6:
case 7:
case 8:
System.out.println("夏季");
break;
case 9:
case 10:
case 11:
System.out.println("秋季");
break;
case 12:
case 1:
case 2:
System.out.println("冬季");
}
}
}
(2)jdk 12 之后
public class SwitchTest {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("欢迎查询季节");
System.out.print("请输入月份:");
int month = in.nextInt();
String season = switch (month) {
case 3, 4, 5 -> "春季";
case 6, 7, 8 -> "夏季";
case 9, 10, 11 -> "秋季";
case 12, 1, 2 -> "冬季";
default -> "输入有误!";
};
System.out.println(season);
}
}
(3)jdk 13 之后
public class SwitchTest {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("欢迎查询季节");
System.out.print("请输入月份:");
int month = in.nextInt();
String season = switch (month) {
case 3:
case 4:
case 5:
yield ("春季");
case 6:
case 7:
case 8:
yield ("夏季");
case 9:
case 10:
case 11:
yield ("秋季");
case 12:
case 1:
case 2:
yield ("冬季");
default:
yield ("输入有误!");
};
System.out.println(season);
}
}
6.文本块
Java 中,通常使用 String 类型表达 HTML、XML、SQL、JSON 等格式的字符串,在进行字符串赋值时,需要进行转义和连接操作,然后才能编译该代码,这种方式难以阅读和维护。
jdk 12 新增文本块,文本块就是多行字符串,有了文本块以后,用户无需转义,由 Java 自动完成。
public class TextBlock {
public static void main(String[] args) {
String text = """
<html>
<head>
<title>Test</title>
</head>
<body>
<p>This is test program.<p>
</body>
</html>
""";
System.out.println(text);
}
}
7.Record
在开发中,需要一个类只做纯数据载体、不负责修改等操作,可以将这种类型定义为 Record 类型。可以省略 构造方法、get / set 方法,toString 方法、equals 方法、 hashCode 方法 等等。
Record 类型可以添加:无参构造方法、静态变量、静态方法、实例方法。
public record Animal(String name, int age) {
// 自动生成构造方法,getter方法,toString方法,equals方法,hashCode方法,还有两个被 final 修饰的实例变量
// 添加无参构造方法
public Animal {
System.out.println("Animal()");
}
// 添加静态变量
public static final String TYPE = "Animal";
// 添加静态方法
public static void eat() {
System.out.println("Animal.eat()");
}
// 添加实例方法
public void run() {
System.out.println("Animal.run()");
}
}
public class RecordTest {
public static void main(String[] args) {
Animal animal = new Animal("Dog", 4);
System.out.println(animal.name());
System.out.println(animal.age());
System.out.println(animal);
System.out.println(Animal.TYPE);
Animal.eat();
animal.run();
}
}
8.密封类 / 接口
- 一种新的类修饰符,可以修饰类和接口可以控制哪些类可以扩展或实现该类或接口;
- 作用:
- 维护类层次结构的密闭性:可以保证一组类不被其他类继承或实现,确保了代码的安全性和稳定性;
- 预防代码意外扩展:可以保证在特定的类中才能继承或实现指定的类,强制限制可以继承或实现类的范围;
- 增强可读性和可维护性。
- jdk 15 新增,使用 sealed关键字修饰的类就是密封类;
- 密封类必须是父类或接口,可以使用 permits 关键字指定哪些子类可以继承该密封类或实现该密封接口;
- 密封类的子类必须使用 sealed、final、non-sealed 来修饰;
- Record 类型默认被 final 修饰,其可以做密封接口的实现类
public sealed class Person permits Student, Teacher{
}
public final class Student extends Person {
}
public non-sealed class Teacher extends Person {
}
二、新 API
1.String
(1)存储结构改变
jdk 9 之前,String 底层采用 char 类型数组存储字符。
从 jdk 9 开始,String 底层采用 byte 数组,目的是为了节省空间。
(2)jdk 11 新增方法
public class StringNewMethod {
public static void main(String[] args) {
/*
* isBlank(),空格、制表符、换行都认为是空
* */
String s1 = " ", s2 = "\t", s3 = "\n";
System.out.println(s1.isBlank() + " " + s2.isBlank() + " " + s3.isBlank()); // true true true
/*
* repeat(),重复字符串 n 次
* */
String s4 = "I Love You! ";
System.out.println(s4.repeat(3)); // I Love You! I Love You! I Love You!
/*
* strip(),去除字符串首尾空格
* */
String s5 = " I Miss You!\t";
System.out.println(s5.strip()); // I Miss You!
/*
* stripLeading(),去除字符串首空格
* */
String s6 = " I Hate You!\t";
System.out.println(s6.stripLeading()); // I Hate You!
/*
* stripTrailing(),去除字符串尾空格
* */
String s7 = " I Forgot You!\t";
System.out.println(s7.stripTrailing()); // I Forgot You!
/*
* lines(),将字符串按行分割,返回一个 Stream
* */
String s8 = "I\nAm\nReborn!";
s8.lines().forEach(System.out::println);
// I
// am
// reborn!
}
}
(3)jdk 12 新增方法
public class StringNewMethod {
public static void main(String[] args) {
/*
* indent(),缩进 n 个字符串
* */
String s9 = "I Like Her!";
System.out.println(s9.indent(6));
}
}
2.接口支持私有方法
jdk 8 中,接口支持公开静态方法和公开默认方法。
jdk 9 中,接口允许定义私有静态方法和私有成员方法,但不能定义私有默认方法。
public interface InterfaceTest {
public static void staticMethods(){
}
public default void defaultMethods(){
}
public void methods();
private static void privateStaticMethods(){
}
private void privateMethods(){
}
}
3.标识符命名变化
jdk 9 之前,标识符可以独立使用【_】命名。
jdk 9 开始,不能用下划线独立命名标识符了。
4.简化编译运行程序
运行一个 java 文件,必须先编译(javac),再运行(java)。
但是 jdk 11 开始,只需要一个 java 命令即可。
5.创建只读集合
jdk 9 开始,可以通过 List、Set、Map 接口提供的 of(E… elements) 静态方法创建不可变集合。
public class NotChangeCollection {
public static void main(String[] args) {
List<Integer> list = List.of(1, 2, 3);
list.forEach(System.out::println);
// 不可以增、删、改
// list.add(4);
// list.remove(0);
// list.set(0, 4);
Set<Double> set = Set.of(3.14, 5.20, 13.14);
set.forEach(System.out::println);
// 不可以增、删、改
// set.add(4.14);
// set.remove(0);
// set.set(0, 4.14);
Map<Integer, String> map = Map.of(1, "I ", 2, "Love ", 3, "You");
map.forEach((k, v) -> System.out.println(k + ":" + v));
// 不可以增、删、改
// map.put(4, "!");
// map.remove(1);
// map.replace(2, "!");
List<String> stringList = Arrays.asList("Hello", "World");
stringList.forEach(System.out::println);
// 不可以增、删
// stringList.add("!");
// stringList.remove(0);
// 可以修改
stringList.set(0, "qqq");
stringList.forEach(System.out::println);
}
}
6.Optional
jdk 8 之前,为了避免错误引用 null 造成空指针异常,经常需要一系列复杂的判空操作。
jdk 8 开始,引入了 Optional 类,可以简单的对 null 值处理,从而避免空指针异常。Optional 类是一个包含有可选项的包装类,意味着 Optional 类中既可以含有对象也可以为 null。
(1)创建 Optional 对象
使用 Optional 类提供的 of() 和 ofNullable() 静态方法创建包含值的 Optional 实例。
若将 null 当作参数传入 of() ,会抛空指针异常。若将 null 当参数传入 ofNullable() ,不会抛出空指针异常。
因此,当对象可能存在或不存在,应使用 ofNullable() 创建Optional 实例。
(2)Optional 类的方法
- T get():若值不为 null 则直接取出该值,若为 null 则抛出空指针异常;
- T orElse(T other):若值不为 null 则直接取出该值,若为 null 则取出参数 other 的值。