目录
一、定义
throwable
exception
error
二、异常类型
三、异常处理5个关键字
try
catch
throw
throws
finally
四、模拟面试
面试题1:try语句可以嵌套吗?
面试题2:下面代码有问题吗?
面试题3:try块中加退出语句return会怎样?
五、异常的栈轨迹(Stack Trace)
1、printStackTrace()
2、getStackTrace()方法
3、fillInStackTrace()
Android 性能优化(一)闪退治理、卡顿优化、耗电优化、APK瘦身 ,这篇中我强调“运行稳定大于一切”,保证程序运行中不出现Crash,要比卡顿、耗电、安装包大小等方面更为重要。
当一个方法发生错误时,此方法会产生一个对象并将其交给运行时系统。 这个对象就叫异常对象,它包含了错误信息、异常类型以及程序的状态。创建一个异常对象并将其交给运行时系统称之为抛出异常。造成Crash的原因有很多,而在程序运行过程中Throwable抛出的异常或错误就是其中最普遍的成因。本篇将对Throwable下的Exception和error有一个比较清晰的认识。
可抛出的意思,是根基类,子类有异常exception和错误error。在java中只有Throwable类型的实例才能被抛出(throw)或者捕获(catch),它是异常处理机制的基本类型。
表示程序运行中出现的非正常状态,并告诉我们程序发生了什么问题,且程序自身可以进行拦截或处理的异常。
是指程序无法处理的错误,其中包括程序运行时 JVM出现的问题。
Throwable分为检查型异常(Checked Exception)和非检查型异常(Unchecked Exception)。
- 检查型异常必须在源码中进行try-catch捕获处理,这是编译检查的一部分。
- 类似NullPointerException,ArrayIndexOfBoundException就是非检查型异常,通常是可以通过编码避免的逻辑错误。
- 编译期不检查,如果抛出了非检查型异常,那就是编码逻辑有问题,需要开发者解决。
异常处理过程:一般情况下是用try来执行一段程序,如果系统会抛出(throw\throws)一个异常对象,可以通过它的类型来捕获(catch)它,或通过总是执行代码块(finally)来处理。
需要注意:try-catch代码段会产生额外的性能开销,Java每实例化一个Exception,都会对当时的栈进行快照。
答:可以的。每当遇到一个try语句,指定的一段可能产生异常的代码块就会被放入异常栈中,直到所有的try语句都完成。如果下一级的try语句没有对某种异常进行处理(catch),异常栈就会执行出栈操作,交给上一层处理,直到遇到有处理这种异常的try语句或者最终将异常抛给JVM。
try {
Thread.sleep(500L);
} catch (Exception e) {
}
答:这段代码违反了异常处理的两个基本原则。 第一,尽量不要捕获类似Exception这样的通用异常,而是应该捕获特定异常。例如,这里Thread.sleep() 应当捕获 InterruptedException。第二,在异常处理(catch)中不要生吞(swallow)异常。这是特别注意的事情,因为很可能会导致非常难以诊断的诡异情况。
try块中有return语句时,仍然会执行finally块中的语句,然后方法再返回。
参考:Java异常的栈轨迹(Stack Trace) - wawlian - 博客园
Exception类本身除了定义了几个构造器之外,所有的方法都是从其父类Throwable继承过来的,而且和异常相关的方法都是从父类继承过来的。其中就有一个 printStackTrace() 方法。
这个方法会将Throwable对象的栈轨迹信息打印到标准错误输出流上。输出的大体样子如下:
java.lang.NullPointerException
at MyClass.mash(MyClass.java:9)
at MyClass.crunch(MyClass.java:6)
at MyClass.main(MyClass.java:3)
输出的第一行是toString()方法的输出,后面几行的内容都是之前通过fillInStackTrace()方法保存的内容。
对printStackTrace()方法所打印信息进行访问。它会返回一个栈轨迹元素的数组 StackTraceElement[]。
下面是一个使用getStackTrace()访问这些轨迹栈元素并打印输出的例子:
public class TestPrintStackTrace {
public static void f() throws Exception{
throw new Exception("出问题啦!");
}
public static void g() throws Exception{
f();
}
public static void main(String[] args) {
try {
g();
}catch(Exception e) {
System.out.println("e.printStackTrace:");
e.printStackTrace();
System.out.println("e.getStackTrace:");
for(StackTraceElement elem : e.getStackTrace()) {
System.out.println(elem);
}
}
}
}
getStackTrace()的输出和printStackTrace()的输出基本上是一样的,如下:
e.printStackTrace:
java.lang.Exception: 出问题啦!
at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
at TestPrintStackTrace.g(TestPrintStackTrace.java:6)
at TestPrintStackTrace.main(TestPrintStackTrace.java:10)
e.getStackTrace:
TestPrintStackTrace.f(TestPrintStackTrace.java:3)
TestPrintStackTrace.g(TestPrintStackTrace.java:6)
TestPrintStackTrace.main(TestPrintStackTrace.java:10)
我们在前面也提到了这个方法。要说清楚这个方法,首先要讲一下捕获异常之后重新抛出的问题。
首先,在catch代码块中捕获到异常,调用printStackTrace()打印栈轨迹,又重新throw出去。在上一级的方法调用中,再捕获这个异常并且打印出栈轨迹信息,这两个栈轨迹信息会一样吗?我们看一下代码:
public class TestPrintStackTrace {
public static void f() throws Exception{
throw new Exception("出问题啦!");
}
public static void g() throws Exception{
try {
f();
}catch(Exception e) {
e.printStackTrace();
throw e;
}
}
public static void main(String[] args) {
try {
g();
}catch(Exception e) {
e.printStackTrace();
}
}
}
在main方法中调用g()方法,并且捕获抛出的异常。此时 f()方法抛出异常“出问题啦!”,按理说两次打印栈轨迹的信息应该不同,第二次打印的信息应该没有关于f()的信息。输出结果如下:事实上,两次打印栈轨迹信息是一样的!
java.lang.Exception: 出问题啦!
at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
at TestPrintStackTrace.main(TestPrintStackTrace.java:16)
java.lang.Exception: 出问题啦!
at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
at TestPrintStackTrace.main(TestPrintStackTrace.java:16)
因此,当捕获到异常立即抛出,在上级方法调用中再次捕获这个异常,两次打印的栈轨迹信息是一样的。
因为,没有将当前线程当前状态下的轨迹栈的状态保存进Throwabe中。而fillInStackTrace()方法刚好做的就是这样的保存工作。
public Throwable fillInStackTrace()
可见,fillInStackTrace()方法的返回值是保存了当前栈轨迹信息的Throwable对象。g()方法代码改动如下:
public static void g() throws Exception{
try {
f();
}catch(Exception e) {
e.printStackTrace();
throw (Exception)e.fillInStackTrace();
}
}
输出结果:
java.lang.Exception: 出问题啦!
at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
at TestPrintStackTrace.main(TestPrintStackTrace.java:17)
java.lang.Exception: 出问题啦!
at TestPrintStackTrace.g(TestPrintStackTrace.java:11)
at TestPrintStackTrace.main(TestPrintStackTrace.java:17)
我们看到,在main方法中打印栈轨迹已经没有了f()相关的信息了。