IE盒子

搜索
查看: 113|回复: 7

java9到18版本中的新特性

[复制链接]

3

主题

7

帖子

13

积分

新手上路

Rank: 1

积分
13
发表于 2022-11-29 14:23:13 | 显示全部楼层 |阅读模式
java 在9~18之间的版本中新增了很多特性,我们针对较为突出的便于理解的一些语法变化进行说明。 除了下面罗列出的新特性之外还有一些其他的内容,这些内容对于初学者来说有的不便于理解,有的是不重要,所以没有罗列出来。
jdk9的变化

jshell

有的时候我们只是想写一段简单的代码,例如HelloWorld,按照以前的方式,还需要自己创建java文件,创建class,编写main方法,但实际上里面的代码其实就是一个打印语句,此时还是比较麻烦的。在jdk9中新增了jshell工具,可以帮助我们快速的运行一些简单的代码。
从命令提示符里面输入jshell,进入到jshell之后输入:
System.out.println("HelloWorld");此时可以直接看到命令提示符中打印了HelloWorld。
我们还可以输入下面代码然后按回车:
int a = 10;再输入下面代码按回车:
int b = 20;再输入下面代码按回车:
System.out.println(a + b);此时可以看到控制台打印出了a+b的值了。
如果要退出jshell的话,输入/exit即可
接口私有方法

在jdk9中新增了接口私有方法,我们可以在接口中声明private修饰的方法了,这样的话,接口越来越像抽象类了
public interface MyInterface {
    //定义私有方法
    private void m1() {
        System.out.println("123");
    }

    //default中调用
    default void m2() {
        m1();
    }
}改进的try with resource

之前我们使用try with resource用来自动关闭资源文件,特别是在IO流部分使用的比较多。使用方式是将需要自动关闭的资源对象的创建放到try后面的小括号中,在jdk9中我们可以将这些资源对象的创建放到外面,然后将需要关闭的对象放到try后面的小括号中即可,示例:
/*
改进了try-with-resources语句,可以在try外进行初始化,在括号内引用,即可实现资源自动关闭
*/
public class TryWithResource {
    public static void main(String[] args) throws FileNotFoundException {
        //jdk8以前
        try (FileInputStream fileInputStream = new FileInputStream("");
             FileOutputStream fileOutputStream = new FileOutputStream("")) {

        } catch (IOException e) {
            e.printStackTrace();
        }

        //jdk9
        FileInputStream fis = new FileInputStream("abc.txt");
        FileOutputStream fos = new FileOutputStream("def.txt");
        //多资源用分号隔开
        try (fis; fos) {
                } catch (IOException e) {
            e.printStackTrace();
        }
    }
}不能使用下划线命名变量

下面语句在jdk9之前可以正常编译通过,但是在jdk9(含)之后编译报错,在后面的版本中会将下划线作为关键字来使用
String _ = "monkey1024";String字符串的变化

写程序的时候会经常用到String字符串,在以前的版本中String内部使用了char数组存储,对于使用英语的人来说,字符用一个字节就能存储,因此在jdk9中将String内部的char数组改成了byte数组,这样就节省了一半的内存占用。String中增加了一个判断,倘若字符超过1个字节的话,会把byte数组的长度改为两倍char数组的长度,用两个字节存放一个char。在获取String长度的时候,其源码中有向右移动1位的操作(即除以2),这样就解决了上面扩容2倍之后长度不正确的问题。
模块化

JDK9将JDK分成一组模块,可以在编译时或运行时进行组合。这样可以减少内存开销,只需必要的模块,并非全部模块,可以简化各种类库和大型应用的开发和维护。比如在使用java开发的时候通常用不到图形化界面的库,分模块之后,在java的base模块中没有这些图形化相关的内容,达到了一个瘦身的效果。
jdk10的变化

局部变量类型推断

在jdk10以前声明变量的时候,我们会像下面这样:
String oldName = "jack";
    int oldAge = 10;
    long oldMoney = 88888888L;
    Object oldObj = new Object();上面我们声明的时候使用了4种不同类型的变量,在jdk10中前面的类型都可以使用var来代替,JVM会自动推断该变量是什么类型的,例如可以这样写:
var newName = "jack";
    var newAge = 10;
    var newMoney = 88888888L;
    var newObj = new Object();注意:
当然这个var的使用是有限制的,仅适用于局部变量,增强for循环的索引,以及普通for循环的本地变量;它不能使用于方法形参,构造方法形参,方法返回类型等。
jdk11的变化

直接运行

在以前的版本中,我们在命令提示下,需要先编译,生成class文件之后再运行,例如:
javac HelloWorld.java
java HelloWorld在java 11中,我们可以这样直接运行
java HelloWorld.javaString新增方法

strip方法,可以去除首尾空格,与之前的trim的区别是还可以去除unicode编码的空白字符,例如:
char c = '\u2000';//Unicdoe空白字符
String str = c + "abc" + c;
System.out.println(str.strip());
System.out.println(str.trim());

System.out.println(str.stripLeading());//去除前面的空格
System.out.println(str.stripTrailing());//去除后面的空格isBlank方法,判断字符串长度是否为0,或者是否是空格,制表符等其他空白字符
String str = " ";
System.out.println(str.isBlank());repeat方法,字符串重复的次数
String str = "monkey";
System.out.println(str.repeat(4));lambda表达式中的变量类型推断

jdk11中允许在lambda表达式的参数中使用var修饰
函数式接口:
@FunctionalInterface
    public interface MyInterface {
        void m1(String a, int b);
    }测试类:
//支持lambda表达式参数中使用var
   MyInterface mi = (var a,var b)->{
       System.out.println(a);
       System.out.println(b);
   };

   mi.m1("monkey",1024);jdk12的变化

升级的switch语句

在jdk12之前的switch语句中,如果没有写break,则会出现case穿透现象,下面是对case穿透的一个应用,根据输入的月份打印相应的季节。
int month = 3;
switch (month) {
    case 3:
    case 4:
    case 5:
        System.out.println("spring");
    break;
    case 6:
    case 7:
    case 8:
        System.out.println("summer");
    break;
    case 9:
    case 10:
    case 11:
        System.out.println("autumn");
    break;
    case 12:
    case 1:
    case 2:
        System.out.println("winter");
    break;
    default:
        System.out.println("wrong");
    break;
}在jdk12之后我们可以省略全部的break和部分case,这样使用
int month = 3;
    switch (month) {
        case 3,4,5 -> System.out.println("spring");
        case 6,7,8 -> System.out.println("summer");
        case 9,10,11 -> System.out.println("autumn");
        case 12, 1,2 -> System.out.println("winter");
        default -> System.out.println("wrong");
    }这个是预览功能,如果需要编译和运行的话需要使用下面命令,预览功能在2个版本之后会成为正式版,即如果你使用的是jdk14以上的版本,正常的编译和运行即可。否则需要使用预览功能来编译和运行
编译:
    javac --enable-preview -source 12 Test.java

运行:
    java --enable-preview Testjdk13的变化

升级的switch语句

jdk13中对switch语句又进行了升级,可以switch的获取返回值
示例:
int month = 3;
   String result = switch (month) {
        case 3,4,5 -> "spring";
        case 6,7,8 -> "summer";
        case 9,10,11 -> "autumn";
        case 12, 1,2 -> "winter";
        default -> "wrong";
    };

    System.out.println(result);对于jdk15之后的版本可以直接编译和运行,否则需要使用下面命令执行该预览功能
编译:
    javac --enable-preview -source 13 Test.java

运行:
    java --enable-preview Test文本块的变化

在jdk13之前的版本中如果输入的字符串中有换行的话,需要添加换行符
String s = "Hello\nWorld\nLearn\nJava";
    System.out.println(s);jdk13之后可以直接这样写:
