以下样例代码均可在样例代码中查看
As of Java 9, '_' is a keyword, and may not be used as an identifier
String _ = "123"; //编译错误
### 接口新增私有方法
新增私有方法,方便在接口多个实现中使用
public interface PrivateInterfaceMethod {
/**
* jdk1.9新增
/
private static void testPri() {
System.out.println(“接口私有方法”);
}
/*
* jdk1.8新增静态方法和默认方法
*/
public static void test1() {
System.out.println(“静态方法”);
testPri();
}
default void test2() { // 可以被实现类覆写
System.out.println(“默认方法”);
testPri();
}
}
### 钻石操作符(泛型)的升级
/**
* 创建范型类的时候,可以重写其中的方法
*/
Set set = new HashSet() {
@Override
public boolean add(String s) {
return super.add(s + "&");
}
};
set.add("1");
set.add("2");
set.add("3");
set.forEach(System.out::println);
### 支持在try外进行实例化
支持在try外进行实例化,try内对象为final不可再修改; 结束后自动释放
InputStreamReader reader = new InputStreamReader(System.in);
InputStreamReader reader1 = new InputStreamReader(System.in);
/**
* 支持在try外进行实例化,try内对象为final不可再修改; 结束后自动释放
*/
try (reader;reader1) { //支持多个,用;分隔即可
// do something
reader.read();
reader1.read();
} catch (IOException e) {
System.err.println("异常:" + e);
}
### String存储结构发生变化
char[]修改为byte[],String,StringBuffer,StringBuilder
jdk1.8及以前
![jdk8String存储](https://img-blog.csdnimg.cn/ff436b68941549729760a1a81fe37505.png#pic_center)
9以后
![jdk9+存储结构](https://img-blog.csdnimg.cn/62ddc0d524184f99aef90d14c54c9964.png#pic_center)
### 集合调整-快速创建只读集合
List integers = Collections.unmodifiableList(Arrays.asList(1, 2, 3));
// java.lang.UnsupportedOperationException
// integers.add(4);
//9以后可使用这种方式
List integers1 = List.of(1, 2, 3);
// java.lang.UnsupportedOperationException
//integers1.add(4);
Map
### 增强流API
- takeWhile()
- dropWhile()
- of()
- ofNullable()
- iterator增加结束条件
List a = new ArrayList<>();
a.add(1);
a.add(3);
a.add(5);
a.add(7);
a.add(9);
// 直到不满足条件,停止读取
a.stream().takeWhile(b -> b < 5).forEach(System.out::println);
System.out.println("----------");
//直到满足条件,才开始读取
a.stream().dropWhile(b -> b < 5).forEach(System.out::println);
System.out.println("----------");
Stream.ofNullable(null).forEach(System.out::println);
System.out.println("----------");
Stream.of("123", "234", null, null, "345").forEach(System.out::println);
//------------------
Stream.iterate(0, x -> x < 3, x -> x + 1).forEach(System.out::println);
System.out.println(“--------------”);
//等价于
for (int x=0;x<3;x++) {
System.out.println(x);
}
### I/O流新特性
- readAllBytes:读取 InputStream 中的所有剩余字节。
- readNBytes: 从 InputStream 中读取指定数量的字节到数组中。
- transferTo:读取 InputStream 中的全部字节并写入到指定的 OutputStream 中。
### HttpClient重写
HttpClient增加异步调用 sendAsync()
## JVM优化
## 其他功能
使用jshell命令启动jshell,输入表达式即可实现基本功能运算
jshell> int add(int x, int y) {
...> return x + y;
...> }
jshell> add(1, 2)
$2 ==> 3
新增4个SHA-3哈希算法,SHA3-224,SHA3-256,SHA3-384和SHA3-512。另外也增加了通过 java.security.SecureRandom 生成使用 DRBG 算法的强随机数
现在有var声明。它允许开发者在不指定其类型的情况下声明局部变量。变量的类型将从创建的实际对象的类型中推断出来。
public static void main(String[] args) {
var a = "123";
var b = 123;
var c = new VarTest();
// Integer aInt = (Integer)a;
var bInt = (Integer)b;
System.out.println(a.getClass()); //String
System.out.println(bInt.getClass());
System.out.println(c.getClass());
/**
* class java.lang.String
* class java.lang.Integer
* class com.mochi.jdk10.VarTest
*/
}
Optional.orElseThrow(),orElseThrow类中添加了一个新方法。
它是现有方法的同义词,现在是现有方法的首选替代get方法。
System.out.println(Optional.ofNullable(null).orElse("123"));
//也可以使用函数式接口实现
System.out.println(Optional.ofNullable(null).orElseGet(() -> "123"));
//或者抛出异常
System.out.println(Optional.ofNullable(null).orElseThrow());
System.out.println(Optional.ofNullable("123").orElseThrow());
Java10为G1引入多线程并行GC,同时会使用与年轻代回收和混合回收相同的并行工作线程数量,从而减少了Full
GC的发生,以带来更好的性能提升,更大的吞吐量。Java中将采用并行化mark-sweep-compact算法,并使用与年轻代回收和混合回收相同数量的线程。具体并行线程数量可以通过: -XX:ParallelGCThreads
参数来调节,但是这也会影响用于年轻代和混合收集的工作线程数量。
在Java10之前,GC回收的组件散落在代码的各个部分。尽管这些管理对于使用GC计划的JDK开发者来说比较熟悉,但对于新的开发人员来说,对于在哪里查找到特定的GC代码或者实现一个新的垃圾收集器,还是感到繁琐。
为解决此问题,需要整合并清理GC接口,以便更容易实现新的GC,并更好的维护现有的GC。在Java10当中 引入一个干净的 GC 接口,改进不同 GC 源代码的隔离性,多个 GC 之间共享的实现细节代码应该存在于辅助类中。
Java10将使得JVM能够适用于不同类型的存储机制的堆,在可选内存设备上进行堆内存分配。要在这样的备用设备上进行堆分配,可以使用堆分配参数-XX:AllocateHeapAt =
,这个参数将指向文件系统的文件并使用内存映射来达到在备用存储设备上进行堆分配的预期结果
在Java5中就已经引入了
类数据共享机制简称CDS,允许将一组类预处理为共享归档文件,以便在运行时能够进行内存映射以减少Java程序的启动时间,当多个Java虚拟机共享相同的归档文件时,还可以减少动态内存的占用量,同时减少多个虚拟机在同一个物理虚拟机的资源占用。CDS的特性在原来的bootstrap类基础上,扩展了应用类的CDS支持,原理为:
在启动时记录加载类的过程,写入到文本文件中,再次启动时直接读取此启动文本并加载,如果应用环境没有大的变化,启动速度就会得到提升。
在已有的Java版本中,JVM线程只能全部启用或者停止,没法对单独某个线程的操作。
Java10中线程管控引入JVM安全点的概念,将允许在不运行全局JVM安全点的情况下实现线程回调,这种方式使得单独停止某个线程变得可能。
显示的提高了现有的JVM性能开销。
增加的参数为:-XX:ThreadLocalHandshakes (默认为开启)
String a = " 1 a 2 a 3 a 4 ";
//删除首尾空格,不可以删除全角空格
System.out.println(a.trim());
//删除首尾空格,包括全角空格 since 11
System.out.println(a.strip());
//删除首空格,不包括全角空格 since 11
System.out.println(a.stripLeading());
//删除尾部空格,不包括全角空格 since 11
System.out.println(a.stripTrailing());
//lines()方法可以对字符串每一行进行流式处理
"asc\nccc\nwww"
.lines()
.map(str -> str.replaceFirst(str.substring(1, 2), str.substring(1, 2).toUpperCase()))
.forEach(System.out::println);
//repeat方法,重复x次
String repeat = "a".repeat(5);
System.out.println(repeat);
Java 11 对 Java 9 中引入并在 Java 10 中进行了更新的 Http Client API 进行了标准化,在前两个版本中进行孵化的同时,Http Client 几乎被完全重写,并且现在完全支持异步非阻塞。
HttpClient client = HttpClient.newHttpClient();
HttpRequest httpRequest = HttpRequest.newBuilder().uri(URI.create("https://www.baidu.com")).build();
// CompleteableFutures 提供非阻塞请求和响应语义,可以联合使用以触发相应的动作
client.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
GC 是一个可伸缩的、低延迟的垃圾收集器,主要为了满足如下目标进行设计:
Epsilon 垃圾回收器的目标是开发一个控制内存分配,但是不执行任何实际的垃圾回收工作。它提供一个完全消极的 GC 实现,分配有限的内存资源,最大限度的降低内存占用和内存吞吐延迟时间。
-XX:+UseEpsilonGC
public class EpsilonCollection {
//-Xms10m -Xmx10m -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
//添加如上参数后,观察对象回收日志即可
public static void main(String[] args) {
var list = new ArrayList<>();
boolean flag = true;
int count = 0;
while (flag) {
list.add(new Garbage());
if (count % 100 == 0){
System.out.println("---------");
}
if (count++ ==500) {
System.out.println("do clear...");
list.clear();
}
}
}
}
class Garbage {
private String bbb = "a".repeat(1024*1024);
@Override
protected void finalize() throws Throwable {
System.out.println(this + " collecting");
}
}
当一个java文件中没有引用其他类,则不会输出编译class文件,直接执行main方法。若一个类有两个main方法,则执行第一个
//以往版本需要先编译成class文件,再执行
//此功能允许使用 Java 解释器直接执行 Java 源代码。源代码在内存中编译,然后由解释器执行。唯一的约束在于所有相关的类必须定义在同一个 Java 文件中。
public static void main(String[] args) {
System.out.println("Hello World!");
}
-java HelloWorldTest.java //不再编译出class文件,直接执行
-Hello World!
jdk11以前的java应用程序在docker中运行的性能会下降,但现在此问题在容器控制组(cgroups)的帮助下得以解决,使JVM和docker配合得更加默契
有两种使用方式:程序开始前,添加-XX:StartFilghtRecording参数;也可以在程序启动后,通过jcmd命令启动jfr
Java 语言中的飞行记录器类似飞机上的黑盒子,是一种低开销的事件信息收集框架,主要用于对应用程序和 JVM 进行故障检查、分析。飞行记录器记录的主要数据源于应用程序、JVM 和 OS,这些事件信息保存在单独的事件记录文件中,故障发生后,能够从事件记录文件中提取出有用信息对故障进行分析。
/**
* -XX:StartFlightRecording=duration=1s, filename=recording.jfr
*/
@Label("Hello World")
@Description("Helps the programmer getting started")
public static class FlightRecorderTest extends Event {
@Label("Message")
String message;
}
public static void readRecordFile() throws IOException {
final Path path = Paths.get("1.jfr");
final List recordedEvents = RecordingFile.readAllEvents(path);
for (RecordedEvent event : recordedEvents) {
System.out.println(event.getStartTime());
}
}
public static void main(String[] args) throws Exception {
FlightRecorderTest event = new FlightRecorderTest();
for (int i = 0; i < 10; i++) {
event.message = "hello, world!";
event.commit();
System.out.println("commit!"+i);
}
readRecordFile();
}
var flag = "123";
switch (flag) {
case "123" -> System.out.println(1);
case "234", "345" -> System.out.println(2);
case "456" -> System.out.println(3);
default -> System.out.println("No such fruit");
}
var flag1 = "123";
int res = switch (flag1) {
case "123" -> 1;
case "234", "345" -> 2;
case "456" -> 3;
default -> 4;
};
System.out.println(res);
var cnf = NumberFormat.getCompactNumberInstance(Locale.CHINA, NumberFormat.Style.SHORT);
System.out.println(cnf.format(1L << 30));
System.out.println(cnf.format(1000));
System.out.println(cnf.format(1L << 40));
System.out.println(cnf.format(1_9_2000));
System.out.println(cnf.format(192000));
/**
11亿
1,000
1万亿
19万
19万*/
//返回两个Path对应的文件内容中首次字节不匹配发生的行索引,从0开始。如果返回-1,就是指两个文件内容完全一样
try (FileWriter fileWriter1 = new FileWriter("test1.txt");
FileWriter fileWriter2 = new FileWriter("test2.txt")) {
fileWriter1.write("a");
fileWriter1.write("b");
fileWriter1.write("c");
fileWriter1.close();
fileWriter2.write("a");
fileWriter2.write("b");
fileWriter2.write("E");
fileWriter2.close();
System.out.println(Files.mismatch(Path.of("test1.txt"), Path.of("test2.txt")));
} catch (IOException e) {
System.err.println("异常:" + e);
}
//对字符串进行链式转换
String transform = "abc".transform(x->x+"def").transform(String::toUpperCase);
System.out.println(transform);
//在字符串的每一行前面加空格
System.out.println("aaa" + transform.indent(10));
String indent = "12345\n67890";
System.out.println(indent.indent(5));
String sql = """
select a,b,c
from test
where 1=1;
""";
String html = """
Hello World!
""";
System.out.println("sql:".concat(sql));
System.out.println("html:".concat(html));
//unicode字符集扩展
//Bidi,Normailzer,regex
引入 NioSocketImpl 的实现用以替换 SocketImpl 的 PlainSocketImpl 实现,此实现与 NIO(新 I/O)实现共享相同的内部基础结构,并且与现有的缓冲区高速缓存机制集成在一起,因此不需要使用线程堆栈。
//可以看底层实现,对比新旧版本
//Server
public static void main(String[] args) throws Exception {
System.out.println("======Server======");
ServerSocket server = new ServerSocket(8080);
System.out.println("Server port:" + server.getLocalPort());
System.out.println("Wait connect...");
Socket socket = server.accept();
PrintWriter writer = new PrintWriter(socket.getOutputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg = reader.readLine();
System.out.println("Server received :" + msg);
if (msg.equals("123123")) {
writer.println("456456");
} else {
writer.println("789789");
}
writer.flush();
System.out.println("Server close");
System.out.println("================");
}
//Client
public static void main(String[] args) throws IOException {
System.out.println("======Client======");
System.out.println("Connecting...");
Socket socket = new Socket("localhost",8080);
socket.setSoTimeout(1000);
System.out.println("Connect Success!");
PrintWriter writer = new PrintWriter(socket.getOutputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer.println("123123");
writer.flush();
String msg = reader.readLine();
System.out.println("Client received :" + msg);
System.out.println("Client close");
System.out.println("================");
}
通过在64位平台上的默认类列表的帮助下生成CDS归档来改进JDK构建过程,从而有效地消除了运行java -Xshare:dump。
此功能的目标包括:
Java 13 中对 ZGC 的改进,主要体现在下面几点:
-XX:SoftMaxHeapSize
来软限制堆大小与雪弗兰异同:
-XX:+UseShenandoahGC
命令行参数打开。 public static void main(String[] args) {
Object obj = "jkdwhfio";
if (obj instanceof String str) {
System.out.println(str.length());
//在此处可以使用str
} else {
//不可使用str
System.out.println(" not a String!");
}
}
public static void main(String[] args) {
String s = "123";
int result = switch (s) {
case "123":
yield 1;
case "234":
yield 2;
default:
System.out.println("error");
yield 0;
};
System.out.println(result);
s = "2344";
int result1 = switch (s) {
case "123" -> 123;
case "234" -> 234;
default -> {
System.out.println("error");
yield 0;
}
};
System.out.println(result1);
}
在之前抛出NPE异常时只会提示哪一行出错了,但是那一行有多个操作,无法分辨出那个是null。
public static void main(String[] args) {
String a = "abc";
String b = "cde";
String c = null;
System.out.println(a.length() + b.length() + c.length());
/*
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "c" is null
at com.mochi.jdk14.NpeTest.main(NpeTest.java:9)
*/
}
/**
* 对于头部中的每个组件,两个成员:一个public与组件同名和返回类型的访问器方法,以及一个private final与组件类型相同的字段;
* 和标题相同方法签名的规范构造,在new方法调用时将赋予相应的值以初始化对象。
* equals以及hashCode确保两个record值相同的方法,如果它们是相同的类型并且包含相同的组件值。
* toString返回所有record组件的字符串表示形式及其名称的方法。
* 继承java.lang.Record
*/
public record RecordEntity(int id,String name) {
}
public static void main(String[] args) {
RecordEntity recordEntity = new RecordEntity(1,"测试");
RecordEntity recordEntity1 = new RecordEntity(1,"测试");
RecordEntity recordEntity2 = new RecordEntity(2,"测试");
System.out.println(recordEntity.id());
System.out.println(recordEntity.name());
System.out.println(recordEntity.toString());
System.out.println(recordEntity.equals(recordEntity1));
System.out.println(recordEntity.equals(recordEntity2));
}
从 Java14 开始,Java 的设计者们在语法层面为大家带来了崭新的 Memory Access API,极大程度上简化了开发难度,并得以有效的解决了安全性和高效性等 2 个核心问题。
// 获取内存访问var句柄
var handle = MemoryHandles.varHandle(char.class,
ByteOrder.nativeOrder());
// 申请200字节的堆外内存
try (MemorySegment segment = MemorySegment.allocateNative(200)) {
for (int i = 0; i < 25; i++) {
handle.set(segment, i << 2, (char) (i + 1 + 64));
System.out.println(handle.get(segment, i << 2));
}
}
在JDK11中JFR只能将运行的数据导出文件,然后通过JMC可视化,这个过程太繁琐也不能应用于实时的监控,所以在JDK14中推出了JFR事件流。通过这个功能可以实时获取到JVM的运行情况。
新的特定于jdk的文件映射模式,以便可以使用FileChannel API创建引用非易失性内存的MappedByteBuffer实例。在non-vllatile的情况下,如果像RAM一样关闭电源,数据也不会保留。唯一需要更改的API是FileChannel客户端使用的新枚举,用于请求位于NVM支持的文件系统(而不是传统的文件存储系统)上的文件的映射。
因为 CMS 回收算法在进行 GC 回收内存过程中是使用并行方式进行的,如果服务器 CPU 核数不多的情况下,进行 CMS 垃圾回收有可能造成比较高的负载。同时在 CMS 并行标记和并行清理时,应用线程还在继续运行,程序在运行过程中自然会创建新对象、释放不用对象,所以在这个过程中,会有新的不可达内存地址产生,而这部分的不可达内存是出现在标记过程结束之后,本轮 CMS 回收无法在周期内将它们回收掉,只能留在下次垃圾回收周期再清理掉。这样的垃圾就叫做浮动垃圾。由于垃圾收集和用户线程是并发执行的,因此 CMS 回收器不能像其他回收器那样进行内存回收,需要预留一些空间用来保存用户新创建的对象。由于 CMS 回收器在老年代中使用标记-清除的内存回收策略,势必会产生内存碎片,内存当碎片过多时,将会给大对象分配带来麻烦,往往会出现老年代还有空间但不能再保存对象的情况。
当在 Java 14 版本中,通过使用参数: -XX:+UseConcMarkSweepGC,尝试使用 CMS 时,将会收到下面信息:
---jdk14----
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option UseConcMarkSweepGC;
support was removed in
----jdk17----
Unrecognized VM option 'UseConcMarkSweepGC'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
创建用于打包自包含 Java 应用程序的工具。
它基于 JavaFX javapackager 工具创建一个简单的打包工具,主要目标是:
private String str;
@Override
public boolean equals(Object o) {
return o instanceof InstanceofTest t && t.str.equals(str); // 更简洁的equals(),避免显式强转
}
public void testString(Object o) {
if (o instanceof String str) {
System.out.println(str); // 临时变量str
} else {
System.out.println(str); // 实例字段str
}
}
密封类用于限制父类的使用,这是JDK15的预览新特性。密封类的目标包括:允许类或接口的开发者控制哪些代码负责实现,提供了比限制使用父类的访问修饰符声明方式更多的选择,并通过支持对模式的详尽分析而支持模式匹配的未来发展。
在java中,类层次结构通过继承实现代码的重用,父类的方法可以被很多子类继承。但是类层次结构的目的不总是重用代码,有时是为了对域中存在的各种可能性进行建模,比如图形库支持的形状类型等,当以这种方式使用类层次结构时,我们可能需要限制子类集从而简化建模。
//密封类,必须被继承。 没继承会编译错误
sealed class A {
}
// 密封类的子类可以是final,sealed或non-sealed
final class A_1 extends A {
}
non-sealed class A_2 extends A {
}
// 密封类可以指定只能哪些子类可以继承它
sealed class A_3 extends A permits A_3_1 {
}
non-sealed class A_3_1 extends A_3 {
}
//compile error
/*non-sealed class A_3_2 extends A_3 {
}*/
Hidden Classes就是不能直接被其他class的二进制代码使用的class。Hidden Classes主要被一些框架用来生成运行时类,但是这些类不是被用来直接使用的,而是通过反射机制来调用。
比如在JDK8中引入的lambda表达式,JVM并不会在编译的时候将lambda表达式转换成为专门的类,而是在运行时将相应的字节码动态生成相应的类对象。
Class classLoader = ClassLoader.getSystemClassLoader().loadClass("xxx");
// Class hidden = MethodHandles.Lookup::defineHiddenClass;
偏向锁对性能的提升作用有限,禁用。
JDK15中,文本块新增了\和\s,分别表示取消换行和一个空格
String sql = """
select a,b,c from test;
""";
System.out.println(sql);
String sql1= """
select a,b,c from \
test;
""";
System.out.println(sql1);
String sql2 = """
select a,b,c\sfrom test;
""";
System.out.println(sql2);
}
区别:
1)性能目标一致,但还是ZGC更优;
2)Shenandoah只存在于OpenJDK中,而ZGC是Oracle JDK出品。
jcmd的GC.heap_dump命令支持gz选项,以dump出gzip压缩版的heap,压缩等级默认为1,可选范围为[1, 9],1压缩速率最快,9压缩速率最慢,但压缩比最高;
新增格式,可在DateTimeFormatter中查看新增格式
String a = DateTimeFormatter.ofPattern("a").format(LocalDateTime.now());
System.out.println(a);
String b = DateTimeFormatter.ofPattern("B").format(LocalDateTime.now());
System.out.println(b);
String k = DateTimeFormatter.ofPattern("K").format(LocalDateTime.now());
System.out.println(k);
String kk = DateTimeFormatter.ofPattern("kk").format(LocalDateTime.now());
System.out.println(kk);
/**
* 下午
* 晚上
* 10
* 22
*/
将 ZGC 线程栈处理从安全点转移到一个并发阶段,甚至在大堆上也允许在毫秒内暂停 GC 安全点。消除 ZGC 垃圾收集器中最后一个延迟源可以极大地提高应用程序的性能和效率。
此特性可将未使用的 HotSpot 类元数据(即元空间,metaspace)内存更快速地返回到操作系统,从而减少元空间的占用空间。具有大量类加载和卸载活动的应用程序可能会占用大量未使用的空间。新方案将元空间内存按较小的块分配,它将未使用的元空间内存返回给操作系统来提高弹性,从而提高应用程序性能并降低内存占用。