Java – Java 新特性说明与使用

switch 表达式(Java 12)

传统switch的弊端

传统的switch声明语句(switch statement)在使用中有一些问题:

  • 匹配是自上而下的,如果忘记写break, 后面的case语句不论匹配与否都会执行;
  • 所有的case语句共用一个块范围,在不同的case语句定义的变量名不能重复;
  • 不能在一个case里写多个执行结果一致的条件;
  • 整个switch不能作为表达式返回值;

Java 12将会对switch声明语句进行扩展,可将其作为增强版的 switch 语句或称为 "switch 表达式"来写出更加简化的代码。

举例:

传统的 switch 代码
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,那么逻辑往往就跑偏了,这种方式既繁琐,又容易出错。如果换成 switch 表达式,Pattern Matching 机制能够自然地保证只有单一路径会被执行:

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);
    }
}

 

 

支持unicode 11(Java 12)

JDK 12版本包括对Unicode 11.0.0的支持。在发布支持Unicode 10.0.0的JDK 11之后,Unicode 11.0.0引入了以下JDK 12中包含的新功能:

  • 684 new characters
  • 11 new blocks
  • 7 new scripts.

其中:

  • 684个新字符,包含以下重要内容:
  • 66个表情符号字符(66 emoji characters)
  • Copyleft符号(Copyleft symbol)
  • 评级系统的半星(Half stars for rating systems)
  • 额外的占星符号(Additional astrological symbols)
  • 象棋中国象棋符号(Xiangqi Chinese chess symbols)

7个新脚本:

  • Hanifi Rohingya
  • Old Sogdian
  • Sogdian
  • Dogra
  • Gunjala Gondi
  • Makasar
  • Medefaidrin

11个新块,包括上面列出的新脚本的7个块和以下现有脚本的4个块:

  • 格鲁吉亚扩展(Georgian Extended)
  • 玛雅数字(Mayan Numerals)
  • 印度Siyaq数字(Indic Siyaq Numbers)
  • 国际象棋符号(Chess Symbols)

 

支持压缩数字格式化(Java 12)

NumberFormat 添加了对以紧凑形式格式化数字的支持。紧凑数字格式是指以简短或人类可读形式表示的数字。例如,

在en_US语言环境中,1000可以格式化为“1K”,1000000可以格式化为“1M”,具体取决于指定的样式
NumberFormat.Style。

public void testCompactNumberFormat(){
        var cnf = NumberFormat.getCompactNumberInstance(Locale.CHINA,
                NumberFormat.Style.SHORT);
        System.out.println(cnf.format(1_0000));
        System.out.println(cnf.format(1_9200));
        System.out.println(cnf.format(1_000_000));
        System.out.println(cnf.format(1L << 30));
        System.out.println(cnf.format(1L << 40));
        System.out.println(cnf.format(1L << 50));
    }

输出

1万
2万
100万
11亿
1兆
1126兆

 

String新增方法(Java 12)

transform 链式处理方法

语法:

“xxx”.transform(word -> {})

作用:

提供的函数作为输入提供给特定的String实例,并返回该函数返回的输出。

举例:

var result = "foo".transform(input -> input + " bar");
System.out.println(result); // foo bar

或者

var result = "foo"
      .transform(input -> input + " bar")
      .transform(String::toUpperCase)
System.out.println(result); // FOO BAR

 

indent 缩进方法

该方法允许我们调整String实例的缩进。

举例:

private static void testIndent() {
     System.out.println("======test java 12 indent======");
     String result = "Java\n Python\nC++".indent(3);
     System.out.println(result);
}

输出

======test java 12 indent======
   Java
     Python
    C++

换行符 \n 后向前缩进 n 个空格,为 0 或负数不缩进。

 

Files 新增 mismatch 方法(Java 12)

对两个文件中的内容进行对比,返回两个文件中首次出现不同内容的所在位置,如果两个文件内容完全相同,则返回 -1L

public void testFilesMismatch() throws IOException {
        FileWriter fileWriter = new FileWriter("tmp\\a.txt");
        fileWriter.write("a");
        fileWriter.write("b");
        fileWriter.write("c");
        fileWriter.close();
        FileWriter fileWriterB = new FileWriter("tmp\\b.txt");
        fileWriterB.write("a");
        fileWriterB.write("1");
        fileWriterB.write("c");
        fileWriterB.close();
        System.out.println(Files.mismatch(Path.of("tmp/a.txt"),Path.of("tmp/b.txt")));
    }

 

