JAVA中的异常处理

异常的基本概念

异常:异常是导致程序中断运行的一种指令流,如果不对异常进行正确处理,则可能导致程序的中断执行,造成不必要的损失,所以在程序的设计中必须考虑各种异常的发生,并正确的做好相应的处理,这样才能保证程序的正常运行。

进行异常处理的目的?


public class ExceptionDemo01{
	public static void main(String args[]){
		System.out.println("********** 计算开始 ***********") ;
		int i = 10 ;		// 定义整型变量
		int j = 0 ;			// 定义整型变量
		int temp = i / j ;	// 此处产生了异常
		System.out.println("两个数字相除的结果:" + temp) ;
		System.out.println("********** 计算结束 ***********") ;
	}
};

一旦产生异常之后,异常之后的语句并不会执行,而是直接结束程序并将错误报告给用户。

在计算机的发展史有两大杀手:断电、被除数为0

处理异常

如果要想处理异常,则必须掌握异常的处理格式。

对异常进行捕捉:
public class ExceptionDemo02{
	public static void main(String args[]){
		System.out.println("********** 计算开始 ***********") ;
		int i = 10 ;		// 定义整型变量
		int j = 0 ;			// 定义整型变量
		try{
			int temp = i / j ;	// 此处产生了异常
			System.out.println("两个数字相除的结果:" + temp) ;
			System.out.println("----------------------------") ;
		}catch(ArithmeticException e){
			System.out.println("出现异常了:" + e) ;
		}
		System.out.println("********** 计算结束 ***********") ;
	}
};



程序的执行流程


以上的操作代码中只使用了基本的异常处理格式:try...catch, try中捕获异常,出现异常之后的代码将不再被执行,而是跳转到相应的catch中执行,用于处理异常。

当然对于异常也可以设置统一的出口,使用finally完成。

public class ExceptionDemo03{
	public static void main(String args[]){
		System.out.println("********** 计算开始 ***********") ;
		int i = 10 ;		// 定义整型变量
		int j = 0 ;			// 定义整型变量
		try{
			int temp = i / j ;	// 此处产生了异常
			System.out.println("两个数字相除的结果:" + temp) ;
			System.out.println("----------------------------") ;
		}catch(ArithmeticException e){	// 捕获算术异常
			System.out.println("出现异常了:" + e) ;
		}finally{	// 作为异常的统一出口
			System.out.println("不管是否出现异常,都执行此代码") ;
		}
		System.out.println("********** 计算结束 ***********") ;
	}
};

以上程序中只是在代码中处理了一个异常,如果存在多个异常呢?

修改代码,通过初始化参数,输入i和j的值

public class ExceptionDemo04{
	public static void main(String args[]){
		System.out.println("********** 计算开始 ***********") ;
		int i = 0 ;		// 定义整型变量
		int j = 0 ;			// 定义整型变量
		try{
			String str1 = args[0] ;		// 接收第一个参数
			String str2 = args[1] ;		// 接收第二个参数
			i = Integer.parseInt(str1) ;	// 将第一个参数由字符串变为整型
			j = Integer.parseInt(str2) ;	// 将第二个参数由字符串变为整型
			int temp = i / j ;	// 此处产生了异常
			System.out.println("两个数字相除的结果:" + temp) ;
			System.out.println("----------------------------") ;
		}catch(ArithmeticException e){	// 捕获算术异常
			System.out.println("出现异常了:" + e) ;
		}
		System.out.println("********** 计算结束 ***********") ;
	}
};
输入合法的参数:10 2


此时的数据实际上是交给用户输入的,既然是用户输入,就有可能输入不正确,实际上以上的代码出现了三个问题:

1、如果没有输入参数或者输入的参数不够,就会出现问题:


数组超出绑定,因为没有输入参数

2、如果现在输入的时候输入的参数不是数字,而是字母;


3、算术异常,如果被除数为0的话


所以以上程序产生了三个比较明显的异常:

数组超出绑定:ArrayIndexOutOfBoundsException

数字格式化异常:NumberFormatException

算术异常:ArithmeticException

如果想要程序执行正确,则必须同时对三个异常进行处理,所以此时就需要三个catch语句。

