JDK新特性

目录

Java8

1、Interface

2、Lambda

2.1、替代匿名内部类

Runnerable

Comparator

Listener

2.2、集合遍历

3、Stream

3.1、流类型

3.2、常用方法

4、Date-Time

4.1、java.time 主要类

4.2、格式化

4.3、字符串转日期

4.4、日期计算

4.5、获取指定日期

4.6、时区

小结

Java9

1、G1 成为默认垃圾回收器

Java10

1、G1 并行 Full GC

Java11

1、String 增强

2、ZGC(可伸缩低延迟垃圾收集器)

Java15

1、ZGC(转正)

Java17

1、switch 的类型匹配(预览)

Java21

1、分代 ZGC


Java8

1、Interface

interface 的设计初衷是面向抽象,提高扩展性。这也留有一点遗憾,Interface 修改的时候,实现它的类也必须跟着改。

为了解决接口的修改与现有的实现不兼容的问题。新 interface 的方法可以用default 或 static修饰,这样就可以有方法体,实现类也不必重写此方法。

一个 interface 中可以有多个方法被它们修饰,这 2 个修饰符的区别主要也是普通方法和静态方法的区别。

  • default修饰的方法,是普通实例方法,可以用this调用,可以被子类继承、重写。
  • static修饰的方法,使用上和一般类静态方法一样。但它不能被子类继承,只能用Interface调用。
public interface InterfaceNew {
    static void sm() {
        System.out.println("interface提供的方式实现");
    }

    default void def() {
        System.out.println("interface default方法");
    }
    
    //须要实现类重写
    void f();
}

public interface InterfaceNew1 {
    default void def() {
        System.out.println("InterfaceNew1 default方法");
    }
}

如果有一个类既实现了 InterfaceNew 接口又实现了 InterfaceNew1接口,它们都有def(),并且 InterfaceNew 接口和 InterfaceNew1接口没有继承关系的话,这时就必须重写def()。不然的话,编译的时候就会报错。

public class InterfaceNewImpl implements InterfaceNew , InterfaceNew1{
    public static void main(String[] args) {
        InterfaceNewImpl interfaceNew = new InterfaceNewImpl();
        interfaceNew.def();
    }

    @Override
    public void def() {
        InterfaceNew1.super.def();
    }

    @Override
    public void f() {
    }
}

2、Lambda

接下来谈众所周知的 Lambda 表达式。它是推动 Java8 发布的最重要新特性。是继泛型(Generics)和注解(Annotation)以来最大的变化。使用 Lambda 表达式可以使代码变的更加简洁紧凑。让 Java 也能支持简单的函数式编程。(Lambda 表达式是一个匿名函数,Java8 允许把函数作为参数传递进方法中。)

语法格式:

(parameters) -> expression 
或
(parameters) -> { statements; }

2.1、替代匿名内部类

过去给方法传动态参数的唯一方法是使用内部类。

Runnerable
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("The runable now is using!");
    }
}).start();
//用lambda
new Thread(() -> System.out.println("It's a lambda function!")).start();

new Thread(new Runnable() {
    @Override
    public void run() {

    }
}, "name").start();
new Thread(() -> System.out.println(""), "name").start();

new Thread内部只有两个public修饰的以 Runnable 为入参的方法,lambda提供了对此方法的简洁写法。

Comparator
List strings = Arrays.asList(1, 2, 3);

Collections.sort(strings, new Comparator() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1 - o2;
    }
});

//Lambda
Collections.sort(strings, (Integer o1, Integer o2) -> o1 - o2);
//分解开
Comparator comparator = (Integer o1, Integer o2) -> o1 - o2;
Collections.sort(strings, comparator);
Listener
JButton button = new JButton();
button.addItemListener(new ItemListener() {
    @Override
    public void itemStateChanged(ItemEvent e) {
       e.getItem();
    }
});

//lambda
button.addItemListener(e -> e.getItem());

2.2、集合遍历

void lamndaFor() {
    List strings = Arrays.asList("1", "2", "3");
    //传统foreach
    for (String s : strings) {
        System.out.println(s);
    }
    
    //Lambda foreach
    strings.forEach((s) -> System.out.println(s));
    //or
    strings.forEach(System.out::println);
    //map
    Map map = new HashMap<>();
    map.forEach((k,v) -> System.out.println(v));
}

3、Stream

