Optional java8新特性
Optional 类是 Java 8 才引入的,Optional 是个容器,它可以保存类型 T 的值,或者仅仅保存 null。Optional 提供了很多方法,这样我们就不用显式进行空值检测。Optional 类的引入很好的解决空指针异常。
Java8之前的空指针异常判断
Java 在使用对象过程中,访问任何方法或属性都可能导致 NullPointerException:
例如我们通过以下方法,获取存在 student 对象中的 Age 值。
public String getIsocode (Student student){
return student.getAge();
}
在这样的示例中,如果我们想要避免由 student 或 student.age 为空而导致的空指针问题,我们就需要采用防御式检查减少 NullPointerException(在访问每一个值之前对其进行明确地检查):
public String getIsocode (Student student){
if (null == student) {
// doSomething
return "Unknown";
}
if (null == student.getAge()) {
// doSomething
return "Unknown";
}
return student.getAge();
}
Java8之后Optional的使用
当需要判断的量多时,此时的这些判断语句可能会导致代码臃肿冗余,为此 Java8 特意推出了 Optional 类来帮助我们去处理空指针异常。
Optional类常用方法总结
Optional对象创建
Optional.empty()方法
使用 Optional.empty() 方法声明一个空的 Optional:
// 通过静态工厂方法 Optional.empty(),创建一个空的 Optional 对象
Optional<Student> optStudent = Optional.empty();Optional.of(T t)方法
使用 Optional.of(T t) 方法创建一个包含非空值的 Optional 对象 (不推荐):
// 静态工厂方法 Optional.of(T t),依据一个非空值创建一个 Optional 对象
Optional<Student> optStudent = Optional.of(student);如果 student 为 null,这段代码会立即抛出一个 NullPointerException,而不是等到访问 student 的属性值时才返回一个错误。
Optional.ofNullable(T t)方法
使用 Optional.ofNullable(T t) 方法创建一个包含可能为空的值的 Optional 对象 (推荐):
// 用静态工厂方法 Optional.ofNullable(T t),你可以创建一个允许 null 值的 Optional 对象
Optional<Student> optStudent = Optional.ofNullable(student);Optional对象获取
get()方法
get() 方法,如果变量存在,它直接返回封装的变量值,否则就抛出一个 NoSuchElementException 异常,不推荐使用:
optional.map(Student::getAge).get()orElse(T other)方法
orElse(T other) 方法,它允许你在 Optional 对象不包含值时提供一个默认值:
optional.map(Student::getAge).orElse(20));orElseGet(Supplier<? extends T> other)方法
orElseGet(Supplier<? extends T> other) 方法,它是 orElse 方法的延迟调用版,Supplier 方法只有在 Optional 对象不含值时才执行调用(懒加载):
optional.map(Student::getAge).orElseGet(() -> Integer.MAX_VALUE);orElseThrow(Supplier<? extends X> exceptionSupplier)方法
orElseThrow(Supplier<? extends X> exceptionSupplier) 方法,它和 get 方法非常类似,它们遭遇 Optional 对象为空时都会抛出一个异常,但是使用 orElseThrow 可以定制希望抛出的异常类型:
optional.orElseThrow(() -> new RuntimeException("student不存在!"));ifPresent(Consumer<? super T> consumer)方法
ifPresent(Consumer<? super T> consumer) 方法,它让能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作:
optional.ifPresent(o -> o.setAge(18));Optional对象中值的提取和转换
map()方法
map() 方法,如果值存在,就对该值执行提供的 mapping 函数调用,如果值不存在,则返回一个空的 Optional 对象。
引入 Optional 以前:
String name = null;
if(insurance != null){
name = insurance.getName();
}
引入 Optional 以后:
Optional<String> name = Optional.ofNullable(insurance).map(Insurance::getName);
Optional 的 map 方法和 Java 8 中 Stream 的 map 方法相差无几。
flatMap()方法
flatMap() 方法,对于嵌套式的 Optiona 结构,我们应该使用 flatMap 方法,将两层的 Optional 合并成一个。
我们试着重构以下代码:
public String getCarInsuranceName(Person person) { return person.getCar().getInsurance().getName(); }
由于我们刚刚学习了如何使用 map,我们的第一反应可能是我们可以利用 map 重写之前的代码:
Optional<Person> optPerson = Optional.of(person);
Optional<String> name =
optPerson.map(Person::getCar)
.map(Car::getInsurance)
.map(Insurance::getName);不幸的是,这段代码无法通过编译。为什么呢? optPerson 是 Optional<Person> 类型的 变量, 调用 map 方法应该没有问题。但 getCar 返回的是一个 Optional<Car> 类型的对象,这意味着 map 操作的结果是一个 Optional<Optional<Car>> 类型的对象。因此,它对 getInsurance 的调用是非法的。
下面应用 map 和 flatMap 对上述示例进行重写:
public String getCarInsuranceName(Optional<Person> person) {
return person.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("Unknown"); // 如果Optional的结果 值为空设置默认值
}Optional对象其他方法
isPresent()方法
可以使用 isPresent() 方法检查 Optional 对象是否包含非空值,例如:
Optional<String> optional = Optional.of("Hello World");
if (optional.isPresent()) {
System.out.println(optional.get());
}filter()方法
filter() 方法接受一个谓词作为参数。如果 Optional 对象的值存在,并且它符合谓词的条件,filter 方法就返回其值,否则它就返回一个空的 Optional 对象。
比如,你可能需要检查保险公司的名称是否为 “Cambridge-Insurance”。
Insurance insurance = ...;
if(insurance != null && "CambridgeInsurance".equals(insurance.getName())){
System.out.println("ok");
}
使用 Optional 对象的 filter 方法,这段代码可以重构如下:
Optional<Insurance> optInsurance = ...;
optInsurance.filter(insurance -> "CambridgeInsurance".equals(insurance.getName()))
.ifPresent(x -> System.out.println("ok"));Arrays
Arrays.toString()方法
快速输出数组内容
int[] a = {1,2,3,4,5};
System.out.println(Arrays.toString(a));
// 输出格式:[1,2,3,4,5]Arrays.sort()方法
给数组排序,默认升序
int[] a = new int[5]{5,4,3,2,1};
Arrays.sort(a); // 1 2 3 4 5
System.out.println(Arrays.toString(a));
// [1,2,3,4,5]①.Arrays.sort(数组名)
②.Arrays.sort(数组名,起始下标,排序个数)
Scanner s = Scanner(System.in);
int n = s.nextInt();
int[] a = new int[n]
for(int i = 0; i < n; i++)
a[i] = s.nextInt();
Arrays.sort(a,0,n - 1);Arrays.equals()方法
比较两个数组内容是否相等
int[] a = {1,2,3};
int[] b = {1,2,3};
boolean isSame = Arrays.equals(a,b);
//trueArrays.binarySearch()
在数组中查找元素
再数组中查找指定值,若找到,则返回此值的下标,
若没找到,返回 -插入点-1;
int Arrays.binarySearch( Datatype[], Datatype key)Arrays.copyOf()
拷贝数组
第一个参数是原数组,第二个参数是拷贝长度,返回值是将原数组拷贝一份返回
public static void main(String[] args) {
int[] arr1 = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] arr2 = new int[5];
arr2 = Arrays.copyOf(arr1, 10);
}Arrays.copyOf()的拷贝是从下标0开始的,如果你想从其他下表开始,可以使用Arrays.copyOfRange()方法
// from 表示开始位置, to 表示结束位置
// 复制下标为 :[from, to)
Arrays.copyOfRange(int[] original, int from, int to)Arrays.sort()[降序排列]
Arrays.asList()
源码
public static <T> List<T> asList(T... a) {
return new ArrayList<T>(a);
}使用该方法 可以将一个变长参数或者数组转换成List
需要注意以下问题
public class ArraysAsListTest {
public static void main(String[] args) {
int[] a = {1,2,3};
Integer[] b = {1,2,3};
List listA = Arrays.asList(a);
List listA1 = Arrays.asList(1,2,3);
List listB = Arrays.asList(b);
System.out.println(listA.size());//out:1
System.out.println(listA1.size());//out:3
System.out.println(listB.size());//out:3
}
}用int类型的数组作为参数为什么输出size是1,使用Integer类型size就是3了呢。
再看源码,asList接收的是一个泛型变长参数,而我们知道基本类型是不能泛型化的,就是说8种基本类型不能作为泛型参数,要想作为泛型参数就要使用其所对应的包装类。
但是listA的Size为什么是1呢,这是因为listA传递的是一个int类型的数组,数组是一个对象,它是可以泛型化的,也就是说例子中是把一个int类型的数组作为了T的类型,所以转换后在List中就只有一个类型为int数组的元素。 后边ListA1与ListB也就可以理解了,一个是进行了自动打包,一个是本来就是包装类型
Util工具类的使用
BooleanUtil: 处理布尔值的判定和转换,常用 isTrue/isFalse 做 null 安全判断,toBoolean(Object) 把字符串/数字等转成 boolean。
StrUtil: 处理字符串的判空、截取、替换、格式化等,常用 isBlank/isEmpty/hasBlank 校验字符串,format 拼接、sub/replace 操作文本。
JSONUtil: 处理 JSON 的序列化/反序列化,常用 toJsonStr 把对象转 JSON 字符串,toBean/toList 把 JSON 转成 Java 对象或集合,以及 parseObj 获取 JSONObject 方便访问字段。