public class ExceptionDemo05{
	public static void main(String args[]){
		System.out.println("********** 计算开始 ***********") ;
		int i = 0 ;		// 定义整型变量
		int j = 0 ;			// 定义整型变量
		try{
			String str1 = args[0] ;		// 接收第一个参数
			String str2 = args[1] ;		// 接收第二个参数
			i = Integer.parseInt(str1) ;	// 将第一个参数由字符串变为整型
			j = Integer.parseInt(str2) ;	// 将第二个参数由字符串变为整型
			int temp = i / j ;	// 此处产生了异常
			System.out.println("两个数字相除的结果:" + temp) ;
			System.out.println("----------------------------") ;
		}catch(ArithmeticException e){	// 捕获算术异常
			// System.out.println("算术异常:" + e) ;
			e.printStackTrace() ;
		}catch(NumberFormatException e){	// 捕获数字转换异常
			System.out.println("数字转换异常:" + e);
		}catch(ArrayIndexOutOfBoundsException e){	// 捕获数组越界异常
			System.out.println("数组越界异常:" + e) ;
		}
		System.out.println("********** 计算结束 ***********") ;
	}
};

以上很小的一个程序就会出现三个异常,当然程序还可能存在其他的异常,那么这样一来程序处理就会非常麻烦了。

异常的继承结结构:

在整个java的异常结构上,实际上有以下两个最常用的类:Exception、Error,这两个类都是Throwable的子类。

Exception:一般表示的是程序中出现的问题,可以直接使用try...catch处理。

Error:一般指的是JVM错误,程序中无法处理。


一般情况下开发者都比较习惯将Error和Exception统称为异常,而之前处理的若干异常,都是Exception的子类。

注意:

一般在输出异常信息的时候,可以直接使用System.out.println()打印异常对象。

也可以通过Exception提供的一个方法:public void printStackTrace()

Java中的异常处理机制

在整个java的异常处理中,实际上也是按照面向对象的方式进行处理,处理的步骤如下:

一旦产生异常,则首先会产生一个异常类的实例化对象:

在try语句中对此异常对象进行捕捉:

产生的异常对象与catch语句中的各个异常类型进行匹配,如果匹配成功,执行catch语句中的代码。


回顾对象的多态性:子类的实例化对象可以直接使用父类的对象进行接收。

实际上在异常处理中也是使用此种概念,因为try中产生的是一个实例化对象。如果现在有一些其他的异常不知道的话,则可以最后使用Exception进行捕获。

public class ExceptionDemo06{
	public static void main(String args[]){
		System.out.println("********** 计算开始 ***********") ;
		int i = 0 ;		// 定义整型变量
		int j = 0 ;			// 定义整型变量
		try{
			String str1 = args[0] ;		// 接收第一个参数
			String str2 = args[1] ;		// 接收第二个参数
			i = Integer.parseInt(str1) ;	// 将第一个参数由字符串变为整型
			j = Integer.parseInt(str2) ;	// 将第二个参数由字符串变为整型
			int temp = i / j ;	// 此处产生了异常
			System.out.println("两个数字相除的结果:" + temp) ;
			System.out.println("----------------------------") ;
		}catch(ArithmeticException e){	// 捕获算术异常
			// System.out.println("算术异常:" + e) ;
			e.printStackTrace() ;
		}catch(NumberFormatException e){	// 捕获数字转换异常
			System.out.println("数字转换异常:" + e);
		}catch(ArrayIndexOutOfBoundsException e){	// 捕获数组越界异常
			System.out.println("数组越界异常:" + e) ;
		}catch(Exception e){
			System.out.println("其他异常:" + e) ;
		}
		System.out.println("********** 计算结束 ***********") ;
	}
};

注意点:在异常处理中,捕获更粗的异常要放在捕获更细的异常之后。

比如如下程序就是错误的

public class ExceptionDemo07{
	public static void main(String args[]){
		System.out.println("********** 计算开始 ***********") ;
		int i = 0 ;		// 定义整型变量
		int j = 0 ;			// 定义整型变量
		try{
			String str1 = args[0] ;		// 接收第一个参数
			String str2 = args[1] ;		// 接收第二个参数
			i = Integer.parseInt(str1) ;	// 将第一个参数由字符串变为整型
			j = Integer.parseInt(str2) ;	// 将第二个参数由字符串变为整型
			int temp = i / j ;	// 此处产生了异常
			System.out.println("两个数字相除的结果:" + temp) ;
			System.out.println("----------------------------") ;
		}catch(Exception e){
			System.out.println("其他异常:" + e) ;
		}catch(ArithmeticException e){	// 捕获算术异常
			// System.out.println("算术异常:" + e) ;
			e.printStackTrace() ;
		}
		System.out.println("********** 计算结束 ***********") ;
	}
};


既然所有的Exception对象都可以使用Exception接收(都可以发生向上转型),那么直接使用Exception岂不更方便?

