Java9到Java13各版本新特性代码所有详解(全网独家原创)

Java如今已经发展到了Java13了(正式版本),相信不少朋友还对各个版本还不是很熟悉,这里面专门把Java9到Java13各个版本的一些新特性作了一些详细讲解。我在网上也找了不少,但基本都是官方文档的CV,没有任何代码演示,并且官方的示例代码也不是很好找获得,官方API目前仍是Java10,官方文档真是坑啊。因此我在这里专门写了一篇文章,主要针对平时开发与有关的功能Java9到Java13各个版本都作了代码详细讲解。html

【PS】:这个季节太冷了,南方湿冷,个人手都生冻疮了,看在年前最后几天了,没办法,我最后选择去网吧花了几天时间,网费都花了好几百块,为了打造这篇干货不惜下血本啊。终于奋战几天写出来了这篇文章。每个语法细节都通过实例演示过的,我特地把每一个版本的Open JDK都下载了一遍,体验里面的细节差距和新特性。java

但愿你们点赞,评论和收藏三连,也不负个人一片苦心,谢谢你们了。shell

想得到更多干货,欢迎你们多多关注个人博客。本文为做者AWeiLoveAndroid原创,未经受权,严禁转载express


1、Java 9

【注:】Java9的更新是最多的,这个须要特别注意学一下。编程

Java 9 集合工厂方法

示例:bash

public static void main(String[] args) {
  Set<String> set = Set.of("set1", "set2", "set3");
  // set: [set1, set3, set2]
  System.out.println("set: " + set);
       
  Map<String, String> maps1 = Map.of(
     "map1","Apple", "map2","Orange","map3","Banana", "map4","cherry");
  // maps1: {map3=Banana, map2=Orange, map1=Apple, map4=cherry}        
  System.out.println("maps1: " + maps1);
    
  Map<String, String> maps2 = Map.ofEntries (
           new AbstractMap.SimpleEntry<>("map1","Apple"),
           new AbstractMap.SimpleEntry<>("map2","Orange"),
           new AbstractMap.SimpleEntry<>("map3","Banana"),
           new AbstractMap.SimpleEntry<>("map4","cherry"),
           new AbstractMap.SimpleEntry<>("map5","Apple"),
           new AbstractMap.SimpleEntry<>("map6","Orange"),
           new AbstractMap.SimpleEntry<>("map7","Banana"),
           new AbstractMap.SimpleEntry<>("map8","cherry"),
           new AbstractMap.SimpleEntry<>("map9","Apple"),
           new AbstractMap.SimpleEntry<>("map10","Orange"),
           new AbstractMap.SimpleEntry<>("map11","Banana"),
           new AbstractMap.SimpleEntry<>("map12","cherry")
  );
  // maps2: {map3=Banana, map2=Orange, map1=Apple, map12=cherry, map11=Banana, map10=Orange, 
  // map9=Apple, map8=cherry, map7=Banana, map6=Orange, map5=Apple, map4=cherry}      
  System.out.println("maps2: " + maps2);
}
复制代码

Java9之前的作法:ide

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

Set<String> set = new HashSet<>();
set.add("A");
set.add("B");
set.add("C");

Map<String, String> map = new HashMap<>();
map.put("A","Apple");
map.put("B","Boy");
map.put("C","Cat");
复制代码

Java9能够直接输出集合的内容,在此以前必须遍历集合才能所有获取里面的元素。这是一个很大的改进。函数

Java 9 List,Set 和 Map 接口中,新增静态工厂方法能够建立这些集合的不可变实例。工具

Java 9 中,可使用如下方法建立 List,Set 和 Map 的集合对象。重载方法有不少,示例以下:oop

static <E> List<E> of()
static <E> List<E> of(E e1)
static <E> List<E> of(E e1, E e2)
static <E> List<E> of(E e1, E e2, E e3)
static <E> List<E> of(E e1, E e2, E e3, E e4)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)
static <E> List<E> of(E... elements)

static <E> Set<E> of()
static <E> Set<E> of(E e1)
static <E> Set<E> of(E e1, E e2)
static <E> Set<E> of(E e1, E e2, E e3)
static <E> Set<E> of(E e1, E e2, E e3, E e4)
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5)
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6)
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7)
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8)
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9)
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)
static <E> Set<E> of(E... elements)


