百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Java9新特性及使用

zhezhongyun 2025-01-19 01:46 34 浏览

Java 9 包含了丰富的特性集。虽然Java 9没有新的语言概念,但在使用、性能上做了很大改进。

模块化系统–Jigsaw 项目

模块化是一个很通用的概念。在软件中,模块化可以运用到编写和实现一个程序和计算系统,他们都是作为独立的模块,而不是作为一个单一的和完整的设计。

Java 9中主要的变化是已经实现的模块化系统。模块化的引入使得JDK可以在更小的设备中使用。采用模块化系统的应用程序只需要这些应用程序所需的那部分JDK模块,而非是整个JDK框架了。模块化系统也可以将公共的类封装到一个模块中。因此一个封装在模块中的定义为public的类不能再任何地方使用,除非一个模块显式的定义了这个模块。由于Java 9的这个变化,Java内部的API(例如com.sun.*)默认情况下是不能使用的。

简而言之,所有的模块将需要在所谓的module-info.java文件中进行描述,这个文件是位于Java代码结构的顶层。

module me.aboullaite.java9.modules.car {
    requires me.aboullaite.java9.modules.engines;//依赖的模块
    exports me.aboullaite.java9.modules.car.handling;//在模块中导出包
}

我们的模块car(汽车)需要依赖+模块engine(引擎)和需要导出handling(操纵)包。

JShell–Java 9 REPL

你可能问:“REPL是什么”?REPL是一种快速运行语句的命令行工具。

在Java中,如果你想执行一个简单的语句,我们要么创建一个带main方法的类,要么创建一个可以执行的Test类。当你正在启动Java程序的时候,如果你想执行某些语句并且想立刻看见执行结果,上面的做法看起来不是那么有用了。

JShell试图去解决这个问题。Java开发者可以利用JShell在没有创建类的情况下直接声明变量,计算表达式,执行语句。JShell也可以从文件中加载语句或者将语句保存到文件中。并且JShell也可以是tab键进行自动补全的特性。

jshell> "1,2,3,,4".split(",")
$2 ==> String[5] { "1", "2", "3", "", "4" }

jshell> "  a   b  ".trim()
$3 ==> "a   b"imports

imports类库

jshell> /imports
|    import java.io.*
|    import java.math.*
|    import java.net.*
|    import java.nio.file.*
|    import java.util.*
|    import java.util.concurrent.*
|    import java.util.function.*
|    import java.util.prefs.*
|    import java.util.regex.*
|    import java.util.stream.*

方法定义及调用

jshell> String hello(){return "htllo";}
|  已创建 方法 hello()

jshell> String hello(){return "hello";}
|  已修改 方法 hello()

jshell>

jshell>

jshell> /methods
|    String hello()

jshell> hello()
$7 ==> "hello"

查看历史

jshell> /list

   1 : int a = 1;
   2 : "1,2,3,,4".split(",")
   3 : "  a   b  ".trim()
   4 : new Date()
   6 : String hello(){return "hello";}
   7 : hello()
   8 : 1/0

不可变集合工厂

在Java 9之前,Java只能利用一些实用方法(例如:Collections.unmodifiableCollection(Collection<? extends T> c))创建一个不可修改视图的集合。例如,我们可以在Java 8中使用一条如下所示的语句,创建一个Collection的不可修改的视图。虽然这是最简单的创建方式,但是看起来很糟糕!不是吗?

Map<String, String> immutableMap = Collections.unmodifiableMap(
            new HashMap<String, String>() {{
                put("key1", "Value1");
                put("key2", "Value2");
                put("key3", "Value3");
}});

现在,Java 9引入了一些有用的工厂方法来创建不可修改的集合。我们现在在Java 9中创建不可修改的Map集合,如下所示。

Map<String, String> immutableMap = Map.of("key1", "Value1", "key2", "Value2","key3", "Value3");

下面是工厂方法的例子:

 // empty immutable collections 不可修改的空集合
List<String> emptyImmutableList = List.of();
Set<String> emptyImmutableSet = Set.of();
Map emptyImmutableMap = Map.of();

