Java 6 - 12 版本特性

171 阅读12分钟

1. Java 6

1.1. 脚本引擎 (JSR 233)

  描述应用中复杂多变的业务逻辑,并在应用运行过程中进行动态修改;为应用提供一种领域特定语言(Domainspecific Language,DSL),供没有技术背景的普通用户使用;作为应用中各个组件之间的“胶水”,快速进行组件之间的整合;快速开发出应用的原型系统,从而迅速获取用户反馈,并进行改进;帮助开发人员快速编写测试用例等。

  应用开发中使用脚本语言,是“多语言开发”的一种实践,即根据应用的需要和语言本身的特性来选择最合适的变成语言,以快速高效地解决应用中的某一部分问题。多种不同语言实现的组件组合起来,用Java编写核心业务逻辑,用Ruby来进行数据处理。不同语言编写的代码可以同时运行的同一个Java虚拟机之上。这些脚本语言和Java语言之间的交互,是由脚本语言支持API来完成。

  JSR 233中定义了脚本引擎的注册和查找机制,Java SE6中自带了JavaScript语言的脚本引擎,是基于Mozilla的Rhino来实现,对于其他的脚本语言,则需要下载对应的脚本引擎的库并放在程序的类路径中。一般只要放在类路径中中,脚本引擎就可以被应用程序发现并使用。

用法

  首先创建一个脚本引擎管理器javax.script.ScriptEngineManager对象,再通过管理器来查询所需的JavaScript脚本引擎,最后通过脚本引擎来执行JavaScript代码。

ScriptEngineManagerDemo.png

参考:www.cnblogs.com/xiao2/p/661…

1.2. Compiler API (JSR 199)

  动态编译Java源文件,Compiler API联合反射功能就能够告终动态的发生Java代码并编译厉行这些代码。

CompilerAPIDemo.png

1.3. HttpService

  略。

2. Java 7

2.1. @SuppressWarnings

  Java编译过程中会出现很多警告,但每次编译有很多警告影响对error的过滤和修改,在代码中加上 @SuppressWarnings("type") 解决。

  • unchecked:抑制未检查的转化,如集合没有指定类型的警告。
  • unused:抑制未使用的变量的警告。 
  • resource:抑制与使用Closeable类型资源相关的警告。  
  • path:抑制在类路径,原文件路径中有不存在的路径的警告。
  • deprecation:抑制使用了某些不赞成使用的类和方法的警告。
  • fallthrough:抑制switch语句执行到底没有break关键字的警告   
  • serial:抑制某类实现Serializable,但未定义serialVersionUID。
  • rawtypes:抑制没有传递带有泛型的参数的警告。 
  • all:抑制全部类型的警告。

2.2. 捕获多异常

  略。

2.3. Try-with-resources

  声明一种或多种资源的try语句。资源是指在程序用完之后必须关闭的对象。try-with-resources语句保证了每个声明了的资源在语句结束的时候都会被关闭。任何实现了java.lang.AutoCloseable或java.io.Closeable接口对象,都可以当做资源使用。

TryWithResourcesDemo.png

  上下两个方法等价,BufferedReader从Java SE7开始就实现了java.lang.AutoCloseable接口。

2.4. InvokeDynamic

  • invokestatic:调用静态方法。
  • invokespecial:调用私有方法、实例构造器方法、父类方法。
  • invokevirtual:调用实例方法。
  • invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象。

  运行时动态解析出调用点限定符所引用的方法,然后再执行该方法,在此之前的4条调用指令,分派逻辑是固化在java虚拟机内部的,而invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。

静态类型和动态类型理解InvokeDynamic

  • 静态类型:每个变量在初始化时候就要声明唯一的类型并且不能改变。
  • 动态类型:指变量没有固定类型,变量的类型取决于它里面元素的类型。

  Java语言是静态类型。有人可能会提到泛型,Java的泛型是擦除式的,虽然在编写Java源码时看起来好像不能确定变量类型,但是在Java编译为字节码的过程中,每一个变量都是有确定的类型。所以从语言的角度,之前的4条方法调用指令是完全够用,但JVM不只是跨平台的,还是跨语言的,当试图开发动态类型语言的时候,问题就来了:

  jvm大多数指令都是类型无关的,但是在方法调用的时候,却不是这样,每个方法调用在编译阶段就必须指明方法参数和返回值类型,但是动态类型语言的方法参数,直到运行时刻才能知道类型,因此jdk就做了这样一个“补丁”:用invokedynamic调用方法的时候,会转到bootstrap方法,在这个方法里可以动态获取参数类型,然后根据参数类型分派合适的方法作为CallSite(动态调用点),最后真实调用的就是CallSize里的方法。如此便能在JVM上实现动态类型语言的方法调用了。

参考:www.cnblogs.com/sheeva/p/63…

