E. Java Exception Handling
1.Java异常是什么?
异常是一种错误,甚至可以在程序运行中发生,打断正常的执行流程。异常会导致不同的情况发生,例如数据错误,硬件失败,网络连接失败等。
一旦在Java语句执行期间产生错误,异常对象都会产生,然后JRE会试图用异常处理句柄来处理异常。如果有合适的异常处理那么异常对象会传递给处理代码来处理异常,这就叫捕获异常。如果没有找到处理者,应用会把异常抛给运行环境,JRE会中断程序。
Java异常处理框架仅是用于处理运行错误,编译错误不会被异常处理框架处理。
2.Java异常处理的关键词是什么?
Java有四个关键词处理异常:
∆ throw:有时候我们需要明确想要创建的异常对象并把它抛出来以停止正常运行的程序。throw关键词经常用来处理运行时捕获异常;
∆ throws:当我们在方法中抛出任何不被处理的检查到的异常时,我们需要在方法签名上使用throws关键词来让程序调用方知道该方法抛出的异常。调用这个方法的地方可以处理异常,或者使用throws往上继续抛出异常。我们可以使用throws语句抛出多个异常,main()方法也能使用。
∆ try-catch:我们可以在代码中使用try-catch块来处理异常。try用来开始代码块,catch结束代码块中捕获的异常。我们可以处理多个异常,catch需要异常类型的参数。
∆ finally:finally块是可选的,且只能用于try-catch块中。由于异常停止了程序,我们可能有些资源没有关闭,所以我们可以使用finally块来处理。无论异常发生与否finally块总会被执行。
3.解释Java异常类等级关系?
异常是分等级的,继承inheritance经常用来将不同类型的异常区分。Throwable是Java异常类等级的父类,它有两个子对象——Error和Exception。Exception进一步细分为检查异常和运行时异常。
∆ Error是应用范围外,无法预料且无法挽回情况的异常,例如硬件失败,JVM奔溃或者内存溢出;
∆ Checked Exceptions是我们可以在程序中预料的且可以挽救的异常,例如文件找不到异常。我们需要捕获这种异常,在日志中打印出有用的信息用以debugging。Exception是所有检查异常的父类;
∆ Runtime Exceptions是由烂程序引起的,例如尝试取回数组的元素。我们首先需要检查数组长度,然后再获取其元素,否则可能会在运行时出现数组越界异常。RuntimeException是所有运行时异常的父类。
4.Java异常类的重要方法有哪些?
异常和其异常子类都没有提供确切的方法,所有方法都定义在基类Throwable中:
∆ 1.String getMessage() :该方法返回Throwable的字符串信息,这些信息在创建异常的构造方法里提供;
∆ 2.String getLocalizedMessage():该方法提供程序调用时子类能重写局部的异常信息。Throwable实现这个方法用getMessage()来返回异常信息;
∆ 3.synchronized Throwable getCause():该方法返回引起异常的原因或者是不被知晓的空id引起的原因;
∆ 4.String toString():该方法返回有关Throwable的异常信息,返回的字符串包括Throwable类名和本地化的消息;
∆ 5.void printStackTrace():该方法打印追踪的栈信息到标准错误流,这个方法被重载,我们可以传递PrintStream或PrintWriter作为参数来把栈追中信息写到文件或流。
5.解释Java7自动资源管理和多异常块catch?
如果在一个try块里捕获很多异常,你会注意到catch块代码看着很丑陋,大多数都是包含重复的日志错误代码,为了保持简介,Java7的一个新特性就是在一个catch块里有多个异常捕获块。像下面这样:
catch(IOException | SQLException | Exception ex){
logger.error(ex);
throw new MyException(ex.getMessage());
}
大多数时候,我们使用finally块只是为了关闭资源,有时候我们会忘记关闭它们,当资源耗尽时会出现运行时异常。这些异常很难调试,我们可能需要找每一个使用到资源的地方确保它是关闭的。所以在Java7中引进了try-with-resources,当我们创建资源的时候写在try语句里,当执行的代码跳出try-catch块,运行环境会自动关闭资源。try-catch块优化的例子:
try (MyResource mr = new MyResource()) {
System.out.println("MyResource created in try-with- resources");
} catch (Exception e) {
e.printStackTrace();
}
阅读Java 7 ARM了解更多。
6.Checked和Unchecked异常区别有哪些?
∆ a.检查异常会被代码try-catch块处理或者主函数应该用throws关键词抛出异常让JRE知道。非检查异常不是程序必须要处理的异常;
∆ b.Exception是所有检查异常的父类,而RuntimeException是所有非检查异常的父类;
∆ c.检查异常是非程序引起的错误,例如在读取不存在的文件时抛出的FileNotFoundException,而费检查异常大多是由不规范代码引起的,例如调用一个不确定存在的对象索引的方法时可能会抛出NullPointerException。
7.throw和throws关键词有什么区别?
throws关键词是用在方法签名上用来声明该方法可能会抛出的异常,而throw关键词用来打断程序流来处理运行是的异常对象。
8.怎么写定制的异常?
我们可以通过扩展Exception类或者任何它的子类来创建定制异常类。定制的异常可以有自己的变量和方法,我们可以传错误码或异常相关的信息给异常处理者。
9.OutOfMemoryError是什么?
OutOfMemoryError是java.lang.VirtualMachineError的子类,JVM会在耗尽内存时候抛出这个异常。我们可以通过运行Java应用时指定参数,提供更多的内存来解决。
$>java MyProgram -Xms1024m -Xmx1024m -XX:PermSize=64M -XX:MaxPermSize=256m
10.有哪些不同情况导致“主线程异常”?
一些常见的线程异常情况如下:
∆ Exception in thread main java.lang.UnsupportedClassVersionError:
这个异常发生在你用不同版本的JDK来编译和运行时;
∆ Exception in thread main java.lang.NoClassDefFoundError:
有两种不同情况的异常,第一张你提供的class全名是.class扩展;第二种情况是Class找不到;
∆ Exception in thread main java.lang.NoSuchMethodError:main:
这种异常发生在你试图运行一个class类里没有的方法;
∆ Exception in thread “main” java.lang.ArithmeticException:
任何从main方法抛出的异常,都可以在控制板打印出来。第一部分解释这个异常从上面方法里抛出,第二部分打印异常类名称,然后冒号后面是异常的详细信息。
11.final, finally和finalize有什么区别?
final和finally是关键词而finalize是方法。
final关键词被用来修饰类,变量,使它们不能再被分配,用在类上则该类不能被继承,用在方法上则该方法不能被子类重写,finally关键词用在try-catch代码块来确保一定会执行某些代码,例如用在资源回收时候。finalize()方法是对象在销毁时垃圾回收执行的,它用以确保所有全局资源被关闭掉。
12.主线程方法抛出异常会发生什么?
当异常被main()方法抛出,Java运行会中断程序,打印异常信息和系统堆栈。
13.是否可以有空catch代码块?
我们可以有空的catch块,但这种代码是最差的代码。我们不应该写没有catch块的代码,因为异常被捕获,我们得不到任何异常信息,那对于调试来说将是噩梦。应该至少有日志语句来记录异常信息到日志文件里。
14.举一些Java异常处理最佳实践的例子?
一些关于异常处理的最佳实践:
∆ 为方便调试使用明确的异常捕获;
∆ 优先在程序中使用Throw异常(Fail-Fast);
∆ 在程序里捕获异常后,让调用来处理异常;
∆ 使用Java7的自动资源管理特性来确保资源回收掉,或者使用finally块来正确的关闭资源;
∆ 始终使用日志来调试异常;
∆ 使用多异常块使代码简洁;
∆ 使用定制的异常来抛特定类型的异常;
∆ 遵守命名规范,异常定义类使用Exception结尾;
∆ 文档化异常使用@throws;
∆ 异常是有开销的,所以只有当异常确实需要捕获时抛出来。不然你就catch它们,返回null或者空的response。