java 新增了 java.util.stream 包,它和之前的流大同小异。之前接触最多的是资源流,比如java.io.FileInputStream,通过流把文件从一个地方输入到另一个地方,它只是内容搬运工,对文件内容不做任何CRUD。Stream依然不存储数据,不同的是它可以检索(Retrieve)和逻辑处理集合数据、包括筛选、排序、统计、计数等。可以想象成是 Sql 语句。它的源数据可以是 Collection、Array 等。由于它的方法参数都是函数式接口类型,所以一般和 Lambda 配合使用。

3.1、流类型

  1. stream 串行流
  2. parallelStream 并行流,可多线程执行

3.2、常用方法

/**
* 返回一个串行流
*/
default Stream stream()

/**
* 返回一个并行流
*/
default Stream parallelStream()

/**
* 返回T的流
*/
public static Stream of(T t)

/**
* 返回其元素是指定值的顺序流。
*/
public static Stream of(T... values) {
    return Arrays.stream(values);
}

/**
* 过滤,返回由与给定predicate匹配的该流的元素组成的流
*/
Stream filter(Predicate predicate);

/**
* 此流的所有元素是否与提供的predicate匹配。
*/
boolean allMatch(Predicate predicate)

/**
* 此流任意元素是否有与提供的predicate匹配。
*/
boolean anyMatch(Predicate predicate);

/**
* 返回一个 Stream的构建器。
*/
public static Builder builder();

/**
* 使用 Collector对此流的元素进行归纳
*/
 R collect(Collector collector);

/**
 * 返回此流中的元素数。
*/
long count();

/**
* 返回由该流的不同元素(根据 Object.equals(Object) )组成的流。
*/
Stream distinct();

/**
 * 遍历
*/
void forEach(Consumer action);

/**
* 用于获取指定数量的流,截短长度不能超过 maxSize 。
*/
Stream limit(long maxSize);

/**
* 用于映射每个元素到对应的结果
*/
 Stream map(Function mapper);

/**
* 根据提供的 Comparator进行排序。
*/
Stream sorted(Comparator comparator);

/**
* 在丢弃流的第一个 n元素后,返回由该流的 n元素组成的流。
*/
Stream skip(long n);

/**
* 返回一个包含此流的元素的数组。
*/
Object[] toArray();

/**
* 使用提供的 generator函数返回一个包含此流的元素的数组,以分配返回的数组,以及分区执行或调整大小可能需要的任何其他数组。
*/
 A[] toArray(IntFunction generator);

/**
* 合并流
*/
public static  Stream concat(Stream a, Stream b)

4、Date-Time

这是对java.util.Date强有力的补充,解决了 Date 类的大部分痛点:

  • 非线程安全
  • 时区处理麻烦
  • 各种格式化、和时间计算繁琐
  • 设计有缺陷,Date 类同时包含日期和时间;还有一个 java.sql.Date,容易混淆。

我们从常用的时间实例来对比 java.util.Date 和新 Date 有什么区别。用java.util.Date的代码该改改了。

4.1、java.time 主要类

java.util.Date 既包含日期又包含时间,而 java.time 把它们进行了分离

LocalDateTime.class //日期+时间 format: yyyy-MM-ddTHH:mm:ss.SSS
LocalDate.class //日期 format: yyyy-MM-dd
LocalTime.class //时间 format: HH:mm:ss

4.2、格式化

// Java8 之前
public void oldFormat(){
    Date now = new Date();
    //format yyyy-MM-dd
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    String date  = sdf.format(now);
    System.out.println(String.format("date format : %s", date));

    //format HH:mm:ss
    SimpleDateFormat sdft = new SimpleDateFormat("HH:mm:ss");
    String time = sdft.format(now);
    System.out.println(String.format("time format : %s", time));

    //format yyyy-MM-dd HH:mm:ss
    SimpleDateFormat sdfdt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String datetime = sdfdt.format(now);
    System.out.println(String.format("dateTime format : %s", datetime));
}

// Java8
public void newFormat(){
    //format yyyy-MM-dd
    LocalDate date = LocalDate.now();
    System.out.println(String.format("date format : %s", date));

    //format HH:mm:ss
    LocalTime time = LocalTime.now().withNano(0);
    System.out.println(String.format("time format : %s", time));

    //format yyyy-MM-dd HH:mm:ss
    LocalDateTime dateTime = LocalDateTime.now();
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String dateTimeStr = dateTime.format(dateTimeFormatter);
    System.out.println(String.format("dateTime format : %s", dateTimeStr));
}

4.3、字符串转日期

// Java8 之前
//已弃用
Date date = new Date("2021-01-26");
//替换为
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date1 = sdf.parse("2021-01-26");

