前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java17新特性详解与安装

Java17新特性详解与安装

作者头像
Jensen_97
发布2023-09-09 15:46:26
1.6K0
发布2023-09-09 15:46:26
举报
文章被收录于专栏:技术客栈

前言

Java 17 是 2021 年 9 月 14 日正式发布的,距今也已经快2年多了,是一个长期支持(LTS)版本。Java 17 这个版本非常重要,Spring Framework 6.0Spring Boot 3.0 最低支持都是 Java 17,搞Java开发肯定是离不开Spring这个主流的框架。下面这张图是 Oracle 官方给出的 Oracle JDK 支持的时间线,可以看得到Java17支持到2029年9月。

增强Switch

之前的 switch 写法如下:

代码语言:javascript
复制
String a = "jdk17";
     switch (a){
         case "jdk8":
             System.out.println("我是jdk8");
             break;
         case "jdk17":
             System.out.println("我是jdk17");
             break;
         default:
             System.out.println("未知");
             break;
     }

从jdk12后可以通过 switch 表达式来进行简化。使用箭头“->”,并且不需要每个 casebreak,大大提高了我们的编写效率,如下所示:

代码语言:javascript
复制
switch (a) {
            case "jdk8" -> System.out.println("我是jdk8");
            case "jdk17" -> System.out.println("我是jdk17");
            default -> System.out.println("未知");
        }

当我们需要赋值给一个变量的时候,不需要在每个case里头挨个赋值,其实 switch 也可以返回一个值,例如:

代码语言:javascript
复制
String a = "jdk17";
        String who = switch (a) {
            case "jdk8" -> "我是jdk8";
            case "jdk17" -> "我是jdk17";
            default -> "未知";
        };
        System.out.println(who);

输出:

代码语言:javascript
复制
我是jdk17

如果是匹配多个case 的话用逗号分开,例如:

代码语言:javascript
复制
String a = "jdk17";
        String who = switch (a) {
            case "jdk8","jdk17" -> "我是jdk家族";
            case "spring","spring boot" -> "我是spring家族";
            default -> "未知";
        };
 System.out.println(who);

如果你想在case里做不止一件事,比如在返回之前先进行一些计算或者打印操作。可以通过大括号来作为case块,最后的返回值使用关键字 yield 进行返回。

代码语言:javascript
复制
String a = "spring";
        String who = switch (a) {
            case "jdk8","jdk17" -> {
                System.out.println(1+1);
                yield "我是jdk家族";
            }
            case "spring","spring boot" -> {
                System.out.println(2+2);
                yield "我是spring家族";
            }
            default -> "未知";
        };
        System.out.println(who);

输出:

代码语言:javascript
复制
4
我是spring家族

以及之前的switch只支持 数值和字符串常量的匹配 ,而现在还支持对对象的类型来进行匹配,例如:

代码语言:javascript
复制
Object a = 888;
        String who = switch (a) {
            case null -> "is null";
            case Integer i -> String.format("i is %s",i);
            case String s -> String.format("s is string %s",s);
            default -> a.toString();
        };
        System.out.println(who);

输出:

代码语言:javascript
复制
i is 888

文本块

在Java17之前的版本里,如果我们需要定义一个字符串,比如一个JSON数据或者一些html等,基本都是采用拼接的方式去定义,大量的加号和转义的双引号非常的恶心且难看,例如:

代码语言:javascript
复制
String text = "{\n" +
                "  \"name\": \"小黑说Java\",\n" +
                "  \"age\": 18,\n" +
                "  \"address\": \"北京市西城区\"\n" +
                "}";

