JAVA8与JAVA11到底该怎么选?
很多初学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版本是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.http包下,⽀持发送同步、异步的HTTP请求,这样⼀来,以前咱们常⽤的HTTP请求客户端诸如:OKHttp、HttpClient这种现
在都可以退下了!
//发送同步请求:
var request = wBuilder()
.uri( ate("desheep") )
.GET()
.build();
// 同步请求⽅式,拿到结果前会阻塞当前线程
var httpResponse = wHttpClient()
.send( request, HttpResponse.BodyHandlers.ofString());
System.out.println( httpResponse.body() ); // 打印获取到的⽹页内容
//发送异步请求:
CompletableFuture<String> future = wHttpClient().
sendAsync( request, HttpResponse.BodyHandlers.ofString() )
.thenApply( HttpResponse::body );
System.out.println("我先继续⼲点别的事情...");
System.out.println( () ); // 打印获取到的⽹页内容
//当然你也可以⾃定义请求头,⽐如携带JWT Token权限信息去请求等:
var requestWithAuth = wBuilder()
.uri( ate("/sth") )
.header("Authorization", "JzdWIiOiIxNTIwNTE2MTE5NiIsImNyZWF0ZWQiOjE1ODMzMTA2ODk0MzYsImV4cCI6MTU4MzM5NzA4OSwidXNlcmlkIjoxMDAwNH0.OE9R5PxxsvtVJZn8ne-ksTb2aXXi7ipzuW9        .GET()
.build();
var response = wHttpClient()
.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( peat(2) );      // 打印 codesheep  codesheep
集合增强
主要是增加了诸如of()和copyOf()等⽅法⽤于更加⽅便的创建和复制集合类型
var upList = List.of( "刘能", "赵四", "谢⼴坤" );
var upListCopy = pyOf( upList );
System.out.println(upList);    // 打印 [刘能, 赵四, 谢⼴坤]
System.out.println(upListCopy); // 打印 [刘能, 赵四, 谢⼴坤]
var upSet = Set.of("刘能","赵四");
var upSetCopy = pyOf( upSet );
System.out.println(upSet);      // 打印 [赵四, 刘能]
System.out.println(upSetCopy);  // 打印 [赵四, 刘能]
var upMap = Map.of("刘能","58岁","赵四","59岁");
var upMapCopy = pyOf( 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( List() );
System.out.println(upListSub1);  // 打印 [赵四, 谢⼴坤]
/
/ 从集合中依次获取满⾜条件的元素,知道不满⾜条件为⽌
var upListSub2 = upList.stream()
.takeWhile( item -> item.equals("刘能") )
.collect( List() );
System.out.println( upListSub2 ); // 打印 [刘能]
⽂件读写增强
1、Files类增强
我们以前⼼⼼念的直接能把⽂件内容读取到String以及String回写到⽂件的功能终于⽀持了,可以通过Files类的静态⽅法writeString()和readString()完成:Path path = ("/Users/");
String content = adString(path, StandardCharsets.UTF_8);
System.out.println(content);
Files.writeString( path, "王⽼七", StandardCharsets.UTF_8 );
2、InputStream增强
InputStream则增加了⼀个transferTo()⽅法,直接将数据丢到OutputStream去:
InputStream inputStream = new FileInputStream( "/Users/" );
OutputStream outputStream = new FileOutputStream( "/Users/" );
⽀持源⽂件直接运⾏(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) {
jdk怎么使用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.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了,没必要刻意求新,稳才是最重要的

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。