专业的JAVA编程教程与资源

网站首页 > java教程 正文

Java异常详细介绍

temp10 2025-01-03 19:04:36 java教程 14 ℃ 0 评论

1、处理错误


1.1、异常分类

Java异常详细介绍

1)java.lang.Throwable,所有异常类都是继承自该类;

2)过滤异常堆栈轨迹信息;

package com.mfz.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class ExceptionUtil {

    /**
     * 对堆栈内的异常信息做拦截,过滤掉非 com.mfz 包内的记录
     * @param stackTrace
     * @return
     */
    public static StackTraceElement[] mfzInterceptStackTrace(StackTraceElement[] stackTrace) {
        return interceptStackTrace(stackTrace, "com.mfz");
    }
    
    /**
     * 对堆栈内的异常信息做拦截,过滤掉非 packagePrefix 指定包内的记录
     * @param stackTrace
     * @param packagePrefix
     * @return
     */
    public static StackTraceElement[] interceptStackTrace(StackTraceElement[] stackTrace, String packagePrefix) {
        if (Objects.isNull(stackTrace)) {
            return new StackTraceElement[0];
        }
        List<StackTraceElement> interceptStackTraceS = new ArrayList<>();
        for (StackTraceElement st : stackTrace) {
            if (st.getClassName().contains(packagePrefix)) {
                interceptStackTraceS.add(st);
            }
        }
        return interceptStackTraceS.toArray(new StackTraceElement[0]);
    }
}


实践一:

package com.mfz.exception;

import com.mfz.utils.ExceptionUtil;

public class Junk {
    
    public static void main(String args[]) {
        try {
            a();
        } catch(HighLevelException e) {
            e.printStackTrace();
        }
    }
    static void a() throws HighLevelException {
        try {
            b();
        } catch(MidLevelException e) {
            throw new HighLevelException(e);
        }
    }
    static void b() throws MidLevelException {
        c();
    }

    static void c() throws MidLevelException {
        try {
            d();
        } catch(LowLevelException e) {
            StackTraceElement[] stackTrace = e.getStackTrace();
            e.setStackTrace(ExceptionUtil.interceptStackTrace(stackTrace, "com.mfz.exception"));
            throw new MidLevelException(e);
        }
    }

    public static void d() throws LowLevelException {
        e();
    }
    
    public static void e() throws LowLevelException {
        throw new LowLevelException();
    }
    
}

class HighLevelException extends Exception {
    HighLevelException(Throwable cause) { super(cause); }
}
class MidLevelException extends Exception {
    MidLevelException(Throwable cause)  { super(cause); }
}
class LowLevelException extends Exception {

}


1.2、声明检查型异常类 throws

1)通过 throws 关键字在方法声明时指定该方法可能抛出的检查型异常类;

2)Error 及运行时异常不需要抛出;

3)如果父类中的方法声明了检查型异常类,那子类覆盖该方法时声明的检查型异常类不能比父类中的异常更通用,即子类方法声明的异常类必须和父类方法声明的异常相同或是异常子类;

4)子类方法也完全可以不用声明方法,可以通过 try/ catch 捕获异常;

5)如果超类方法声明时没有 throws 抛出任何检查型异常,那子类中的覆盖方法也不能抛出任何检查型异常;


1.3、抛出异常 throw

1)在方法中通过 throw 关键字抛出异常;


1.4、创建自定义异常类

1)继承JDK中一个已经存在的、较合适的异常类;

2)自定义异常类中定义两个构造器方法,一个是空构造器,一个是带异常信息入参的构造器,该入参会传给超类,在堆栈信息中会打印出来;


2、捕获异常


2.1、try/catch 捕获异常

1)如果在 try 语句块中发生异常,抛出的异常类是 catch 子语句块中指定的异常类,则:

1) try 语句块中的剩余代码不会执行了;

2) 跳到 catch 子语句块中执行该语句块的代码;

2)如果 try 语句块中没有发生任何异常,则 catch 子语句块中的代码不会执行到;

3)如果 try 语句块中发生了 catch 子语句块没有捕获的异常,则整个方法都退出了。我们应该在方法中声明可能产生的检查型异常,如 SocketTimeoutException。不过我们在写业务代码时就需要 try/catch 处理掉可能的异常,而不是往外抛;


2.2、try/catch 捕获多个异常

1)可以通过 e.getMessage() 方法获取到异常的详细信息;

2)可以通过 e.getClass().getName() 方法获取到异常对象的实际类型;

3)建议是不同异常类分别用不同的 catch 子语句处理;


