JAVA异常处理机制

1. 异常处理机制

1.1. 如何捕获异常

Java语言给程序员提供了try…catch代码段,用来捕获可能出现的异常。

try

{

  可能会出现异常的代码段;

}

catch (异常类型名 处理该异常对象)

{

   异常处理代码段;

}

如果try中的代码段可能会出现多种异常,则可以在try语句段后使用多个catch语句段来捕获这些异常。但catch捕获异常是按顺序的,如果前一个异常类被捕获,后面的异常类即使发生,也不会被捕获了。因此, 将最细化的异常类(子异常类),先用catch捕获,之后再用catch捕获父异常类( 简称:先处理小异常,再处理大异常)。

package com;

import java.io.*;

class TryCatchDemo {
	public static void main(String argv[]) {
		File file = new File("abc.txt");
		int a[] = { 1, 2 };
		try {
			System.out.println(3 / 0);
		} catch (ArithmeticException e1) {
			System.out.print("3/0: ");
			System.out.println("This is ArithmeticException");
		}
		try {
			System.out.println(a[2]);
		} catch (ArrayIndexOutOfBoundsException e2) {
			System.out.print("a[2] is out of Array: ");
			System.out.println("This is ArrayIndexOutOfBoundsException");
		}
		try {
			BufferedReader input = new BufferedReader(new FileReader(file));
		} catch (FileNotFoundException e3) {
			System.out.print("abc.txt is not found: ");
			System.out.println("This is FileNotFoundException");
		} catch (IOException e) {
			System.out.println("This is IOException");
		}
	}
}

1.2. Java 7提供的多异常捕获

从Java 7开始,一个catch块可以捕获多种类型的异常,但需要注意以下两个地方:

Ø  捕获多种类型的异常时,多种异常类型之间用竖线(|)隔开。

Ø  捕获多种类型的异常时,异常变量有隐式的final修饰,因此程序不能对异常变量重新赋值;而捕获一种类型的异常时,异常变量没有final修饰。

package com.exception;

public class MultiExceptionTest {
	public static void main(String[] args) {
		try {
			int a = Integer.parseInt(args[0]);
			int b = Integer.parseInt(args[1]);
			int c = a / b;
			System.out.println("您输入的两个数相除的结果是:" + c);
		} catch (IndexOutOfBoundsException | NumberFormatException
				| ArithmeticException ie) {
			System.out.println("程序发生了数组越界、数字格式异常、算术异常之一");
			// 捕捉多异常时,异常变量默认有final修饰,
			// 所以下面代码有错:
			ie = new ArithmeticException("test"); // ①
		} catch (Exception e) {
			System.out.println("未知异常");
			// 捕捉一个类型的异常时,异常变量没有final修饰
			// 所以下面代码完全正确。
			e = new RuntimeException("test"); // ②
		}
	}
}

1.3. 访问异常信息

如果程序需要在catch代码块中访问异常对象的相关信息,可以通过调用catch语句块中的异常形参的方法来获取异常信息,当Java运行时决定调用某个catch语句块来处理该异常对象时,会将该异常对象赋给catch块后的异常参数,程序就可以通过该参数来获得异常的相关信息。

异常对象常用的几个方法:

Ø  getMessage():返回该异常的详细描述字符串。

Ø  printStackTrace():将该异常的跟踪栈信息输出到标准错误输出。

Ø  printStackTrace(PrintStream  s):将该异常信息的跟踪栈信息输出到指定输出流。

Ø  getStackTrace():返回该异常的跟踪栈信息。

1.4. 使用finally回收资源

finally语句块主要作用是回收资源,比如:关闭数据库连接,网络连接,磁盘文件,输入输出流等等。这些资源必须显示的回收。Java的垃圾回收机制不会回收任何物理资源,它只回收堆内存中对象所占用的内存。

在很多情况下,希望无论是否出现异常,某些语句段都需要被执行,特别是释放资源的操作,例如打开文件后的关闭操作。那么就可以把这部分代码放在finally语句段中,即使try或catch语句段含有return语句,程序都会在异常抛出后限制性finally语句段,除非try或catch语句段中执行system.exit()方法,或是出现Error错误,finally语句段才不会被执行而退出程序。

try···catch···finally结构的语法格式为:

try{

    可能会出现异常的代码段;

}

catch (异常类型名 处理该异常对象){

   异常处理代码段;

}

 finally{

总是需要执行的代码段;

}

package com.exception;

import java.io.*;

public class FinallyTest {
	public static void main(String[] args) {
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("a.txt");
		} catch (IOException ioe) {
			System.out.println(ioe.getMessage());
			// return语句强制方法返回
			return; // ①
			// 使用exit来退出虚拟机
			// System.exit(1); //②
		} finally {
			// 关闭磁盘文件,回收资源
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException ioe) {
					ioe.printStackTrace();
				}
			}
			System.out.println("执行finally块里的资源回收!");
		}
	}
}

