深入理解java异常体系

深入理解java异常体系,第1张

文章目录
    • Error
    • Excetion
      • 受检查异常
      • 不受检查异常
    • Throwable常用方法:
    • try-catch-finally 注意事项
      • 注意
    • finally中代码一定会执行吗?
    • try-with-resources
    • 与try-catch涉及的性能方面
    • ClassNotFoundException与NoClassDefFoundError的区别
      • ClassNotFoundException的产生原因:
      • ClassNotFoundException的产生原因主要是:
      • NoClassDefFoundError产生的原因在于:

首先我们先看下图的异常体系结构


Throwable类(不是接口)是所有异常类的跟类
我们主要看看继承他的Error类与Exception

Error

Error 属于程序无法处理的错误 ,我们没办法通过 catch 来进行捕获 。例如Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。

常见的Error:
VirtualMachineErrorNoClassDefFoundErrorOutOfMemeryErrorStackOverFlowError

Excetion

程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为 Checked Exception (受检查异常,必须处理) 和 Unchecked Exception (不受检查异常,可以不处理)。

受检查异常

主要是指在代码编译时期就能价差出来的错误。也即是编译时异常:继承自Exception的异常或者其子类,编译阶段就会报错,必须程序员处理的。否则代码编译就不能通过!!

ParseExcetionClassNotFoundExceptionFileNotFoundExceptionIOExceptionSQLException

不受检查异常

编译器能通过,在运行期可能出现的异常。也即是运行时异常(RuntimeException),继承自RuntimeException的异常或者其子类,编译阶段是不会出错的,它是在运行时阶段可能出现,运行时异常可以处理也可以不处理,编译阶段是不会出错的,但是运行阶段可能出现,还是建议提前处理!!

ArithmeticExceptionIndexOutOfBoundExceptionClassCastExceptionNullPointerExceptionNumberFormatException

除了RuntimeException及其子类以外的类都是受检查异常

Throwable常用方法:

String getMessage(): 返回异常发生时的简要描述
String toString(): 返回异常发生时的详细信息
String getLocalizedMessage(): 返回异常对象的本地化信息。使用 Throwable 的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与 getMessage()返回的结果相同
void printStackTrace(): 在控制台上打印 Throwable 对象封装的异常信息

try-catch-finally 注意事项

try块: 用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。
catch块: 用于处理 try 捕获到的异常。
finally 块: 无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。

注意

如果一个方法有返回值,那么finally语句中一定不要写return,因为正常语句在return语句前,无论如何都会执行finally中的语句,这时如果finally中有return,则无论try中是否报错,都会返回finally中的数据

public static void main(String[] args) {
    System.out.println(f(2));
}

public static int f(int value) {
    try {
        return value * value;
    } finally {
        if (value == 2) {
            return 0;
        }
    }
}

输出0

finally中代码一定会执行吗?

不一定。
1.在try中执行了System.exit() JVM会直接收
2. 程序所在的线程死亡
3. 关闭 CPU

catch中建议捕获具体的异常而不是Exception。原因:方便代码阅读理解。

try-with-resources

java中对资源的定义: 任何实现java.lang.AutoCloseable或者 java.io.Closeable 的对象都可以看作资源。

关闭资源和 finally 块的执行顺序: 在 try-with-resources 语句中,任何 catch 或 finally 块在声明的资源关闭后运行

面对必须要关闭的资源,我们总是应该优先使用 try-with-resources 而不是try-finally。随之产生的代码更简短,更清晰,产生的异常对我们也更有用。try-with-resources语句让我们更容易编写必须要关闭的资源的代码,若采用try-finally则几乎做不到这点。

例如如下代码:
声明了一个Scanner对象后,我们必须在finally块中手动关闭资源。但是我们经常会忘了关闭,或者忘记了非空判断。

Scanner scanner = null;
try {
    scanner = new Scanner(new File("D://read.txt"));
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} finally {
    if (scanner != null) {
        scanner.close();
    }
}

这时就可以引入jdk7新增的try-with-resource,

try (Scanner scanner = new Scanner(new File("test.txt"))) {
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException fnfe) {
    fnfe.printStackTrace();
}

即在try后面的() 中创建资源,系统会在进入catch或者finally之前会先自动关闭scanner资源。如果有多个资源,可以在() 中以; 的形式进行隔开

try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
             BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
            int b;
            while ((b = bin.read()) != -1) {
                bout.write(b);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
与try-catch涉及的性能方面

我们从性能角度来审视一下Java的异常处理机制,这里有两个可能会相对昂贵的地方:
try-catch代码段会产生额外的性能开销,或者换个角度说,它往往会影响JVM对代码进行优化,所以建议仅捕获有必要的代码段,尽量不要一个大的try包住整段的代码;与此同
时,利用异常控制代码流程,也不是一个好主意,远比我们通常意义上的条件语句(if/else、switch)要低效。

Java每实例化一个Exception,都会对当时的栈进行快照,这是一个相对比较重的 *** 作。如果发生的非常频繁,这个开销可就不能被忽略了

ClassNotFoundException与NoClassDefFoundError的区别 ClassNotFoundException的产生原因:

Java支持使用Class.forName方法来动态地加载类,任意一个类的类名如果被作为参数传递给这个方法都将导致该类被加载到JVM内存中,如果这个类在类路径中没有被找到,那么此时就会在
运行时抛出ClassNotFoundException异常。

ClassNotFoundException的产生原因主要是:

Java支持使用反射方式在运行时动态加载类,例如使用Class.forName方法来动态地加载类时,可以将类名作为参数传递给上述方法从而将指定类加载到JVM内存中,如果这个类在类路径中没有被找到,那么此时就会抛出ClassNotFoundException异常。
解决该问题需要确保所需的类连同它依赖的包存在于类路径中,常见问题在于类名书写错误。
另外还有一个导致ClassNotFoundException的原因就是:当一个类已经某个类加载器加载到内存中了,此时另一个类加载器又尝试着动态地从同一个包中加载这个类。通过控制动态类加载过程,可以避免上述情况发生。

NoClassDefFoundError产生的原因在于:

如果JVM或者ClassLoader实例尝试加载(可以通过正常的方法调用,也可能是使用new来创建新的对象)类的时候却找不到类的定义。要查找的类在编译的时候是存在的,运行的时候却找不到了。这个时候就会导致NoClassDefFoundError.
造成该问题的原因可能是打包过程漏掉了部分类,或者jar包出现损坏或者篡改。解决这个问题的办法是查找那些在开发期间存在于类路径下但在运行期间却不在类路径下的类。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/787981.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-05
下一篇 2022-05-05

发表评论

登录后才能评论

评论列表(0条)

保存