JDK8的新特性
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google在著名的Guava项目引入了Optional类,通过检查空值的方式避免空指针异常。受到Google的启发,Optional类已经成为Java 8类库的一部分。
Optional
类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。如果值存在,则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
创建Optional类对象的方法:
static Optional empty() :用来创建一个空的Optional实例
static Optional ofNullable(T value)
:用来创建一个Optional实例,value可能是空,也可能非空判断Optional容器中是否包含对象:
获取Optional容器的对象:
T get(): 如果调用对象包含值,返回该值。否则抛异常。T get()与of(T value)配合使用
T orElse(T other)
:orElse(T other) 与ofNullable(T value)配合使用,如果Optional容器中非空,就返回所包装值,如果为空,就用orElse(T other)other指定的默认值(备胎)代替
T orElseGet(Supplier extends T> other) :如果Optional容器中非空,就返回所包装值,如果为空,就用Supplier接口的Lambda表达式提供的值代替
T orElseThrow(Supplier extends X> exceptionSupplier) :如果Optional容器中非空,就返回所包装值,如果为空,就抛出你指定的异常类型代替原来的NoSuchElementException
@Test
public void test1(){
String str = "hello";
Optional<String> opt = Optional.of(str);
System.out.println(opt);
}
@Test
public void test2(){
Optional<String> opt = Optional.empty();
System.out.println(opt);
}
@Test
public void test3(){
String str = null;
Optional<String> opt = Optional.ofNullable(str);
System.out.println(opt);
}
@Test
public void test4(){
String str = "hello";
Optional<String> opt = Optional.of(str);
String string = opt.get();
System.out.println(string);
}
@Test
public void test5(){
String str = null;
Optional<String> opt = Optional.ofNullable(str);
// System.out.println(opt.get());//java.util.NoSuchElementException: No value present
}
@Test
public void test6(){
String str = "hello";
Optional<String> opt = Optional.ofNullable(str);
String string = opt.orElse("atguigu");
System.out.println(string);
}
@Test
public void test7(){
String str = null;
Optional<String> opt = Optional.ofNullable(str);
String string = opt.orElseGet(String::new);
System.out.println(string);
}
@Test
public void test8(){
String str = null;
Optional<String> opt = Optional.ofNullable(str);
String string = opt.orElseThrow(()->new RuntimeException("值不存在"));
System.out.println(string);
}
@Test
public void test9(){
String str = "Hello1";
Optional<String> opt = Optional.ofNullable(str);
//判断是否是纯字母单词,如果是,转为大写,否则保持不变
String result = opt.filter(s->s.matches("[a-zA-Z]+"))
.map(s -> s.toUpperCase()).orElse(str);
System.out.println(result);
}
这是JDK9-11的新特性
新增方法 | 描述 | 新增的版本 |
---|---|---|
boolean isEmpty() | 判断value是否为空 | JDK 11 |
ifPresentOrElse(Consumer super T> action, Runnable emptyAction) | value非空,执行参数1功能;如果value为空,执行参数2功能 | JDK 9 |
Optional or(Supplier extends Optional extends T>> supplier) | value非空,返回对应的Optional;value为空,返回形参封装的Optional | JDK 9 |
Stream stream() | value非空,返回仅包含此value的Stream;否则,返回一个空的Stream | JDK 9 |
T orElseThrow() | value非空,返回value;否则抛异常NoSuchElementException | JDK 10 |
这是JDK9的新特性。
产生背景:
Motivation
The current implementation of the String class stores characters in a char array, using two bytes (sixteen bits) for each character. Data gathered from many different applications indicates that strings are a major component of heap usage and, moreover, that most String objects contain only Latin-1 characters. Such characters require only one byte of storage, hence half of the space in the internal char arrays of such String objects is going unused.
String类的当前实现将字符存储在一个字符数组中,每个字符使用两个字节(16位)。从许多不同应用程序收集的数据表明,字符串是堆使用的主要组成部分,而且大多数字符串对象只包含Latin-1字符。这样的字符只需要1字节的存储空间,因此这些字符串对象的内部字符数组中有一半的空间没有使用。
使用说明:
Description
We propose to change the internal representation of the String class from a UTF-16 char array to a byte array plus an encoding-flag field. The new String class will store characters encoded either as ISO-8859-1/Latin-1 (one byte per character), or as UTF-16 (two bytes per character), based upon the contents of the string. The encoding flag will indicate which encoding is used.
我们建议将String类的内部表示形式从UTF-16字符数组更改为字节数组加上编码标志字段。新的String类将根据字符串的内容存储编码为ISO-8859-1/Latin-1(每个字符1字节)或UTF-16(每个字符2字节)的字符。encoding标志表示使用哪种编码。
结论:String 再也不用 char[] 来存储啦,改成了 byte[] 加上编码标记,节约了一些空间。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
@Stable
private final byte[] value;
...
}
拓展:StringBuffer 与 StringBuilder
那StringBuffer 和 StringBuilder 是否仍无动于衷呢?
String-related classes such as AbstractStringBuilder, StringBuilder, and StringBuffer will be updated to use the same representation, as will the HotSpot VM’s intrinsic string operations.
与字符串相关的类,如AbstractStringBuilder、StringBuilder和StringBuffer,将更新为使用相同的表示形式,HotSpot虚拟机的内部字符串操作也将更新。
JDK11新特性:新增了一系列字符串处理方法
描述 | 举例 |
---|---|
判断字符串是否为空白 | " ".isBlank(); // true |
去除首尾空白 | " Javastack ".strip(); // “Javastack” |
去除尾部空格 | " Javastack “.stripTrailing(); // " Javastack” |
去除首部空格 | " Javastack ".stripLeading(); // "Javastack " |
复制字符串 | “Java”.repeat(3);// “JavaJavaJava” |
行数统计 | “A\nB\nC”.lines().count(); // 3 |
JDK12新特性:String 实现了 Constable 接口
String源码:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence,Constable, ConstantDesc {
java.lang.constant.Constable接口定义了抽象方法:
public interface Constable {
Optional<? extends ConstantDesc> describeConstable();
}
Java 12 String 的实现源码:
/**
* Returns an {@link Optional} containing the nominal descriptor for this
* instance, which is the instance itself.
*
* @return an {@link Optional} describing the {@linkplain String} instance
* @since 12
*/
@Override
public Optional<String> describeConstable() {
return Optional.of(this);
}
很简单,其实就是调用 Optional.of 方法返回一个 Optional 类型。
举例:
private static void testDescribeConstable() {
String name = "尚硅谷Java高级工程师";
Optional<String> optional = name.describeConstable();
System.out.println(optional.get());
}
结果输出:
尚硅谷Java高级工程师
JDK12新特性:String新增方法
String的transform(Function)
var result = "foo".transform(input -> input + " bar");
System.out.println(result); //foo bar
或者
var result = "foo".transform(input -> input + " bar").transform(String::toUpperCase)
System.out.println(result); //FOO BAR
对应的源码:
/**
* This method allows the application of a function to {@code this}
* string. The function should expect a single String argument
* and produce an {@code R} result.
* @since 12
*/
public <R> R transform(Function<? super String, ? extends R> f) {
return f.apply(this);
}
在某种情况下,该方法应该被称为map()。
举例:
private static void testTransform() {
System.out.println("======test java 12 transform======");
List<String> list1 = List.of("Java", " Python", " C++ ");
List<String> list2 = new ArrayList<>();
list1.forEach(element -> list2.add(element.transform(String::strip)
.transform(String::toUpperCase)
.transform((e) -> "Hi," + e))
);
list2.forEach(System.out::println);
}
结果输出:
======test java 12 transform======
Hi,JAVA
Hi,PYTHON
Hi,C++
如果使用Java 8的Stream特性,可以如下实现:
private static void testTransform1() {
System.out.println("======test before java 12 ======");
List<String> list1 = List.of("Java ", " Python", " C++ ");
Stream<String> stringStream = list1.stream().map(element -> element.strip()).map(String::toUpperCase).map(element -> "Hello," + element);
List<String> list2 = stringStream.collect(Collectors.toList());
list2.forEach(System.out::println);
}
Applet API 提供了一种将 Java AWT/Swing 控件嵌入到浏览器网页中的方法。不过,目前 Applet 已经被淘汰。大部分人可能压根就没有用过 Applet。
Applet API 实际上是无用的,因为所有 Web 浏览器供应商都已删除或透露计划放弃对 Java 浏览器插件的支持。Java 9 的时候,Applet API 已经被标记为过时,Java 17 的时候终于标记为删除了。
具体如下:
java.applet.Applet
java.applet.AppletStub
java.applet.AppletContext
java.applet.AudioClip
javax.swing.JApplet
java.beans.AppletInitializer