空指针异常是导致java程序运行中断最常见的原因,也就是NullPointException,我们通常简称为NPE。
实战:获取用户所在的城市
public class User {
private String name;
private int age;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public User(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
public class Address {
private String street;
private String city;
private String country;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Address(String street, String city, String country) {
this.street = street;
this.city = city;
this.country = country;
}
@Override
public String toString() {
return "Address{" +
"street='" + street + '\'' +
", city='" + city + '\'' +
", country='" + country + '\'' +
'}';
}
}
解决空指针方案:
-
直接获取;容易出现空指针异常。
public class BaiLiNpeDemo { public static void main(String[] args) { Address myAddress = new Address(); User myUser = new User("John Doe", 35, myAddress); String city = myUser.getAddress().getCity().trim(); System.out.println(city); } }
-
使用if-else判断;避免了出现空指针的问题,但是代码结构层次嵌套多,不美观
public class BaiLiSimpleNpeDemo { public static void main(String[] args) { Address myAddress = new Address(); User myUser = new User("John Doe", 35, myAddress); if (myUser != null) { Address address = myUser.getAddress(); if (address != null) { String city = address.getCity(); if (city != null && !"".equals(city)) { System.out.println("使用if判断字符串:" + "一键三连"); } } } } }
-
使用工具类美化一下if判断代码
public class BaiLiUtilsNpeDemo { public static void main(String[] args) { Address myAddress = new Address("123 Main St", " Austin ", "CA"); User myUser = new User("John Doe", 35, myAddress); //针对对象与字符串 if (ObjectUtils.isNotEmpty(myUser)) { Address address = myUser.getAddress(); if (ObjectUtils.isNotEmpty(address)) { String city = address.getCity(); if (StringUtils.isNotEmpty(city)) { System.out.println("使用StringUtils工具类判断字符串:" + "一键三连"); } } } //针对数组使用工具类 ArrayList<User> users = new ArrayList<>(); users.add(myUser); if (CollectionUtils.isNotEmpty(users)) { System.out.println("使用CollectionUtils工具类判断数组对象:" + "一键三连"); } } }
-
使用Optional解决了层次多的问题也避免了空指针的问题,当我们配合使用orElse时,会先执行orElse方法,然后执行逻辑代码,不管是否出现了空指针。
public class BaiLiOptionalNpeDemo { public static void main(String[] args) { Address myAddress = new Address(); User myUser = new User("John Doe", 35, myAddress); System.out.println("使用Optional判断 + orElse:" + Optional.ofNullable(myUser) .map(User::getAddress) .map(Address::getCity) .map(String::trim) .orElse(getDefaultCity()) ); } //初始化城市 public static String getDefaultCity() { System.out.println("初始化默认城市"); return null; } }
-
使用断言处理接口入参,检查假设和前置条件是否满足,以及检查空值情况,提前捕获空指针异常并进行处理
public class BaiLiAssertNpeDemo { public static void main(String[] args) { Address myAddress = new Address("123 Main St", " Austin ", "CA"); User user = new User("John Doe", 35, myAddress); getUserCity(user); getUserCity(null); } public static void getUserCity(User user){ Assert.notNull(user,"user is null"); Address address = user.getAddress(); Assert.notNull(address,"address is null"); String city = address.getCity(); System.out.println(city); } }
-
使用@Nullable注解,标识变量或方法参数和返回值是否可以为 null,以便在编译期或开发工具中提示可能的 NullPointerException 风险
public class BaiLiNonNullDemo { public static void printString(@Nullable String str) { System.out.println(str.toString()); } @Nullable public static String getString() { return null; } public static void main(String[] args) { String str = null; printString(str); getString().toString(); User user = new User(); user.getAddress().getCity(); } }
-
额外补充
JDK17优化了空指针异常信息(Helpful NullPointerExceptions),通过精确描述哪个变量为空来提高JVM生成的空指针异常信息的可用性。
即,以前的空指针异常信息不会告诉你具体是哪个对象为null,当运行的语句是对一个嵌套结构的对象做连续的方法调用(如"a.getb().getc().xxx()")时,就需要进一步分析或调试才能判断出谁是null。而该特性加入以后则直接在异常信息中说明值为null的对象是哪个。
public class BaiLiNpeDemo {
public static void main(String[] args) {
Address myAddress = new Address("123 Main St", null, "CA");
User myUser = new User("John Doe", 35, myAddress);
System.out.println(myUser.getAddress().getCity().trim());
}
}
执行结果:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.trim()" because the return value of "npe.Address.getCity()" is null
at npe.BaiLiNpeDemo.main(BaiLiNpeDemo.java:16)