异常原理分析
在我们编写一个代码的时候必然会出现这样那样的问题,这些问题可能是明显的,也有可能是不明显的。从理论上来说,一个程序员应当具备处理代码出现的大多数问题的能力,但是这并不意味着程序员要思考处理所有的代码问题。假如一个程序员要思考并解决所写代码的所有问题的话,那么不仅程序员要耗费大量的时间与精力,编写代码的效率也会大大降低,这样对一个公司的产出是非常不利的。因此,在变成语言中大多会加上一些对常见异常的处理手段。
异常的英文是Exception,翻译为例外,说的就是正常情况之外的情况,故异常指的是程序在运行过程中出现的非正常情况,例如用户输入不合规数据、除数为零、需要处理的文件不存以及数组的下标越界等情况。一般情况之下,运行中的程序在遇到这些情况时会直接报错,然后在遇到异常的地方停止执行代码,退出程序。在实际编程中,程序员一般不希望出现这种情况,当然对于整个公司来说更不愿意出现这种代码遇到异常直接退出运行的情况。这里打一个不是很恰当的比方,现在很多人都玩游戏,而在游戏中或多或少都会遇到bug,如果在玩家遇到bug时游戏直接退出了,而当玩家再次登录时很大概率会重复触发bug,于是玩家会再次退出,由此陷入死循环,这样不仅玩家的游戏体验非常糟糕,对于游戏开发公司来说也是一次致命的错误。因此游戏开发公司希望的是,即使遇到一个小bug游戏依然能正常运行。
所以异常的处理指的就是即使程序出现问题,但是程序依旧可以完整走完。比如我们对比一下两个程序的运行结果,第一个程序没有进行异常处理,这时程序在遇到异常后就停止了,并没有执行异常之后的代码:System.out.println("step2");,而当进行异常处理之后,代码继续执行异常之后的代码,运行结果后面会打印出异常信息。
package cn.test.exception;
public class Test04 {
public static void main(String[] args) {
System.out.println("step1");
int a = 2/0;
System.out.println("step2");
}
}
package cn.test.exception;
public class Test04 {
public static void main(String[] args) {
System.out.println("step1");
try{
int a = 2/0;
}catch (Exception e){
e.printStackTrace();//打印错误信息
}
System.out.println("step2");
}
}
异常处理
java中,JDK定义了很多的异常类,这些异常类都是Throwable类的子类,具体可以分为Exception和Error两大类,其中Exception类又可以分为CheckedExcception和UncheckedException两大类。在这两大类中CheckedException类中定义了非常多的异常,但是这些异常一般通不过编译,也就是编译时系统就能检查出异常来,而UncheckedException中只有RuntimeException这一类,这个类中的异常是运行时的异常,在编译时并不会被发现,比如上面的例子中 a = 2/0;这一行的异常就属于这一类。Error类的异常已经超出了程序员要处理的范畴,它表示的java本身运行的异常,是系统的原因。因此总结下来,java中的异常处理目前只有UncheckedException类中的RunException。
java中对异常的处理除了程序员手动排除,对代码进行修改,使程序符合现实逻辑外,还有捕获异常、声明异常以及try-with-resourse三种方式。
异常捕获
语法结构
捕获异常通过trw-catch-finally关键字来执行,它的语法结构为:
try{
语句1;
语句2;
}catch(Exception1 e){
}catch(Exception2 e){
}finally{
}
其中,try结构是用来执行程序语句的,如果语句中出现异常,系统会抛出异常,随后catch会对系统抛出的异常进行捕获并处理。在这里如果出现的多个异常之间存在关系,则遵循子类在前,父类在后的异常捕获方式。最后,finally语句为捕获的异常提供了一个统一的处理出口,finally语句最多只能有一条,可以没有,但是一旦finally中拥有语句,则这个语句必须被执行,finally结构中的语句一般用于关闭资源。
在上面的这个语句结构中,有一点要注意的是,当try结构中的语句1出现异常后可能会抛出一个或者几个异常,当出现异常后语句2是被跳过的,并不会被执行,即使异常处理结束了也不会回去执行语句2,要排除Excepton1是语句1的异常,Exception2是语句2的异常的这种错误想法。
演示代码
package cn.test.exception;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Test01 {
public static void main(String[] args) {
FileReader reader = null;
try {
reader = new FileReader("F:/a.txt");
char c = (char)reader.read();
char c1 = (char)reader.read();
System.out.println(""+c+c1);
}catch ( FileNotFoundException e){
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}finally{
try{
if(reader != null){
reader.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
}
异常声明
概述
用异常的声明的方式来处理异常的方法是通过关键字throws关键字来实现的,这种处理异常的方法一般用于java中的方法中,在方法后面添加throws Exception即可将方法体中的异常进行“抛出”处理。异常声明的方式是在异常捕获的基础上实现的,采用异常声明的时候方法体语句中就没了catch语句,但是try语句是必不可少的。
异常声明的方式处理异常最简单的理解方式就是请别人帮你解决问题,将难题抛给别人。因此不是问题不处理,而是把问题交给别人处理而已,比如我们将上面的那个程序中读取文件的部分封装为一个方法,在这个方法中将异常用关键字throws关键字抛出,这时如果我们在main方法中调用该方法,则系统会提示异常,这时就需要我们在main方法中处理异常。同样,在main方发中也可以选择用异常的捕获处理异常,也可以再次将异常抛出,但是在main方法中将异常抛出不是很规范,可以用,但不建议。
演示代码
package cn.test.exception;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Test02 {
public static void main(String[] args) {
try {
readFile("F:/a.txt");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void readFile(String path) throws Exception {
FileReader reader = null;
try {
reader = new FileReader(path);
char c = (char) reader.read();
char c1 = (char) reader.read();
System.out.println("" + c + c1);
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
try-with-resource
try-with-resourcre是JDK7之后出现的一种新的结构,不过说是新的结构其实是对异常捕获的语法结构进行了简化。在异常捕获的语法结构中finally中的内容一般是对已经打开的资源进行关闭操作,以免导致外部资源泄露的问题。但是每一次进行异常处理都需要手动写代码对资源进行关闭无形中增加了程序员的冗杂的工作内容,那么可不可以让它自动关闭呢?于是try-with-resource结构就出现了,在这个结构中,省略了finally的部分,在进行异常处理后,打开的资源会直接被关闭。而这种省略只是在程序员的书写中表现出来而已,编译的时候体现的仍然是异常捕获的代码,也就是说系统自动在代码中添加了finally的部分。
package cn.test.exception;
import java.io.FileReader;
public class Test02 {
public static void main(String[] args) {
try(FileReader reader = new FileReader("F:/a.txt")){
char c = (char)reader.read();
char c1 = (char)reader.read();
System.out.println(""+c+c1);
}catch (Exception e){
e.printStackTrace();
}
}
}