对 switch 引入 yield (Java 13)

在JDK 12中引入了Switch表达式作为预览特性。JDK 13提出了第二个switch表达式预览。JEP 354修改了这个特性,它引入了yield语句,用于返回值。这意味着,switch表达式(返回值)应该使用yield, switch语句(不返回值)应该使用break。

    public void testSwitch2(){
        String x = "3";
        int i = switch (x) {
            case "1" -> 1;
            case "2" -> 2;
            default -> {
                yield 3;
            }
        };
        System.out.println(i);
    }

或者

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);
    }

在这之后,switch中就多了一个关键字用于跳出switch块了,那就是yield,他用于返回一个值。

和return的区别在于:return会直接跳出当前循环或者方法,而yield只会跳出当前switch块。

 

文本块(Java 13)

在Java中,通常需要使用String类型表达HTML,XML,SQL或JSON等格式的字符串,在进行字符串赋值时需要进行转义和连接操作,然后才能编译该代码,这种表达方式难以阅读并且难以维护。

文本块就是指多行字符串,例如一段格式化后的xml、json等。而有了文本块以后,用户不需要转义,Java能自动搞定。因此,文本块将提高Java程序的可读性和可写性。

"""
<html>
    <body>
        <p>Hello, world</p>
    </body>
</html>
""";

 

关于删除前后空格的问题

使用文本块,会对包裹的文本进行删首尾空的操作,对于文本块中的每一行文本后面的空格,文本块都会进行删尾空。

对于文本块前的空格,以【文本块结束符】所在的位置决定

如果文本块中的每一行后存在空格,文本块会直接删除
"""
line1........
line2...
line3......
"""

删除后的效果
"""
line1
line2
line3
"""

对于文本块中的每一行前面的空格删除规则如下:

如果文本块前的每一行前面都有空格如下
"""
........line1
........line2
........line3
...."""

文本块会以【结束符】所在的位置进行计算,如果【结束符】所在的位置前有若干个空格,则删除每一行前面若干个空格
"""
    ....line1
    ....line2
    ....line3
    """

注意:如果需要在每一行后面增加空格,可以使用转义符【\s】表示

如果希望两行内容不作为真正的换行【即不换行】,可以使用【\】来表示不换行

 

关于文本块的开始和结尾定义

1.文本块的开始必须开始在【开始符】之后的【换行符】也叫【行终止符】

2.文本块的结束不需要【行终止符】

1.正确,"abc"在 """ 的换行之后,换行符即为【行终止符】
"""
abc
"""

2.正确,“abc”在 """ 之前
"""
abc"""

3.错误,“abc” 不在 “”” 的【行终止符】之后
““”abc
“””

 

文本块拼接

使用一般方式进行拼接

String code = """
      public void print(""" + type + """o) {
            System.out.println(Objects.toString(o));
       }
       """;

 

使用文本替换方式拼接

String code = """
     public void print($type o) {
         System.out.println(Objects.toString(o));
     }
     """.replace("$type", type);

 

使用format格式化文本方式拼接

String code = String.format("""
     public void print(%s o) {
         System.out.println(Objects.toString(o));
     }
     """, type);

 

instanceof 模式匹配 (Java 14)

instanceof 是用于判断某个变量是否为指定对象类型,在Java 中一直都有这样的功能,但在Java14中提出了一种新的匹配,可以省下比较多的代码操作:

判断 obj 是否为 String 类型,如果是,则对 obj 对象进行文本操作
if (obj instanceog String){
   // 需要强转类型后再做相应的对象操作
   String str = (String) obj;
   str.toupcase(); 
}else{
   System.out.println("不是String类型");
}

以往的操作方式需要对对象进行强转

 

在Java14后,可以通过语法优化实现直接赋值,无需强制转化类型

判断 obj 是否为 String 类型,如果是,则对 obj 对象进行文本操作
if (obj instanceog String str){
   // 不再需要强转类型后再做相应的对象操作
   System.out.println(str);
}else{
   // 根据逻辑上来说,else 中就不能访问 str 变量了
   System.out.println("不是String类型");
}

 

 