// Java8
LocalDate date = LocalDate.of(2021, 1, 26);
LocalDate.parse("2021-01-26");

LocalTime time = LocalTime.of(12, 12, 22);
LocalTime.parse("12:12:22");

LocalDateTime dateTime = LocalDateTime.of(2021, 1, 26, 12, 12, 22);
LocalDateTime.parse("2021-01-26 12:12:22");

Java 8 之前 转换都需要借助 SimpleDateFormat 类,而Java 8 之后只需要 LocalDate、LocalTime、LocalDateTime的 of 或 parse 方法。

LocalDateTime.parse("2021-01-26 12:12:22") 默认使用的格式化方式是:DateTimeFormatter.ISO_LOCAL_DATE_TIME,这通常与我们常用的格式不同会报错,可自定义格式化方式:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime parse = LocalDateTime.parse("2024-03-04 12:12:21", formatter);

4.4、日期计算

下面仅以一周后日期为例,其他单位(年、月、日、1/2 日、时等等)大同小异。另外,这些单位都在 java.time.temporal.ChronoUnit 枚举中定义。
 

// Java8之前
public void afterDay(){
    //一周后的日期
    SimpleDateFormat formatDate = new SimpleDateFormat("yyyy-MM-dd");
    Calendar ca = Calendar.getInstance();
    ca.add(Calendar.DATE, 7);
    Date d = ca.getTime();
    String after = formatDate.format(d);
    System.out.println("一周后日期:" + after);

    //算两个日期间隔多少天,计算间隔多少年,多少月方法类似
    String dates1 = "2021-12-23";
    String dates2 = "2021-02-26";
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    Date date1 = format.parse(dates1);
    Date date2 = format.parse(dates2);
    int day = (int) ((date1.getTime() - date2.getTime()) / (1000 * 3600 * 24));
    System.out.println(dates1 + "和" + dates2 + "相差" + day + "天");
    //结果:2021-02-26和2021-12-23相差300天
}

// Java之后
public void afterDay(){
     //一周后的日期
     SimpleDateFormat formatDate = new SimpleDateFormat("yyyy-MM-dd");
     Calendar ca = Calendar.getInstance();
     ca.add(Calendar.DATE, 7);
     Date d = ca.getTime();
     String after = formatDate.format(d);
     System.out.println("一周后日期:" + after);

     //算两个日期间隔多少天,计算间隔多少年,多少月方法类似
     String dates1 = "2021-12-23";
     String dates2 = "2021-02-26";
     SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
     Date date1 = format.parse(dates1);
     Date date2 = format.parse(dates2);
     int day = (int) ((date1.getTime() - date2.getTime()) / (1000 * 3600 * 24));
     System.out.println(dates1 + "和" + dates2 + "相差" + day + "天");
     //结果:2021-02-26和2021-12-23相差300天
}

4.5、获取指定日期

除了日期计算繁琐,获取特定一个日期也很麻烦,比如获取本月最后一天,第一天。

// Java8之前
public void getDay() {
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    //获取当前月第一天:
    Calendar c = Calendar.getInstance();
    c.set(Calendar.DAY_OF_MONTH, 1);
    String first = format.format(c.getTime());
    System.out.println("first day:" + first);

    //获取当前月最后一天
    Calendar ca = Calendar.getInstance();
    ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
    String last = format.format(ca.getTime());
    System.out.println("last day:" + last);

    //当年最后一天
    Calendar currCal = Calendar.getInstance();
    Calendar calendar = Calendar.getInstance();
    calendar.clear();
    calendar.set(Calendar.YEAR, currCal.get(Calendar.YEAR));
    calendar.roll(Calendar.DAY_OF_YEAR, -1);
    Date time = calendar.getTime();
    System.out.println("last day:" + format.format(time));
}

// Java8之后
public void getDayNew() {
    LocalDate today = LocalDate.now();
    //获取当前月第一天:
    LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());
    // 取本月最后一天
    LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
    //取下一天:
    LocalDate nextDay = lastDayOfThisMonth.plusDays(1);
    //当年最后一天
    LocalDate lastday = today.with(TemporalAdjusters.lastDayOfYear());
    //2021年最后一个周日,如果用Calendar是不得烦死。
    LocalDate lastMondayOf2021 = LocalDate.parse("2021-12-31").with(TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY));
}

java.time.temporal.TemporalAdjusters 里面还有很多便捷的算法,这里就不带大家看 Api 了,都很简单,看了秒懂。

4.6、时区

