首页 > 代码库 > 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来自动生成相应的构造方法。

  1. 声明一个类并继承Exception
  2. 右键点击Source
  3. 选择Generate Constructors from Superclass
  4. 选中父类中所有构造方法后确认生成
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 异常处理