static <K, V> Map<K, V> of() 
static <K, V> Map<K, V> of(K k1, V v1)
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2)
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3)
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4)
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5)
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
                               K k6, V v6)
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
                               K k6, V v6, K k7, V v7)
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
                               K k6, V v6, K k7, V v7, K k8, V v8)
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
                               K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9)
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
                               K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10)
static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries)
复制代码

List ,Set 和Map 接口, of(...) 方法重载了 0 ~ 10 个参数的不一样方法 。Map 接口若是超过 10 个参数, 可使用 ofEntries(...) 方法。

REPL (JShell)

REPL(Read Eval Print Loop)意为交互式的编程环境。JShell 是 Java 9 新增的一个交互式的编程环境工具。它容许你无需使用类或者方法包装来执行 Java 语句。它与 Python 的解释器相似,能够直接输入表达式并查看其执行结果。

例如:

输入“jshell”打开jshell命令窗口:

输入“/help”查看帮助信息:

进行运算,建立和使用函数,以及退出:

接口支持私有方法和私有静态方法:

下图是Java8和java9的接口的变化的对比:

示例以下:

interface Test{
    String fields = "interface field";

    public abstract void abstractMethods();

    default void defaultMethods() {
        System.out.println("default Method");
        staticMethods();
        privateMethods();
        privateStaticMethods();
     }

    static void staticMethods() {
        System.out.println("static Method");
     }

    private void privateMethods() {
        System.out.println("private Method");
    }
   
    private static void privateStaticMethods() {
        System.out.println("private Static Method");
    }
   
}
复制代码

接口实现类:

public class TestImpl implements Test{

    @Override
    public void abstractMethods() {
        System.out.println("abstract Method");
    }
    
}
复制代码

测试类:

public class Demo{
    public static void main(String[] args) {
        TestImpl testImpl = new TestImpl();
        System.out.println(testImpl.fields);
        testImpl.abstractMethods();
        testImpl.defaultMethods();
    }
}
复制代码

测试结果:

interface field
abstract Method
default Method
static Method
private Method
private Static Method
复制代码

改进的 Stream API和Optional 类

Java 9 改进的 Stream API ,为 Stream 新增了几个方法:dropWhile、takeWhile、ofNullable,为 iterate 方法新增了一个重载方法,使流处理更容易。

Optional 类在Java8中引入,它的引入很好的解决空指针异常,在 java 9 中, 添加了stream(),ifPresentOrElse()和or()三个方法来改进它的功能。

示例以下:

Stream.of("a","b","c","","e","f").takeWhile(s->!s.isEmpty())
    .forEach(System.out::print);
System.out.println();
Stream.of("10","20","30","","40","50").dropWhile(s-> !s.isEmpty())
    .forEach(System.out::print);
System.out.println();
IntStream.iterate(3, x -> x < 10, x -> x+ 3).forEach(System.out::print);
System.out.println();
System.out.println(Stream.ofNullable(100).count());
System.out.println(Stream.ofNullable(null).count());
复制代码

结果:

// abc
// 4050
// 369
// 1
// 0
复制代码
public static void main(String[] args) {
    // stream()用法:
    List<Optional<String>> list = Arrays.asList (
        Optional.of("data1"), 
        Optional.empty(), 
        Optional.of("data2"),
        Optional.empty(),
        Optional.of("data3"));

    List<String> result = list.stream()
    .flatMap(Optional::stream)
    .collect(Collectors.toList());
    // 结果 [data1, data2, data3]
    System.out.println(result); 

  // ifPresentOrElse使用:
  Optional<String> optional = Optional.of("datas");
  // 结果 Value: datas
  optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() -> 
    System.out.println("No data found"));
  optional = Optional.empty();
  // 结果 No data found
  optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() -> 
  System.out.println("No data found"));

    Optional<String> optional1 = Optional.of("datas");
    Supplier<Optional<String>> nullData = () -> Optional.of("No data found");
    optional1 = optional1.or(nullData);
    // Value: datas
    optional1.ifPresent( x -> System.out.println("Value: " + x));

    optional1 = Optional.empty();    
    optional1 = optional1.or(nullData);
    // Value: No data found
    optional1.ifPresent( x -> System.out.println("Value: " + x));

}
复制代码

改进的 CompletableFuture API

支持 delays 和 timeouts,提高了对子类化的支持,新的工厂方法:

public CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit):在timeout(单位在java.util.concurrent.Timeunits units中,好比 MILLISECONDS )前以给定的 value 完成这个 CompletableFutrue。返回这个 CompletableFutrue。

public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit):若是没有在给定的 timeout 内完成,就以 java.util.concurrent.TimeoutException 完成这个 CompletableFutrue,并返回这个 CompletableFutrue。

public <U> CompletableFuture<U> newIncompleteFuture():使得CompletableFuture能够被更简单的继承

<U> CompletionStage<U> completedStage(U value):返回一个新的以指定 value 完成的CompletionStage ,而且只支持 CompletionStage 里的接口。

<U> CompletionStage<U> failedStage(Throwable ex):返回一个新的以指定异常完成的CompletionStage ,而且只支持 CompletionStage 里的接口。

异常处理机制改进try-with-resources

try-with-resources 声明在 JDK 9 已获得改进。若是你已经有一个资源是 final 或等效于 final 变量,您能够在 try-with-resources 语句中使用该变量,而无需在 try-with-resources 语句中声明一个新变量。

示例以下:

public static void main(String[] args) throws IOException {
  System.out.println(readData("test"));// 结果:test
}

static String readData(String message) throws IOException {
    Reader inputString = new StringReader(message);
    BufferedReader br = new BufferedReader(inputString); 
    // Java8处理方式:
    // try (BufferedReader br1 = br) {
    //    return br1.readLine();
    // }
    // Java9处理方式:
    try (br) {
      return br.readLine();
    }
}
复制代码

改进的 @Deprecated 注解

Java 9 中注解增长了两个新元素:sinceforRemovalsince: 元素指定已注解的API元素已被弃用的版本。 forRemoval: 元素表示注解的 API 元素在未来的版本中被删除,应该迁移 API。 示例以下:

@Deprecated(since = "1.9", forRemoval = true)
class Test{
}
复制代码

钻石操做符(Diamond Operator“<>”)

在 java 9 中, “<>”能够与匿名的内部类一块儿使用,从而提升代码的可读性。

示例:

public class Test {
   public static void main(String[] args) {
      Handler<Integer> intHandler = new Handler<>(1) {
         @Override
         public void handle() {
            System.out.println(content);
         }
      };
      intHandler.handle();
      Handler<? extends Number> intHandler1 = new Handler<>(2) {
         @Override
         public void handle() {
            System.out.println(content);
         }
      };
      intHandler1.handle();
      Handler<?> handler = new Handler<>("test") {
         @Override
         public void handle() {
            System.out.println(content);
         }
      };
 
      handler.handle();    
   }  
}
 
abstract class Handler<T> {
   public T content;
 
   public Handler(T content) {
      this.content = content; 
   }
   
   abstract void handle();
}
复制代码

在java8中,上例中的 newHandler<>后面的<>里面必须带有泛型类型。Java9就不须要了。

Unicode 7.0扩展支持:

从Java SE 9,升级现有平台的API,支持7.0版本的Unicode标准,主要在如下类中:

java.lang.Character和java.lang.String java.text包中的Bidi,BreakIterator和Normalizer 这次升级将包括改善双向行为,从而能够更好地显示Unicode 6.3中引入的阿拉伯语和希伯来语等文本。 Unicode 7.0自己将添加大约三千个字符和二十多个脚本。

更多详情请查看: openjdk.java.net/projects/jd…


2、Java 10

这里重点看咱们开发者可以直接体验到的一些功能:

var 局部变量类型推断

示例:

var list = new ArrayList<String>();  // 表明 ArrayList<String>
var stream = list.stream();          // 表明 Stream<String>
复制代码

这种处理将仅限于带有初始值设定项的局部变量,加强的for循环中的索引以及在传统的for循环中声明的局部变量。它不适用于方法形式,构造函数形式,方法返回类型,字段,catch形式或任何其余类型的变量声明。

支持Unicode 8.0。

加强了java.util.Locale和相关的API,以实现BCP 47语言标签的其余Unicode扩展。

这次针对BCP 47语言标签扩展包括:

  • cu (货币类型)
  • fw (一周的第一天)
  • rg (区域覆盖)
  • tz (时区)