时区:正式的时区划分为每隔经度 15° 划分一个时区,全球共 24 个时区,每个时区相差 1 小时。但为了行政上的方便,常将 1 个国家或 1 个省份划在一起,比如我国幅员宽广,大概横跨 5 个时区,实际上只用东八时区的标准时即北京时间为准。

java.util.Date 对象实质上存的是 1970 年 1 月 1 日 0 点( GMT)至 Date 对象所表示时刻所经过的毫秒数。也就是说不管在哪个时区 new Date,它记录的毫秒数都一样,和时区无关。但在使用上应该把它转换成当地时间,这就涉及到了时间的国际化。java.util.Date 本身并不支持国际化,需要借助 TimeZone。

//北京时间:Wed Jan 27 14:05:29 CST 2021
Date date = new Date();

SimpleDateFormat bjSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//北京时区
bjSdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
System.out.println("毫秒数:" + date.getTime() + ", 北京时间:" + bjSdf.format(date));

//东京时区
SimpleDateFormat tokyoSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
tokyoSdf.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo"));  // 设置东京时区
System.out.println("毫秒数:" + date.getTime() + ", 东京时间:" + tokyoSdf.format(date));

//如果直接print会自动转成当前时区的时间
System.out.println(date);
//Wed Jan 27 14:05:29 CST 2021

在新特性中引入了 java.time.ZonedDateTime 来表示带时区的时间。它可以看成是 LocalDateTime + ZoneId。

//当前时区时间
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println("当前时区时间: " + zonedDateTime);

//东京时间
ZoneId zoneId = ZoneId.of(ZoneId.SHORT_IDS.get("JST"));
ZonedDateTime tokyoTime = zonedDateTime.withZoneSameInstant(zoneId);
System.out.println("东京时间: " + tokyoTime);

// ZonedDateTime 转 LocalDateTime
LocalDateTime localDateTime = tokyoTime.toLocalDateTime();
System.out.println("东京时间转当地时间: " + localDateTime);

//LocalDateTime 转 ZonedDateTime
ZonedDateTime localZoned = localDateTime.atZone(ZoneId.systemDefault());
System.out.println("本地时区时间: " + localZoned);

//打印结果
当前时区时间: 2021-01-27T14:43:58.735+08:00[Asia/Shanghai]
东京时间: 2021-01-27T15:43:58.735+09:00[Asia/Tokyo]
东京时间转当地时间: 2021-01-27T15:43:58.735
当地时区时间: 2021-01-27T15:53:35.618+08:00[Asia/Shanghai]

小结

通过上面比较新老 Date 的不同,当然只列出部分功能上的区别,更多功能还得自己去挖掘。总之 date-time-api 给日期操作带来了福利。在日常工作中遇到 date 类型的操作,第一考虑的是 date-time-api,实在解决不了再考虑老的 Date。

Java9

1、G1 成为默认垃圾回收器

在 Java 8 的时候,默认垃圾回收器是 Parallel Scavenge(新生代)+Parallel Old(老年代)。到了 Java 9, CMS 垃圾回收器被废弃了,G1(Garbage-First Garbage Collector) 成为了默认垃圾回收器。G1 还是在 Java 7 中被引入的,经过两个版本优异的表现成为成为默认垃圾回收器。

Java10

1、G1 并行 Full GC

从 Java9 开始 G1 就成为了默认的垃圾回收器,G1 是以一种低延时的垃圾回收器来设计的,旨在避免进行 Full GC,但是 Java9 的 G1 的 FullGC 依然是使用单线程去完成标记清除算法,这可能会导致垃圾回收期在无法回收内存的时候触发 Full GC。为了最大限度地减少 Full GC 造成的应用停顿的影响,从 Java10 开始,G1 的 FullGC 改为并行的标记清除算法,同时会使用与年轻代回收和混合回收相同的并行工作线程数量,从而减少了 Full GC 的发生,以带来更好的性能提升、更大的吞吐量。

Java11

1、String 增强

Java 11 增加了一系列的字符串处理方法:

//判断字符串是否为空
" ".isBlank();		//true
//去除字符串首尾空格
" Java ".strip();	// "Java"
//去除字符串首部空格
" Java ".stripLeading();   // "Java "
//去除字符串尾部空格
" Java ".stripTrailing();  // " Java"
//重复字符串多少次
"Java".repeat(3);          // "JavaJavaJava"
//返回由行终止符分隔的字符串集合。
"A\nB\nC".lines().count();    // 3
"A\nB\nC".lines().collect(Collectors.toList());