// immutable collections 不可修改的集合
List<String> immutableList = List.of("one", "two");
Set<String> immutableSet = Set.of("value1", "value2");
Map<String, String> immutableMap = Map.of("key1", "Value1", "key2", "Value2", "key3", "Value3");

接口中的私有方法

Java 8的接口引入了默认方法和静态方法。虽然Java 8首次计划实现接口的私有方法,却是在Java 9中实现。默认方法和静态方法可以共享接口中的私有方法,因此避免了代码冗余,这也使代码更加清晰。如果私有方法是静态的,那这个方法就属于这个接口的。并且没有静态的私有方法只能被在接口中的实例调用。

public interface PrivateMethod {
    // java7以前
    // 默认为 public static final
    String NAME = "xiao";
    // 默认为 public
    String get();

    // java8
    default void giveMyMoney() {
        System.out.println("No way!!!");
    }

    static void makeFriend() {
        System.out.println("Hello, my friend.");
    }
    
    // java9
    private void show() {
       System.out.println("Nobody can find me.");
    }
}

try/catch增强

Java6以及之前的版本中,在使用一些资源时,通常会使用try/catch语句,并且在finally语句中来关闭资源,Java7做了改进,Java9也做了一点改进,可以使用 effectively-final 变量。

什么是 effectively-final 变量? 简单来说就是没有被 final 修饰但是值在初始化后从未更改的变量。

public class TryCatchImprove {
    public static void main(String[] args) {
        // Java 6 or early
        // 使用的资源,建议都在finally块中关闭
        InputStreamReader inputStream7 = new InputStreamReader(System.in);
        try {
            int i = inputStream7.read();
        } catch (IOException ie) {
            ie.printStackTrace();
        } finally {
            try {
                inputStream7.close();
            } catch (IOException ie) {
                ie.printStackTrace();
            }
        }

        // Java 7,8
        // 当有多个资源被使用时,括号中使用 ; 分割,不再需要finally语句块
        try (InputStreamReader inputStream8 = new InputStreamReader(System.in)) {
            int i = inputStream8.read();
        } catch (IOException ie) {
            ie.printStackTrace();
        }

        // Java 9
        // 能够直接在try的括号中使用变量,多个变量时用 ; 分割
        InputStreamReader inputStream9 = new InputStreamReader(System.in);
        try (inputStream9) {
           int i = inputStream9.read(); 
        } catch (IOException ie) {
            ie.printStackTrace();
        }
    }
}

正如上面的代码所演示的那样,即使 inputStream9 变量没有被显示声明为 final,但它在第一次被复制后就不会改变了,因此,它就是 effectively-final 变量。

String 存储结构优化

Java 8 及之前的版本,String 一直是用 char[] 存储。在 Java 9 之后,String 的实现改用 byte[] 数组存储字符串,节省了空间。

public final class String implements java.io.Serializable,Comparable<String>, CharSequence {
    // @Stable 注解表示变量最多被修改一次,称为“稳定的”。
    @Stable
    private final byte[] value;
}

Stream & Optional 增强

Stream 中增加了新的方法 ofNullable()dropWhile()takeWhile() 以及 iterate() 方法的重载方法。

Java 9 中的 ofNullable() 方 法允许我们创建一个单元素的 Stream,可以包含一个非空元素,也可以创建一个空 Stream。 而在 Java 8 中则不可以创建空的 Stream

Stream<String> stringStream = Stream.ofNullable("Java");
System.out.println(stringStream.count());// 1
Stream<String> nullStream = Stream.ofNullable(null);
System.out.println(nullStream.count());//0

takeWhile() 方法可以从 Stream 中依次获取满足条件的元素,直到不满足条件为止结束获取。

List<Integer> integerList = List.of(11, 33, 66, 8, 9, 13);
integerList.stream().takeWhile(x -> x < 50).forEach(System.out::println);// 11 33

dropWhile() 方法的效果和 takeWhile() 相反。

List<Integer> integerList2 = List.of(11, 33, 66, 8, 9, 13);
integerList2.stream().dropWhile(x -> x < 50).forEach(System.out::println);// 66 8 9 13