具体API变动有:

  • java.text.DateFormat::get*Instance将根据扩展名返回实例ca,rg和/或tz
  • java.text.DateFormatSymbols::getInstance 将根据扩展名返回实例 rg
  • java.text.DecimalFormatSymbols::getInstance 将根据扩展名返回实例 rg
  • java.text.NumberFormat::get*Instance将根据扩展名nu和/或返回实例rg
  • java.time.format.DateTimeFormatter::localizedBy将返回DateTimeFormatter基于扩展状况下ca,rg和/或tz
  • java.time.format.DateTimeFormatterBuilder::getLocalizedDateTimePattern将根据rg扩展名返回模式字符串。
  • java.time.format.DecimalStyle::of将DecimalStyle根据扩展名返回实例nu,和/或rg
  • java.time.temporal.WeekFields::of将WeekFields根据扩展名fw和/或返回实例rg
  • java.util.Calendar::{getFirstDayOfWeek,getMinimalDaysInWeek}将根据扩展名fw和/或返回值rg
  • java.util.Currency::getInstance将Currency根据扩展名cu和/或返回实例rg
  • java.util.Locale::getDisplayName 将返回一个字符串,其中包括这些U扩展名的显示名称
  • java.util.spi.LocaleNameProvider 这些U扩展的键和类型将具备新的SPI

其余特性都是有关垃圾回收,编译器,证书,以及命令工具等有关的,这里就不列举了。

更多详情请查看: openjdk.java.net/projects/jd…


3、Java 11

局部变量的语法lambda参数

Java11中的lambda表达式能够为隐式类型,其中类型的形式参数均可以被推断出。对于隐式类型的lambda表达式的形式参数,容许使用保留的类型名称var,以便: (var x, var y) -> x.process(y) 等效于:

(x, y) -> x.process(y) // 这样的对的
(var x, int y) -> x.process(y)  // 这样就会报错
复制代码

其余的lambda用法和Java8里的lambda用法同样。

启动单文件源代码程序

加强java启动器以运行做为Java源代码的单个文件提供的程序,包括经过“ shebang”文件和相关技术从脚本内部使用该程序。

从JDK 10开始,java启动器以三种模式运行:启动类文件,启动JAR文件的main类或启动模块的main类。在这里,咱们添加了新的第四种模式:启动在源文件中声明的类。

若是“类名”标识具备.java扩展名的现有文件,则选择源文件模式,并编译和运行该文件。该--source选项可用于指定源代码的源版本。

若是文件没有.java扩展名,则--source必须使用该选项来强制源文件模式。例如当源文件是要执行的“脚本”而且源文件的名称不遵循Java源文件的常规命名约定时。

更多详情请查看: openjdk.java.net/projects/jd…


4、Java 12

对 switch 语句进行扩展:

扩展switch语句,以即可以将其用做语句或表达式,而且两种形式均可以使用“传统”或“简化”做用域并控制流的行为。这些变化将简化平常编码在switch中。这是JDK 12中的预览功能。

请注意:此JEP已被JDK 13的JEP 354取代。

普通写法:

switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        System.out.println(6);
        break;
    case TUESDAY:
        System.out.println(7);
        break;
    case THURSDAY:
    case SATURDAY:
        System.out.println(8);
        break;
    case WEDNESDAY:
        System.out.println(9);
        break;
}
复制代码

如今引入一种新的switch标签形式,写为“case L ->”,表示若是匹配标签,则只执行标签右边的代码。例如,如今能够编写之前的代码:

switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}
复制代码

再好比局部变量,普通写法是这样的:

int numLetters;
switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        numLetters = 6;
        break;
    case TUESDAY:
        numLetters = 7;
        break;
    case THURSDAY:
    case SATURDAY:
        numLetters = 8;
        break;
    case WEDNESDAY:
        numLetters = 9;
        break;
    default:
        throw new IllegalStateException("Wat: " + day);
}
复制代码

如今的写法是这样的:

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    case THURSDAY, SATURDAY     -> 8;
    case WEDNESDAY              -> 9;
};
复制代码

更多详情请查看: openjdk.java.net/projects/jd…


5、Java 13

switch表达式预览版

JDK 13中新增 switch 表达式beta 版本,这是对Java12 switch表达式功能的加强版本,而且Java13版本的switch表达式的更新能够用于生产环境中。switch 表达式扩展了 switch 语句,使其不只能够做为语句(statement),还能够做为表达式(expression),而且两种写法均可以使用传统的 switch 语法。

