主要特性
- 接口中支持定义
private
方法 try-with-resource
方式优化- 不可以使用 “_”下划线命名变量
@Deprecated
注解支持指定废弃版本(since
), 以及标记未来版本是否删除(forRemoval
)String
字符串的变化- 模块化
- jshell
接口定义private
方法
import java.util.concurrent.TimeUnit;
/**
* @author imyzt
* @date 2023/12/16
* @description java 9 在接口中可以定义 private 方法, 只可在本接口中调用
*/
public interface InterfaceFuture {
void foo();
/**
* java 8
*/
default void foo1() throws InterruptedException {
sleep();
}
/**
* java 8
*/
static void foo2() throws InterruptedException {
sleep2();
}
/**
* java 9
*/
private void sleep() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
}
/**
* java 9
*/
private static void sleep2() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
}
}
try-with-resource
方式优化
package top.imyzt.jdk.features.jdk9;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
* @author imyzt
* @date 2023/12/16
* @description 1. try 代码块简化, 2. 不可以再使用 "_"(下划线)命名变量
*/
public class TryWithResource {
public static void main(String[] args) throws FileNotFoundException {
// java 8
try (FileInputStream fis1 = new FileInputStream("");
FileOutputStream fos1 = new FileOutputStream("")) {
fis1.read();
} catch (Exception e) {
}
// java 9
// 可以将变量写到 try 代码块中, 让代码块更简洁
FileInputStream fis2 = new FileInputStream("");
FileOutputStream fos2 = new FileOutputStream("");
try (fis2; fos2) {
} catch (Exception e) {
}
// java 9 运行报错 As of Java 9, '_' is a keyword, and may not be used as an identifier
// String _ = "123";
}
/**
* `@Deprecated` 注解支持指定废弃版本(since), 以及标记未来是否废弃(forRemoval)
*/
@Deprecated(since = "9", forRemoval = true)
private void test() {
}
}
@Deprecated
注解升级
例如java.lang.Object#finalize
方法就在 Java9 中被标记废弃,并且在未来可能会被删除。
@Deprecated(since="9", forRemoval=true)
protected void finalize() throws Throwable { }
String字符串的变化
在 Java9 之前的版本中,String
内部使用char
数组存储,对于使用英语的人来说,一个字符用一个byte
就能存储,使用char
存储字符会浪费一半的内存空间,因此在 Java9 中将String内部的char
数组改成了byte
数组,这样就节省了一半的内存占用。
char c = 'a'; // 2个字节
byte b = 97; // 1个字节
String中增加了2个成员变量
static final boolean COMPACT_STRINGS;
:判断是否压缩,默认为true,如果=false,则不压缩,使用UTF-16编码。private final byte coder;
:用来区分使用的字符编码- LATIN1,值为0,存储英文
- UTF-16,值为1,存储中文,或夹杂中文的英文(为了区分字符边界,如果英文使用
1byte
,则不利于字符串的sub
等操作,也无法计算长度)
@Native static final byte LATIN1 = 0;
@Native static final byte UTF16 = 1;
public int length() {
// 如果开启压缩,`coder()=0`,不进行位移,返回value数组长度
// 如果开启压缩,`coder()=1`,右移1=除以2,因为中文存储1个字符占用2个byte的空间
return value.length >> coder();
}
byte coder() {
// 如果开启压缩,则`coder=LATIN1=0`,否则`coder=UTF16=1`
return COMPACT_STRINGS ? coder : UTF16;
}
byte
数组如何存储中文?通过源码 java.lang.StringUTF16#toBytes(char[], int, int)
可以看到,1个中文会被存储到byte
数组中的两个元素上 ,即存储一个中文,byte
数组长度为2,存储2个中文,byte
数组长度为4.
StringUTF16
部分源码截取,高八位和低八位分别存储,占用2个byte
数组的空间:
static final int HI_BYTE_SHIFT;
static final int LO_BYTE_SHIFT;
static {
// CPU架构大小端
if (isBigEndian()) {
HI_BYTE_SHIFT = 8;
LO_BYTE_SHIFT = 0;
} else {
HI_BYTE_SHIFT = 0;
LO_BYTE_SHIFT = 8;
}
}
@IntrinsicCandidate
// intrinsic performs no bounds checks
static void putChar(byte[] val, int index, int c) {
assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
index <<= 1;
val[index++] = (byte)(c >> HI_BYTE_SHIFT);
val[index] = (byte)(c >> LO_BYTE_SHIFT);
}
@IntrinsicCandidate
// intrinsic performs no bounds checks
static char getChar(byte[] val, int index) {
assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
index <<= 1;
return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
((val[index] & 0xff) << LO_BYTE_SHIFT));
}
模块化
Java8 和之前的版本中,主要源代码是放在 rt.jar
中的,但是其中很多包是我们平时不会用到的,比如 java.awt
如果全载入到内存中,会造成一定的浪费。
所以在 Java9 开始,将 rt.jar
分成了不同的模块,一个模块下可以包含多个包,模块之间存在依赖关系。其中 java.base
为基础模块(包含java.lang
,java.util
..),不依赖其他模块。
模块与包类似,只不过一个模块下可以包含多个包。
.jar
包中含有.class
文件,配置文件。
.jmod
除了上述文件外,还包含navite library
,legal licenses
等。
两者主要区别是.jmod
主要用在编译器和链接期,并非运行期,对于开发者来说,运行期任然需要使用jar包。
模块化的优点
- 精简JVM运行所需加载的class类,提升加载速度。
- 对包更精细的控制,提高安全。
关键字
- exports: 声明导出包,正常可使用,反射可以使用。
- opens:声明导出包,只有反射可以使用,正常编写代码编译无法通过,报错
Package 'package_name' is declared in module 'develop', which does not export it to module 'test'
- requires:声明依赖包。
示例工程
- 创建项目
- 创建模块
develop
、test
- 在模块
develop
下创建Cat.java
,Apple.java
,创建module-info.java
,声明export develop
package top.imyzt.learing.jdkfuture.dev1;
/**
* @author imyzt
* @date 2023/12/16
* @description Cat
*/
public class Cat {
public void eat() {
System.out.println("吃鱼");
}
}
package top.imyzt.learing.jdkfuture.dev2;
/**
* @author imyzt
* @date 2023/12/16
* @description Apple
*/
public class Apple {
public Apple() {
System.out.println("Apple Constructor");
}
}
module develop {
// 导出包
exports top.imyzt.learing.jdkfuture.dev1;
// 导出包, 只能通过反射访问
opens top.imyzt.learing.jdkfuture.dev2;
}
- 在模块
test
下创建Test.java
,创建module-info.java
,声明requie develop
package main.top.imyzt.learing.jdkfuture.test;
import top.imyzt.learing.jdkfuture.dev1.Cat;
// Package 'top.imyzt.learing.jdkfuture.dev2' is declared in module 'develop', which does not export it to module 'test'
// import top.imyzt.learing.jdkfuture.dev2.Apple;
/**
* @author imyzt
* @date 2023/12/16
* @description 描述信息
*/
public class Test {
public static void main(String[] args) throws Exception {
Cat cat = new Cat();
cat.eat();
Class<?> clazz = Class.forName("top.imyzt.learing.jdkfuture.dev2.Apple");
clazz.getDeclaredConstructor().newInstance();
}
}
module test {
requires develop;
}
项目结构图
源代码
jshell
作用不大,主要对于初学者可以学习语法。
➜ jshell
| 欢迎使用 JShell -- 版本 21.0.1
| 要大致了解该版本, 请键入: /help intro
jshell> System.out.println("hello world");
hello world
jshell> int a = 1;
a ==> 1
jshell> int b = 2;
b ==> 2
jshell> System.out.println(a+b);
3
jshell> /exit
| 再见