而从jdk15后这个写法才得以改善,只需要在前后加上 """ 。

代码语言:javascript
复制
String text = """
                {
                  "name": "小黑说Java",
                  "age": 18,
                  "address": "北京市西城区"
                }
                """;

显然,更方便,更简洁了。若是在文本块内加入空格或者换行之类的,请参考以下:

代码语言:javascript
复制
\s:表示空格
\:表示不换行
\s\:表示添加一个空格且不换行

instanceof增强

通常我们使用instanceof时,一般发生在需要对一个变量的类型进行判断,如果符合指定的类型,则强制类型转换为一个新变量。

之前的写法:

代码语言:javascript
复制
List<?> rows = selectMenuList(menu, getUserId()).getRows();
List<SysMenu> menus =  new ArrayList<>();
rows.forEach(r->{
   if (r instanceof SysMenu)
       menus.add((SysMenu)r);
});

jdk17后的写法:

代码语言:javascript
复制
List<?> rows = selectMenuList(menu, getUserId()).getRows();
List<SysMenu> menus =  new ArrayList<>();
rows.forEach(r->{
   if (r instanceof SysMenu sysMenu)
       menus.add(sysMenu);
});

Record关键字

record用于创建不可变的数据类。在这之前如果你需要创建一个存放数据的类,通常需要先创建一个Class,然后生成构造方法、getter、setter、hashCode、equals和toString等这些方法,或者使用Lombok来简化这些操作。而record就基本相当于实体类+Lombok的效果,但是 record 是不支持继承的,适合数据量少且不复杂的情况下使用。

创建一个实体类 Pon:

代码语言:javascript
复制
@Data
@AllArgsConstructor
public class Pon {
    private String name;
    private String nickName;
    private Integer addrNum;
}

然后进行一些简单的测试

代码语言:javascript
复制
public class JunitTest {
    @Test
    void test() {

        Pon pon = new Pon("张三", "小三", 65220);
        System.out.println(pon.getName());
        System.out.println(pon.getNickName());
        System.out.println(pon.getAddrNum());
    }
}

输出:

代码语言:javascript
复制
张三
小三
65220

以下是使用record代替前面的Pon类,record的属性不需要去定义,直接通过参数的形式设置。

代码语言:javascript
复制
public record RecordPon(String name, String nikName, Integer addrNum) {
  
}

接下来对RecordPon进行一些简单的测试

代码语言:javascript
复制
public class JunitTest {
    @Test
    void test() {
        RecordPon recordPon = new RecordPon("张三", "小三", 65220);
        System.out.println(recordPon.name());
        System.out.println(recordPon.nikName());
        System.out.println(recordPon.addrNum());
        RecordPon recordPon2 = new RecordPon("李四", "小四", 65220);
        System.out.println(recordPon2.name());
        System.out.println(recordPon2.nikName());
        System.out.println(recordPon2.addrNum());
    }
}

输出:

代码语言:javascript
复制
张三
小三
65220
李四
小四
65220

当然了,record 同样也有构造方法,可以在构造方法中对数据进行一些操作,例如:

代码语言:javascript
复制
public record RecordPon(String name, String nikName, Integer addrNum) {
    public RecordPon {
        if (Objects.equals(name, ""))
            System.err.println( "name is not null");
    }
}

总结:Lombokrecord 是不同的工具,服务于不同的目的。此外,Lombok 更加灵活,它可以用于 record 受限的场景。

Helpful NullPointerExceptions

Java8 我们如果遇到NPE,通常只会输出将显示NullPointerException发生的行号,但不知道哪个方法调用时产生的null,必须通过调试的方式找到。对于复杂度高的代码来讲就非常耗时,而从Helpful NullPointerExceptions可以在我们遇到NPE时节省一些时间,会准确显示发生NPE的精确位置,比如以下代码会发生一个NPE:

代码语言:javascript
复制
public class JunitTest {
    @Test
    void test() {
        String str = null;
        int length = str.length();
    }
}

输出:

密封类 sealed class

密封类是一种特殊的类,它用来表示受限的类继承结构,即一个类只能有有限的几种子类,而不能有任何其他类型的子类。这样可以让我们更好的控制哪些类可以对我定义的类进行扩展,而在这之前一个类要么是可以被extends的,要么是final的任何人都不能继承的,只有这两种选项,没有什么灵活性。首先创建Animal、DogCat

代码语言:javascript
复制
public abstract class Animal {
     public void put(){
        System.out.println("我是动物");
    }
}
代码语言:javascript
复制
public class Dog extends Animal {

}
代码语言:javascript
复制
public class Cat extends Animal {

}

在这里这个Animal 是可以被任何一个类继承的,例如:

代码语言:javascript
复制
public class JunitTest {
    @Test
    void test() {
        Cat cat = new Cat();
        Dog dog = new Dog();
        Animal animal = cat;
        Animal animal2 = dog;
        class china extends Animal{}
        class usa extends Dog{}
    }
}

除了DogCat类能继承之外,这里创建的china类也能继承Animal类,usa类也继承Dog类。ok这本来没什么问题,就是这个关系有点不正常,国家怎么能继承于动物呢?

Jdk17中通过密封类可以解决这个问题,主要就这几个关键字:

  • final:不允许被继承
  • sealed:密封类(需要指定哪个类可以扩展它)
  • non-sealed:可以被任何类继承
  • permits :指定哪个类可以继承于我

现在我们将上面的Animal 类改成密封类,例如:

代码语言:javascript
复制
public sealed class Animal permits Dog{
    public void put(){
        System.out.println("我是动物");
    }
}

此处的Animal是密封类,指定了只有Dog类可以继承它

代码语言:javascript
复制
public sealed class Dog extends Animal permits DogSon{
}

而此时Dog类也是一个密封类,它也指定了只有DogSon这个类可以继承它。这里做个小测试,创建一个BigDog类并继承Dog

直接告诉你 密封层次结构中不允许使用"BigDog" ,需要给BigDog指定这个permits ,其他亦是如此,除非你定义成non-sealed

备注????:密封类子类必须是 final类、sealed类或non-sealed类,并且 父类子类 必须在同一个包下

开始安装

下载安装可以参考我之前的文章:《Jdk17安装+环境配置详细教程【Windows》

总结

以上主要介绍了Jdk17的一些主流的新特性,更多具体特性请参考官方文档: https://www.oracle.com/java/technologies/javase/17-relnote-issues.html#NewFeature

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023/08/25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 增强Switch
  • 文本块
  • instanceof增强
  • Record关键字
  • Helpful NullPointerExceptions
  • 密封类 sealed class
  • 开始安装
  • 总结
相关产品与服务
腾讯云服务器利旧
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档