IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> 还在用if进行参数判空?太丑了吧 -> 正文阅读

[开发测试]还在用if进行参数判空?太丑了吧

前言

在Java中,有一个臭名昭著的异常–空指针(NullPointerException),为了防止空指针异常带来的危害,我们通常会在程序中进行大量的判空操作,但是使用if…else语法来写的话往往会使代码显得臃肿不堪。其实,我们可以使用Java中的断言机制来巧妙的解决这个问题,从而达到优化代码的目的。

日常的场景

大家都知道,我们在日常的项目中经常会遇到这样子的场景,例如下面是一个普通的controller,我们根据请求传入的id进行数据库查询。

但是如果这个时候传入的id为空,则会引起程序的异常。

@RestController
public class TestController {
    
    @RequestMapping(value = "/test")
    public String test(Long id){
        System.out.println("打印出你要寻找的id");
        System.out.println("执行操作数据库");
        return "你要查找的id是" + id;
    }
}

所以,我们不得不引入这样子的一个判断:

@RestController
public class TestController {

    @RequestMapping(value = "/test")
    public String test(Long id){
        // --------- 参数判断 start------------
        if (Objects.isNull(id)){
            return "输入的id不能为空";
        }
        if (id < 0){
            return "输入的id不能小于0";
        }
        // --------- 参数判断 end-------------
        System.out.println("打印出你要寻找的id");
        System.out.println("执行操作数据库");
        return "你要查找的id是" + id;
    }
}

这是大家常用的场景,但是如果传入的参数多了,我们进行这么多的判断,同样会影响代码的可读性。那么有没有一个更加优雅的方式来解决这个问题呢?

这里就要给大家介绍一下–Java中的断言机制

什么是断言机制?

提到断言机制,大家应该都不陌生,我们在看一下开源的代码或者是看一些别人的测试用例的时候,经常会看到这样子的写法:

image-20210917000025311

?

在写测试用例的时候,我们常常使用 断言来判断我们的输出结果是否符合自己的预期,借此来帮助自己判断程序是否良好的运行。

官方的解释呢,是这样子说的:

编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设。

但是你知道吗?

除了测试用例,我们同样可以在自己的代码中使用断言机制来进行参数判断

org.springframework.util中为我们集成了断言的工具类,我们可以使用Assert这个方法来对程序中不满足程序预期的内容进行判断

举个例子,我来改写一下之前的案例:

 @RequestMapping(value = "/test2")
    public String test2(Long id){
        // --------- 参数判断 start------------
        Assert.notNull(id, "参数不能为空");
        Assert.isTrue(id > 0, "ID必须大于0");
        // --------- 参数判断 end-------------
        System.out.println("打印出你要寻找的id");
        System.out.println("执行操作数据库");
        return "你要查找的id是" + id;
    }

这样子是不是就清晰多了呢?

那么具体这个 Assert.notNull方法中做了什么呢?我们可以点进他的源码中查看:

   public static void notNull(@Nullable Object object, String message) {
        if (object == null) {
            throw new IllegalArgumentException(message);
        }
    }

可以看到 Assert.notNull方法是对我们的一个常见异常 IllegalArgumentException(非法参数异常)做出了一个封装

这样子我们通过使用这个封装好的util类就可以对代码进行简化。

但是接下来又会出现一个问题:

假如传入的参数就是非法的,那么会出现什么后果的呢?我们可以试一试:

image-20210917003157590

发送请求:

image-20210917003352998

控制台给出报错。

image-20210917003421468

接口返回了服务端异常。

当然,这样子的结果是我们不希望看到呢,那么有没有更加优雅的解决这个问题的方式呢?

我们可以通过全局异常处理来完成

在Spring的3.2版本中增加了一个注解@ControllerAdvice,这个注解可以帮助我们进行异常拦截。

当然,如果你使用的微服务,同样可以使用AOP来对这个问题进行处理。同样简单且使用。

首先我们来看一下使用@ControllerAdvice注解来进行全局异常处理:

首先写出一个全局异常处理类:GlobalExceptionHandler

image-20210917005000075

然后我们给出一个简易的统一返回Result格式:

image-20210917005118123