2.5. Fork / Join (JSR 166)

  原由 :“分治”思想。可以使用ExecutorService和Callable解决,但他俩的主要问题是,Callable实例在本质上是阻塞的,一旦某个Callable实例开始执行,其他所有Callable都会被阻塞。由于队列后面的Callable实例在前一实例未执行完成的时候不会被执行,因此许多资源无法得到利用。Fork/Join框架被引入来解决这一并行问题,而Executor解决的是并发问题。

  Fork/Join 框架与传统线程池的区别采用“工作窃取”模式(work-stealing):当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。

  相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上。在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态。而在fork/join框架实现中,如果某个任务由于等待另外一个任务的完成而无法继续运行。那么处理该任务的线程会主动寻找其他尚未运行的任务来执行。从而减少线程等待时间,提升性能。

  • ForkJoinPool:实现ExecutorService和工作窃取算法。管理工作者线程,并提供任务的状态信息,以及任务的执行
  • ForkJoinTask:在ForkJoinPool中执行的基类。

实现Fork/Join任务,需要实现以下两个类之一:

  • RecursiveAction:适用任务没有返回结果场景。
  • RecursiveTask:适用任务有返回结果场景。

  ForkJoinPool类是ForkJoinTask实例的执行者,ForkJoinPool的主要任务就是”工作窃取”,其线程尝试发现和执行其他任务创建的子任务。ForkJoinTask实例与普通Java线程相比是非常轻量的。一 旦ForkJoinTask被启动,就会启动其子任务并等待它们执行完成。执行者ForkJoinPool负责将任务赋予线程池中处于等待任务状态的另一线程。线程池中的活动线程会尝试执行其他任务所创建的子任务。ForkJoinPool会尝试在任何时候都维持与可用的处理器数目一样数目的活动线程数。

ForkJoinTask两个主要的方法:

  • fork():决定ForkJoinTask的异步执行,凭借这个方法可以创建新的任务。
  • join():当主任务等待其创建的多个子任务的完成执行。

Fork/Join框架执行的任务具有以下限制:

  • 任务只能使用fork()和join()操作当做同步机制。如果使用其他的同步机制,工作者线程就不会执行任务。
  • 任务不能执行I/0操作,比如文件数据的读取与写入。
  • 任务不能抛出非运行时异常(CheckedException),必须在代码中处理这些异常。

ForkJionTask主要包括以下方法:

  • invokeAll():执行主任务创建的多个子任务,这是一个同步调用,当一个主任务等待它的子任务是,执行这个主任务的工作者线程接受另一个等待执行的任务开始执行。
  • complete(reslut):结束任务的执行并返回任务的结果。这个方法接受一个对象,对象的类型是泛型参数,然后在任务调用join操作时返回这个对象作为结果。这一过程采用了推荐的异步任务来返回任务结果。
  • get():获取任务的结果。

ForkJoinPool主要包括以下方法:

  • execute(Runnabletask)。发送任务给ForkJoinPool类,使用Runnable对象时ForkJoinPool类不采用工作窃取算法,仅仅在使用ForkJoinTask类时才采用工作窃取算法。

ForkJoinDemo.png

参考:www.cnblogs.com/wxgblogs/p/…

3. Java 8

3.1. Lambda

  略。

3.2. @FunctionalInterface

  函数式接口:接口里面只能有一个抽象方法,也称为SAM(Single Abstract Method interfaces)接口。

  • 接口有且仅有一个抽象方法。
  • 允许定义静态方法。
  • 允许定义默认方法。
  • 允许java.lang.Object中的public方法。
  • 该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响,加上该注解能够更好地让编译器进行检查,如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。

FunctionalInterfaceDemo.png

参考:www.jianshu.com/p/52cdc402f…

3.3. 方法引用 (::)

  当lambda表达式只是执行一个方法调用时,直接通过方法引用的形式调用。方法引用是一种更简洁的lambda表达式。方法引用是lambda表达式的简写,提高了代码可读性。引用的方法要满足引用的方法的参数列表必须与实现的抽象方法参数列表保持一致。

静态方法引用:

String::valueOf 等价  s -> String.valueOf(s);
Math::pow       等价  (x,y) -> Math.pow(x,y);

实例方法引用:

String::length  等价  s -> s.length();
this::equals    等价  s -> this.equals(s);
super::equals   等价  s -> super.equals(s);

构造方法引用:

String::new 等价  s -> new String(s);
            等价  () -> new String();
int[]::new  等价  x -> new int[x];

JDK原生示例:

  • java.lang.Runnable
  • java.awt.event.ActionListener
  • java.util.Comparator ...

参考:www.cnblogs.com/hujingnb/p/…

3.4. 接口default

  略,见4.2截图。

3.5. Stream

  略。

3.6. Optional

  略。

3.7. 时间处理

  略,参考Joda-Time。

4. Java 9

4.1. 模块化 (Module)

  类似webpackage。

  模块化就是增加了更高级别的聚合,是Package的封装体。Package是一些类路径名字的约定,而模块是一个或多个Package组成的封装体。

  • Java9以前 :package => class / interface。
  • Java9以后 :module => package => class / interface。

