你好,我是看山。
本文收录在 《从小工到专家的 Java 进阶之旅》 系列专栏中。
概述
从 2017 年开始,Java 版本更新遵循每六个月发布一次的节奏,LTS版本则每两年发布一次,以快速验证新特性,推动 Java 的发展。
有意思的特性
可以从 《从小工到专家的 Java 进阶之旅》 系列专栏中查看各个版本的特性。
大家都很熟悉的特性
Java8中的Lambda表达式、Stream流、Optional是大版本特性,Java8是2014年发布,已有十年历史了。大家应该分厂熟悉了,这里不在赘述。推荐两篇文章:
补全技能树
Record 类型(Java 16)
Java新增了一个关键字record
,它是定义不可变数据类型封装类的关键字,主要用在特定领域类上。
我们都知道,在Java开发中,我们需要定义POJO作为数据存储对象,根据规范,POJO中除了属性是个性化的,其他的比如getter
、setter
、equals
、hashCode
、toString
都是模板化的写法,所以为了简便,很多类似Lombok的组件提供Java类编译时增强,通过在类上定义@Data
注解自动添加这些模板化方法。在Java14中,我们可以直接使用record
解决这个问题。
比如,我们定义一个Person
类:
public record Person(String name, String address) {
}
我们转换为之前的定义会是一坨下面这种代码:
public final class PersonBefore {
private final String name;
private final String address;
public PersonBefore(String name, String address) {
this.name = name;
this.address = address;
}
public String name() {
return name;
}
public String address() {
return address;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PersonBefore14 that = (PersonBefore14) o;
return Objects.equals(name, that.name) && Objects.equals(address, that.address);
}
@Override
public int hashCode() {
return Objects.hash(name, address);
}
@Override
public String toString() {
return "PersonBefore{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
我们可以发现Record类有如下特征:
- 一个构造方法
- getter方法名与属性名相同
- 有
equals()
、hashCode()
方法 - 有
toString()
方法 - 类对象和属性被
final
修饰,所以构造函数是包含所有属性的,而且没有setter方法
在Class
类中也新增了对应的处理方法:
getRecordComponents()
:返回一组java.lang.reflect.RecordComponent
对象组成的数组,该数组的元素与Record
类中的组件相对应,其顺序与在记录声明中出现的顺序相同,可以从该数组中的每个RecordComponent
中提取到组件信息,包括其名称、类型、泛型类型、注释及其访问方法。isRecord()
:返回所在类是否是 Record 类型,如果是,则返回 true。
看起来,Record类和Enum很像,都是一定的模板类,通过语法糖定义,在Java编译过程中,将其编译成特定的格式,功能很好,但如果没有习惯使用,可能会觉得限制太多。
密封类和接口(Java 17)
目前,Java 没有提供对继承的细粒度控制,只有 public、protected、private、包内控制四种非常粗粒度的控制方式。
为此,密封类的目标是允许单个类声明哪些类型可以用作其子类型。这也适用于接口,并确定哪些类型可以实现它们。该功能特性新增了sealed
和non-sealed
修饰符和permits
关键字。
我们可以做如下定义:
public sealed class Person permits Student, Worker, Teacher {}
public sealed class Student extends Person
permits Pupil, JuniorSchoolStudent, HighSchoolStudent, CollegeStudent, GraduateStudent {}
public final class Pupil extends Student {}
public non-sealed class Worker extends Person {}
public class OtherClass extends Worker {}
public final class Teacher extends Person {}
我们可以先定义一个sealed
修饰的类Person
,使用permits
指定被继承的子类,这些子类必须是使用final
或sealed
或non-sealed
修饰的类。其中Student
是使用sealed
修饰,所以也需要使用permits
指定被继承的子类。Worker
类使用non-sealed
修饰,成为普通类,其他类都可以继承它。Teacher
使用final
修饰,不可再被继承。
从类图上看没有太多区别:
但是从功能特性上,起到了很好的约束作用,我们可以放心大胆的定义可以公开使用,但又不想被非特定类继承的类了。
全新的 HTTP 客户端(Java 11)
老版 HTTP 客户端存在很多问题,大家开发的时候基本上都是使用第三方 HTTP 库,比如 Apache HttpClient、Netty、Jetty 等。
新版 HTTP 客户端的目标很多,毕竟这么多珠玉在前,如果还是做成一坨,指定是要被笑死的。所以新版 HTTP 客户端列出了 16 个目标,包括简单易用、打印关键信息、WebSocket、HTTP/2、HTTPS/TLS、良好的性能、非阻塞 API 等等。
@Test
void testHttpClient() throws IOException, InterruptedException {
final HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(20))
.build();
final HttpRequest httpRequest = HttpRequest.newBuilder()
.GET()
.uri(URI.create("https://www.howardliu.cn/robots.txt"))
.build();
final HttpResponse<String> httpResponse = httpClient.send(httpRequest, BodyHandlers.ofString());
final String responseBody = httpResponse.body();
assertTrue(responseBody.contains("Allow"));
}
有序集合(Java 21)
有序集合是Java21版本中引入的一个新特性,旨在为Java集合框架添加对有序集合的支持。
有序集合是一种具有定义好的元素访问顺序的集合类型,它允许以一致的方式访问和处理集合中的元素,无论是从第一个元素到最后一个元素,还是从最后一个元素到第一个元素。
在 Java 中,集合类库非常重要且使用频率非常高,但是缺乏一种能够表示具有定义好的元素访问顺序的集合类型。
例如,List
和Deque
都定义了元素的访问顺序,但它们的共同父接口Collection
却没有。同样,Set不定义元素的访问顺序,其子类型如HashSet
也没有定义,但子类型如SortedSet
和LinkedHashSet
则有定义。因此,支持访问顺序的功能散布在整个类型层次结构中,使得在API中表达某些有用的概念变得困难。Collection
太通用,将此类约束留给文档规范,可能导致难以调试的错误。
而且,虽然某些集合有顺序操作方法,但是却不尽相同,比如
First element | Last element | |
---|---|---|
List | list.get(0) | list.get(list.size() - 1) |
Deque | deque.getFirst() | deque.getLast() |
SortedSet | sortedSet.first() | sortedSet.last() |
LinkedHashSet | linkedHashSet.iterator().next() | 缺失 |
提供了有序集合SequencedCollection
、SequencedSet
、SequencedMap
:
interface SequencedCollection<E> extends Collection<E> {
// new method
SequencedCollection<E> reversed();
// methods promoted from Deque
void addFirst(E);
void addLast(E);
E getFirst();
E getLast();
E removeFirst();
E removeLast();
}
interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {
SequencedSet<E> reversed(); // covariant override
}
interface SequencedMap<K,V> extends Map<K,V> {
// new methods
SequencedMap<K,V> reversed();
SequencedSet<K> sequencedKeySet();
SequencedCollection<V> sequencedValues();
SequencedSet<Entry<K,V>> sequencedEntrySet();
V putFirst(K, V);
V putLast(K, V);
// methods promoted from NavigableMap
Entry<K, V> firstEntry();
Entry<K, V> lastEntry();
Entry<K, V> pollFirstEntry();
Entry<K, V> pollLastEntry();
}
SequencedCollection
的reversed()
方法提供了一个原始集合的反向视图。任何对原始集合的修改都会在视图中可见。如果允许,视图中的修改会写回到原始集合。
我们看一个例子,假设我们有一个LinkedHashSet,现在我们想要获取它的反向视图并以反向顺序遍历它:
LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>(Arrays.asList(3, 2, 1));
// 获取反向视图
SequencedCollection<Integer> reversed = linkedHashSet.reversed();
// 反向遍历
System.out.println("原始数据:" + linkedHashSet);
System.out.println("反转数据:" + reversed);
// 运行结果:
// 原始数据:[3, 2, 1]
// 反转数据:[1, 2, 3]
这些方法都是便捷方法,内部数据结构没有变化,其实本质也是原来的用法。比如ArrayList
中的getFirst
和getLast
方法:
/**
* {@inheritDoc}
*
* @throws NoSuchElementException {@inheritDoc}
* @since 21
*/
public E getFirst() {
if (size == 0) {
throw new NoSuchElementException();
} else {
return elementData(0);
}
}
/**
* {@inheritDoc}
*
* @throws NoSuchElementException {@inheritDoc}
* @since 21
*/
public E getLast() {
int last = size - 1;
if (last < 0) {
throw new NoSuchElementException();
} else {
return elementData(last);
}
}
文末总结
想要了解各版本的详细特性,可以从从小工到专家的 Java 进阶之旅 系列专栏中查看。
青山不改,绿水长流,我们下次见。
推荐阅读
- 从小工到专家的 Java 进阶之旅
- 一文掌握 Java8 Stream 中 Collectors 的 24 个操作
- 一文掌握 Java8 的 Optional 的 6 种操作
- 使用 Lambda 表达式实现超强的排序功能
- Java8 的时间库(1):介绍 Java8 中的时间类及常用 API
- Java8 的时间库(2):Date 与 LocalDate 或 LocalDateTime 互相转换
- Java8 的时间库(3):开始使用 Java8 中的时间类
- Java8 的时间库(4):检查日期字符串是否合法
- Java8 的新特性
- Java9 的新特性
- Java10 的新特性
- Java11 中基于嵌套关系的访问控制优化
- Java11 的新特性
- Java12 的新特性
- Java13 的新特性
- Java14 的新特性
- Java15 的新特性
- Java16 的新特性
- Java17 的新特性
- Java18 的新特性
- Java19 的新特性
- Java20 的新特性
- Java21 的新特性
- Java22 的新特性
- Java23 的新特性
- Java24 的新特性
你好,我是看山。游于码界,戏享人生。如果文章对您有帮助,请点赞、收藏、关注。我还整理了一些精品学习资料,关注公众号「看山的小屋」,回复“资料”即可获得。
个人主页:https://www.howardliu.cn
个人博文:从Java8到Java23值得期待的x个特性(1):有意思的特性
CSDN 主页:https://kanshan.blog.csdn.net/
CSDN 博文:从Java8到Java23值得期待的x个特性(1):有意思的特性