iterate() 方法的新重载方法提供了一个 Predicate 参数 (判断条件)来决定什么时候结束迭代。

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
}
// 新增加的重载方法
public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {

}

两者的使用对比如下,新的 iterate() 重载方法更加灵活一些。

// 使用原始 iterate() 方法输出数字 1~10
Stream.iterate(1, i -> i + 1).limit(10).forEach(System.out::println);
// 使用新的 iterate() 重载方法输出数字 1~10
Stream.iterate(1, i -> i <= 10, i -> i + 1).forEach(System.out::println);

Optional 类中新增了 ifPresentOrElse()or()stream() 等方法

ifPresentOrElse() 方法接受两个参数 ConsumerRunnable ,如果 Optional 不为空调用 Consumer 参数,为空则调用 Runnable 参数。

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

Optional<Object> objectOptional = Optional.empty();
objectOptional.ifPresentOrElse(System.out::println, () -> System.out.println("Empty!!!"));// Empty!!!

or() 方法接受一个 Supplier 参数 ,如果 Optional 为空则返回 Supplier 参数指定的 Optional 值。

public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)

Optional<Object> objectOptional = Optional.empty();
objectOptional.or(() -> Optional.of("java")).ifPresent(System.out::println);//java

进程 API

Java 9 增加了 java.lang.ProcessHandle 接口来实现对原生进程进行管理,尤其适合于管理长时间运行的进程。

// 获取当前正在运行的 JVM 的进程
ProcessHandle currentProcess = ProcessHandle.current();
// 输出进程的 id
System.out.println(currentProcess.pid());
// 输出进程的信息
System.out.println(currentProcess.info());

ProcessHandle 接口概览:

响应式流

JDK9中的Flow API对应响应式流规范,响应式流规范是一种事实标准。JEP 266包含了一组最小接口集合,这组接口能捕获核心的异步发布与订阅。希望在未来第三方能够实现这些接口,并且能共享其方式。

java.util.concurrent.Flow包含以下4个接口:

* Flow.Processor(处理器)

* Flow.Publisher(发布者)

* Flow.Subscriber(订阅者)

* Flow.Subscription(订阅管理器)

这些接口都支持响应式流发布-订阅框架。Java 9也提供了实用类SubmissionPublisher。一个发布者产生一个或多个物品,这些物品由一个或多个消费者消耗。并且订阅者由订阅管理器管理。订阅管理器连接发布者和订阅者。

多分辨率图像API–JEP 251

目标是定义多分辨率图像API,这样开发者就能很容易的操作和展示不同分辨率的图像了。

这个新的API定义在java.awt.image包中,这个API能给我们带来如下的帮助:

* 将不同分辨率的图像封装到一张(多分辨率的)图像中,作为它的变体。

* 获取这个图像的所有变体。

* 获取特定分辨率的图像变体–表示一张已知分辨率单位为DPI的特定尺寸大小的逻辑图像,并且这张图像是最佳的变体。

基于当前屏幕分辨率大小和运用的图像转换算法,java.awt.Graphics类可以从接口MultiResolutionImage获取所需的变体。java.awt.image.AbstractMultiResolutionImage类提供了ava.awt.image.AbstractMultiResolutionImage 默认实现。AbstractMultiResolutionImage的基础实现是java.awt.image.BaseMultiResolutionImage

钻石(diamond)操作符范围的延伸

Java 7给出的钻石操作符使我们编写代码更简单了。在下面的例子中,你可以看见Java 7中List(列表)的可读性更强了,并且使代码更加的简洁了。

List<String> preJava7 = new ArrayList<String>();//java 7 之前的写法
List<String> java7 = new ArrayList<>();//java 7 之后的写法

但是Java 7中钻石操作符不允许在匿名类上使用。但在Java 9中改善了这一情况,允许钻石操作符在匿名类上使用。下面的例子只有在Java 9中才能通过编译。

List<String> list = new ArrayList<>(){};

增强的注释Deprecated

在Java SE 8和更早版本中,@Deprecated注解只是一个Marker接口,没有任何方法。 它用于标记Java API的类,字段,方法,接口,构造函数,枚举等。

