Java异常
Java 中的异常(Exception)又称为例外,是一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流。为了能够及时有效地处理程序中的运行错误,Java中引入了异常类的概念。异常类的引入即明确了程序在执行过程中发生的什么问题,又提供类针对异常进行处理的解决方案。
Error:是程序中无法处理的错误,表示运行应用程序中出现了严重的错误。此类错误一般表示代码运行时JVM出现问题。通常有Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如说当jvm耗完可用内存时,将出现OutOfMemoryError。此类错误发生时,JVM将终止线程。非代码性错误。因此,当此类错误发生时,应用不应该去处理此类错误。
Exception::程序本身可以捕获并且可以处理的异常(包括自定义异常)。
运行时异常(不受检异常):RuntimeException类极其子类表示JVM在运行期间可能出现的错误。编译器不会检查此类异常,并且不要求处理异常,比如用空值对象的引用(NullPointerException)、数组下标越界(ArrayIndexOutBoundException)。此类异常属于不可查异常,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。
非运行时异常(受检异常):Exception中除RuntimeException极其子类之外的异常。编译器会检查此类异常,如果程序中出现此类异常,比如说IOException,必须对该异常进行处理,要么使用try-catch捕获,要么使用throws语句抛出,否则编译不通过。包括我们在编写代码时的一些错误,无法通过编译的代码,称谓编译期异常,也属于非运行时异常。
异常产生的原因
在 Java 中一个异常的产生,主要有如下三种原因:
- Java 内部错误发生异常,Java 虚拟机产生的异常。
- 编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等。这种异常称为未检査的异常,一般需要在某些类中集中处理这些异常。
- 手动生成的异常,这种异常称为检査的异常,一般用来告知该方法的调用者一些必要的信息。
异常的处理方式
捕获异常:try,catch,finally
try {
...
} catch (Exception e) {
...
} finally {
...
}
- try代码块:监视代码执行过程,如果代码发生异常,则跳转到catch代码块进行处理。
- catch代码块:可选执行代码块,如果try代码块未发生异常,则不会被执行。并且一个try代码块可以跟随多个catch代码块,根据不同的Exception处理不同的异常,这种情况称之为多重捕获。
- finally代码块:不论try代码块是否发生异常,finally代码块都会被执行,通常用于处理工作。
抛出异常:throws
Java允许在方法的后面使用throws关键字对外声明该方法有可能发生异常,这样调用者在调用方法时,就明确地知道该方法有异常,并且必须在程序中对异常进行处理,否则编译无法通过。
程序员在写代码时,可能预感到会发生异常,那么会手动抛出一个异常,交由使用者去处理
public static void main(String[] args) {
TryTest t=new TryTest();
double d=t.chu(4,2);
System.out.printLn(d);
}
public double chu(double a,double b)throws Exception{
return a/b;
}
如果方法被调用方法抛出一个异常,那么调用者会出现编译错误,无法执行。需要修改成为:
public static void main(String[] args) {
TryTest t=new TryTest();
double d=0;
try {
d = t.chu(4,2);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(d);
}
public double chu(double a,double b)throws Exception{
return a/b;
}
当方法抛出一个异常时,调用者要么使用try代码块进行捕获,要么继续在本方法上抛出异常,由后续的调用者去处理。
声明异常:throw
如果需要在程序中自行抛出异常,则应使用 throw 语句,throw 语句可以单独使用,throw 语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常实例。当 Java 运行时接收到开发者自行抛出的异常时,会中止当前的执行流,跳到该异常对应的 catch 块来处理该异常。即不管是系统自动抛出的异常还是程序员手动抛出的异常,Java 运行时环境对异常的处理没有任何差异。
如果 throw 语句抛出的异常是 Checked 异常,则该 throw 语句要么处于 try 块里,显式捕获该异常,要么放在一个带 throws 声明抛出的方法中,即把该异常交给该方法的调用者处理;
如果 throw 语句抛出的异常是 Runtime 异常,则该语句无须放在 try 块里,也无须放在带 throws 声明抛出的方法中;程序既可以显式使用 try…catch来捕获并处理该异常,也可以完全不理会该异常,把该异常交给该方法调用者处理。
public static void main(String[] args) {
TryTest t = new TryTest();
try {
double d = t.chu(4, 0);
System.out.println(d);
}catch (Exception e) {
System.out.println("0不能做除数");
}
}
public double chu(double a, double b) {
if(b==0) {
throw new RuntimeException();
}
return a / b;
}
常见异常
自定义异常
在程序开发中,可能会遇到程序特有的问题,那么程序员在处理这些特有的问题时,一般会自定义一些异常来使用,这样既能保证程序的安全性和稳定性,又能给出较为友好的提示,以便于使用。
异常可以继承的类
- 继承Throwable类
- 继承Exception类
- 继承RunTimeException类
一般会选择继承Exception和RuntimeException,如果不要求调用者一定要处理抛出的异常,就继承RuntimeException。
public class NumberIsZoraException extends Exception{
public NumberIsZoraException(String msg) {
super(msg);
}
}
public static void main(String[] args) {
TryTest t = new TryTest();
double d=0;
try {
d = t.chu(4, 0);
} catch (NumberIsZoraException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public double chu(double a, double b) throws NumberIsZoraException {
if(b==0) {
throw new NumberIsZoraException("除数不能为0");
}
return a / b;
}
异常的使用原则
java的异常处理涉及到程序流程的跳转,所以虚拟机需要保存程序的执行流程,以便异常发生时能正确的跳转,这也就导致了使用异常时会引起额外的开销,所以,要谨慎地使用异常。
使用异常有如下几个原则:
1、尽量避免使用异常,将异常情况提前检测出来。
2、不要为每个可能会出现异常的语句都设置try和catch。
3、避免在方法中抛出(throw)或者捕获(catch)运行时异常RuntimeException和Error。
4、避免总是catch Exception或Throwable,而要catch具体的异常类。这样可以使程序更加清晰。
5、不要压制、隐瞒异常。将不能处理的异常往外抛,而不是捕获之后随便处理。
6、不要在循环中使用try…catch,尽量将try…catch放在循环外或者避免使用。