2、ZGC(可伸缩低延迟垃圾收集器)

ZGC 即 Z Garbage Collector,是一个可伸缩的、低延迟的垃圾收集器。

ZGC 主要为了满足如下目标进行设计:

  • GC 停顿时间不超过 10ms
  • 既能处理几百 MB 的小堆,也能处理几个 TB 的大堆
  • 应用吞吐能力不会下降超过 15%(与 G1 回收算法相比)
  • 方便在此基础上引入新的 GC 特性和利用 colored 针以及 Load barriers 优化奠定基础
  • 当前只支持 Linux/x64 位平台

ZGC 目前 处在实验阶段,只支持 Linux/x64 平台。

与 CMS 中的 ParNew 和 G1 类似,ZGC 也采用标记-复制算法,不过 ZGC 对该算法做了重大改进。

在 ZGC 中出现 Stop The World 的情况会更少!

详情可以看:《新一代垃圾回收器 ZGC 的探索与实践》open in new window

Java15

1、ZGC(转正)

Java11 的时候 ,ZGC 还在试验阶段。

当时,ZGC 的出现让众多 Java 开发者看到了垃圾回收器的另外一种可能,因此备受关注。

经过多个版本的迭代,不断的完善和修复问题,ZGC 在 Java 15 已经可以正式使用了!

不过,默认的垃圾回收器依然是 G1。你可以通过下面的参数启动 ZGC:

java -XX:+UseZGC className

Java17

Java 17 在 2021 年 9 月 14 日正式发布,是一个长期支持(LTS)版本。下面这张图是 Oracle 官方给出的 Oracle JDK 支持的时间线。可以看得到,Java17 最多可以支持到 2029 年 9 月份。

JDK新特性_第1张图片

Java 17 将是继 Java 8 以来最重要的长期支持(LTS)版本,是 Java 社区八年努力的成果。Spring 6.x 和 Spring Boot 3.x 最低支持的就是 Java 17。

1、switch 的类型匹配(预览)

正如 instanceof 一样, switch 也紧跟着增加了类型匹配自动转换功能。

instanceof 代码示例:

// Old code
if (o instanceof String) {
    String s = (String)o;
    ... use s ...
}

// New code
if (o instanceof String s) {
    ... use s ...
}

switch 代码示例:

// Old code
static String formatter(Object o) {
    String formatted = "unknown";
    if (o instanceof Integer i) {
        formatted = String.format("int %d", i);
    } else if (o instanceof Long l) {
        formatted = String.format("long %d", l);
    } else if (o instanceof Double d) {
        formatted = String.format("double %f", d);
    } else if (o instanceof String s) {
        formatted = String.format("String %s", s);
    }
    return formatted;
}

// New code
static String formatterPatternSwitch(Object o) {
    return switch (o) {
        case Integer i -> String.format("int %d", i);
        case Long l    -> String.format("long %d", l);
        case Double d  -> String.format("double %f", d);
        case String s  -> String.format("String %s", s);
        default        -> o.toString();
    };
}

对于 null 值的判断也进行了优化。

// Old code
static void testFooBar(String s) {
    if (s == null) {
        System.out.println("oops!");
        return;
    }
    switch (s) {
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    }
}

// New code
static void testFooBar(String s) {
    switch (s) {
        case null         -> System.out.println("Oops");
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    }
}

Java21

1、分代 ZGC

JDK21 中对 ZGC 进行了功能扩展,增加了分代 GC 功能。不过,默认是关闭的,需要通过配置打开:

// 启用分代ZGC
java -XX:+UseZGC -XX:+ZGenerational ...

在未来的版本中,官方会把 ZGenerational 设为默认值,即默认打开 ZGC 的分代 GC。在更晚的版本中,非分代 ZGC 就被移除。

In a future release we intend to make Generational ZGC the default, at which point -XX:-ZGenerational will select non-generational ZGC. In an even later release we intend to remove non-generational ZGC, at which point the ZGenerational option will become obsolete.

在将来的版本中,我们打算将 Generational ZGC 作为默认选项,此时-XX:-ZGenerational 将选择非分代 ZGC。在更晚的版本中,我们打算移除非分代 ZGC,此时 ZGenerational 选项将变得过时。分代 ZGC 可以显著减少垃圾回收过程中的停顿时间,并提高应用程序的响应性能。这对于大型 Java 应用程序和高并发场景下的性能优化非常有价值。

你可能感兴趣的:(Java,Web,Service,java,开发语言)