注释@Deprecated可以标记Java API。注释@Deprecated有很多种含义,例如它可以表示在不远的将来的某个时间,被标记的API将会被移除。它也可以表示这个API已经被破坏了,并不应该再被使用。它还有其它很多含义。为了提供更多有关@Deprecated的信息,@Deprecated添加了forRemoval元素和since元素。

Java SE 9 中也提供了扫描jar文件的工具jdeprscan。这款工具也可以扫描一个聚合类,这个类使用了Java SE中的已废弃的API元素。 这个工具将会对使用已经编译好的库的应用程序有帮助,这样使用者就不知道这个已经编译好的库中使用了那些已废弃的API。

G1 成为默认垃圾回收器

在 Java 8 的时候,默认垃圾回收器是 Parallel Scavenge(新生代)+Parallel Old(老年代)。到了 Java 9, CMS 垃圾回收器被废弃了,G1(Garbage-First Garbage Collector) 成为了默认垃圾回收器。

G1 还是在 Java 7 中被引入的,经过两个版本优异的表现成为成为默认垃圾回收器。

统一的JVM日志

Java 9可以为JVM组件提供详细级别的通用日志记录系统。通过使用新的命令行选项: -Xlog用于所有日志记录设置 统一的JVM日志记录,为我们提供了易于配置的工具,可以对复杂的系统级JVM组件进行根本原因分析(RCA)

命令行 -Xlog可用于控制所有日志记录JVM组件。-Xlog参数 遵循以下规则:

  • 已按照在命令行中显示的顺序应用了多个参数。
  • 最后的配置规则:对于相同的输出,多个参数可以按给定的顺序相互覆盖。

Xlog:disable关闭所有日志记录并清除日志记录框架的所有配置(包括警告 错误)。

语法

-Xlog:tag[*][=level][:output:decoration:output-option],tag...

-Xlog:帮助打印-Xlog 使用语法和可用的标签,级别,修饰符以及一些示例命令行。

1)标签:显示日志消息后,它与JVM中的一组标签相关联,这些标签通过名称进行标识:os,gc,模块。我们为单个标签应用了不同的设置,“ *”表示“通配符”标签匹配。

2)级别:我们在不同级别执行日志记录,可用级别为错误,警告,信息,调试,跟踪和开发。要禁用日志记录,然后使用替代关闭。

3)输出:输出支持三种类型:stdout,stderr和文本文件,用于根据写入的大小和要旋转的文件数来设置日志文件的旋转。

4)装饰器:还有更多有关装饰器的消息的详细信息。这是列表:

  • time / timemillis / timenanos:当前时间和日期(ISO-8601格式)
  • uptime / uptimemillis / uptimenanos:自JVM启动以来的时间
  • pid:进程标识符
  • tid:线程标识符
  • 级别:与日志消息关联的级别
  • 标签:与日志消息关联的标签
C:\Program Files\Java\jdk-9.0.4\bin>java -Xlog:help-Xlog Usage: -Xlog[:[:[output][:[decorators][:output-options]]]]
where 'what' is a combination of tags and levels of the form tag1[+tag2...][*][=level][,...]
Unless wildcard (*) is specified, only log messages tagged with exactly the tags specified will be matched.

Available log levels:
off, trace, debug, info, warning, error

Available log decorators:
time (t), utctime (utc), uptime (u), timemillis (tm), uptimemillis (um), timenanos (tn), uptimenanos (un), hostname(hn), pid (p), tid (ti), level (l), tags (tg)
Decorators can also be specified as 'none' for no decoration.

Available log tags:
add, age, alloc, aot, annotation, arguments, attach, barrier, biasedlocking, blocks, bot, breakpoint, census, class, classhisto, cleanup, compaction, constraints, constantpool, coops, cpu, cset, data, defaultmethods, dump, ergo, exceptions, exit, fingerprint, freelist, gc, hashtables, heap, humongous, ihop, iklass, in it, itables, jni, jvmti,liveness, load, loader, logging, mark, marking, methodcomparator, metadata, metaspace, mmu, module, monitorinflation,monitormismatch, nmethod, normalize, objecttagging, obsolete, oopmap, os, pagesize, patch, path, phases, plab, promotion,preorder, protectiondomain, ref, redefine, refine, region, remset, purge, resolve, safepoint, scavenge, scrub, stacktrace,stackwalk, start, startuptime, state, stats, stringdedup, stringtable, stackmap, subclass, survivor, sweep, task, thread,tlab, time, timer, update, nload, verification, verify, vmoperation, vtables, workgang, jfr, system, parser, bytecode,setting, event Specifying 'all' instead of a tag combination matches all tag combinations.