public class ExceptionDemo08{
	public static void main(String args[]){
		System.out.println("********** 计算开始 ***********") ;
		int i = 0 ;		// 定义整型变量
		int j = 0 ;			// 定义整型变量
		try{
			String str1 = args[0] ;		// 接收第一个参数
			String str2 = args[1] ;		// 接收第二个参数
			i = Integer.parseInt(str1) ;	// 将第一个参数由字符串变为整型
			j = Integer.parseInt(str2) ;	// 将第二个参数由字符串变为整型
			int temp = i / j ;	// 此处产生了异常
			System.out.println("两个数字相除的结果:" + temp) ;
			System.out.println("----------------------------") ;
		}catch(Exception e){
			System.out.println("其他异常:" + e) ;
		}
		System.out.println("********** 计算结束 ***********") ;
	}
};
当然所有的异常处理的方式是一样的时候就可以使用以上的形式,直接使用Exception进行捕获。当然,在一个比较细致的开发中是不建议这样使用的,所有的异常最好分别捕获。

问题:

既然捕获Exception是最方便的,那么直接捕获Throwable不就行了?

首先,这样的做法是可以的,但是正常的开发人员是不会这样做的,因为在程序中的try中永远只会抛出Exception的子类,而Throwable中不光有Exception还有Error。

异常概念的总结:

1、异常出现之后,如果没有合理的处理的话,则会让整个程序中断执行。

2、使用try...catch和try...catch...finally可以处理异常,finally将作为异常的统一出口,不管是否有此异常都会执行此语句。

3、一个异常处理中可以同时出现多个catch,但是捕获更粗的异常要放在更细的异常之后,否则程序编译时将出现错误。

4、在异常中最大的类是Throwable,分为两个子类:Exception、Error。

Exception:是程序可以自己处理的异常。

Error:表示JVM错误,一般程序无法处理。

5、捕获的时候可以直接捕获Exception,但是最好分开捕获,如果所有的异常后的处理操作都是一样的话,则也可以直接捕获Exception。

6、每当异常产生后,会在程序中产生一个异常类的实例化对象,之后使用此对象与catch中的异常类型相匹配,如果匹配成功吗,则执行catch语句中的内容,如果匹配不成功,则继续向下进行匹配,如果都无法成功,程序中出现中断执行的问题。

异常的深入研究

研究要点:掌握throws与throw关键字的作用,掌握Exception与RuntimeException的区别、可以自定义异常类、了解断言的作用以及应用。
throws关键字:在定义一个方法的时候可以使用throws关键字声明,使用throws声明的方法表示此方法不处理异常,而是交给调用处进行处理。
throws的使用格式
public 返回值类型 方法名称(参数列表...)throws 异常类{}
假设定义一个除法的方法(div()),对于除法操作来说操作的时候有可能出现异常,有可能不出现异常,所以对于这样的方法最好将它使用throws关键字声明,一旦出现了异常,则应该交给调用处处理。此时如果调用此类的div方法的时候就必须进行异常处理。

class Math{
	public int div(int i,int j) throws Exception{	// 定义除法操作,如果有异常,则交给被调用处处理
		int temp = i / j ;	// 计算,但是此处有可能出现异常
		return temp ;
	}
};
public class ThrowsDemo01{
	public static void main(String args[]){
		Math m = new Math() ;		// 实例化Math类对象
		try{
			System.out.println("除法操作:" + m.div(10,0)) ;
		}catch(Exception e){
			e.printStackTrace() ;	// 打印异常
		}
	}
};

注意:如果不在调用处进行异常的捕获的话,在编译时就会出现错误

如果现在在主方法的声明中也使用了throws关键字呢?那么是不是也就意味着主方法也可以不处理异常了?

class Math{
	public int div(int i,int j) throws Exception{	// 定义除法操作,如果有异常,则交给被调用处处理
		int temp = i / j ;	// 计算,但是此处有可能出现异常
		return temp ;
	}
};
public class ThrowsDemo02{
	// 在主方法中的所有异常都可以不使用try...catch进行处理
	public static void main(String args[]) throws Exception{
		Math m = new Math() ;		// 实例化Math类对象
		System.out.println("除法操作:" + m.div(10,0)) ;
	}
};

在本程序中主方法不处理任何的异常了,而是交给最大的头,java中最大的头就是JVM,所以如果在主方法中使用了throws关键字,则表示一切的异常交给JVM进行处理,默认的处理方式也是使用JVM完成。

throw关键字:

throw关键字的作用是在程序中抛出一个异常。抛出的时候抛出的是一个异常类的实例化对象。

与throws不同的是,可以直接使用throw抛出一个异常。抛出的时候直接抛出异常类的实例化对象即可。

在异常的处理中,try语句中要捕获的是一个异常的对象,那么异常对象也可以自己抛出。

