你好,我是看山。
本文收录在 《从小工到专家的 Java 进阶之旅》 系列专栏中。
概述
从 2017 年开始,Java 版本更新遵循每六个月发布一次的节奏,LTS版本则每两年发布一次,以快速验证新特性,推动 Java 的发展。
一些小特性
增强 String
String中新增的方法:repeat、strip、stripLeading、stripTrailing、isBlank、lines、indent 和 transform。
这些方法还是挺有用的,以前我们可能需要借助第三方类库(比如 Apache 出品的 commons-lang)中的工具类,现在可以直接使用嫡亲方法了。
repeat
repeat是实例方法,顾名思义,这个方法是返回给定字符串的重复值的,参数是int类型,传参的时候需要注意:
- 如果重复次数小于 0 会抛出IllegalArgumentException异常;
- 如果重复次数为 0 或者字符串本身是空字符串,将返回空字符串;
- 如果重复次数为 1 直接返回本身;
- 如果字符串重复指定次数后,长度超过Integer.MAX_VALUE
,会抛出
OutOfMemoryError`错误。
用法很简单:
@Test
void testRepeat() {
String output = "foo ".repeat(2) + "bar";
assertEquals("foo foo bar", output);
}
小而美的一个工具方法。
strip、stripLeading、stripTrailing
strip
方法算是trim
方法的增强版,trim
方法可以删除字符串两侧的空白字符(空格、tab 键、换行符),但是对于Unicode
的空白字符无能为力,strip
补足这一短板。
用起来是这样的:
@Test
void testTrip() {
final String output = "
hello \u2005".strip();
assertEquals("hello", output);
final String trimOutput = "
hello \u2005".trim();
assertEquals("hello \u2005", trimOutput);
}
对比一下可以看到,trim
方法的清理功能稍弱。
stripLeading
和stripTrailing
与strip
类似,区别是一个清理头,一个清理尾。用法如下:
@Test
void testTripLeading() {
final String output = "
hello \u2005".stripLeading();
assertEquals("hello \u2005", output);
}
@Test
void testTripTrailing() {
final String output = "
hello \u2005".stripTrailing();
assertEquals("
hello", output);
}
isBlank
这个方法是用于判断字符串是否都是空白字符,除了空格、tab 键、换行符,也包括Unicode
的空白字符。
用法很简单:
@Test
void testIsBlank() {
assertTrue("
\u2005".isBlank());
}
lines
最后这个方法是将字符串转化为字符串Stream
类型,字符串分隔依据是换行符:\n
、\r
、\r\n
,用法如下:
@Test
void testLines() {
final String multiline = "This is
a multiline
string.";
final String output = multiline.lines()
.filter(Predicate.not(String::isBlank))
.collect(Collectors.joining(" "));
assertEquals("This is a multiline string.", output);
}
indent
indent
方法是对字符串每行(使用\r
或\n
分隔)数据缩进指定空白字符,参数是 int 类型。
如果参数大于 0,就缩进指定数量的空格;如果参数小于 0,就将左侧的空字符删除指定数量,即右移。
我们看下源码:
public String indent(int n) {
if (isEmpty()) {
return "";
}
Stream<String> stream = lines();
if (n > 0) {
final String spaces = " ".repeat(n);
stream = stream.map(s -> spaces + s);
} else if (n == Integer.MIN_VALUE) {
stream = stream.map(s -> s.stripLeading());
} else if (n < 0) {
stream = stream.map(s -> s.substring(Math.min(-n, s.indexOfNonWhitespace())));
}
return stream.collect(Collectors.joining("
", "", "
"));
}
indent
最后会将多行数据通过Collectors.joining("\n", "", "\n")
方法拼接,结果会有两点需要注意:
\r
会被替换成\n
;- 如果原字符串是多行数据,最后一行的结尾没有
\n
,最后会补上一个\n
,即多了一个空行。
我们看下测试代码:
@Test
void testIndent() {
final String text = " 你好,我是看山。
\u0020\u2005Java12 的 新特性。
欢迎三连+关注哟";
assertEquals(" 你好,我是看山。
\u0020\u2005Java12 的 新特性。
欢迎三连+关注哟、n", text.indent(4));
assertEquals(" 你好,我是看山。
\u2005Java12 的 新特性。
欢迎三连+关注哟、n", text.indent(-2));
final String text2 = "山水有相逢";
assertEquals("山水有相逢", text2);
}
transform
我们再来看看transform
方法,源码一目了然:
public <R> R transform(Function<? super String, ? extends R> f) {
return f.apply(this);
}
通过传入的Function
对当前字符串进行转换,转换结果由Function
决定。比如,我们要对字符串反转:
@Test
void testTransform() {
final String text = "看山是山";
final String reverseText = text.transform(s -> new StringBuilder(s).reverse().toString());
assertEquals("山是山看", reverseText);
}
增强文件读写(Java 11)
本次更新在Files
中增加了两个方法:readString
和writeString
。writeString
作用是将指定字符串写入文件,readString
作用是从文件中读出内容到字符串。是一个对Files
工具类的增强,封装了对输出流、字节等内容的操作。
用法比较简单:
@Test
void testReadWriteString() throws IOException {
final Path tmpPath = Path.of("./");
final Path tempFile = Files.createTempFile(tmpPath, "demo", ".txt");
final Path filePath = Files.writeString(tempFile, "看山 howardliu.cn
公众号:看山的小屋");
assertEquals(tempFile, filePath);
final String fileContent = Files.readString(filePath);
assertEquals("看山 howardliu.cn
公众号:看山的小屋", fileContent);
Files.deleteIfExists(filePath);
}
readString
和writeString
还可以指定字符集,不指定默认使用StandardCharsets.UTF\_8
字符集,可以应对大部分场景了。
增强函数 Predicate(Java 11)
这个也是方法增强,在以前,我们在Stream
中的filter
方法判断否的时候,一般需要!
运算,比如我们想要找到字符串列表中的数字,可以这样写:
final List<String> list = Arrays.asList("1", "a");
final List<String> nums = list.stream()
.filter(NumberUtils::isDigits)
.collect(Collectors.toList());
Assertions.assertEquals(1, nums.size());
Assertions.assertTrue(nums.contains("1"));
想要找到非数字的,filter
方法写的就会用到!
非操作:
final List<String> notNums = list.stream()
.filter(x -> !NumberUtils.isDigits(x))
.collect(Collectors.toList());
Assertions.assertEquals(1, notNums.size());
Assertions.assertTrue(notNums.contains("a"));
Predicate
增加not
方法,可以更加简单的实现非操作:
final List<String> notNums2 = list.stream()
.filter(Predicate.not(NumberUtils::isDigits))
.collect(Collectors.toList());
Assertions.assertEquals(1, notNums2.size());
Assertions.assertTrue(notNums2.contains("a"));
有些教程还会推崇静态引入,比如在头部使用import static java.util.function.Predicate.not
,这样在函数式编程时,可以写更少的代码,语义更强,比如:
final List<String> notNums2 = list.stream()
.filter(not(NumberUtils::isDigits))
.collect(toList());
未命名模式和变量(Java 22)
该特性使用下划线字符 \_
来表示未命名的模式和变量,从而简化代码并提高代码可读性和可维护性。
比如:
public static void main(String[] args) {
var _ = new Point(1, 2);
}
record Point(int x, int y) {
}
这个可以用在任何定义变量的地方,比如:
... instanceof Point(\_, int y)
r instanceof Point \_
switch …… case Box(\_)
for (Order \_ : orders)
for (int i = 0, \_ = sideEffect(); i < 10; i++)
try { ... } catch (Exception \_) { ... } catch (Throwable \_) { ... }
只要是这个不准备用,可以一律使用\_
代替。
Markdown格式文档注释(Java 23)
Markdown是一种轻量级的标记语言,可用于在纯文本文档中添加格式化元素,具体语法可以参考Markdown Guide。本文就是使用Markdown语法编写的。
在Java注释中引入Markdown,目标是使API文档注释以源代码形式更易于编写和阅读。主要收益包括:
- 提高文档编写的效率:Markdown语法相比HTML更为简洁,开发者可以更快地编写和修改文档注释。
- 增强文档的可读性:Markdown格式的文档在源代码中更易于阅读,有助于开发者快速理解API的用途和行为。
- 促进文档的一致性:通过支持Markdown,可以确保文档风格的一致性,减少因格式问题导致的文档混乱。
- 简化文档维护:Markdown格式的文档注释更易于维护和更新,特别是在多人协作的项目中,可以减少因文档格式问题导致的沟通成本。
具体使用方式是在注释前面增加///
,比如java.lang.Object.hashCode
的注释:
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <p>
* The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code equals} comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
* <li>If two objects are equal according to the {@link
* #equals(Object) equals} method, then calling the {@code
* hashCode} method on each of the two objects must produce the
* same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link #equals(Object) equals} method, then
* calling the {@code hashCode} method on each of the two objects
* must produce distinct integer results. However, the programmer
* should be aware that producing distinct integer results for
* unequal objects may improve the performance of hash tables.
* </ul>
*
* @implSpec
* As far as is reasonably practical, the {@code hashCode} method defined
* by class {@code Object} returns distinct integers for distinct objects.
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
如果使用JEP 467的Markdown方式:
/// Returns a hash code value for the object. This method is
/// supported for the benefit of hash tables such as those provided by
/// [java.util.HashMap].
///
/// The general contract of `hashCode` is:
///
/// - Whenever it is invoked on the same object more than once during
/// an execution of a Java application, the `hashCode` method
/// must consistently return the same integer, provided no information
/// used in `equals` comparisons on the object is modified.
/// This integer need not remain consistent from one execution of an
/// application to another execution of the same application.
/// - If two objects are equal according to the
/// [equals][#equals(Object)] method, then calling the
/// `hashCode` method on each of the two objects must produce the
/// same integer result.
/// - It is _not_ required that if two objects are unequal
/// according to the [equals][#equals(Object)] method, then
/// calling the `hashCode` method on each of the two objects
/// must produce distinct integer results. However, the programmer
/// should be aware that producing distinct integer results for
/// unequal objects may improve the performance of hash tables.
///
/// @implSpec
/// As far as is reasonably practical, the `hashCode` method defined
/// by class `Object` returns distinct integers for distinct objects.
///
/// @return a hash code value for this object.
/// @see java.lang.Object#equals(java.lang.Object)
/// @see java.lang.System#identityHashCode
简单两种写法的差异,相同注释,Markdown的写法更加简洁:
文末总结
想要了解各版本的详细特性,可以从从小工到专家的 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个特性(5):增强小特性
- CSDN 主页:https://kanshan.blog.csdn.net/
- CSDN 博文:从Java8到Java23值得期待的x个特性(5):增强小特性