Described tag combinations:
logging: Logging for the log framework itself

Available log outputs:
stdout, stderr, file=
Specifying %p and/or %t in the filename will expand to the JVM's PID and startup timestamp, respectively.

Some examples:
-Xlog
Log all messages using 'info' level to stdout with 'uptime', 'levels' and 'tags' decorations.
(Equivalent to -Xlog:all=info:stdout:uptime,levels,tags).

-Xlog:gc
Log messages tagged with 'gc' tag using 'info' level to stdout, with default decorations.

-Xlog:gc,safepoint
Log messages tagged either with 'gc' or 'safepoint' tags, both using 'info' level, to stdout, with default
decorations.
(Messaged tagged with both 'gc' and 'safepoint' will not be logged.)

注释SafeVarargs范围的延伸

直到Java 8,@SafeVarargs才能在静态方法、final方法和构造器上使用。但是这些方法或者构造器是不能被覆盖的。这些方法中缺少另一个不能被覆盖的方法,这个方法就是私有方法。Java 9可以将@SafeVarargs添加到私有方法上。下面的例子在Java 9中是正确的,但是在Java 8中就会抛出编译时错误: 注释@SafeVarargs不能在非final的实例方法iAmSafeVaragrsMethod上使用

@SafeVarargs
  private void iAmSafeVaragrsMethod(String... varagrgs)
  {
     for (String each: varagrgs) {
        System.out.println(each);
     }
  }

HTTP 2 客户端

Java 9采用了全新的HTTP客户端API,这些API支持HTTP/2协议和WebSocket协议,并且替换了遗留的HTTPURLConnectionAPI。这些改变并不应该在Java 9中完成。这些API可以从Incubator(孵化器)模块中获取。因此在默认情况下,这个模块是不能根据classpath获取的,需要使用--add-modules命令选项配置这个模块,将这个模块添加到classpath中。

我们创建一个HTTPRequest请求和获取异步的响应:

 URI testPageURI = new URI("http://127.0.0.1:8080/test");
  CompletableFuture<HttpResponse> nonBlockingResponse =  HttpRequest
          .create(testPageURI)
          .GET().responseAsync();
  int tries = 0;
  while(!nonBlockingResponse.isDone() && tries++ < 5) { Thread.sleep(5); } if (nonBlockingResponse.isDone()) { HttpResponse response = nonBlockingResponse.get(); System.out.println("status code : " + response.statusCode() + " --> " +  response.body(HttpResponse.asString()));
  }
  else {
      nonBlockingResponse.cancel(true);
      System.out.println("Cancelling, could not get response");
  }

HTML5风格的Java帮助文档

Java 8以及之前的版本生成的Java帮助文档是在HTML 4中,而HTML 4已经是很久的标准了。在Java 9中,javadoc命令行中选项部分添加了输出选项,这个选项的值要么是HTML 4,要么是HTML 5。现在HTML 4是默认的输出标记语言,但是在之后发布的JDK中,HTML 5将会是默认的输出标记语言。Java帮助文档还是由三个框架组成的结构构成,这是不会变的,并且以HTML 5输出的Java帮助文档也保持相同的结构。

更多的特性

* 保留下划线字符。变量不能被命名为_

* 废弃Applet API;

* javac不再支持Java1.4以及之前的版本;

* 废弃Java浏览器插件;

* 栈遍历API–栈遍历API能过滤和迟访问在堆栈跟踪中的信息。

相关推荐

JavaScript中常用数据类型,你知道几个?

本文首发自「慕课网」,想了解更多IT干货内容,程序员圈内热闻,欢迎关注!作者|慕课网精英讲师Lison这篇文章我们了解一下JavaScript中现有的八个数据类型,当然这并不是JavaScr...

