【Java】理解异常 异常处理
异常处理
Error 和Exception
Exception
和Error
, ⼆者都是Java 异常处理的重要⼦类, 各⾃都包含⼤量⼦类。均继承自Throwable
类。
Error
表⽰系统级的错误, 是java 运⾏环境内部错误或者硬件问题, 不能指望程序来处理这样的问题, 除了退出运⾏外别⽆选择, 它是Java 虚拟机抛出的。
Exception
表⽰程序需要捕捉、需要处理的常, 是由与程序设计的不完善⽽出现的问题, 程序必须处理的问题。
异常类型
Java 中的异常, 主要可以分为两⼤类, 即受检异常(checked exception)和非受检异常( unchecked exception)。
受检异常
对于受检异常来说, 如果⼀个⽅法在声明的过程中证明了其要有受检异常抛出:
public void test() throws new Exception{
}
那么,当我们在程序中调⽤他的时候, ⼀定要对该异常进⾏处理( 捕获或者向上抛出),否则是⽆法编译通过的。这是⼀种强制规范。
这种异常在IO操作中⽐较多。⽐如FileNotFoundException
, 当我们使⽤IO流处理⼀个⽂件的时候, 有⼀种特殊情况, 就是⽂件不存在。 所以 在⽂件处理时他会显⽰抛出FileNotFoundException
, ⽬的就是告诉这个⽅法的调⽤者,这个⽅法不保证⼀定可以成功, 是有可能找不到对应的⽂件的,你要明确的对这种情况做特殊处理。
当我们希望我们的⽅法调⽤者, 明确的处理⼀些特殊情况的时候, 就应该使⽤受检异常。
非受检异常
对于⾮受检异常来说, ⼀般是运⾏时异常, 继承⾃RuntimeException。在编写代码的时候, 不需要显⽰的捕获,但是如果不捕获, 在运⾏期如果发⽣异常就会中断程序的执⾏。
这种异常⼀般可以理解为是代码原因导致的。⽐如发⽣空指针、数组越界等。所以,只要代码写的没问题, 这些异常都是可以避免的。也就不需要我们显⽰的进⾏处理。试想⼀下, 如果你要对所有可能发⽣空指针的地⽅做异常处理的话, 那相当于你的所有代码都需要做这件事。
异常相关关键字
try
⽤来指定⼀块预防所有异常的程序;
catch
⼦句紧跟在try 块后⾯, ⽤来指定你想要捕获的异常的类型;
finally
为确保⼀段代码不管发⽣什么异常状况都要被执⾏;
throw
语句⽤来明确地抛出⼀个异常;
throws
⽤来声明⼀个⽅法可能抛出的各种异常;
正确处理异常
异常的处理⽅式有两种:
1、⾃⼰处理(捕获)。
2、向上抛,交给调⽤者处理。
异常,千万不能捕获了之后什么也不做,或者只是使⽤e.printStacktrace。
处理异常的原则:自己明确知道如何处理的,就处理掉;不知道如何处理的,就交给调⽤者处理。
自定义异常
⾃定义异常就是开发人员⾃⼰定义的异常, ⼀般通过继承Exception
的⼦类的⽅式实现。
编写⾃定义异常类实际上是继承⼀个API 标准异常类,⽤新定义的异常处理信息覆盖原有信息的过程。这种⽤法在Web 开发中也⽐较常见,一般可以⽤来⾃定义业务异常。如余额不⾜、重复提交等。这种⾃定义异常有业务含义,更容易让上层理解和处理
异常链
“异常链”是Java 中⾮常流⾏的异常处理概念, 是指在进⾏⼀个异常处理时抛出了另外⼀个异常, 由此产⽣了⼀个异常链条。
该技术⼤多⽤于将“ 受检查异常” ( checked exception) 封装成为“⾮受检查异常”( unchecked exception)或者RuntimeException。
并且要注意的是,如果因为异常你决定抛出⼀个新的异常,新的异常⼀定要包含原有的异常,这样, 处理程序才可以通过getCause()
和initCause()
⽅法来访问异常最终的根源。
从Java 1.4 版本开始,几乎所有的异常都支持异常链。
以下是Throwable 中支持异常链的方法和构造函数。
Throwable getCause()
Throwable initCause(Throwable)
Throwable(String, Throwable)
Throwable(Throwable)
initCause
和Throwable
构造函数的Throwable
参数是导致当前异常的异常。getCause
返回导致当前异常的异常,initCause
设置当前异常的原因。
以下示例显示如何使用异常链:
try {
//...
} catch (IOException e) {
throw new SampleException("Other IOException", e);
}
上面的示例中,当捕获到IOException 时,将创建一个新的SampleException 异常,并附加原始的异常原因,并将异常链抛出到下一个更高级别的异常处理程序。
try-with-resources
Java 里,对于文件操作IO 流、数据库连接等开销非常昂贵的资源,用完之后必须及时通过close
方法将其关闭,否则资源会一直处于打开状态,这样可能会导致内存泄露等问题。
关闭资源的常用方式就是在finally
块里是释放,即调用close
方法。比如,我们经常
会写这样的代码:
public static void main(String[] args) {
BufferedReader br = null;
try {
String line;
br = new BufferedReader(new FileReader("d:\\123.txt"));
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
// handle exception
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException ex) {
// handle exception
}
}
}
从Java 7 开始,jdk 提供了一种更好的方式关闭资源,使用try-with-resources
语句,改写一下上面的代码,效果如下:
public static void main(String... args) {
try (BufferedReader br = new BufferedReader(new FileReader("d:\\123.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
// handle exception
}
}
finally 和return 的执行顺序
try()
⾥⾯有⼀个return
语句, 那么后⾯的finally{}
⾥⾯的code 会不会被执⾏, 什么时候执⾏, 是在return 前还是return 后?
如果try
中有return
语句, 那么finally
中的代码还是会执⾏。因为return
表⽰的是
要整个⽅法体返回, 所以,finally 中的语句会在return 之前执⾏。
但是return 前执行的finally 块内,对数据的修改效果对于引用类型和值类型会不同:
//测试修改值类型
static int f() {
int ret = 0;
try {
return ret; // 返回0,finally 内的修改效果不起作用
} finally {
ret++;
System.out.println("finally 执行");
}
}
// 测试修改引用类型
static int[] f2(){
int[] ret = new int[]{
0};
try {
return ret; // 返回[1],finally 内的修改效果起了作用
} finally {
ret[0]++;
System.out.println("finally 执行");
}
}
还没有评论,来说两句吧...