一、空指针异常的介绍
------NullPointerException 是 runtimeexception运行时异常的子类,因此,Javac 编译器并不会强迫你使用 try-catch 代码块来捕获该异常。
------在Java中,null 作为一个特殊值被对象引用,用来表示该对象当前指向的是一块未知内存数据。然而NullPointerException这个异常,则是程序在使用或访问一个对象的引用时,而该对象等于null则被抛出。
------那些情况会引发该异常呢?
被调用方法的对象为null。
访问或修改一个null对象的字段。
求一个数组为null对象的长度。
访问或修改一个数组为null对象中的某一个值。
被抛出的值是null并且是一个throwable的子类。
当你用null对象进行synchronized代码块。
向set方法存一个空值时。
二、处理空指针异常
1、业务场景
import com.java.entity.User;
import com.java.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
@RequestMapping
public class UserController {
@Autowired
private UserService service;
@RequestMapping("/getUserList")
public List<User> getUserList(){
return service.listUser();
}
@RequestMapping("/get")
public User get(String id){
return service.get(id);
}
}
import com.java.entity.User;
import java.util.List;
public interface UserService {
List<User> listUser();
User get(String id);
}
import com.java.entity.User;
import com.java.mapper.UserDao;
import com.java.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Service("UserService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao dao;
@Override
public List<User> listUser() {
List<User> userList = dao.selectAll();
if(CollectionUtils.isEmpty(userList)){//spring util工具类
return null;
}
return userList;
}
@Override
public User get(String id) {
return dao.selectByPrimaryKey(id);
}
}
对于以上描述的接口方法来看,它包含了以下两个含义:
listUser(): 查询用户列表
get(Integer id): 查询单个用户
1-1、 listUser研究
public List<User> listUser(){
List<User> userList = userListRepostity.selectByExample(new UserExample());
if(CollectionUtils.isEmpty(userList)){//spring util工具类
return null;
}
return userList;
}
这段代码返回是null,对于集合这样返回值,最好不要返回null,因为如果返回了null,会给调用者带来很多麻烦。你将会把这种调用风险交给调用者来控制。
如果调用者是一个谨慎的人,他会进行是否为null的条件判断。否则,他会按照自己的理解去调用接口,而不进行是否为null的条件判断,如果这样的话,是非常危险的,它很有可能出现空指针异常!
根据墨菲定律来判断: “很有可能出现的问题,在将来一定会出现!”
基于此,我们将它进行优化:
*****对于接口(List listUser()),它一定会返回List,如果返回的集合为空不再返回null,即使没有数据,它仍然会返回List(集合中没有任何元素);
通过以上的修改,我们成功的避免了有可能发生的空指针异常,这样的写法更安全!
@Override
public List<User> listUser() {
List<User> userList = dao.selectAll();
if(CollectionUtils.isEmpty(userList)){//spring util工具类
return Lists.newArrayList();//com.google.common.collect.Lists提供的方法
}
return userList;
}
2-1:研究get方法
@Override
public User get(String id) {
return dao.selectByPrimaryKey(id);
}
通过代码的时候它的返回值很有可能是null! 但我们通过的接口是分辨不出来的!对于调用者来说是很危险的事情。
这里的建议是,在接口补充文档注释,比如对于异常的说明,使用注解@exception:
public interface UserSearchService{
/**
* 根据用户id获取用户信息
* @param id 用户id
* @return 用户实体
* @exception UserNotFoundException
*/
User get(Integer id);
}
这里的注释是写在后台代码中,如果调用者忽略了注释,这其实是一种弱约束性。
如何确定传入的参数不为空。JDK1.8提供了操作方式:
2-2:强制约束,我们可以通过jsr 303进行严格的约束声明:
public interface UserSearchService{
/**
* 根据用户id获取用户信息
* @param id 用户id
* @return 用户实体
* @exception UserNotFoundException
*/
User get(@NotNull Integer id);
/**
* 根据用户id获取用户信息
* @param id 用户id
* @return 用户实体,此实体有可能是缺省值
*/
Optional<User> getOptional(@NotNull Integer id);
}
可以使用注解: @Nullable @Nonnull @CheckForNull 进行接口说明
public interface UserSearchService{
/**
* 根据用户id获取用户信息
* @param id 用户id
* @return 用户实体
* @exception UserNotFoundException
*/
@CheckForNull
User get(@NonNull Integer id);
/**
* 根据用户id获取用户信息
* @param id 用户id
* @return 用户实体,此实体有可能是缺省值
*/
Optional<User> getOptional(@NonNull Integer id);
}
三、避免空指针异常的常用技巧
1、优先使用String.valueOf() 代替toString()
当代码中的某个对象需要用字符串的方式来表示时,请避免使用toString方法;因为若你的对象引用为null,则会抛出 NullPointerException。
相反,使用静态String.valueOf方法,该方法不会抛出任何异常,若对象引用为空,则打印「null」字符串。
下面是基类 Object 的 toString() 方法:
public static String toString(int i) {
if (i == Integer.MIN_VALUE)
return "-2147483648";
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
getChars(i, size, buf);
return new String(buf, true);
}
再看string.valueof()的源码:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
2、使用三元运算符来校验null值:使用场景常见于分页查询
@Override
public PageInfo selectByPage(SysMachine machine, Integer page, Integer size, boolean isOnDealer) {
page = page == null ? 1 : page;
size = size == null ? 10 : size;
PageHelper.startPage(page, size);
return new PageInfo<>(dao.selectByMachine(machine, isOnDealer));
}
3、使用Apache的StringUtils类,代码片段
@Transactional(rollbackFor = Exception.class)
@Override
public void bindCompany(List<String> machineIds, String companyId) {
if (!StringUtils.isEmpty(companyId) && !StringUtils.isEmpty(machineIds)) {
for (String machineId : machineIds) {
machineCompanyService.delete(new SysMachineCompany().setMachineId(machineId));
machineCompanyService.insertSelective(new SysMachineCompany().setMachineId(machineId).setCompanyId(companyId));
}
}
}
4、创建返回空集合而不是null值的方法。
注意:要熟悉 Collections 这个集合工具类,里面有太多好用的方法了
import org.springframework.util.CollectionUtils;
public void st(){
List<String> list=new ArrayList<>();
CollectionUtils.isEmpty(list);
}
请看源码-------------写的太好了。
public abstract class CollectionUtils {
public CollectionUtils() {
}
***
> public static boolean isEmpty(Collection collection) {
> return collection == null || collection.isEmpty();
> }
***
public static boolean isEmpty(Map map) {
return map == null || map.isEmpty();
}
public static List arrayToList(Object source) {
return Arrays.asList(ObjectUtils.toObjectArray(source));
}
public static void mergeArrayIntoCollection(Object array, Collection collection) {
if (collection == null) {
throw new IllegalArgumentException("Collection must not be null");
} else {
Object[] arr = ObjectUtils.toObjectArray(array);
Object[] arr$ = arr;
int len$ = arr.length;
for(int i$ = 0; i$ < len$; ++i$) {
Object elem = arr$[i$];
collection.add(elem);
}
}
}
public static void mergePropertiesIntoMap(Properties props, Map map) {
if (map == null) {
throw new IllegalArgumentException("Map must not be null");
} else {
String key;
Object value;
if (props != null) {
for(Enumeration en = props.propertyNames(); en.hasMoreElements(); map.put(key, value)) {
key = (String)en.nextElement();
value = props.getProperty(key);
if (value == null) {
value = props.get(key);
}
}
}
}
}
public static boolean contains(Iterator iterator, Object element) {
if (iterator != null) {
while(iterator.hasNext()) {
Object candidate = iterator.next();
if (ObjectUtils.nullSafeEquals(candidate, element)) {
return true;
}
}
}
return false;
}
public static boolean contains(Enumeration enumeration, Object element) {
if (enumeration != null) {
while(enumeration.hasMoreElements()) {
Object candidate = enumeration.nextElement();
if (ObjectUtils.nullSafeEquals(candidate, element)) {
return true;
}
}
}
return false;
}
}
5、instanceof 操作符
instanceof 即使对象的引用等于 null,也可以使用该运算符。在 instanceof 操作时,参考值等于为null,不抛出 NullPointerException,而是返回 false 。例如,下面的代码片段:
public boolean st(Object obj){
if (obj instanceof User){
return true;
}
return false;
}
还有很多方法,大家可以尽情的探索。一起交流技术。