String s = """
            Hello
            World
            Learn
            Java
           """;
  System.out.println(s);这样的字符串更加一目了然。
jdk14的变化

instanceof模式匹配

该特性可以减少强制类型转换的操作,简化了代码,代码示例:
public class TestInstanceof{
    public static void main(String[] args){

        //jdk14之前的写法
        Object obj = new Integer(1);
        if(obj instanceof Integer){
            Integer i = (Integer)obj;
            int result = i + 10;
            System.out.println(i);
        }

        //jdk14新特性  不用再强制转换了
        //这里相当于是将obj强制为Integer之后赋值给i了
        if(obj instanceof Integer i){
            int result = i + 10;
            System.out.println(i);
        }else{
            //作用域问题,这里是无法访问i的
        }
    }
}这个是预览版的功能所以需要使用下面命令编译和运行
编译:
    javac --enable-preview -source 14 TestInstanceof.java

运行:
    java --enable-preview TestInstanceof友好的空指针(NullPointerException)提示

jdk14中添加了对于空指针异常友好的提示,便于开发者快速定位空指针的对象。示例代码:
class Machine{
    public void start(){
        System.out.println("启动");
    }
}

class Engine{
    public Machine machine;
}

class Car{
    public Engine engine;

}

public class TestNull{
    public static void main(String[] args){
        //这里会报出空指针,但是哪个对象是null呢?
        new Car().engine.machine.start();
    }
}我们在运行上面代码的时候,错误信息就可以明确的指出那个对象为null了。此外,还可以使用下面参数来查看:
java -XX:+ShowCodeDetailsInExceptionMessages TestNull这样编译器会明确的告诉开发者哪个对象是null。
record类型

之前在编写javabean类的时候,需要编写成员变量,get方法,构造方法,toString方法,hashcode方法,equals方法。这些方法通常会通过开发工具来生成,在jdk14中新增了record类型,通过该类型可以省去这些代码的编写。
jdk14编写User
public record User(String name,Integer age){}通过反编译命令可以看到该字节码文件中的内容,User类是继承了Record类型:
javap -p -private user编写测试类:
public class TestUser{
    public static void main(String[] args){
        User u = new User("jack",15);
        System.out.println(u);
        System.out.println(u.name());
    }
}这个是预览版的功能所以需要使用下面命令编译和运行
编译:
    javac --enable-preview -source 14 TestUser.java

运行:
    java --enable-preview TestUser记录类型有自动生成的成员,包括:

  • 状态描述中的每个组件都有对应的private final字段。
  • 状态描述中的每个组件都有对应的public访问方法。方法的名称与组件名称相同。
  • 一个包含全部组件的公开构造器,用来初始化对应组件。
  • 实现了equals()和hashCode()方法。equals()要求全部组件都必须相等。
  • 实现了toString(),输出全部组件的信息。
jdk15的变化
Sealed Classes

密封类和接口,作用是限制一个类可以由哪些子类继承或者实现。

  • 如果指定模块的话,sealed class和其子类必须在同一个模块下。如果没有指定模块,则需要在同一个包下。
  • sealed class指定的子类必须直接继承该sealed class。
  • sealed class的子类要用final修饰。
  • sealed class的子类如果不想用final修饰的话,可以将子类声明为sealed class。
Animal类,在指定允许继承的子类时可以使用全限定名
public sealed class Animal
    permits Cat, Dog{//多个子类之间用,隔开

        public void eat(){}
}Cat类
public final class Cat extends Animal{
    public void eat(){
        System.out.println("123");
    }
}Dog类
public sealed class Dog extends Animal
    permits Husky {}Husky类
public final class Husky extends Dog{
}Test类
public class Test{
    public static void main(String[] args){
        Cat c = new Cat();
        c.eat();
        Dog d = new Dog();
    }
}CharSequence新增的方法

