JDK8-JDK17中的新特性(var类型推断、模式匹配、Record、密封类)(一)

简介: JDK8-JDK17中的新特性(var类型推断、模式匹配、Record、密封类)(一)

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、这些资源实现类必须实现AutoCloseableCloseable接口,实现其中的close()方法。CloseableAutoCloseable的子接口。Java7几乎把所有的“资源类”(包括文件IO的各种类JDBC编程的ConnectionStatement等接口…)都进行了改写,改写后资源类都实现了AutoCloseableCloseable接口,并实现了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表达式(返回值)应该使用yieldswitch语句(不返回值)应该使用break

yieldreturn的区别在于: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

相关文章
|
3月前
|
Java Linux
java基础(3)安装好JDK后使用javac.exe编译java文件、java.exe运行编译好的类
本文介绍了如何在安装JDK后使用`javac.exe`编译Java文件,以及使用`java.exe`运行编译好的类文件。涵盖了JDK的安装、环境变量配置、编写Java程序、使用命令行编译和运行程序的步骤,并提供了解决中文乱码的方法。
76 2
|
2月前
|
Java Go Scala
jdk10的var局部变量类型推理
本文介绍了JDK 10中引入的局部变量类型推断功能,即`var`关键字,它允许开发者在声明具有初始值的局部变量时省略显式类型,以简化代码并提高可读性。
15 1
jdk10的var局部变量类型推理
|
3月前
|
存储 Java 开发者
【Java新纪元启航】JDK 22:解锁未命名变量与模式,让代码更简洁,思维更自由!
【9月更文挑战第7天】JDK 22带来的未命名变量与模式匹配的结合,是Java编程语言发展历程中的一个重要里程碑。它不仅简化了代码,提高了开发效率,更重要的是,它激发了我们对Java编程的新思考,让我们有机会以更加自由、更加创造性的方式解决问题。随着Java生态系统的不断演进,我们有理由相信,未来的Java将更加灵活、更加强大,为开发者们提供更加广阔的舞台。让我们携手并进,共同迎接Java新纪元的到来!
74 11
|
3月前
|
Java API 开发者
【Java字节码操控新篇章】JDK 22类文件API预览:解锁Java底层的无限可能!
【9月更文挑战第6天】JDK 22的类文件API为Java开发者们打开了一扇通往Java底层世界的大门。通过这个API,我们可以更加深入地理解Java程序的工作原理,实现更加灵活和强大的功能。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来!
|
3月前
|
Java API 开发者
【Java字节码的掌控者】JDK 22类文件API:解锁Java深层次的奥秘,赋能开发者无限可能!
【9月更文挑战第8天】JDK 22类文件API的引入,为Java开发者们打开了一扇通往Java字节码操控新世界的大门。通过这个API,我们可以更加深入地理解Java程序的底层行为,实现更加高效、可靠和创新的Java应用。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来,并积极探索类文件API带来的无限可能!
|
4月前
|
存储 Java
【Java集合类面试七】、 JDK7和JDK8中的HashMap有什么区别?
JDK7中的HashMap使用数组加链表解决冲突,而JDK8增加了红黑树结构以优化链表过长时的性能,提高查找效率。
|
4月前
|
Oracle 安全 Java
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
|
4月前
|
Java 编译器 API
JDK版本特性问题之在 JDK 17 中,想声明一个密封类,如何解决
JDK版本特性问题之在 JDK 17 中,想声明一个密封类,如何解决
|
4月前
|
Java 编译器 API
JDK8到JDK18版本升级的新特性问题之在JDK10中,实现局部变量类型推断,如何操作
JDK8到JDK18版本升级的新特性问题之在JDK10中,实现局部变量类型推断,如何操作
|
4月前
|
Java API
JDK版本特性问题之Hidden Classes(隐藏类)在 JDK 15 中有何用途,如何实现
JDK版本特性问题之Hidden Classes(隐藏类)在 JDK 15 中有何用途,如何实现