记录一下9-19版本中的常见的一些和开发相关的新特性
Jdk9
接口私有方法
/**
* 接口可以定义私有方法了
*/
public interface privateInterface {
private void method(){
System.out.printf("this is private");
}
}
try-with-resource
可以将要关闭的流定义在括号外面了
public class closeIO {
public static void main(String[] args) throws FileNotFoundException {
FileInputStream fis = new FileInputStream("");
FileOutputStream fos = new FileOutputStream("");
// 变量可以定义在外面
try (fis; fos) {
} catch (Exception e) {
}
// 不能使用下划线作为变量名了
// String _ = "";
}
}
@Deprecated注解
注解添加了since(代表从哪个版本开始废弃),forRemoval(代表将来会被废弃),比如下面Object的finalize方法再9被标记为废弃
@Deprecated(since="9", forRemoval=true)
protected void finalize() throws Throwable { }
String实现变更
用byte数组代替char数组来保存数据,当字符集为拉丁文的时候,一个字节就可以代表一个字符,这个时候使用字节数组会节省内存
/**
* 直接数组
*/
@Stable
private final byte[] value;
/**
* 使用拉丁还是UTF16编码(一般中文为2个字节)
* LATIN1
* UTF16
*/
private final byte coder;
@Native static final byte LATIN1 = 0;
@Native static final byte UTF16 = 1;
// 是否开启压缩,默认开启
static final boolean COMPACT_STRINGS;
static {
COMPACT_STRINGS = true;
}
public String(char[] value) {
this(value, 0, value.length, null);
}
String(char[] value, int off, int len, Void sig) {
if (len == 0) {
this.value = "".value;
this.coder = "".coder;
return;
}
// 默认是开启压缩的
if (COMPACT_STRINGS) {
// 生成压缩的字节数组
byte[] val = StringUTF16.compress(value, off, len);
if (val != null) {
this.value = val;
this.coder = LATIN1;
return;
}
}
// 压缩失败,代表是UTF16格式的
this.coder = UTF16;
this.value = StringUTF16.toBytes(value, off, len);
}
// StringUTF16.compress
public static byte[] compress(char[] val, int off, int len) {
byte[] ret = new byte[len];
// 如果长度相同
if (compress(val, off, ret, 0, len) == len) {
return ret;
}
return null;
}
// StringUTF16.compress -> compress
public static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) {
for (int i = 0; i < len; i++) {
char c = src[srcOff];
// 11111111 大于代表不是拉丁格式的,不能进行压缩
if (c > 0xFF) {
len = 0;
break;
}
dst[dstOff] = (byte)c;
srcOff++;
dstOff++;
}
return len;
}
// StringUTF16.toBytes
public static byte[] toBytes(char[] value, int off, int len) {
// 创建一个 len << 1,即2倍大小的数组
byte[] val = newBytesFor(len);
for (int i = 0; i < len; i++) {
putChar(val, i, value[off]);
off++;
}
return val;
}
static void putChar(byte[] val, int index, int c) {
assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
// index当然也要翻倍
index <<= 1;
// 高八位
val[index++] = (byte)(c >> HI_BYTE_SHIFT);
// 低八位
val[index] = (byte)(c >> LO_BYTE_SHIFT);
}
static {
// 高低位是根据大小端来判断的,大端是高字节保存在低地址,小端反过来
// 主要是网络和硬件的大小端不一致的处理
if (isBigEndian()) {
HI_BYTE_SHIFT = 8;
LO_BYTE_SHIFT = 0;
} else {
HI_BYTE_SHIFT = 0;
LO_BYTE_SHIFT = 8;
}
}
模块化
很容易想到Maven或者gradle,不多这两个工具对应的最小粒度是jar;在jdk9中新增了module,之前更多的是package的层级,增加module是为了更好的管理代码,比如在jdk8中的rt.jar其实有很多在web中用不到的东西(awt,applet这些),在jdk9中模块化后在jmods目录下面的java.base.jmod中就找不到了
class是字段和方法的集合,package是class的集合,而module是package的集合。
类比maven,创建一个controller、service的module
package com.module.service;
public class HelloService {
public String hello(){
return "hello";
}
}
package com.module.controller;
import com.module.service.HelloService;
public class HelloController {
private HelloService helloService = new HelloService();
public String hello(){
return helloService.hello();
}
public static void main(String[] args) {
HelloController controller = new HelloController();
System.out.println(controller.hello());
}
}
因为是两个不同的module,HelloController需要引用HelloService,就需要通过类似maven的pom文件表示依赖关系,build后才能使用;在两个module下创建module-info.java来描述引用关系
moduleService将com.module.service
包暴露出去
module moduleService {
exports com.module.service;
}
moduleController描述引用
module moduleController {
requires moduleService;
}
在moduleService模块下,其实还有util包,因为没有exports出去,所以moduleController使用不到
当前案例只是当了解入门,以后有使用的时候再深入看其他的关键字和使用
jshell
简单的代码可以直接用jshell来进行处理
集合工厂方法
集合类可以通过 of 方法来进行初始化创建,以map为例
public static void main(String[] args) {
// 9之前,创建并初始化,方式1
Map<String,Integer> mapInit1 = new HashMap<>(){{
put("ABC",123);
put("DEF",123);
put("GHI",123);
}};
// 方式2
Map<String,Integer> mapInit2 = new HashMap<>();
mapInit2.put("ABC",123);
// 9,of初始化
Map<String, Integer> mapInit3 = Map.of("ABC", 123, "DEF", 123);
mapInit3.put("GHI",123);
}
生成的是AbstractImmutableMap的子类,是不可变更的
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2);
}
Stream
Stream新增了一些方法
public static void main(String[] args) {
// ofNullable类似Optional
Stream
.ofNullable(null)
.forEach(System.out::println);
//
Stream
// iterate 第二个参数增加断言,和for循环类似
.iterate(0, i -> i < 20, i -> i + 1)
// 当i小于17的时候获取
.takeWhile(i -> i < 17)
// 当i小于15的时候丢弃
.dropWhile(i -> i < 15)
// 最后打印结果是15 16
.forEach(System.out::println);
}
Optional
optional中新增的一些方法
public static void main(String[] args) {
// ifPresentOrElse 当元素为空时可以执行else操作了
Optional.ofNullable(null)
.ifPresentOrElse(e -> {
System.out.println("当前元素是:" + e);
}, () -> {
System.out.println("当前元素为空");
});
// or 当元素为空的时候,使用or里面的
String str = null;
Optional.ofNullable(str)
.or(() -> Optional.of("ABC"))
.ifPresent(System.out::println);
}
Jdk10
局部变量推断
类似js这种脚本语言,可以用var来定义变量了;不过局限于局部变量、增强for循环的索引、普通for循环的本地变量;像是方法形参、返回值类型、构造方法形参这种还是不行的
public static void main(String[] args) {
var str = "ABC";
var i = 10;
var l = 10000L;
var c = 'c';
}
Jdk11
编译运行
之前都是需要javac编译,再java运行;现在可以直接java运行
String
展示部分String新的API
public static void main(String[] args) {
char space = '\u2000';
String str1 = "ABD";
str1 = space + str1 + space;
// 无法去掉Unicode的空格
System.out.println(str1.trim());
// 使用 strip 方法
System.out.println(str1.strip());
String emptyStr = " ";
System.out.println(emptyStr.isEmpty());
// 使用 isBlank 来判断空格字符串
System.out.println(emptyStr.isBlank());
String repeatStr = "ABC";
// 重复字符串
System.out.println(repeatStr.repeat(3));
}
lambda表达式中的局部变量推断
public class Lambda {
public static void main(String[] args) {
// 使用var代替
Inter inter = (var a, var b) -> {
return a + b;
};
}
}
public interface Inter {
String method(Integer a, String b);
}
Jdk12
switch增强
12的版本是预览版,在14的时候正式使用
public static void main(String[] args) {
int num = 3;
switch (num) {
case 1, 2, 3 -> System.out.println(1);
case 4, 5, 6 -> System.out.println(2);
default -> System.out.println(3);
}
}
Jdk13
switch 返回值
public static void main(String[] args) {
int num = 3;
int result = switch (num) {
case 1, 2, 3 -> 1;
case 4, 5, 6 -> 2;
default -> 3;
};
}
文本块
public static void main(String[] args) {
String str = """
这是字符串
换行
换行
""";
}
Jdk14
instanceof 赋值
public static void main(String[] args) {
Object obj = 1;
// 当obj为Integer类型的时候,强转赋值到变量 i
if(obj instanceof Integer i){
System.out.println(i);
}
}
空指针提示
还可以通过 -XX:+ShowCodeDetailsInExceptionMessages 的方式来查看
record类型
通常我们会定义一些只有get、set方法的javabean,就可以使用record类型来替换
public record Person(String name, String addr) {
public String info() {
return name() + addr();
}
public static void main(String[] args) {
Person person = new Person("小红","桥洞下");
System.out.println(person.info());
}
}
Jdk15
密封类 sealed class
通过sealed class我们可以指定那些之类可以继承
// 通过sealed指定只能被Father类继承
public sealed class GrandFather permits Father{
}
// Father类还希望能被Son继承的话,也需要设置为sealed
sealed class Father extends GrandFather permits Son{
}
// son没有其他继承关系,需要指定为final
final class Son extends Father{
}
CharSequence
新增了isEmpty方法,判断是否为空
TreeMap
新增如下方法,这些方法其他map应该是早就有了的
- putIfAbsent
- computeIfAbsent
- computeIfPresent
- compute
- merge
环境变量
15之后安装jdk不需要再手动配置环境变量了
Jdk16
date格式
可以通过如下的parttern打印出当前是上午还是下午
System.out.println(DateTimeFormatter.ofPattern("B").format(LocalDateTime.now()));
invocationHandler的default方法增强
通过 InvocationHandler.invokeDefault 可以增强default方法了
public class InvocationHandlerTest {
public static void main(String[] args) {
Hello helloProxy = (Hello)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[]{Hello.class}, ((proxy, method, args1) -> {
if (method.isDefault()) {
// invokeDefault
return InvocationHandler.invokeDefault(proxy, method, args1) + "world";
}
return null;
}));
System.out.println(helloProxy.hello());
}
}
interface Hello {
default String hello() {
return "hello";
}
}
Jdk 17
switch 多态
现在可以用switch来代替 if instanceof的方式来调用方法了
public class SwitchTest {
public static void main(String[] args) {
A t = new C();
switch (t){
case B b -> b.b();
case C c -> c.c();
case null -> System.out.println("null");
default -> System.out.println("default");
}
}
}
class A {
}
class B extends A {
public void b() {
System.out.println("b");
}
}
class C extends A {
public void c() {
System.out.println("c");
}
}
增强型伪随机数发生器
- RandomGenerator
- RandomGeneratorFactory
Jdk18
默认使用UTF-8编码
jwebserver
通过该命令可以启动一个简易的web服务器
被移除的方法
- Object的finalize
- Thread的stop
@snippet注解
可以在文档注解中对代码进行注释
/**
* {@snippet :
* System.out.println("begin") 打印begin
* }
* @return
*/
public String test(){
System.out.println("begin");
return "";
}
Jdk19
虚拟线程
java现在也有虚拟线程了,相较之之前的平台线程,消耗要小得多,也不需要线程池来进行管理,适合IO密集型任务,详细使用后面深入研究
public static void main(String[] args) throws InterruptedException {
Runnable task = ()->{
System.out.println("task run:"+Thread.currentThread().getName());
};
// 方式1
Thread.startVirtualThread(task);
// 方式2
Thread virtual = Thread.ofVirtual().name("virtual").unstarted(task);
virtual.start();
// 方式3
Executors.newVirtualThreadPerTaskExecutor().submit(task);
Thread.sleep(5000);
}