2.3、再次抛出异常与异常链

1)如果想改变异常的类型,可以在 catch 子语句块中抛出一个新类型的异常;

2)如果我们在自己项目中调用第三方系统接口,可以为该第三方系统接口定义一个异常类型,当接口报错可以在 catch 语句块中抛出该特定的异常类型;

3)在 catch 语句块中抛出新类型异常时可以采用包装技术,即把原始异常设置为新类型异常的原因(cause)字段,这样就不会丢失原始异常的细节信息;

实验一:


实验二:

4)可以使用 e.getCause() 获取原始异常类型;

实验三:

实验四:


2.4、finally 子语句块

1)finally 子语句块在任何时候都会被执行到,不管是否发生异常,及异常是否被捕获;

2)finally 子语句块一般用于不管如何都是要做的操作:

1)如释放资源,不管是否发生异常都要释放资源;

2)可以 catch 所有的异常情况,但是在 try 块和 catch 子块都要写释放资源的代码;

3)finally 子语句块中不要放控制流程的语句,如 return 等;

3)try {} catch(e) {} finally {},格式中 catch 语句块是可以不要的;

可以 try / catch 搭配;

也可以 try / finally 搭配

也可以 try / catch / finally 搭配

不能只有 try 语句块


代码执行顺序:

1)如果 try 语句块中没有发生异常;

执行顺序是 try > finally > return(try 语句块中);

try 语句块中代码会执行完,之后跳 finally 子语句块中执行,之后才 return try 语句块中的结果;

2)如果 try 语句块中发生异常,且被 catch 子语句块捕获,且 catch 子语句块没有抛出异常;

执行顺序 try > catch > finally > 后面非语句块中代码;

try 语句块抛出异常后,剩余代码不会再执行了;

最后返回的结果是,语句块之外的代码处理结果;

3)如果 try 语句块中发生异常,且被 catch 子语句块捕获,但是 catch 子语句块抛出异常;

此时整个 try / catch / finally 语句之后不能再写代码了,会编译报错;

执行顺序 try > catch > finally > catch 块抛异常;

try 语句块抛出异常后,剩余代码不会再执行了;

4)catch 子语句块中有 return 语句时,则在 try / catch / finally 语句块之外不能写代码了,会编译报错,跟在 catch 中抛出异常一样;

5)finally 子语句块中有 return 语句时,则在 try / catch / finally 语句块之外不能写代码了,会编译报错;

6)catch 子语句块中有 return 语句,最后返回的结果是 catch 中返回的;

7)finally 子语句块中有 return 语句,最后返回的结果是 finally 中返回的;

8)catch 子语句块中有 return 语句,finally 子语句块中也有 return 语句,最后返回的结果是 finally 中返回的;


2.5、try-with-Resources 语句

1)java.lang.AutoCloseable;

2)java.io.Closeable 接口继承自 java.lang.AutoCloseable 接口;

3)try-with-resources 语句格式:

try() {

........

}

4)try-with-resources 语句可以不需要 catch / finally 子语句块,独立存在;

1) try 后面的括号内定义的变量类型必须是实现 java.lang.AutoCloseable 接口的;

2) 括号内可以定义多个变量,即可以有多条定义变量的语句;

3) 括号内定义变量,语句块内使用,语句块执行完会自动执行 close 方法释放资源;

5)在 try 后面的括号内可以使用事实最终变量;

6)如果 try 块抛出异常,close 方法也抛出异常;

1) try-with-resources 语句能够很好的处理这种情况,try 块抛出的异常会被抛出去,而 close 方法抛出的异常会被抑制不抛;

2) 被抑制的异常可以通过 getSuppressed() 方法获取到;

7)只要需要关闭资源,则尽量使用 try-with-resources 语句;

8)try-with-resources 语句,可以有 catch / finally 块,这些块是在 close 方法执行完之后执行;


2.6、分析堆栈轨迹元素

1)堆栈轨迹 stack trace;


实验一:


实验二:

1)把堆栈轨迹信息打印到指定输出流;


实验三:

2)在 Java9 中有 StackWalker 类,会更好用;


3、使用异常的技巧

1)只有在异常的情况下才使用异常;

即可能发生异常的地方都需要做相应判断,让其不可能发生异常,而只有发生不可预知的异常时才在 catch 块中处理;

捕获异常是比较耗时的;

2)充分利用异常的层次结构,尽量使用合适的异常类,也可以自己创建异常类来使用;

3)不要压制异常,尽量处理异常,即在 catch 块中尽量处理异常,而不是什么也不做;


本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表