该接口中新增了default方法isEmpty(),作用是判断CharSequence是否为空。
TreeMap新增方法


  • putIfAbsent
  • computeIfAbsent
  • computeIfPresent
  • compute
  • merge
文本块

文本块由预览版变为正式版
无需配置环境变量

win系统中安装完成之后会自动将java.exe, javaw.exe, javac.exe, jshell.exe这几个命令添加到环境变量中。这部分可以打开环境变量看下。不过还是建议配置环境变量,因为这几个命令不够用
jdk16的变化

这里只介绍一些跟开发关联度较大的特性,除此之外JDK16还更新了许多其他新特性,感兴趣的同学可以去Oracle官网查看
包装类构造方法的警告

​       使用包装类的构造方法在编译的时候会出现警告,不建议再使用包装类的构造方法。下面代码在javac编译之后会出现警告。
Integer i = new Integer(8);不建议使用包装类作为锁对象,倘若使用包装类作为锁对象,在编译时会出现警告。
Integer i = 8;
    synchronized(i){

    }新增日时段

在DateTimeFormatter.ofPattern传入B可以获取现在时间对应的日时段,上午,下午等
System.out.println(DateTimeFormatter.ofPattern("B").format(LocalDateTime.now()));InvocationHandler新增方法

在该接口中添加了下面方法
public static Object invokeDefault(Object proxy, Method method, Object... args)该方法可以调用父接口中defalut方法,比如有下面接口
interface Girl{
    default void eat(){
        System.out.println("cucumber");
    }

}实现类
public class Lucy implements Girl{
    public void eat(){
        System.out.println("banana");
    }
}测试类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Test{
    public static void main(String[] args) {
        Girl girl = new Lucy();


        //不使用invokeDefault会调用重写的eat方法
        Girl proxy1 = (Girl)Proxy.newProxyInstance(girl.getClass().getClassLoader(),girl.getClass().getInterfaces(),
            (obj,method,params)->{
            Object invoke = method.invoke(girl);
            return invoke;
        });
        proxy1.eat();

        //使用invokeDefault会调用父接口中的default方法
        Girl proxy2 = (Girl)Proxy.newProxyInstance(Girl.class.getClassLoader(),new Class<?>[]{Girl.class},
            (obj,method,params)->{
            if (method.isDefault()) {
                return InvocationHandler.invokeDefault(obj, method, params);
            }
            return null;
        });
        proxy2.eat();

    }

}其他

​       在之前jdk版本中作为预览功能的Record类,模式匹配的instanceof,打包工具jpackage,已成为正式版。jdk16对GC,jvm运行时内存等内容有一些变化,例如:ZGC并发栈处理弹性meta space
jdk17的变化

java17是一个LTS(long term support)长期支持的版本,根据计划来看java17会支持到2029年(java8会支持到2030年,OMG),同时Oracle提议下一个LTS版本是java21,在2023年9月发布,这样讲LST版本的发布周期由之前的3年变为了2年。这里只介绍一些跟开发关联度较大的特性,除此之外JDK17还更新了一些其他新特性,感兴趣的同学可以从这里查看:https://www.oracle.com/news/announcement/oracle-releases-java-17-2021-09-14/


switch语法的变化(预览)

在之前版本中新增的instanceof模式匹配的特性在switch中也支持了,即我们可以在switch中减少强转的操作。比如下面的代码:
Rabbit和Bird均实现了Animal接口
interface Animal{}

class Rabbit implements Animal{
    //特有的方法
    public void run(){
        System.out.println("run");
    }
}

class Bird implements Animal{
    //特有的方法
    public void fly(){
        System.out.println("fly");
    }
}新特性可以减少Animal强转操作代码的编写:
public class Switch01{
    public static void main(String[] args) {
        Animal a = new Rabbit();
        animalEat(a);
    }

