1. 新语法结构
新的语法结构,勾勒出了 Java 语法进化的一个趋势,将开发者从复杂、繁琐
的低层次抽象中逐渐解放出来,以更高层次、更优雅的抽象,既降低代码量
,又避免意外编程错误的出现,进而提高代码质量和开发效率。
1.1 Java的REPL工具: jShell命令
JDK9的新特性
Java 终于拥有了像Python 和 Scala 之类语言的REPL工具(交互式编程环境,read - evaluate - print - loop):jShell
。以交互式的方式对语句和表达式进行求值。即写即得
、快速运行
。
利用jShell在没有创建类的情况下,在命令行里直接声明变量,计算表达式,执行语句。无需跟人解释”public static void main(String[] args)
”这句"废话"。
使用举例
- 调出
jShell
- 获取帮助
- 基本使用
- 导入指定的包
- 默认已经导入如下的所有包:(包含java.lang包)
- 只需按下 Tab 键,就能自动补全代码
- 列出当前 session 里所有有效的代码片段
- 查看当前 session 下所有创建过的变量
- 查看当前 session 下所有创建过的方法
Tips:我们还可以重新定义相同方法名和参数列表的方法,即对现有方法的修改(或覆盖)。
- 使用外部代码编辑器来编写 Java 代码
从外部文件加载源代码【HelloWorld.java】
public void printHello() { System.out.println("马上周末了,祝大家周末快乐!"); } printHello();
- 使用
/open
命令调用 - 退出
jShell
1.2 异常处理之try-catch资源关闭
在JDK7 之前,这样处理资源的关闭:
@Test public void test01() { FileWriter fw = null; BufferedWriter bw = null; try { fw = new FileWriter("d:/1.txt"); bw = new BufferedWriter(fw); bw.write("hello"); } catch (IOException e) { e.printStackTrace(); } finally { try { if (bw != null) { bw.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (fw != null) { fw.close(); } } catch (IOException e) { e.printStackTrace(); } } }
JDK7的新特性
在try
的后面可以增加一个()
,在括号中可以声明流对象并初始化。try中的代码执行完毕,会自动把流对象释放,就不用写finally
了。
格式:
try(资源对象的声明和初始化){ 业务逻辑代码,可能会产生异常 }catch(异常类型1 e){ 处理异常代码 }catch(异常类型2 e){ 处理异常代码 }
说明:
1、在try()
中声明的资源,无论是否发生异常,无论是否处理异常,都会自动关闭资源对象,不用手动关闭了。
2、这些资源实现类必须实现AutoCloseable
或Closeable
接口,实现其中的close()
方法。Closeable
是AutoCloseable
的子接口。Java7几乎把所有的“资源类”(包括文件IO的各种类、JDBC编程的Connection、Statement等接口…)都进行了改写,改写后资源类都实现了AutoCloseable
或Closeable
接口,并实现了close()
方法。
3、写到try()
中的资源类的变量默认是 final 声明的,不能修改。
举例:
//举例1 @Test public void test02() { try ( FileWriter fw = new FileWriter("d:/1.txt"); BufferedWriter bw = new BufferedWriter(fw); ) { bw.write("hello"); } catch (IOException e) { e.printStackTrace(); } } //举例2 @Test public void test03() { //从d:/1.txt(utf-8)文件中,读取内容,写到项目根目录下1.txt(gbk)文件中 try ( FileInputStream fis = new FileInputStream("d:/1.txt"); InputStreamReader isr = new InputStreamReader(fis, "utf-8"); BufferedReader br = new BufferedReader(isr); FileOutputStream fos = new FileOutputStream("1.txt"); OutputStreamWriter osw = new OutputStreamWriter(fos, "gbk"); BufferedWriter bw = new BufferedWriter(osw); ) { String str; while ((str = br.readLine()) != null) { bw.write(str); bw.newLine(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
JDK9的新特性
try的前面可以定义流对象,try后面的()中可以直接引用流对象的名称。在try代码执行完毕后,流对象也可以释放掉,也不用写finally
了。
格式:
A a = new A(); B b = new B(); try(a;b){ 可能产生的异常代码 }catch(异常类名 变量名){ 异常处理的逻辑 }
举例:
@Test public void test04() { InputStreamReader reader = new InputStreamReader(System.in); OutputStreamWriter writer = new OutputStreamWriter(System.out); try (reader; writer) { //reader是final的,不可再被赋值 // reader = null; } catch (IOException e) { e.printStackTrace(); } }
1.3 局部变量类型推断
JDK 10的新特性
局部变量的显示类型声明,常常被认为是不必须的,给一个好听的名字反而可以很清楚的表达出下面应该怎样继续。本新特性允许开发人员省略通常不必要的局部变量类型声明,以增强Java语言的体验性、可读性。
- 使用举例
//1.局部变量的实例化 var list = new ArrayList<String>(); var set = new LinkedHashSet<Integer>(); //2.增强for循环中的索引 for (var v : list) { System.out.println(v); } //3.传统for循环中 for (var i = 0; i < 100; i++) { System.out.println(i); } //4. 返回值类型含复杂泛型结构 var iterator = set.iterator(); //Iterator<Map.Entry<Integer, Student>> iterator = set.iterator();
- 不适用场景
- 声明一个成员变量
- 声明一个数组变量,并为数组静态初始化(省略new的情况下)
- 方法的返回值类型
- 方法的参数类型
- 没有初始化的方法内的局部变量声明
- 作为catch块中异常类型
- Lambda表达式中函数式接口的类型
- 方法引用中函数式接口的类型
代码举例:
声明一个成员变量,并初始化值为null
声明一个数组变量,并为数组静态初始化(省略new的情况下)
没有初始化的方法内的局部变量声明
方法的返回值类型
方法的参数类型
构造器的参数类型
作为catch
块中异常类型
Lambda表达式中函数式接口的类型
方法引用中函数式接口的类型
注意:
- var不是一个关键字,而是一个类型名,将它作为变量的类型。不能使用 var 作为类名。
- 这不是JavaScript。var并不会改变 Java是一门静态类型语言的事实。编译器负责推断出类型,并把结果写入字节码文件,就好像是开发人员自己敲入类型一样。
1.4 instanceof的模式匹配
JDK14中预览特性:
instanceof
模式匹配通过提供更为简便的语法,来提高生产力。有了该功能,可以减少Java程序中显式强制转换的数量,实现更精确、简洁的类型安全的代码。
Java 14之前旧写法:
if(obj instanceof String){ String str = (String)obj; //需要强转 .. str.contains(..).. }else{ ... }
Java 14新特性写法:
if(obj instanceof String str){ .. str.contains(..).. }else{ ... }
举例:
/** * instanceof的模式匹配(预览) * * @author shkstart * @create 上午 11:32 */ public class Feature01 { @Test public void test1(){ Object obj = new String("hello,Java14"); obj = null;//在使用null 匹配instanceof 时,返回都是false. if(obj instanceof String){ String str = (String) obj; System.out.println(str.contains("Java")); }else{ System.out.println("非String类型"); } //举例1: if(obj instanceof String str){ //新特性:省去了强制类型转换的过程 System.out.println(str.contains("Java")); }else{ System.out.println("非String类型"); } } } // 举例2 class InstanceOf{ String str = "abc"; public void test(Object obj){ if(obj instanceof String str){//此时的str的作用域仅限于if结构内。 System.out.println(str.toUpperCase()); }else{ System.out.println(str.toLowerCase()); } } } //举例3: class Monitor{ private String model; private double price; // public boolean equals(Object o){ // if(o instanceof Monitor other){ // if(model.equals(other.model) && price == other.price){ // return true; // } // } // return false; // } public boolean equals(Object o){ return o instanceof Monitor other && model.equals(other.model) && price == other.price; } }
JDK15中第二次预览:
没有任何更改。
JDK16中转正特性:
在Java16中转正。
1.5 switch表达式
传统switch
声明语句的弊端:
- 匹配是自上而下的,如果忘记写
break
,后面的case
语句不论匹配与否都会执行; —>俗称:case穿透 - 所有的case语句共用一个块范围,在不同的case语句定义的变量名不能重复;
- 不能在一个
case
里写多个执行结果一致的条件; - 整个
switch
不能作为表达式返回值;
//常见错误实现 switch(month){ case 3|4|5://3|4|5 用了位运算符,11 | 100 | 101结果是 111是7 System.out.println("春季"); break; case 6|7|8://6|7|8用了位运算符,110 | 111 | 1000结果是1111是15 System.out.println("夏季"); break; case 9|10|11://9|10|11用了位运算符,1001 | 1010 | 1011结果是1011是11 System.out.println("秋季"); break; case 12|1|2://12|1|2 用了位运算符,1100 | 1 | 10 结果是1111,是15 System.out.println("冬季"); break; default: System.out.println("输入有误"); }
JDK12中预览特性:
- Java 12将会对
switch
声明语句进行扩展,使用case L ->
来替代以前的break;
,省去了break
语句,避免了因少写 break 而出错。 - 同时将多个
case
合并到一行,显得简洁、清晰,也更加优雅的表达逻辑分支。 - 为了保持兼容性,case 条件语句中依然可以使用字符
:
,但是同一个 switch 结构里不能混用->
和:
,否则编译错误。
举例:
Java 12之前
public class SwitchTest { public static void main(String[] args) { 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); } } enum Fruit { PEAR, APPLE, GRAPE, MANGO, ORANGE, PAPAYA; }
switch
语句如果漏写了一个 break
,那么逻辑往往就跑偏了,这种方式既繁琐,又容易出错。
Java 12中:
public class SwitchTest1 { public static void main(String[] args) { Fruit fruit = Fruit.GRAPE; switch(fruit){ case PEAR -> System.out.println(4); case APPLE,MANGO,GRAPE -> System.out.println(5); case ORANGE,PAPAYA -> System.out.println(6); default -> throw new IllegalStateException("No Such Fruit:" + fruit); }; } }
更进一步:
public class SwitchTest2 { public static void main(String[] args) { Fruit fruit = Fruit.GRAPE; int 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中二次预览特性:
JDK13中引入了yield
语句,用于返回值。这意味着,switch
表达式(返回值)应该使用yield
,switch
语句(不返回值)应该使用break
。
yield
和return
的区别在于:return
会直接跳出当前循环或者方法,而yield
只会跳出当前switch
块。
在以前:
@Test public void testSwitch1(){ 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); }
在JDK13中:
@Test public void testSwitch2(){ String x = "3"; int i = switch (x) { case "1" -> 1; case "2" -> 2; default -> { yield 3; } }; System.out.println(i); }
或者
@Test public void testSwitch3() { String x = "3"; int i = switch (x) { case "1": yield 1; case "2": yield 2; default: yield 3; }; System.out.println(i); }
JDK14中转正特性:
这是JDK 12和JDK 13中的预览特性,现在是正式特性了。
JDK17的预览特性:switch的模式匹配
旧写法:
static String formatter(Object o) { String formatted = "unknown"; if (o instanceof Integer i) { formatted = String.format("int %d", i); } else if (o instanceof Long l) { formatted = String.format("long %d", l); } else if (o instanceof Double d) { formatted = String.format("double %f", d); } else if (o instanceof String s) { formatted = String.format("String %s", s); } return formatted; }
模式匹配新写法:
static String formatterPatternSwitch(Object o) { return switch (o) { case Integer i -> String.format("int %d", i); case Long l -> String.format("long %d", l); case Double d -> String.format("double %f", d); case String s -> String.format("String %s", s); default -> o.toString(); }; }
直接在 switch
上支持 Object
类型,这就等于同时支持多种类型,使用模式匹配得到具体类型,大大简化了语法量,这个功能很实用。
JDK8-JDK17中的新特性(var类型推断、模式匹配、Record、密封类)(二):https://developer.aliyun.com/article/1416376