zoukankan      html  css  js  c++  java
  • JAVA 8与JAVA 11到底该怎么选?

    很多初学Java的小伙伴经常咨询:

    • 到底该安装哪个版本的JDK比较好?
    • Java 8到底还够不够用?
    • Java 11究竟有什么改进?
    • 是不是Java版本越新越好?
    • ……

    是这样,官网现在其实都已经出到Java 13版本了,并且提供下载使用。

    但目前市场上主流的稳定版当然还得属Java 8和Java 11,而目前大部分公司的生产环境还是Java 8居多。

    所以如果从自学角度出发,我觉得这两个版本都OK,其他中间的一些比如Java 9、Java 10这些非稳定版就不用考虑了。

    Java11 vs Java8

     

    Java 11相对于Java 8确实有一部分进化,除了有很多内部的升级(比如开销和时延更低的GC、TLS1.3加持等等)之外,对于初学使用者来说也有一些语言使用层面的进化。

    正好最近我在自己的个人小项目上尝试升级使用了一下Java 11(公司项目咱也不敢动、也不敢问,只好动自己的个人项目),因此本文从实际代码编写角度来大致体验一下我个人使用Java 11之后相对Java 8所感觉到的一些比较深刻的进化,官方文档里说得也非常清楚了:

    我这次实验装的Java 11版本是11.0.6:

    下文将要实验验证的一些新特性其实也并非Java 11才引入,很多其实在Java 9和Java 10时就已经引入,只不过到了Java 11这个稳定版才沉淀下来。

    变量类型推断

    新版Java引入了一个全新的类型关键字var,用var来定义的变量不用写具体类型,编译器能根据=右边的实际赋值来自动推断出变量的类型:

    1、普通局部变量

    var name = "codesheep"; // 自动推断name为String类型
    System.out.println(name);

    怎么样?是不是有一种在使用类似JavaScript这种弱类型语言的错觉?

    2、for循环中使用

    var upList1 = List.of( "刘能", "赵四", "谢广坤" );
    var upList2 = List.of( "永强", "玉田", "刘英" );
    var upList3 = List.of( "谢飞机", "兰妮", "兰娜" );
    var upListAll = List.of( upList1, upList2, upList3 );
    for( var i : upListAll ) { // 用var接受局部变量的确非常简洁!
        for( var j : i  ) {
            System.out.println(j);
        }
    }

    这地方就能看出用var定义局部变量的优势了,假如这个例子中集合里的元素类型更为复杂,是类似List<List<String>>这种嵌套类型的话,var定义就非常简洁明了!

    3、当然,有些情况是不能使用的

    //var类型变量一旦赋值后,重新赋不同类型的值是不行的,比如:
    
    var name = "codesheep";
    name = 666;  // 此时编译会提示不兼容的类型
    
    //定义var类型变量没有初始化是不行的,比如:
    
    var foo;  // 此时编译会提示无法推断类型
    foo = "Foo";
    
    //另外,像类的成员变量类型、方法入参类型、返回值类型等是不能使用var的,比如:
    
    public class Test {
        
        private var name; // 会提示不允许使用var           
    
        public void setName( var name ) { // 会提示不允许使用var
            this.name = name;
        }
    
        public var getName() { // 会提示不允许使用var
            return name;
        }
         
    }

    官方HTTP Client加持

    是的!

    现在JDK官方就自带HTTP Client了,位于java.net.http包下,支持发送同步、异步的HTTP请求,这样一来,以前咱们常用的HTTP请求客户端诸如:OKHttp、HttpClient这种现在都可以退下了!

    //发送同步请求:
    
    var request = HttpRequest.newBuilder()
            .uri( URI.create("https://www.codesheep.cn") )
            .GET()
            .build();
    // 同步请求方式,拿到结果前会阻塞当前线程
    var httpResponse = HttpClient.newHttpClient()
            .send( request, HttpResponse.BodyHandlers.ofString());
    System.out.println( httpResponse.body() ); // 打印获取到的网页内容
    
    //发送异步请求:
    
    CompletableFuture<String> future = HttpClient.newHttpClient().
            sendAsync( request, HttpResponse.BodyHandlers.ofString() )
            .thenApply( HttpResponse::body );
    System.out.println("我先继续干点别的事情...");
    System.out.println( future.get() ); // 打印获取到的网页内容
    
    //当然你也可以自定义请求头,比如携带JWT Token权限信息去请求等:
    
    var requestWithAuth = HttpRequest.newBuilder()
            .uri( URI.create("http://www.xxxxxx.com/sth") )
            .header("Authorization", "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxNTIwNTE2MTE5NiIsImNyZWF0ZWQiOjE1ODMzMTA2ODk0MzYsImV4cCI6MTU4MzM5NzA4OSwidXNlcmlkIjoxMDAwNH0.OE9R5PxxsvtVJZn8ne-ksTb2aXXi7ipzuW9kbCiQ0uNoW0fJJr_wckLFmgDzxmBs3IdzIhWDAtaSIvmTshK_RQ")
            .GET()
            .build();
    var response = HttpClient.newHttpClient()
            .send( requestWithAuth, HttpResponse.BodyHandlers.ofString() );
    System.out.println( response.body() ); // 打印获取到的接口返回内容

    String处理增强

    //新版字符串String类型增加了诸如:isBlank()、strip()、repeat()等方便的字符串处理方法
    
    String myName = " codesheep ";
    System.out.println( "  ".isBlank() ); // 打印:true
    System.out.println( "  ".isEmpty() ); // 打印:false
    
    System.out.println( myName.strip() );         // 打印codesheep,前后空格均移除
    System.out.println( myName.stripLeading() );  // 打印codesheep ,仅头部空格移除
    System.out.println( myName.stripTrailing() ); // 打印 codesheep,仅尾部空格移除
    System.out.println( myName.repeat(2) );       // 打印 codesheep  codesheep

    集合增强

    主要是增加了诸如of()和copyOf()等方法用于更加方便的创建和复制集合类型

    var upList = List.of( "刘能", "赵四", "谢广坤" );
    var upListCopy = List.copyOf( upList );
    System.out.println(upList);     // 打印 [刘能, 赵四, 谢广坤]
    System.out.println(upListCopy); // 打印 [刘能, 赵四, 谢广坤]
    
    var upSet = Set.of("刘能","赵四");
    var upSetCopy = Set.copyOf( upSet );
    System.out.println(upSet);      // 打印 [赵四, 刘能]
    System.out.println(upSetCopy);  // 打印 [赵四, 刘能]
    
    var upMap = Map.of("刘能","58岁","赵四","59岁");
    var upMapCopy = Map.copyOf( upMap );
    System.out.println(upMap);      // 打印 {刘能=58岁, 赵四=59岁}
    System.out.println(upMapCopy);  // 打印 {刘能=58岁, 赵四=59岁}

    函数式编程增强

    我印象最深的是对Stream流增加了诸如takeWhile()和dropWhile()的截止结算方法:

    var upList = List.of( "刘能", "赵四", "谢广坤" );
    
    // 从集合中依次删除满足条件的元素,直到不满足条件为止
    var upListSub1 = upList.stream()
            .dropWhile( item -> item.equals("刘能") )
            .collect( Collectors.toList() );
    System.out.println(upListSub1);  // 打印 [赵四, 谢广坤]
    
    // 从集合中依次获取满足条件的元素,知道不满足条件为止
    var upListSub2 = upList.stream()
            .takeWhile( item -> item.equals("刘能") )
            .collect( Collectors.toList() );
    System.out.println( upListSub2 ); // 打印 [刘能]

    文件读写增强

    1、Files类增强

    我们以前心心念的直接能把文件内容读取到String以及String回写到文件的功能终于支持了,可以通过Files类的静态方法writeString()和readString()完成:

    Path path = Paths.get("/Users/CodeSheep/test.txt");
    String content = Files.readString(path, StandardCharsets.UTF_8);
    System.out.println(content);
    Files.writeString( path, "王老七", StandardCharsets.UTF_8 );

    2、InputStream增强

    InputStream则增加了一个transferTo()方法,直接将数据丢到OutputStream去:

    InputStream inputStream = new FileInputStream( "/Users/CodeSheep/test.txt" );
    OutputStream outputStream = new FileOutputStream( "/Users/CodeSheep/test2.txt" );
    inputStream.transferTo( outputStream );

    支持源文件直接运行(666!)

    比如我写一个最简单的Hello World程序:

    public class Hello {
        
        public static void main( String[] args ) {
            System.out.println("hello world");
        }
         
    }
    

    并保存为hello.java文件,这时候可以直接用java指令去运行这个Java源文件,直接省去以前javac编译源文件的过程:

    java hello.java

    怎么样?是不是和python源文件的运行有点像?这个信息量就有点大了,大家可以自行脑补一下
    案例1
    public class Test {
    
        public static void main(String[] args) {
            System.out.println("Test ...");
        }
     
    }

    执行上面的代码

    //jdk11之前 :
    
    javac Test.java
    java Test
    
    //jdk11:
    
    java Test.java

    结论:jdk11中,通过 java xxx.java 命令,就可直接运行源码文件程序,而且不会产生.class 文件。

    案例二

    问题:如果一个java文件中存在多个类 ,通过 java xxx.java 运行源码文件,会执行哪一个main方法 ?

    创建一个 Test1.java 文件,代码如下:

    class Test2 { 
        public static void main(String[] args) {
            System.out.println("Test2");
        }
    }
    
    public class Test {
    
        public static void main(String[] args) {
            System.out.println("Test");
        }
     
    }

    通过 java Test.java 运行后输出 "Test2"。

    下面我们颠倒 Test Test2 两个类的位置:

    public class Test {
    
        public static void main(String[] args) {
            System.out.println("Test");
        }
     
    }
    
    class Test2 { 
        public static void main(String[] args) {
            System.out.println("Test2");
        }
    }

    通过 java Test.java 运行后输出 "Test"。

    结论:一个java文件中包含多个类时,java xxx.java 执行排在最上面的一个类的main方法。

    案例三

    问题:如果一个java文件中类的方法中调用了另一个java文件中类的方法,通过 java xxx.java 运行源码文件,能运行通过吗 ?

    创建两个java文件 Student.java 、Teacher.java。

    Student.java:

    public class Student { 
    
        public static void main(String[] args) {
            Teacher teacher = new Teacher();
            teacher.toString();
        }
    }

    Teacher.java:

    public class Teacher {
    
        private String name;
        private String subject;
    
        public void setName(String name){
            this.name = name;
        }
        public String getName(){
            return name;
        }
        public void setSubject(String subject){
            this.subject = subject;
        }
        public String getSubject(){
            return subject;
        }
     
    }

    执行 java Student.java 报错 :

    Student.java:4: 错误: 找不到符号
            Teacher teacher = new Teacher();
            ^
      符号:   类 Teacher
      位置: 类 Student
    Student.java:4: 错误: 找不到符号
            Teacher teacher = new Teacher();
                                  ^
      符号:   类 Teacher
      位置: 类 Student
    2 个错误

    把 Student 和 Teacher 连个类 放在一个java文件中 ,重新运行,运行通过。

    结论:java xxx.java 启动单个Java源代码文件的程序时,相关个类必须定义在同一个java文件中。

    结论

    通过上面的三个案例,我得出以下结论:

    • jdk11中,通过 java xxx.java 命令,就可直接运行源码文件程序,而且不会产生.class 文件。
    • 一个java文件中包含多个类时,java xxx.java 执行排在最上面的一个类的main方法。
    • java xxx.java 启动单个Java源代码文件的程序时,相关个类必须定义在同一个java文件中。

    小结

    Java 11确有很多改进,但还是那句话,对于初学者来说Java 8了,没必要刻意求新,稳才是最重要的

     

  • 相关阅读:
    第三节课: Python 基本数据类型讲解(1/3)
    第二节课 虚拟机安装
    Java生成带LOGO的二维码
    Oracle中存储图片的类型为BLOB类型,Java中如何将其读取并转为字符串?
    简单分析下mybatis中mapper文件中小知识
    Oracle中,如何将String插入到BLOB类型字段
    Oracle + Mybatis批量插入数据,xml.mapper种的写法
    java中拼接两个对象集合
    关于spring boot在IDE工具中可以启动成功,但是打成jar包以及运行jar包失败的问题
    springboot整合mybatis之注解方式
  • 原文地址:https://www.cnblogs.com/lukelook/p/13588801.html
Copyright © 2011-2022 走看看