前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你有没有掉进去过这些 Exception 的“陷阱“(Part C)

你有没有掉进去过这些 Exception 的“陷阱“(Part C)

作者头像
RiemannHypothesis
发布2022-08-19 16:41:31
2910
发布2022-08-19 16:41:31
举报
文章被收录于专栏:Elixir

七、除了NullPointException外的其他常见异常

ConcurrentModificationException

在test包中新增测试类ConcurrentModificationExceptionTest

代码语言:javascript
复制
public class ConcurrentModificationExceptionTest {

    List<User> userList = new ArrayList<>();

    @Before
    public void before() {

        User stark = new User();
        stark.setName("stark");
        User thor = new User();
        thor.setName("thor");
        userList.add(stark);
        userList.add(thor);
    }

    @Test
    public void testModifyWhileIteratoringByFor(){

        // 直接使用for循环,触发并发修改异常
        for (User user : userList) {
            if (user.getName().equals("thor")){
                userList.remove(user);
            }
        }
    }
}

在使用for循环进行遍历集合同时将符合条件的元素移出集合会报并发修改异常,也就是触发了Java中的fail-fast机制。

fail-fast 机制是 java 集合(Collection)中的一种错误机制。 当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。

要解决并发修改异常,可以使用迭代器进行遍历。增加测试方法testModifyWhileIteratoringByIterator()

代码语言:javascript
复制
@Test
public void testModifyWhileIteratoringByIterator(){

    // 直接使用迭代器
    Iterator<User> iter = userList.iterator();
    while (iter.hasNext()){
        User user = iter.next();
        if (user.getName() == "thor"){
            iter.remove();
        }
    }
}

next()方法一定在remove()方法之前调用,方法执行没有任何异常,虽然迭代器能够避免并发修改异常的问题,但是最好不要在遍历中删除

ClassCastException

在entity包中定义两个User的子类Admin和Employee

代码语言:javascript
复制
public class Admin extends User {
}
代码语言:javascript
复制
public class Employee extends User {
}

在test包下新增测试类ClassCastExceptionTest

代码语言:javascript
复制
public class ClassCastExceptionTest {
    
    User employee = new Employee();

    @Test
    public void testCastWithDifferentClass(){
        // 子类之间转换
        Admin admin = (Admin) employee;
    }
}

两个子类之间是没有继承关系的,子类之间直接转换会抛出类型转换异常的错误,解决这类问题可以先进行类型关系判断,通过getClass().getName()来得到具体类型,再通过instanceof进行判断是否含有继承关系,如果有继承关系再进行类型转换,否则无法进行类型转换

IllegalArgumentException

日期转换时的非法参数异常

在日期转换时,如果传入的参数不对也会报错非法参数异常

代码语言:javascript
复制
@Test
public void testUser(){
    Date date = new Date();
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String simpleDate = simpleDateFormat.format(date);
    System.out.println(simpleDate);

    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM");
    String format = dateFormat.format("");
    System.out.println(format);
}

format()函数的参数是一个Object类型,所以传入String类型时不会报编译错误,但是运行时会出现IllegalArgumentException的异常

枚举查找时的非法参数异常

新建一个enums包,增加一个枚举类LoginErrorEnum,包含了三个枚举值

代码语言:javascript
复制
public enum LoginErrorEnum {
    USERNAME_OR_PASSWORD_NOT_CORRECT,
    NOT_ADMIN_ROLE,
    USERNAME_NOT_REGISTER,
}

在test包下新增IllegalArgumentExceptionTest,测试查找一个不存在的枚举值

代码语言:javascript
复制
public class IllegalArgumentExceptionTest {

    @Test
    public void testGetValueFromEnum(){
        LoginErrorEnum passwordNotCorrect = LoginErrorEnum.valueOf("PASSWORD_NOT_CORRECT");
        System.out.println(passwordNotCorrect);
    }

}
枚举查找异常解决方案

第一种方式是使用try-catch这种比较通用的方式来解决枚举查找异常

代码语言:javascript
复制
@Test
public void testGetValueFromEnum(){
    try {
        LoginErrorEnum passwordNotCorrect = LoginErrorEnum.valueOf("PASSWORD_NOT_CORRECT");
        System.out.println(passwordNotCorrect);
    } catch (IllegalArgumentException e){
        System.out.println(e.getMessage());
    }
}

当要查找的枚举值不存在时,直接在控制台输出异常信息

第二种方式可以使用for循环遍历的方式,遍历所有的枚举值,查看是否有符合条件的枚举值,但是for循环效率较低

第三种方式可以使用Guava,首先在pom.xml文件中导入guava依赖

代码语言:javascript
复制
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>12.0</version>
</dependency>

新增测试方法testGetValueFromEnumWithGuava

代码语言:javascript
复制
@Test
public void testGetValueFromEnumWithGuava(){

    System.out.println(Enums.getIfPresent(LoginErrorEnum.class, "PASSWORD_NOT_CORRECT").orNull());
}