目的:

  • 让Java SE程序更加容易轻量级部署。
  • 强大的封装能力。
  • 改进组件间的依赖管理,引入比jar粒度更大的Module。
  • 改进性能和安全性。

4.2. JShell

  提供了一个交互式 shell,用于快速原型、调试、使用Java及Java API,不需要main方法,也不需要在执行之前编译代码。

4.3. HTTP/2 客户端

  略。

4.4. 增强 JavaDoc

  Javadoc工具生成Java文档, Java 9的javadoc兼容HTML5标准。

4.5. 集合工厂方法 (JEP 269)

  使用工厂方法创建出的集合均为不可修改,换而言之无法CURD。

List / Set:

List<String> lists = List.of("a","b","c");
Set<String> sets = Set.of("a","b","c");

Map:

Map<String, String> map = Map.ofEntries(entry("key","value"), entry("key","value"));

4.6. 接口允许私有方法

  略。

4.7. 增强 @Deprecated

  增加两个元素:

  • since:元素指定已注解的API元素已被弃用的版本。
  • forRemoval:元素表示注解的 API 元素在将来的版本中被删除,应该迁移 API。

4.8. 内部允许使用砖石操作符

  略。

4.9. 改进 Process API

  略。

4.10. 改进 try-with-resources

  对比Java 7参考:

public void demo(String filename)
  FileOutputStream outputStream = new FileOutputStream(filename);
  OutputStreamWriter streamWriter = new OutputStreamWriter(fos);
  BufferedWriter bufferWriter = new BufferedWriter(osw);
  // 下方
  try(outputStream; streamWriter; bufferWriter;){ 
    bufferWriter.write("Hello World !");
    bufferWriter.flush();
  }
}

4.11. 改进 Optional

  如果为空返回一个空流,如果不为空将Optional的值转成一个流。

Stream<Object> stream = Optional.ofNullable(null).stream();

ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)

  结合isPresent()对Else增强,ifPresentOrElse方法用途:当一个 Optional包含值,则对其包含的值调用函数action,即 action.accept(value),这与ifPresent一致;与ifPresent方法的区别在于,ifPresentOrElse还有第二个参数emptyAction,当Optional 不包含值,那么ifPresentOrElse调用emptyAction,即emptyAction.run()。

4.12. Reactive Streams

  响应式流:提供非阻塞背压的异步流处理标准的倡议,旨在解决处理元素流的问题:如何将元素流从发布者传递到订阅者,而不需要发布者阻塞,或订阅者有无限制的缓冲区或丢弃。

  响应式流模型:订阅者向发布者发送多个元素的异步请求,发布者向订阅者异步发送多个或稍少的元素。

参考:www.cnblogs.com/IcanFixIt/p…

5. Java 10

5.1. 局部变量类型推断

var 示例:

var i = 10; 等价 int i = 10;

接收返回:

var str = service.getString(); 等价 String str = service.getString();

不适用条件:

  • 类成员变量类型。
  • 方法返回值类型。
  • Java 10 Lambda不能使用var。

5.2. 删除 javah (JEP 313)

  被java -h代替。

5.3. 新JIT编译器:Graal

参考:zhuanlan.zhihu.com/p/137836206

5.4. 增强集合工厂方法

  List / Set / Map 增加不可变集合 copyOf() 方法。

6. Java 11

6.1. Lambda var

示例:

Bean<String> bean = (var i) -> System.out.print(i); 
等价  
Bean<String> bean = (String i) -> System.out.println(i);

6.2. 增强字符串API

  • 判空:" ".isBlank(); // true。
  • 去除首尾空格:" Hello Java11 ".strip(); // "Hello Java11"。
  • 去除尾部空格:" Hello Java11 ".stripTrailing(); // " Hello Java11"。
  • 去除首部空格:Hello Java11 ".stripLeading(); // "Hello Java11 "。
  • 复制字符串:"Java11".repeat(3); // "Java11Java11Java11"。
  • 行数统计:"A\nB\nC".lines().count(); // 3。

6.3. 标准化HttpClient API

同步:

public void syncMethod(){
  var request = HttpRequest.newBuilder().uri(URI.create("https://www.baidu.com/")).build();
  var client = HttpClient.newHttpClient();
  HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
  System.out.println(response.body()); 
}

异步:

public void asyncMethod(){
  var request = HttpRequest.newBuilder().uri(URI.create("https://www.baidu.com/")).build();
  var client = HttpClient.newHttpClient();
  var headers = HttpResponse.BodyHandlers.ofString();
  CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync(request, headers);
  HttpResponse<String> response = sendAsync.get();
  System.out.println(response.body()); 
}

6.4. 直接运行Java文件

  跳过编译直接运行java文件。

java Demo.java

7. Java 12

7.1. 改进 Switch (JEP 325)

switch(type){
    case A, B -> System.out.print("case 1."); 
    case B, C, D -> System.out.print("case 2.");
    default -> System.out.print("unkonwn.");
}