运行结果:

a.txt (系统找不到指定的文件。)

执行finally块里的资源回收!

如果将①处的语句注释掉,取消②处的注释,则运行结果为:

a.txt (系统找不到指定的文件。)

上面程序的try块后增加了finally语句块,用于回收在try语句块中打开的物理资源。注意程序的catch块中有一条return语句,该语句强制方法返回。通常情况下,一旦在方法里执行到return语句时,程序将立即结束该方法,但这里不同,虽然return也是强制方法立即结束,但一定会在先执行finally代码块中的代码。

       注:除非在try块、catch块中调用了退出虚拟机的方法,否则不管在在try块、catch块中在try块、catch块中执行怎样的代码,出现怎样的情况,异常处理的finally块总会被执行。  

       当Java程序执行try和catch语句块遇到了return语句或者throw语句,这两个语句都会导致该方法立即结束,但系统并不会立即执行这两个语句,而是去寻找该异常处理流程中是否包含finally语句块,如果没有finally语句块,程序立即执行return或throw语句,方法终止,如果有finally语句块,系统立即开始执行finally语句块,只有当finally语句块执行完成后,系统才会再次调回执行try语句块,catch语句块里的return或throw语句。如果finally语句块中也使用了return或throw等导致方法终止的语句,则finally语句块已经终止了方法,系统将不会再跳回执行try或catch语句块中的任何代码。因此,应该尽量避免在finally块里使用return或throw语句等导致方法终止的语句

2. Checked异常和Runtime异常体系

Java的异常被分为两大类:Checked异常和Runtime异常(运行时异常)。所有RuntimeException类极其子类的实例被称为Rtuntime异常;不是RuntimeException类极其子类的异常实例则称为Checked异常。Checked异常是Java语言特有的异常,其他语言都没有提供Checked异常。

处理Checked异常的两种方式:

Ø  当前方法明确指定如果处理该异常,程序应该使用try…catch块来捕获该异常,然后在对应的catch块中修补该异常。

Ø  当前方法不知道如果处理这些异常,应该在定义该方法时声明抛出该异常。

Runtime异常则更加灵活,Runtime异常无须显式声明抛出,如果程序需要捕捉Runtime异常,也可以使用try...catch语句块来捕捉Runtime异常。

2.1.使用Throws 声明抛出异常

当前方法不知道应该如果处理这种类型的异常,该异常应该由上一级调用者处理,如果main方法也不指定该如果处理这种类型的异常,也可以使用throws声明抛出异常,该异常将交给Java虚拟机处理。

Throws声明抛出异常只能在方法签名中使用,throws可以声明抛出多个异常类,多个异常类之间可以逗号隔开。

格式: public void add()throws ExceptionClass1,ExceptionClass2{ }

注意:使用Throws声明抛出异常时有一个限制:在方法重写时,子类方法中声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,子类方法中不允许比父类方法声明抛出更多的异常。

package com.exception;

import java.io.*;
public class OverrideThrows
{
	public void test()throws IOException
	{
		FileInputStream fis = new FileInputStream("a.txt");
	}
}
class Sub extends OverrideThrows
{
	// 子类方法声明抛出了比父类方法更大的异常
	// 所以下面方法出错
	public void test()throws Exception
	{
	}
}

2.2.使用throw抛出异常

当程序出现错误时,系统会自动抛出异常;除此之外,Java也允许程序自行抛出异常,自行抛出异常使用throw语句完成。 throw语句抛出的不是异常类,而是一个异常实例而且每次只能抛出一个异常实例。throw语句的语法格式如下:

throw ExceptionInstance;

package com.exception;

public class ThrowTest {
	public static void main(String[] args) {
		try {
			// 调用声明抛出Checked异常的方法,要么显式捕获该异常
			// 要么在main方法中再次声明抛出
			throwChecked(-3);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		// 调用声明抛出Runtime异常的方法既可以显式捕获该异常,
		// 也可不理会该异常
		throwRuntime(3);
	}

	public static void throwChecked(int a) throws Exception {
		if (a > 0) {
			// 自行抛出Exception异常
			// 该代码必须处于try块里,或处于带throws声明的方法中
			throw new Exception("a的值大于0,不符合要求");
		}
	}

	public static void throwRuntime(int a) {
		if (a > 0) {
			// 自行抛出RuntimeException异常,既可以显式捕获该异常
			// 也可完全不理会该异常,把该异常交给该方法调用者处理
			throw new RuntimeException("a的值大于0,不符合要求");
		}
	}
}

