https://blog.jetbrains.com/idea/2017/08/code-smells-null/
避免抛出NullPointerException
1.在确定的String调用 equal()和 equalsingnoreCase()而不是未知的对象
a.equals(b)和b.equals(a)在两个对象都不为null时表现结果一致,但是如果a.equals(b)的a为null,就会报NPE,此时如果能确定对象b非空,调用b.equals(a)是更好的选择
2.在两者返回相同结果的时候偏向使用valueOf()而非toString()
同样的,在空对象调用toString()方法时会报NPE,但是String.valueOf()会传递一个空对象,如果你不确定你的Integer,Float,Double或者BigDecimla之类的包装类对象非空,请使用String.valueOf()
3.使用@NotNull注解
if (fromDBObject == null) {
return null;
}
对于这种情况,我们可以使用@NotNull来代替null检查,但是
4.返回一个容量为0的容器而不是一个空指针
这样在别人使用你传递的这个集合对象方法时,比如Map.length(),Map.size()这些函数时不会报NPE,而是能正确的返回0
5.可以使用默认值来避免NPE
在java领域,一个最佳的避免空指针的方法之一就是和定下约定和遵守约定。大部分的NullPointException发生原因就是使用了一个不完整的信息或者并没有被提供所有的依赖信息来创建对象。如果你不允许创建不完整的对象和否定任何这种要求,你可以预防很多一段时间之后发生的NullPointException。如果对象被允许创建,那么你应该设定合理的默认值。但是记住在你的文档里面声明这件事情并且让调用你方法的所有人知道,
6.设置数据库对空值的约束
如果你使用数据库去存储你的域对象(demain object),例如:Customer、Orders等等,那么你应该定义一些在数据库对空值的约束。因为数据库可以要求获得从多个来源来的数据,在数据库中拥有对空值的检查将会确保数据的完整性。在数据库中保留对空值约束的约束也是会让你减少在JAVA中减少空检查的代码。当从数据库中取出一个对象是,你可以确保那些属性可以是空而那些属性不可以使空,这将会让那些空检查的代码最小化
7.使用空对象模式
这是另外一个在JAVA里面避免NullPointException的方法。如果一个方法返回一个对象,哪个调用者要遍历这个对象,哪个调用者就要使用一些类似Collection.iterator()的方法去返回iterator。如果调用者没有任何的上述的这些方法,那么有可能返回的是空对象而不是空(null)。空对象是一个特别的对象,它在不同的上下文中有不同的含义。像这些返回Contrainter或者Conllection类型的方法的情况中,里面为空的对象(Empty object)应该被使用而不是返回空。
空对象模式的步骤
第一步:
Class类中加入
boolean isNull(){return false;}和newNull(){return new NullClass();}
然后加入一个子类NullClass作为Class的子类,子类中重写isNull(){return true;}
这一步可以直接写也可以通过实现一个Nullable接口来实现
第二步:
在NullClass中制造一份处理空对象的方法副本(在Class为null时需要处理的函数都需要进行修改),然后对其进行修改(当为null时会产生的变化放在其中处理,比如当空对象时返回0等)
使用这种做法之后,只在对象变为null时调用newNull()将其转为NullClass即可,用户在使用类中的方法时不需要再判断是否为null。
这个重构技巧还可以扩展到类的有特殊情况的时候,比如Float中的NaN,可以通过构建特例类子类来处理而绝对不会抛出异常。
8.Java中的Optional
Java8中加入了 Optional 类。用于避免空指针的出现,也无需在写大量的if(obj!=null)这样的判断了,前提是你得将数据用Optional装着,它就是一个包裹着对象的容器。
JDK 提供三个静态方法来构造一个 Optional:
1.Optional.of(T value)
该方法通过一个非 null 的 value 来构造一个 Optional,返回的 Optional 包含了 value 这个值。对于该方法,传入的参数一定不能为 null,否则便会抛出 NullPointerException。
3.Optional.empty()
该方法用来构造一个空的 Optional,即该 Optional 中不包含值 —— 其实底层实现还是 如果 Optional 中的 value 为 null 则该 Optional 为不包含值的状态,然后在 API 层面将 Optional 表现的不能包含 null 值,使得 Optional 只存在 包含值 和 不包含值 两种状态。
2.Optional.ofNullable(T value)
该方法和 of 方法的区别在于,传入的参数可以为 null。判断传入的参数是否为 null,如果为 null 的话,返回的就是 Optional.empty()。
Optional 提供的方法
1.ifPresent()
如果 Optional 中有值,则对该值调用 consumer.accept,否则什么也不做。
public void ifPresent(Consumer consumer) {
if(value !=null) {
consumer.accept(value);
}
}
2.orElse()
public T orElse(T other) {
returnvalue !=null? value : other;
}
如果 Optional 中有值则将其返回,否则返回 orElse 方法传入的参数。
User user = Optional
.ofNullable(getUserById(id))
.orElse(newUser(0, "Unknown"));
3.orElseGet()
publicT orElseGet(Supplier ither) {
returnvalue !=null? value : other.get();
}
orElseGet 与 orElse 方法的区别在于,orElseGet 方法传入的参数为一个 Supplier 接口的实现 —— 当 Optional 中有值的时候,返回值;当 Optional 中没有值的时候,返回从该 Supplier 获得的值。
User user = Optional
.ofNullable(getUserById(id))
.orElseGet(() ->newUser(0, "Unknown"));
4.orElseThrow()
public T orElseThrow(Supplier exceptionSupplier)throws X {
if(value !=null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
orElseThrow 与 orElse 方法的区别在于,orElseThrow 方法当 Optional 中有值的时候,返回值;没有值的时候会抛出异常,抛出的异常由传入的 exceptionSupplier 提供。
User user = Optional
.ofNullable(getUserById(id))
.orElseThrow(() ->newEntityNotFoundException("id 为 " + id + " 的用户没有找到"));
举一个 orElseThrow 的用途:在 SpringMVC 的控制器中,我们可以配置统一处理各种异常。查询某个实体时,如果数据库中有对应的记录便返回该记录,否则就可以抛出 EntityNotFoundException ,处理 EntityNotFoundException 的方法中我们就给客户端返回Http 状态码 404 和异常对应的信息 —— orElseThrow 完美的适用于这种场景。
5.map()
public Optional map(Function mapper) {
Objects.requireNonNull(mapper);
if(!isPresent()){
return empty();
} else {
return Optional.ofNullable(mapper.apply(value));
}
}
如果当前 Optional 为 Optional.empty,则依旧返回 Optional.empty;否则返回一个新的 Optional,该 Optional 包含的是:函数 mapper 在以 value 作为输入时的输出值。
Optional username = Optional
.ofNullable(getUserById(id))
.map(user -> user.getUsername());
而且我们可以多次使用 map 操作:
6.flatMap()
public Optional flatMap(Function> mapper) {
Objects.requireNonNull(mapper);
if(!isPresent()){
return empty();
} else {
return Objects.requireNonNull(mapper.apply(value));
}
}
flatMap 方法与 map 方法的区别在于,map 方法参数中的函数 mapper 输出的是值,然后 map 方法会使用 Optional.ofNullable 将其包装为 Optional;而 flatMap 要求参数中的函数 mapper 输出的就是 Optional。
Optional username = Optional
.ofNullable(getUserById(id))
.flatMap(user -> Optional.of(user.getUsername()))
.flatMap(name -> Optional.of(name.toLowerCase()));
7.filter()
publicOptional filter(Predicate predicate) {
Objects.requireNonNull(predicate);
if(!isPresent()) {
returnthis;
} else {
returnpredicate.test(value) ?this : empty();
}
}
filter 方法接受一个 Predicate 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty。