首页 > 代码库 > Java 异常处理
Java 异常处理
1.异常处理
1)异常处理机制
当程序中抛出一个异常后,程序从程序中导致异常的代码处跳出,Java虚拟机检测寻找和try关键字匹配的处理该异常的catch块,如果找到,将控制权交给catch块中的代码,然后继续往下执行程序,try块中发生异常的代码不会被重新执行。如果没有找到处理该异常的catch块,在所有的finnally块代码被执行和当前线程所属的ThreadGroup的uncaughtException方法被调用后,遇到异常的当前线程被终止。
2)异常的捕获和处理
2.1)Throwable、Error、Exception
Java异常结构中定义有Throwable类,Error和Exception是其派生的两个子类。其中Exception表示由于网络故障、文件损坏、设备错误、用户输入非法等情况导致的异常,这类异常是可以通过Java异常捕获机制处理的。而Error表示Java运行时环境出现的错误,如JVM内存溢出等。
2.2)try-catch
try语句指定了一段代码,该段代码就是一次捕获并处理例外的范围。在执行过程中,该段代码可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句分别对这些异常做相应的处理。
如果没有例外产生,所有的catch代码段都被略过不执行。
在catch语句块中是对异常进行处理的代码,catch中声明的异常对封装了异常事件发生的信息,在catch语句块中可以使用这个对象的一些方法获取这些信息。
public class Try_catchDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
try{
String str = null;
System.out.println(str);
}catch(NullPointerException e){
System.out.println("出现空指针异常");
}
System.out.println("程序结束了");
}
}
/*
运行结果:
程序开始了
null
程序结束了
*/
2.3)多个catch
每个try语句可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常。catch捕获的异常类型由上至下的捕获异常类型的顺序应该是子类到父类。
通常在写代码的时候,应该在最后一个cathc中捕获Exception,这样可以保证不会因为出现一个未在catch中声明的异常而导致捕获失败使得程序终止。
public class Try_catchDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
try{
String str = "A";
System.out.println(str);
System.out.println(str.charAt(0));
System.out.println(Integer.parseInt(str));
System.out.println("我是伪代码");
}catch(NullPointerException e){
System.out.println("出现空指针异常");
}catch(StringIndexOutOfBoundsException e){
System.out.println("字符串下标越界了");
}catch(Exception e){
System.out.println("发生异常了");
}
System.out.println("程序结束了");
}
}
try程序块中,如果出现了异常,那么从这一条异常代码开始后面的代码就不执行了,如上案例出现字符下标异常,后面的两行代码就不会执行。
2.4)finally
finally语句为异常处理提供一个统一的出口,使得在控制流程转到程序其他部分以前,能够对程序的状态作统一管理。
无论try所指定的程序块中是否抛出异常,finally所指定的代码都要被执行,通常在finaly语句中可以进行资源的消除工作,如关闭打开的文件、删除临时文件等。
finally语句块只能定义在try语句块之后,或者最后一个catch语句块之后,且只能定义一次。
public class Try_catchDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
try{
String str = null;
System.out.println(str);
System.out.println(str.charAt(0));
System.out.println(Integer.parseInt(str));
System.out.println("我是伪代码");
}catch(NullPointerException e){
System.out.println("出现空指针异常");
}catch(StringIndexOutOfBoundsException e){
System.out.println("字符串下标越界了");
}catch(Exception e){
System.out.println("发生异常了");
}finally{
System.out.println("我是真代码,必须执行"); //不管是否发生异常,finally语句块中代码都会执行
}
System.out.println("程序结束了");
}
}
/*
运行结果:
程序开始了
null
出现空指针异常
我是真代码,必须执行
程序结束了
*/
2.5)throw关键字
当程序发生错误而无法处理的时候,会抛出对应的异常对象,除此之外,在某些时候,你可能会想要自行抛出异常。例如在异常处理结束后,再将异常抛出,让下一层异常处理块来捕获,若想自行抛出异常,就需要throw关键字,并生成指定的异常对象。
public class Exception_throw {
public static void main(String[] args) {
Person p = new Person();
try {
p.setAge(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("年龄:"+p.getAge());
}
}
class Person{
private int age;
public int getAge(){
return age;
}
public void setAge(int age) throws Exception{
if(age<0||age>100){
throw new Exception("年龄不合法");
}
this.age = age;
}
}
/*
运行结果:
java.lang.Exception: 年龄不合法
at javase.day08.Person.setAge(Exception_throw.java:22)
at javase.day08.Exception_throw.main(Exception_throw.java:7)
年龄:0
*/
2.6)throws关键字
程序中会声明许多方法(Method),这些方法中可能会因为某些错误而引发异常,但你不希望直接在这个方法中处理这些异常,而希望调用这个它的方法来统一处理,这时候可以使用throws关键字来声明这个方法将会抛出异常。
public class Exception_throw {
public static void main(String[] args) throws Exception {
Person p = new Person();
p.setAge(1000);
System.out.println("年龄:"+p.getAge());
}
}
class Person{
private int age;
public int getAge(){
return age;
}
public void setAge(int age) throws Exception{
if(age<0||age>100){
throw new Exception("年龄不合法");
}
this.age = age;
}
}
/*
运行结果:
Exception in thread "main" java.lang.Exception: 年龄不合法
at javase.day08.Person.setAge(Exception_throw.java:18)
at javase.day08.Exception_throw.main(Exception_throw.java:6):
*/
注意:通常在一个方法中主动抛出一个异常时就要在当前方法上使用throws声明该类异常的抛出以通知调用者处理该异常,只有抛出RuntimeException编译器不要求必须这样做,除此之外,不使用throws声明该类异常编译器就不通过。
public class Exception_throw {
public static void main(String[] args) {
Person p = new Person();
p.setAge(1000);
System.out.println("年龄:"+p.getAge());
}
}
class Person{
private int age;
public int getAge(){
return age;
}
public void setAge(int age){
if(age<0||age>100){
throw new RuntimeException("年龄不合法");
}
this.age = age;
}
}
/*
运行结果:
Exception in thread "main" java.lang.RuntimeException: 年龄不合法
at javase.day08.Person.setAge(Exception_throw.java:18)
at javase.day08.Exception_throw.main(Exception_throw.java:6)
*/
2.7)重写方法时的throws
当使用继承时,在父类的某个方法上声明了throws抛出某些异常,而在子类中重写该方法时,我们可以做以下操作:
- 不处理异常(重写方法时不声明throws)
- 可仅在throws中声明父类中声明的部分异常
- 可在throws中声明父类方法中抛出的异常的子类异常
但是不能做以下操作:
- 重写方法时在throws中声明抛出额外的异常
- 重写方法时在throws中声明父类方法中声明的抛出异常的父类异常
import java.awt.AWTException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
public class Exception_throws {
public void dosome() throws IOException,AWTException{
}
}
class Son extends Exception_throws{
//可以不再抛出任何异常
// public void dosome(){
//
// }
//
//可以仅抛出部分异常
// public void dosome() throws IOException{
//
// }
//可以抛出父类方法抛出异常的子类型异常
// public void dosome() throws FileNotFoundException{
//
// }
//不允许抛出额外异常
// public void dosome() throws SQLException{
//
// }
//不允许抛出父类方法抛出异常的父类型异常
// public void dosome() throws Exception{
//
// }
}
2.Java异常API
1)RuntimeException
Java异常分为可检测异常、非检测异常。
可检测异常:可检测异常经编译器验证,对于声明抛出异常的任何方法,编译器将强制执行处理或声明规则,不捕捉这个异常,编译器就通不过,不允许编译。
非检测异常:非检测异常不遵循处理或者声明规则。在产生此类异常时,不一定非要采取任何适当操作,编译器不会检查是否已经解决了这个异常。
RuntimeException类属于非检测异常,因为普通JVM操作引起的运行时异常随时可能发生,此类异常一般是由特定操作引发。但这些操作在Java应用程序中会频繁出现。因此它们不受编译器检查与处理或声明规则的限制。
2)常见RuntimeException
IllegalArgumentException:抛出的异常表明向方法传递了一个不合法或不正确的参数
NullPointerException:当应用程序试图在需要对象的地方使用null时,抛出该异常
ArrayIndexOutOfBoundsException:当使用的数组下标超出数组允许范围时,抛出该异常
ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出该异常
NumberFormatException:当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常
3.Exception常用API
1)printStackTrace
Throwable中定义了一个方法可以输出错误信息,用来跟踪异常事件发生时执行堆栈的内容。
方法定义:
void printStackTrace()
try{
...
}catch(Exception e){
e.printStackTrace(); //输出执行堆栈信息
}
2)getMessage
Throwable中定义了一个方法可以得到有关异常事件的信息。
方法定义:
String getMessage()
try{
...
}catch(Exception e){
System.out.println(e.getMessage());
}
3)getCause
很多时候,当一个异常由另一个异常导致异常而被抛出的时候,Java库和开放源代码会将一种异常包装成另一种异常。这时,日志记录和打印根异常就变得非常重要。Java异常类提供了getCause()方法来检索导致异常的原因,这些可以对异常根层次的原因提供更多的信息。该Java实践对代码的调试或故障排除有很大的帮助。另外,如果要把一个异常包装成另一个异常,构造一个新异常就要传递源异常。
Throwable getCause()
案例:printStackTrace getMessage演示
public class Exception_throw {
public static void main(String[] args) {
Person p = new Person();
try {
p.setAge(1000);
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
System.out.println("年龄:"+p.getAge());
}
}
class Person{
private int age;
public int getAge(){
return age;
}
public void setAge(int age) throws Exception{
if(age<0||age>100){
throw new Exception("年龄不合法");
}
this.age = age;
}
}
/*
运行结果:
java.lang.Exception: 年龄不合法
at javase.day08.Person.setAge(Exception_throw.java:23)
at javase.day08.Exception_throw.main(Exception_throw.java:7)
年龄不合法
年龄:0
*/
4.自定义Exception
1)自定义异常的意义
Java异常机制可以保证程序更安全和更健壮。虽然Java类库已经提供很多可以直接处理异常的类,但是有时候为了更加精准的捕获和处理异常以呈现更好的用户体验,需要开发者自定义异常。
2)继承Exception自定义异常
创建自定义异常类语法:
public class 自定义异常类名 extends Exception{
...
}
3)编写构造方法
当定义好自定义异常后,可以通过Eclipse来自动生成相应的构造方法。
- 声明一个类并继承Exception
- 右键点击Source
- 选择Generate Constructors from Superclass
- 选中父类中所有构造方法后确认生成
public class MyException extends Exception{
public MyException() {
super();
// TODO 自动生成的构造函数存根
}
public MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO 自动生成的构造函数存根
}
public MyException(String message, Throwable cause) {
super(message, cause);
// TODO 自动生成的构造函数存根
}
public MyException(String message) {
super(message);
// TODO 自动生成的构造函数存根
}
public MyException(Throwable cause) {
super(cause);
// TODO 自动生成的构造函数存根
}
}
Java 异常处理