    public static void animalEat(Animal a){
        switch(a){
            //如果a是Rabbit类型,则在强转之后赋值给r,然后再调用其特有的run方法
            case Rabbit r -> r.run();
            //如果a是Bird类型,则在强转之后赋值给b,然后调用其特有的fly方法
            case Bird b -> b.fly();
            //支持null的判断
            case null -> System.out.println("null");
            default -> System.out.println("no animal");
        }
    }

}该功能在java17中是预览的,编译和运行需要加上额外的参数:
javac --enable-preview -source 17 Switch01.java
java  --enable-preview Switch01Sealed Classes

在jdk15中已经添加了Sealed Classes,只不过当时是作为预览版,经历了2个版本之后,在jdk17中Sealed Classes已经成为正式版了。Sealed Classes的作用是可以限制一个类或者接口可以由哪些子类继承或者实现。
伪随机数的变化

增加了伪随机数相关的类和接口来让开发者使用stream流进行操作

  • RandomGenerator
  • RandomGeneratorFactory
之前的java.util.Random和java.util.concurrent.ThreadLocalRandom都是RandomGenerator接口的实现类。
去除了AOT和JIT

AOT(Ahead-of-Time)是java9中新增的功能,可以先将应用中中的字节码编译成机器码。
Graal编译器作为使用java开发的JIT(just-in-time )即时编译器在java10中加入(注意这里的JIT不是之前java中的JIT,在JEP 317中有说明https://openjdk.java.net/jeps/317)。
以上两项功能由于使用量较少,且需要花费很多精力来维护,因此在java17中被移除了。当然你可以通过Graal VM来继续使用这些功能。
jdk18的变化

除了下面变化之外jdk18还有一些其他小的变化,感兴趣的童鞋可以去oracle官网查看。
从jdk18开始,默认使用UTF-8字符编码。我们可以通过如下参数修改其他字符编码:
-Dfile.encoding=UTF-8 简单的web服务器

可以通过jwebserver命令启动jdk18中提供的静态web服务器,可以利用该工具查看一些原型,做简单的测试。在命令提示符中输入jwebserver命令后会启动,然后在浏览器中输入:http://127.0.0.1:8000/ 即可看到当前命令提示符路径下的文件了。
将被移除的方法

在jdk18中标记了Object中的finalize方法,Thread中的stop方法将在未来被移除。
@snippet注解

以前在文档注释中编写代码时需要添加code标签,使用较为不便,通过@snippet注解可以更方便的将文档注释中的代码展示在api文档中。
回复

使用道具 举报

1

主题

4

帖子

5

积分

新手上路

Rank: 1

积分
5
发表于 2022-11-29 14:24:10 | 显示全部楼层
总结的很全,补充几点,JDK9的Stream加强,集合加强(list.of(),map.of(),copyof等只读集合的快速创建), JDK11的Optional加强,InputStream.transferTo,HTTP Client API正式版,很多时候可以抛弃apache http client了,JDK16的Stream().toList()
回复

使用道具 举报

2

主题

7

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2022-11-29 14:24:31 | 显示全部楼层
感谢补充[赞同]
回复

使用道具 举报

3

主题

9

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2022-11-29 14:25:05 | 显示全部楼层
您好  现在学的话下载哪个版本比较好
回复

使用道具 举报

1

主题

3

帖子

3

积分

新手上路

Rank: 1

积分
3
发表于 2022-11-29 14:25:37 | 显示全部楼层
jdk8
回复

使用道具 举报

4

主题

7

帖子

15

积分

新手上路

Rank: 1

积分
15
发表于 2022-11-29 14:26:10 | 显示全部楼层
有链接分享吗,去官网下我只能找到jdk17
回复

使用道具 举报

2

主题

8

帖子

13

积分

新手上路

Rank: 1

积分
13
发表于 2022-11-29 14:26:44 | 显示全部楼层
感谢总结
回复

使用道具 举报

1

主题

3

帖子

5

积分

新手上路

Rank: 1

积分
5
发表于 2022-11-29 14:27:24 | 显示全部楼层
跟kotlin越来越像[思考]
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表