控制台输出为null,避免了非法参数异常

八、 资源关闭

资源以及资源泄露

资源有哪些?

  • 文件
  • socket连接
  • 数据库连接
  • ....

资源在使用过之后要进行关闭或者释放,如果没有释放怎会导致资源泄露的问题

try finally 关闭资源的问题

新增一个测试类HandlerResoucesTest,使用try-catch-finally关闭单个资源的代码如下

代码语言:javascript
复制
@Test
public void testCloseSingleByTryCatchFinally() throws IOException {
    String context = null;
    BufferedReader bufferedReader = new BufferedReader(new FileReader("info.txt"));
    try {
        context = bufferedReader.readLine();
        System.out.println(context);
    } catch (Exception e){
        System.out.println(e.getMessage());
    } finally {
        bufferedReader.close();
    }
}

finally代码块中的代码无论是否出现异常都会执行,因此将资源关闭的代码放在finally中,确保操作结束后关闭资源

当try代码块中又包含另外一个资源的读取的时候,代码会变成这样

代码语言:javascript
复制
@Test
public void testCloseMultiByTryCatchFinally() throws IOException{
    String context = null;
    String message = null;
    BufferedReader bufferedReader = new BufferedReader(new FileReader("info.txt"));
    try {
        context = bufferedReader.readLine();
        BufferedReader messBufferedReader = new BufferedReader(new FileReader("message.txt"));
        try {
            message = messBufferedReader.readLine();
            System.out.println(message);
        } catch (Exception e){
            System.out.println(e.getMessage());
        } finally {
            messBufferedReader.close();
        }
        System.out.println(context);
    } catch (Exception e){
        System.out.println(e.getMessage());
    } finally {
        bufferedReader.close();
    }
}

使用try-catch关闭多个资源时代码冗长且不易阅读

try-with-resources 解决资源泄露隐患

try-with-resources只需要声明和使用,不需要考虑关闭的问题,在try关键字后面的括号中里new一些需要自动关闭的资源。

BufferedRead从java 7开始就实现了 AutoCloseable 接口,无论try-with关闭资源是正常关闭还是异常关闭,autoClose都能关闭他们

关闭单个资源的代码

代码语言:javascript
复制
@Test
public void testCloseSingleByTryWithResources() throws IOException {
    try(BufferedReader reader = new BufferedReader(new FileReader("info.txt"))){
        System.out.println(reader.readLine());
    }
}

关闭多个资源的代码

代码语言:javascript
复制
@Test
public void testCloseMultiByTryWithResources() throws IOException{
    try(FileInputStream inputStream = new FileInputStream("info.txt");
        FileOutputStream outputStream  = new FileOutputStream("message.txt")) {
         byte[] buffer = new byte[100];
         int n = 0;
         while ((n = inputStream.read(buffer)) != -1){
             outputStream.write(buffer, 0, n);
         }
    }
}

异常被覆盖的情况

如果try finally都抛出异常,finally中抛出的异常会抑制try中的异常,导致很难发现最初的异常。

在exceptions包中自定义一个异常

代码语言:javascript
复制
public class LiException extends Exception {

    public LiException() {
        super();
    }

    public LiException(String message) {
        super(message);
    }
}

定义一个类实现AutoCloseable接口实现AutoCloseable接口,这个接口可以被try-with-resource自动关闭掉

代码语言:javascript
复制
public class LilithAutoCloseable implements AutoCloseable {


    @Override
    public void close() throws Exception {
        System.out.println("close()方法被调用");
        throw new RuntimeException("close()方法中抛出的异常");
    }

    public void work() throws LiException{
        System.out.println("work()方法被调用");
        throw new LiException("work()方法中抛出的异常");
    }
}

增加测试方法testCloseWithAutoCloseable()

代码语言:javascript
复制
@Test
public void testCloseWithAutoCloseable() throws Exception {

    LilithAutoCloseable lilithAutoCloseable = new LilithAutoCloseable();
    try {
        lilithAutoCloseable.work();
    } finally {
        lilithAutoCloseable.close();
    }
}

work()方法中的异常被finally中的close()方法的异常覆盖掉了

使用try-with-resources则不会出现这中问题

代码语言:javascript
复制
@Test
public void testCloseWithAutoCloseableByTryWith() throws Exception {

    try(LilithAutoCloseable lilithAutoCloseable = new LilithAutoCloseable()) {
        lilithAutoCloseable.work();
    }

}

work()方法和close()方法抛出的异常都被展示出来了

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 七、除了NullPointException外的其他常见异常
    • ConcurrentModificationException
      • ClassCastException
        • IllegalArgumentException
          • 日期转换时的非法参数异常
          • 枚举查找时的非法参数异常
      • 八、 资源关闭
        • 资源以及资源泄露
          • try finally 关闭资源的问题
            • try-with-resources 解决资源泄露隐患
              • 异常被覆盖的情况
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档