record 结构类

当我们需要创建一个只需数据类(即只是用来存储和读取数据的类)时,我们往往需要在这个类上,定义好 final 常量成员,和 get 方法,以及 toSting 和 hashcode 方法,这样创建的数据类显得异常麻烦,在Java14中提供了一种新特性,可以通过定义 record 类,直接自动化创建以上功能。

public final class User {
    private final String name;
    private final User partner;

    public User(String name, User partner) {
        this.name = name;
        this.partner = partner;
    }

    public String getName() {
        return name;
    }

    public User getPartner() {
        return partner;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return name.equals(user.name) &&
                partner.equals(user.partner);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, partner);
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", partner=" + partner +
                '}';
    }
}

不使用新特性创建数据类的方法,显得非常麻烦

 

public record Person(String name,Person partner) { }

使用新特性后,只需要声明 record 类即可达到相同效果。

 

注意:

1.可以在 Record 声明的类中定义静态字段、静态方法、构造器或实例方法。

2. 不能在 Record 声明的类中定义实例 字段

类不能声明为 abstract ,因为 Record 会被声明为 final 类型,与 abstract 发生冲突了;

不能声明显式的父类等,因为 Record 类会被继承为 java.lang.Record 父类了,只能单继承。

 

 

sealed 密封类(Java 15)

Java提出了一个问题,在以往对于Java类的继承问题上,只允许【可被继承】和【不可被继承】两种状态,默认则是【可被继承】状态的,当某个类不希望被其它类继承时,可以使用【final】修饰符来定义该类不能被继承。

但是,单从【可被继承】和【不可被继承】两种状态,在类继承上会显得比较单一,于是Java15中提出了一个新的慨念,称为【密封类】

密封类并不完全拒绝被继承,也判不完全允许被继承,而是允许特定的子类继承。

这种思想是因为,继承类的诞生不全是为了代码复用,也有作为规范类的用途范围的作用。而sealed 密封类则是作为规范某些子类的用途范围而设的思想。

定义 sealed 类后,该类需要声明指定【允许继承的子类】,则除指定子类允许被继承外,其它子类都不能被继承。

规则:

1.子类若要继承 sealed 父类,则子类必须满足【final】类,阻止后代类再次继承子类。

2.子类若要继承 sealed 父类,则子类必须满足【non-sealed】 类,表示子类可以被任意后代类继承

3.如果子类也需要规范后代类的继承,则子类必须满足【sealed】类,并指定可被继承的孙类

// 祖类是 sealed 类,并指定允许被继承的子类为 Teacher,Student,Worker
public sealed class Person permits Teacher,Student,Worker{ } //人

// 若Teacher 类想继承 Person 类,则必须修饰 final 类
final class Teacher extends Person { }//教师

// 若 Student 类还希望它的指定子类能被继承,也可以修饰为 sealed 类
sealed class Student extends Person permits MiddleSchoolStudent,GraduateStudent{ } //学生

// MiddleSchoolStudent 孙类要继承父类,则必须修饰 sealed 类
final class MiddleSchoolStudent extends Student { }  //中学生

// GraduateStudent 孙类要继承父类,则必须修饰 sealed 类
final class GraduateStudent extends Student { }  //研究生

// 如果子类希望任意孙类继承它,则需要声明为 non-sealed 类
non-sealed class Worker extends Person { }  //工人

// 这样 RailWayWorker  就可以任意继承 声明为 non-sealed 类的 Worker  类了
class RailWayWorker extends Worker{} //铁路工人

 

如果您喜欢本站,点击这儿不花一分钱捐赠本站

这些信息可能会帮助到你: 下载帮助 | 报毒说明 | 进站必看

修改版本安卓软件,加群提示为修改者自留,非本站信息,注意鉴别

THE END
分享
二维码
打赏
海报
Java – Java 新特性说明与使用
switch 表达式(Java 12) 传统switch的弊端 传统的switch声明语句(switch statement)在使用中有一些问题: 匹配是自上而下的,如果忘记写break, 后面的case语句不论匹配与否都会执行; 所有的……
<<上一篇
下一篇>>