踩坑:前端的z-index 之bug一二(zh1es前端)

IE6下浮动元素bug给IE6下的一个div设置元素样式,无论z-index设置多高都不起作用。这种情况发生的条件有三个:1.父标签position属性为relative;2.问题标签无posi...

两栏布局、左边定宽200px、右边自适应如何实现?

一、两栏布局(左定宽,右自动)1.float+margin即固定宽度元素设置float属性为left,自适应元素设置margin属性,margin-left应>=定宽元素宽度。举例:HTM...

前端代码需要这样优化才是一个标准的网站

  网站由前端和后端组成,前端呈现给用户。本文将告诉您前端页面代码的优化,当然仍然是基于seo优化的。  就前端而言,如果做伪静态处理,基本上是普通的html代码,正常情况下,这些页面内容是通过页面模...

网页设计如何自学(初学网页设计)

1在Dreamweaver中搭建不同的页面,需要掌握HTML的语句了,通过调整各项数值就可以制作出排版漂亮的页面,跟着就可以学习一些可视化设计软件。下面介绍网页设计如何自学,希望可以帮助到各位。Dre...

1、数值类型(数值类型有)

1.1数据类型概览MySQL的数据类型可划分为三大类别:数值类型:旨在存储数字(涵盖整型、浮点型、DECIMAL等)。字符串类型:主要用于存储文本(诸如CHAR、VARCHAR之类)。日期/...

网页设计的布局属性(网页设计的布局属性是什么)

布局属性是网站设计中必不可少的一个重要的环节,主要用来设置网页的元素的布局,主要有以下属性。1、float:该属性设置元素的浮动方式,可以取none,left和right等3个值,分别表示不浮动,浮在...

Grid网格布局一种更灵活、更强大的二维布局模型!

当涉及到网页布局时,display:flex;和display:grid;是两个常用的CSS属性,它们都允许创建不同类型的布局,但有着不同的用法和适用场景。使用flex布局的痛点当我们使...

React 项目实践——创建一个聊天机器人

作者:FredrikStrandOseberg转发链接:https://www.freecodecamp.org/news/how-to-build-a-chatbot-with-react/前言...

有趣的 CSS 数学函数(css公式)

前言之前一直在玩three.js,接触了很多数学函数,用它们创造过很多特效。于是我思考:能否在CSS中也用上这些数学函数,但发现CSS目前还没有,据说以后的新规范会纳入,估计也要等很久。然...

web开发之-前端css(5)(css前端设计)

显示控制一个元素的显示方式,我们可以使用display:block;display:inline-block;display:none;其中布局相关的还有两个很重要的属性:display:flex;和...

2024最新升级–前端内功修炼 5大主流布局系统进阶(分享)

获课:keyouit.xyz/14642/1.前端布局的重要性及发展历程前端布局是网页设计和开发的核心技能之一,它决定了页面元素如何组织和呈现。从早期的静态布局到现代的响应式布局,前端布局技术经历了...

教你轻松制作自动换行的CSS布局,轻松应对不同设备!

在网页设计中,自动换行的CSS布局是非常常见的需求,特别是在响应式设计中。它可以让网页内容自动适应不同屏幕尺寸,保证用户在不同设备上都能够获得良好的浏览体验。本文将介绍几种制作自动换行的CSS布局的方...

晨光微语!一道 CSS 面试题,伴你静享知识治愈时光

当第一缕阳光温柔地爬上窗台,窗外的鸟鸣声清脆悦耳,空气中弥漫着清新的气息。在这宁静美好的清晨与上午时光,泡一杯热气腾腾的咖啡,找一个舒适的角落坐下。前端的小伙伴们,先把工作的疲惫和面试的焦虑放在一边,...

2023 年的响应式设计指南(什么是响应式设计优缺点)

大家好,我是Echa。如今,当大家考虑构建流畅的布局时,没有再写固定宽度和高度数值了。相反,小编今天构建的布局需要适用于几乎任何尺寸的设备。是不是不可思议,小编仍然看到网站遵循自适应设计模式,其中它有...