    如果throw语句抛出的异常是Checked异常,则该throw语句要么处于try块中,显式捕获异常,要么放在一个带throws声明抛出的方法中,即把该异常交给该方法的调用者处理;如果throw语句抛出的异常是Runtime异常,则该语句无须放在try块中,也无须放在带throws声明抛出的方法中,程序既可以显示使用try…catch来捕获并处理该异常,也可以完全不理会该异常,将该异常交给该方法调用者处理。

2.3. 自定义异常类

用户自定义异常都必须继承Exception基类,如果希望自定义Runtime异常,则应该继承RuntimeException基类。 定义异常类时通常需要提供两种构造器 :一个是无参数的构造器;另一种是带一个字符串参数的构造器,这个字符串将作为该异常对象的详细说明,也就是异常对象的getMessage方法的返回值。

package com.exception;

public class AuctionException extends Exception {
	// 无参数的构造器
	public AuctionException() {
	} // ①

	// 带一个字符串参数的构造器
	public AuctionException(String msg) // ②
	{
		super(msg);
	}
}
如果需要自定义Runtime异常类,只需将程序中的Exception基类改为RuntimeException基类,其他地方无须修改。

3 如何抛出异常

  throw和throws抛出异常:

②  throws语句在方法声明中使用,抛出异常

  throw语句在方法体内部使用,抛出异常

方法体中若使用了throw语句抛出异常,则必须在该方法的声明中,采用throws语句来声明该方法体中抛出的异常,同时,throws语句声明抛出的异常,必须是方法体中throw语句抛出的异常或该异常的父类。

package com;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;

class ThrowTest {
	// 定义throwTest1()方法,并抛出ArithmeticException异常
	public void throwTest1() throws ArithmeticException {
		// 方法中输出3/0的值,会出现异常
		System.out.println(3 / 0);
	}

	// 定义throwTest2()方法,并抛出ArrayIndexOutOfBoundsException异常
	public void throwTest2() throws ArrayIndexOutOfBoundsException {
		int a[] = { 1, 2 };
		// 方法中输出数组a[2]的值,由于数组只包含a[0]=1,a[1]=2两个元素,所以出现异常
		System.out.println(a[2]);
	}

	// 定义throwTest3()方法,并抛出FileNotFoundException异常
	public void throwTest3() throws FileNotFoundException {
		File file = new File("abc.txt");
		// 方法中打开文件使用BufferedReader类打开文件,由于文件不存在出现异常
		new BufferedReader(new FileReader(file));
	}

	// 定义throwTest4()方法,并抛出FileNotFoundException异常
	public void throwTest4() throws FileNotFoundException {
		// 直接用throw语句抛出FileNotFoundException异常
		throw new FileNotFoundException("abc.txt");
	}

	public static void main(String argv[]) {
		ThrowTest throwTest = new ThrowTest();
		// 调用抛出算术异常的方法throwTest1(),并捕获处理异常
		try // 检查是否出现异常
		{
			throwTest.throwTest1();
		} catch (ArithmeticException e1) // 捕捉异常
		{
			System.out.print("3/0: ");
			System.out.println("This is ArithmeticException");
		}
		// 调用抛出数组越界异常的方法throwTest2(),并捕获处理异常
		try // 检查是否出现异常
		{
			throwTest.throwTest2();
		} catch (ArrayIndexOutOfBoundsException e2) // 捕捉异常
		{
			System.out.print("a[2] is out of Array: ");
			System.out.println("This is ArrayIndexOutOfBoundsException");
		}
		// 调用抛出I/O异常的方法throwTest3(),并捕获处理异常
		try // 检查是否出现异常
		{
			throwTest.throwTest3();
		} catch (FileNotFoundException e3) // 捕捉异常
		{
			System.out.print("abc.txt is not found: ");
			System.out.println("This is FileNotFoundException");
		}
		// 调用抛出I/O异常的方法throwTest4(),并捕获处理异常
		try // 检查是否出现异常
		{
			throwTest.throwTest4();
		} catch (FileNotFoundException e4) // 捕捉异常
		{
			System.out.print("abc.txt is not found: ");
			System.out.println("This is FileNotFoundException");
		}
	}
}

4. 异常处理的语法规则

①  try语句不能单独存在,可以和catch、finally组成 try…catch…finally、try…catch、try…finally三种结构,catch语句可以有一个或多个,finally语句最多一个,try、catch、finally这三个关键字均不能单独使用。

②  try、catch、finally三个代码块中变量的作用域分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。

③  多个catch块时候,Java虚拟机会匹配其中一个异常类或其子类,就执行这个catch块,而不会再执行别的catch块。

④  throw语句后不允许有紧跟其他语句,因为这些没有机会执行。

⑤  如果一个方法调用了另外一个声明抛出异常的方法,那么这个方法要么显示采用try…catch块处理异常,要么在方法中声明抛出。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值