除了Java12的用法以外,Java13的更新引入一个新的关键字yield。大多数switch表达式在“case L ->”开关标签的右侧都有一个表达式。若是须要一个完整的块,须要使用yield语句来产生一个值,该值是封闭switch表达式的值。

示例:

int j = switch (day) {
    case MONDAY  -> 0;
    case TUESDAY -> 1;
    default      -> {
        int k = day.toString().length();
        int result = f(k);
        yield result;
    }
};
复制代码

上例也可使用传统的switch语句:

int result = switch (s) {
    case "Foo": 
        yield 1;
    case "Bar":
        yield 2;
    default:
        System.out.println("Neither Foo nor Bar, hmmm...");
        yield 0;
};
复制代码

switch表达的状况必须详细;对于全部可能的值,必须有一个匹配的switch标签。(显然,switch声明并不是必须详细。)这一般意味着须要一个default子句。可是enum switch对于覆盖全部已知常量的表达式,default编译器会插入一个子句以指示该enum定义在编译时和运行时之间已更改。依靠这种隐式default子句的插入可使代码更健壮。如今,当从新编​​译代码时,编译器将检查全部状况是否获得明确处理。

此外,switch表达式必须以一个值正常完成,或者必须经过引起异常来忽然完成。这有许多后果。首先,编译器会检查每一个开关标签是否匹配,而后产生一个值。

示例:

int i = switch (day) {
    case MONDAY -> {
        System.out.println("Monday"); 
        // ERROR! Block doesn't contain a yield statement } default -> 1; }; i = switch (day) { case MONDAY, TUESDAY, WEDNESDAY: yield 0; default: System.out.println("Second half of the week"); // ERROR! Group doesn't contain a yield statement
};
复制代码

另外一种后果是,控制语句,break,yield,return和continue,没法经过跳switch表达式,示例:

z: 
    for (int i = 0; i < MAX_VALUE; ++i) {
        int k = switch (e) { 
            case 0:  
                yield 1;
            case 1:
                yield 2;
            default: 
                continue z; 
                // ERROR! Illegal jump through a switch expression 
        };
    ...
    }

复制代码

Text Blocks预览版(文字块)

简单地说就是:能够跨多行显示字符串而且不对转义字符进行转义。目标是编写Java程序的任务,同时避免了常见状况下的转义序列,加强Java程序中表示用非Java语言编写的代码的字符串的可读性。

在Java中,在字符串文字中嵌入HTML,XML,SQL或JSON片断"..."一般须要先进行转义和串联的大量编辑工做,而后才能编译包含该代码块的代码。该代码快一般难以阅读且难以维护。可是Java13的代码块功能会更直观地表示字符串,并且能够跨越多行,并且不会出现转义的视觉混乱,这将提升Java程序的可读性和可写性。本质上是二维文本块,而不是一维字符序列。

基本语法形式:

""" line 1 line 2 line 3 """
复制代码

等效于:"line 1\nline 2\nline 3\n"

或字符串文字的串联:

"line 1\n" +
"line 2\n" +
"line 3\n"
复制代码

若是在字符串的末尾不须要行终止符,则能够将结束定界符放在内容的最后一行。例如,文本块:

""" line 1 line 2 line 3"""
复制代码

具体使用:

字符串里面写HTML代码,

Java13以前写法:

String html = "<html>\n" +
              " <body>\n" +
              " <p>Hello, world</p>\n" +
              " </body>\n" +
              "</html>\n";
复制代码

Java13写法:

String html = """ <html> <body> <p>Hello, world</p> </body> </html> """;
复制代码

再好比SQL示例:

Java13以前写法:

String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +
               "WHERE `CITY` = 'INDIANAPOLIS'\n" +
               "ORDER BY `EMP_ID`, `LAST_NAME`;\n";
复制代码

Java13写法:

String query = """ SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB` WHERE `CITY` = 'INDIANAPOLIS' ORDER BY `EMP_ID`, `LAST_NAME`; """;
               
复制代码

再好比:

Java13以前写法:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval("function hello() {\n" +
                         " print('\"Hello, world\"');\n" +
                         "}\n" +
                         "\n" +
                         "hello();\n");
                         
复制代码

Java13写法:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval(""" function hello() { print('"Hello, world"'); } hello(); """);

复制代码

更多详情请查看: openjdk.java.net/projects/jd…

相关文章
相关标签/搜索