public class ThrowDemo01{
	public static void main(String args[]){
		try{
			throw new Exception("自己抛着玩的。") ;	// 抛出异常的实例化对象
		}catch(Exception e){
			System.out.println(e) ;
		}
	}
};

范例:throw与throws的应用

在一般的开发中try...catch...finally、throw、throws联合使用的情况是最多的,例如:现在要设计一个相除的方法,但是在进行操作之前必须打印“计算开始”信息、结束之后必须打印“异常结束”的信息,如果有异常的话则应该把异常交给调用处处理,面对这样的要求,就必须使用以上的全部操作。

class Math{
	public int div(int i,int j) throws Exception{	// 定义除法操作,如果有异常,则交给被调用处处理
		System.out.println("***** 计算开始 *****") ;
		int temp = 0 ;	// 定义局部变量
		try{
			temp = i / j ;	// 计算,但是此处有可能出现异常
		}catch(Exception e){
			throw e ;
		}finally{	// 不管是否有异常,都要执行统一出口
			System.out.println("***** 计算结束 *****") ;
		}
		return temp ;
	}
};
public class ThrowDemo02{
	public static void main(String args[]){
		Math m = new Math() ;
		try{
			System.out.println("除法操作:" + m.div(10,0)) ;
		}catch(Exception e){
			System.out.println("异常产生:" + e) ;
		}
	}
};


注意:如果出现了异常的话,下面的操作如果不放在finally代码块内是不会执行的。

Exception与RuntimeException的区别

是一个在JAVA面试中经常出现的一个问题

观察以下代码:

public class RuntimeExceptionDemo01{
	public static void main(String args[]){
		String str = "123" ;	// 定义字符串,全部由数字组成
		int temp = Integer.parseInt(str) ; // 将字符串变为int类型
		System.out.println(temp * temp) ;	// 计算乘方
	}
};
parseInt()方法定义格式:

public static int parseInt(String s)
                    throws NumberFormatException
在此方法中明明使用了throws关键字抛出异常,为什么不用处理,编译也可以通过呢?

Exception与RuntimeException

这两个类的区别如下:

Exception在程序中是必须使用try...catch进行处理

RuntimeException可以不用try...catch进行处理,但是如果有异常产生,则异常将由JVM进行处理。


在java的异常处理机制中:

如果抛出的是Exception的类型,则必须使用try...catch进行处理。

如果抛出的是RuntimeException的类型,则不是必须使用try...catch进行处理,一旦发生异常之后将由JVM进行处理,但是为了保证程序的健康性,建议在有可能出现异常的地方还是老实的使用try...catch进行处理。

自定义异常类:

只需要继承Exception就可以完成自定义异常类。因为在JAVA中提供的都是标准的异常类(包括一些异常信息等等),如果需要定义自己想要的异常信息的时候就可以自定义异常类。

只要直接继承Exception类即可。

class MyException extends Exception{	// 自定义异常类,继承Exception类
	public MyException(String msg){
		super(msg) ;	// 调用Exception类中有一个参数的构造方法,传递错误信息
	}
};
public class DefaultException{	
	public static void main(String args[]){
		try{
			throw new MyException("自定义异常。") ;	 // 抛出异常
		}catch(Exception e){
			System.out.println(e) ;
		}
	}
}

一般如果项目很大的时候有可能自定义异常,可以得到一些准确的信息等等。

断言:(assert)

在JDK1.4之后,java中增加了断言的功能,那么什么叫断言呢?断言就是肯定某一个结果的返回值是正确的,如果最终此结果的返回值是错误的,则通过断言检查肯定会为用户提示错误信息。断言的定义格式如下所示:

assert boolean 表达式;

assert boolean 表达式:详细的信息;

public class Test{
	public static void main(String args[]){
		int x[] = {1,2,3} ;	// 定义数组,长度为3
		assert x.length==0 : "数组长度不为0" ;	// 此处断言数组的长度为0
	}
};

断言本身不会影响程序的执行,但是如果想让一个断言起作用,则必须对断言进行验证。

断言验证方式如下:

在DOS命令下编译后 使用java -ea Test即可

在MyEclipse中验证步骤:选中程序,右键Run as—>Run configurations—>VM arguments 中添加-ea即可。


总结:

1、断言在实际开发中并不是很常见,简单了解即可。

2、throw和throws关键字联合使用问题

throw:抛出异常对象

throws:在方法的声明处使用,表示此方法不处理异常。

3、Exception的异常时必须处理的,而RuntimeException的异常是可以不处理的,但是为了保证程序的运行,只要有异常最好全部处理。

4、如果需要自定义异常,则直接继承Exception类即可




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值