jdk17继jdk8后免费商用,持续维护的版本,确实已经被一批人选择,笔者主要从jdk8直接跳jdk17的注意实现
解析当前项目中需要升级的依赖
jdeps --jdk-internals --multi-release 17 --class-path . 包名.jar
3个引号 “”“文本”“”-自带转义符,换行输出更方便
@Test
void testStr(){
String txtOld = "{\n" +
"\"name\":\"网站\",\n" +
"\"num\":3,\n" +
"\"sites\":[ \"Google\", \"FileFox\", \"Edge\" ]\n" +
"}";
System.out.println(txtOld);
String txtNew = """
{
"name":"网站",
"num":3,
"sites":[ "Google", "Runoob", "Taobao" ]
}
""";
System.out.println(txtNew);
}
支持lamda表达式
@Test
void testSwitch(){
Week day = Week.FRIDAY;
switch (day){
case MONDAY,TUESDAY,WEDNESDAY,THURSDAY -> System.out.println("red");
case FRIDAY -> System.out.println("yellow");
case SATURDAY,SUNDAY -> System.out.println("green");
default -> {
throw new IllegalStateException("what day is today?"+day);
}
}
}
简化构造函数设置值
/**
* 类似于
* @Data
* public class Person{
* private String name;
* private Integer age;
* }
*/
public record Person(String name ,Integer age) {
}
有个大病写来给default调用
interface Jdk9Interface{
default String defaultTest() {
privateTest();
return "default";
}
private String privateTest() {
System.out.println("私有方法");
return "private";
}
}
jdk9可以传一个变量就可以关闭流
public static void jdk9Try(){
// jdk8:
try(InputStreamReader reader =new InputStreamReader(System.in)){
reader.read();
}catch (IOException e){
e.printStackTrace();
}
// jdk9以后:
InputStreamReader reader =new InputStreamReader(System.in);
try(reader){
reader.read();
}catch (IOException e){
e.printStackTrace();
}
}
这种写法估计会被规范不让用
public static void jdk10Var() {
var number = 10;
var str = "aabbcc";
var list = new ArrayList<>(1);
var map = new HashMap<>(1);
var set = new HashSet<>(1);
}
在java.util.List/ Set/Map
新增加了一个静态方法copyOf,这些方法按照其迭代顺序返回一个不可修改的列表、集合或映射包含了给定的元素的集合。但是如果将返回后的集合继续修改,那么报异常。
LinkedList<Object> linkedList = new LinkedList<>();
List<Object> copyLinkedList = List.copyOf(linkedList);
在集合框架中,Java 9 增加 了 List.of()、Set.of()、Map.of() 和 Map.ofEntries() 等工厂方法来创建不可变集合
@Test
public void unmodifiableCollectionTest() {
List<Integer> integers = List.of(1, 2, 3);
Set<String> strings = Set.of("a", "b", "c");
Map<String, Integer> stringIntegerMap = Map.of("a", 1, "b", 2, "c", 3);
}
Stream 中增加了新的方法 ofNullable、dropWhile、takeWhile 和 iterate;Collectors 中增加了新的方法 filtering 和 flatMapping。
@Test
public void streamTest() {
List<Integer> list = Arrays.asList(1, 2, 4, 5, 3, 2, 8);
//输出1,2,4,碰到5不成立停止
list.stream().takeWhile(x -> x < 5).forEach(System.out::println);
//丢弃1,2,碰到3不成立停止
List<Integer> collect = Stream.of(1, 2, 3, 4, 5).dropWhile(i -> i%3!=0).collect(Collectors.toList());
System.out.println(collect);
//允许值为空
Stream<Object> stream = Stream.ofNullable(null);
//Optional 转 stream
long count = Stream.of(
Optional.of(1),
Optional.empty(),
Optional.of(2)).flatMap(Optional::stream).count();
Assert.assertEquals(2, count);
//空值 throw
Optional<Integer> optionalInteger = Optional.of(11);
optionalInteger.empty().orElseThrow();
}
Stream 还提供一个 Predicate (判断条件)来指定什么时候结束迭代。
@Test
public void iterateTest() {
Stream.iterate(1, i -> ++i).limit(5).forEach(System.out::println);
//可以直接在 iterate 内部判断
Stream.iterate(1, i -> i <= 5, i -> ++i).forEach(System.out::println);
}
更加精细控制继承
一个类没有被 final 关键字修饰,那么其他类就可以继承该类
Java15(15、16预览,17正式) 开始,可以使用 sealed 修饰一个类或接口,并通过 permits 指定哪几个类可以从该类继承,继承的子类必须使用 final 或者 non-sealed 修饰。
//接口也可以 sealed interface Animal permits Cat, Dog {
abstract sealed class Animal permits Cat, Dog {
void eat() {
System.out.println("have a meal.");
}
abstract void speak();
}
//需要明确用 final 修饰
final class Cat extends Animal {
@Override
void speak() {
System.out.println("miao");
}
}
//如果想被继承的话用 non-sealed 修饰
non-sealed class Dog extends Animal {
@Override
void speak() {
System.out.println("wang");
}
}
class Husky extends Dog {
@Override
void speak() {
System.out.println("Stupid humans!");
}
}
Java 14(14、15预览,16正式) 中,对 instanceof 模式匹配做了改进,允许程序中的逻辑判断从对象中有条件地提取组件,以编写更简洁和安全的表达式。
public class InstanceofExample {
@Test
public void instanceofTest() {
Object obj = "string value";
//传统写法
if (obj instanceof String) {
String str = (String) obj;
Assert.assertEquals(str, obj);
}
//新写法
if (obj instanceof String s) {
Assert.assertEquals(s, obj);
}
//还可以做其他操作
if (obj instanceof String s && s.contains("val")) {
Assert.assertEquals(s, obj);
}
}
}
飞行记录器之前是商业版 JDK 的一项分析工具,低开销的事件信息收集框架,主要用于对应用程序和 JVM 进行故障检查、分析
-XX:StartFlightRecording
也可以使用 bin/jcmd 工具启动和配置飞行记录器
$ jcmd JFR.start
$ jcmd JFR.dump filename=recording.jfr
$ jcmd JFR.stop
JFR 使用示例
public class FlightRecorderTest extends Event {
@Label("Hello World")
@Description("Helps the programmer getting started")
static class HelloWorld extends Event {
@Label("Message")
String message;
}
public static void main(String[] args) {
HelloWorld event = new HelloWorld();
event.message = "hello, world!";
event.commit();
}
}
在运行时加上如下参数:
java -XX:StartFlightRecording=duration=1s, filename=recording.jfr
飞行记录器分析示例
public void readRecordFile() throws IOException {
final Path path = Paths.get("D:\\ java \\recording.jfr");
final List<RecordedEvent> recordedEvents = RecordingFile.readAllEvents(path);
for (RecordedEvent event : recordedEvents) {
System.out.println(event.getStartTime() + "," + event.getValue("message"));
}
}
zgc成为了默认GC
G1的目标是在可控的停顿时间内完成垃圾回收,所以进行了分区设计,停顿时间主要来自垃圾回收(Youth-GC)阶段中的复制算法,在复制算法中,需要把对象转移到新的空间中,并且更新其他对象到这个对象的引用。实际中对象的转移涉及内存的分配和对象成员变量的复制,而对象成员变量的复制是非常耗时的。且都是在STW中并行执行的,而ZGC就是把对象的转移也并发执行,从而满足停顿时间在10ms以下
详细介绍
关于JVM的参数变更、或JDK的一些变更
在 Java 领域,有广为人知的日志框架,slf4j、log4j 等,这些框架提供了统一的编程接口,让用户可以通过简单的配置实现日志输出的个性化配置,比如日志 tag、级别(info、debug 等)、上下文(线程 id、行号、时间等),在 JVM 内部之前一直缺乏这样的规范,于是出来了 Unified Logging,实现了日志格式的大一统,这就是我们接下来要介绍的重点 Unified Logging。
-Xloggc:/tmp/gc.log -升级> -Xlog:/temp/jvm.log
-XX:+PrintGCDetails -升级>-Xlog:all
我们接触最多的是 gc 的日志,在 java8 中,我们配置 gc 日志的参数是 -Xloggc:/tmp/gc.log。在 JVM 中除了 GC,还有大量的其它相关的日志,比如线程、os 等,在新的 Unified Logging 日志中,日志输出的方式变更为了 java -Xlog:xxx,GC 不再特殊只是做为日志的一种存在形式。
java -Xlog -version
可以看到日志输出里,不仅有 GC 相关的日志,还有 os 线程相关的信息。事实上 java 的日志的生产者有非常多部分,比如 thread、class load、unload、safepoint、cds 等。
推荐配置
-Xlog:
// selections
codecache+sweep*=trace,
class+unload, // TraceClassUnloading
class+load, // TraceClassLoading
os+thread,
safepoint, // TraceSafepoint
gc*, // PrintGCDetails
gc+stringdedup=debug, // PrintStringDeduplicationStatistics
gc+ergo*=trace,
gc+age=trace, // PrintTenuringDistribution
gc+phases=trace,
gc+humongous=trace,
jit+compilation=debug
// output
:file=/path_to_logs/app.log
// decorators
:level,tags,time,uptime,pid
// output-options
:filesize=104857600,filecount=5
G1作为JDK17的默认垃圾收集器,也有一些不同于JDK8的垃圾收集器配置
不要配置新生代的大小
这个在《JVM G1 源码分析和调优》一书里有详细的介绍,有两个主要的原因:
G1对内存的管理是不连续的,重新分配一个分区代价很低 G1 的需要根据目标停顿时间动态调整搜集的分区的个数,如果不能调整新生代的大小,那么
G1 可能不能满足停顿时间的要求 诸如 -Xmn, -XX:NewSize, -XX:MaxNewSize,
-XX:SurvivorRatio 都不要在 G1 中出现,只需要控制最大、最小堆和目标暂停时间即可
调整 -XX:InitiatingHeapOccupancyPercent 到合适的值