接着在GlobalExceptionHandler中添加代码:

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
    
    /**
     * 拦截业务异常,返回业务异常信息
     * @param ex
     * @return
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public Result handleIllegalArgumentExceptionError(IllegalArgumentException ex) {
        String message = ex.getMessage();
        return Result.fail(500, message);
    }
}

再次请求,异常就能够被捕获了

image-20210917005330033

Assert的拓展应用

在上面的例子中,我们介绍了Assert的两个方法。

一个是在判断参数是否为null时使用到的 **Assert.notNull()**方法

还有一个是判断参数是否大于0的时候用到的 **Assert.isTrue()**方法

此处需要注意的是,我们查看 **Assert.isTrue()**方法的源码:

  public static void isTrue(boolean expression, String message) {
        if (!expression) {
            throw new IllegalArgumentException(message);
        }
    }

这个方法里面传递的其实是一个boolean类型的表达式,只要我们写出的是一个能够被Java判断为Boolean类型的表达式,这个方法就能帮助我们判断,但是只有当结果为true的时候才会顺利通过,否则就会引发IllegalArgumentException哦。

但是其实Assert这个工具类里面给我们提供了很多很多其他好用到爆的方法。我来给大家判断几个常用的:

对于字符串的判断

hasLength方法

这个方法可以帮助我们判断传入的字符串是否为空,通常在项目中我们习惯用 StringUtils这个工具类来判定字符串是否为空,根据判断结果执行相应的操作。但是对于一些强校验的场景,比如需要用户传入字符串搜索,或者根据字符串去搜索数据库等场景,如果字符串为空会引发相应异常,这个使用我们使用 Assert.hasLength方法就可以直接阻断程序的运行并且返回相应的错误提示。

用法示例:

 @Test
    void testHasLength() {
        String emptyString = "";
        String normalString = "这是用户需要搜索的字符";
        // 正常通过
        Assert.hasLength(normalString,"输入的字符串不能为空");
        // 报出异常,返回提示 "输入的字符串不能为空"
        Assert.hasLength(emptyString,"输入的字符串不能为空");
    }

hasText方法

这个方法用来判断传入的参数中是否包含字符,其实主要是为了验证空格的。如果用户传来的参数中只有一串空格,但是没有任何实际的字符,同样会引发异常的。

用法示例:

@Test
    void testHasText() {
        String notContainText = "   ";
        String containsText = "这是用户需要搜索的字符";
        // 正常通过
        Assert.hasText(containsText,"输入的字符不包含字符串");
        // 报出异常,返回提示 "输入的字符串不能为空"
        Assert.hasText(notContainText,"输入的字符不包含字符串");
    }

doesNotContain方法

这个方法和上面的判断是相反的,用来判断传入的参数中是否 不包含某个字符串

用法示例:

   @Test    void testDoesNotContain() {        String targetStr = "abc";        // 正常通过        Assert.doesNotContain(targetStr,"123","不包含目标字符");        // 提示异常:"不包含必须的字符"        Assert.doesNotContain(targetStr,"a","不包含目标字符");    }

集合类

notEmpty方法

这个方法主要来判定集合类是否为空的情形,并且其中支持了多种数据结构,数组、Map、Collections集合都可以做出判断,接下来给出用法示例:

    @Test    void testNotEmpty() {        ArrayList<Integer> emptyList = new ArrayList<>();        List<Integer> containValueList = List.of(1, 2, 3);        HashMap<Integer, Integer> emptyMap = new HashMap<>();        Map<Integer, Integer> containValueMap = Map.of(1, 2, 3, 4);        Integer[] emptyArray = new Integer[]{};        Integer[] containValueArr = {1,2,3};        // 正常通过        Assert.notEmpty(containValueList,"list不能为空");        Assert.notEmpty(containValueMap,"map不能为空");        Assert.notEmpty(containValueArr,"array不能为空");        // 提示异常        Assert.notEmpty(emptyList,"list不能为空");        Assert.notEmpty(emptyMap,"map不能为空");        Assert.notEmpty(emptyArray,"array不能为空");    }

还有一个比较特殊的状态判断

state方法

这个方法在用法上,和我们的 Assert.isTrue是一样的,我们可以看一下它的底层实现:

  public static void state(boolean expression, String message) {        if (!expression) {            throw new IllegalStateException(message);        }    }

同样是一个表达式,但是区别呢?是它抛出的异常不同,Assert.isTrue抛出的是IllegalArgumentException非法参数异常,而 Assert.state方法抛出的是IllegalStateException非法状态异常。

那么对于一些需要判断状态的场景,我们就可以用到这个方法

用法示例:

public void testState() {    Assert.state(this.state.equals("end"), "状态异常,此时的状态不应该为结束");    // ...}

由于之前我们使用了统一异常处理来拦截异常,那么针对这种场景,我们可以进行一个扩展,回到GlobalExceptionHandler这个全局异常处理类中。

image-20210917212301864

我们添加这样一段代码,对于状态异常进行一个捕获,并且给出状态异常一个特定的标识码,例如文中的1005

 /**     * 拦截业务异常,返回业务异常信息     * @param ex     * @return     */    @ExceptionHandler(IllegalArgumentException.class)    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)    public Result handleIllegalStateExceptionExceptionError(IllegalArgumentException ex) {        String message = ex.getMessage();        // 此处添加自定义异常码,1005 是状态异常的标识        return Result.fail(1005, message);    }

总结

通过使用Java中的断言机制,我们可以更好的简化if判断,对于一些明显异常的参数,明显异常的状态,及时中止并且返回相应的提示,增加了代码的可读性,同时也简化了书写。

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-09-19 08:16:52  更:2021-09-19 08:17:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 23:06:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码