java从零开始系列(三)异常处理

luoyjx · 2014-10-05 11:26 · 1032次阅读

目录

  异常的概念

  异常的分类 
  公共异常 
  异常处理
  自定义异常
  在多态中,异常的声明抛出原则
  异常处理规则

引言

错误类型
语法错误(syntax errors):没有遵循java语言规则
运行错误(runtime errors)发生一个不可以执行的操作
逻辑错误(logic errors)没有按照预期的方案执行
异常:处理运行错误

1 异常概念 在程序执行中,任何中断正常程序流程的条件都是异常。例如,发生下列情况时,会出现异常:

  • 想打开的文件不存在
  • 网络连接中断
  • 操作数超出预定范围
  • 非常感兴趣地正在装载的类文件丢失

2 异常的分类 在Java编程语言中,java.lang.Throwable类充当所有对象的父类,可以使用异常处理机制将这些对象抛出并捕获。

在Throwable类中定义方法来检索与异常相关的错误信息,并打印显示异常发生的栈跟踪信息。它有Error和Exception两个基本子类。

Throwable类不能使用,而使用子类异常中的一个来描述任何特殊异常。 

3 运行时异常 Java编程语言提供几种预定义的异常。下面是可能遇到的更具共同性的异常中的几种:

  • ArrayIndexoutofBoundsException:访问超过数组大小范围的一个元素的尝试。
  • ArithmeticException:整数被0除,运算得出的结果。
  • NullPointerException:当对象没被实例化时,访问对象的属性或方法:

4 异常处理 Java程序在执行中如出现异常,会自动创建一个异常类(Throwable)实例,该实例将被提交给Java运行时系统,这个过程称为抛出(throw)异常。

当Java运行时系统接受到异常时,会寻找能处理这个异常的代码并把当前异常交给其处理,这个过程称为捕获(catch)异常。
如果java运行时系统找不到可以捕获异常的方法,异常直接传递到main方法,相应的Java程序也将退出。

  • 捕获异常 – 积极处理方式: try-catch-finally
  • 声明异常 – 消极处理方式: throws
  • 抛出异常 – 简单处理方式: throw

用try-catch-finally块来处理异常
Try
可能出现异常的代码都应放在try代码块中
当异常出现后程序控制就转交给catch语句块
Catch
Catch语句块按声明的异常进行异常捕获
一个try块可以和多个catch块配合以处理多个异常。
多捕获是要按照从小到大的顺序进行异常声明
Finally
无论是否出现异常,finally代码块最终都将执行。
Finally经常用于资源的释放工作。
Try-catch-finally 可以嵌套try-catch-finally实现更精确的异常控制

public class helloworld {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String[] strings={"32","one","10.8","45"};
		int sum=0;
		try {
			for(String str:strings){
				sum+=Integer.parseInt(str);
			}
			System.out.println(sum);
		} catch (NumberFormatException e) {
			// TODO: handle exception
			System.out.println(e.getMessage());
		}
		
	}

}

访问异常信息

  • getMessage():返回该异常的详细描述字符串
  • printStackTrace():将该异常的跟踪栈信息输出到标准错误输出
  • printStrackTrace(PrintStream s):将该异常的跟踪栈信息输出到指定输出流。
  • getStackTrace():返回该异常的跟踪栈信息。

异常处理的嵌套

在try块、catch块、finally块中包含完整的异常处理流程的情形被称为异常处理的嵌套。
异常处理嵌套的深度没有很明确的限制,但通常没有必要使用超过两层的嵌套异常处理,层次太深的嵌套异常处理没有太大必要,而且导致程序可读性降低。

throws与 throw

通过关键字throws在方法签名中声明上抛异常而不是处理它。
调用它的方法应该处理抛出的异常,否则继续上抛,只到被处理为止。
关键字throws之后是所有异常的列表,使用逗号分开各异常类型。
当程序出现错误时,系统会自动抛出异常,除此之外,java也允许程序自行抛出异常,自行抛出异常使用throw语句完成。
在实际中,可能需要根据应用的业务需求来决定,如果程序中的数据、执行与既定的业务需要不符,那这就是一种异常,这种由于与业务需求不符而产生的异常,必须由程序员来决定抛出,系统无法抛出这种异常。
Throw语句抛出的不是异常了,而是一个异常实例,而且每次只能抛出一个异常实例。

5 自定义异常 通过继承扩展Exception类来创建用户自定义异常。

自定义异常的构造方法总是调用父类的构造方法:super(“message”);
如有必要,需要覆盖下列获得错误信息的方法:

  • toString()
  • getMessage()
  • printStackTrace()

见备注详解
例一: class DemoException extends Exception { public DemoException() { super(); } public DemoException(String message) { super(message); } }

public class Test {
	public static void main(String[] args) throws DemoException {
		try {
			double money = 8668.6d;
			if (money < Math.pow(2, 20)) {
				throw new DemoException("金额不足");
			}
		} catch (DemoException ex) {
			System.out.println(ex.getMessage());
		}		
	}
}

6 在多态中,异常的声明抛出原则 在多态中,异常的声明抛出有一定的原则:

子类覆盖父类方法,子类抛出的异常必须为父类可视异常。参见备注例一。
子类实现接口,如接口定义的方法抛出的异常存在交集,在子类实现中,不能声明抛出任何异常,只能捕获。参见备注例二。
例一: class OneException extends Exception { }

class TwoException extends Exception {
}

class ThreeException extends Exception {
}

class Father {
	void show() throws OneException, TwoException {
	}
}

public class Test extends Father {
	// 所抛出的异常不能多于父类抛出的异常
	void show() throws TwoException, ThreeException {
	}

	public static void main(String[] args) {
		new Test().show();
	}
}

例二:
class OneException extends Exception {
}

class TwoException extends Exception {
}

interface Football {
	void play() throws OneException;
}

interface Basketball {
	void play() throws TwoException;
}

public class Test implements Football, Basketball {
	// 如接口存在交集,不能声明抛出的异常
	public void play() {
	}

	public static void main(String[] args) {
		new Test().play();
	}
}

何时使用异常

异常缺点:处理异常需要更多的时间和资源
原则

  1. 方法内处理异常不需要抛出,如果需要调用者处理异常需要抛出异常需要抛出
  2. 如果多个类发生的共同异常考虑设计异常类
  3. 一般能用简单逻辑判断的不要使用异常。

异常处理规则

成功的异常处理应该达到如下四个目标

  1. 使程序代码混乱最小化
  2. 扑捉并保留诊断信息
  3. 通知合适的人员
  4. 采用合适的方式结束异常活动

7 处理规则 不要过度使用异常

把异常和普通错误混淆在一起,不再编写任何错误处理代码,而是以简单地抛出异常来代替所有错误处理。
使用异常处理来代替流程控制
不要使用过于庞大的try块
Try庞大之后,那么catch块必然多,这样导致编程复杂性增大。
避免使用catch all语句
实际上,catch all语句不过是一种通过避免错误处理而加快编程进度的机制,应尽量避免在实际应用中使用。
不要忽略捕捉到的异常
Catch块应该做些有用的事情—处理并修复这个错误。

断言

断言是Java 1.4版新增的一个特性,并在该版本中增加了一个关键字assert。可以把断言功能看成是异常处理的高级形式。 
所谓断言(assertion)是一个Java语句,布尔表达式,程序员认为在程序执行时该表达式的值应该为true。系统通过计算该布尔表达式执行断言,若该表达式为false系统会报告一个错误。通过验证断言是true,能够使程序员确信程序
为了提高性能,可能要关闭断言检查。通常在程序开发和调试阶段开启断言功能,在部署时关闭。
5.0以前的JDK缺省情况下,若使用assert作为标识符,编译器会给出警告(不是编译错误),从5.0以后将是一个错误。
打开和关闭断言功能
默认情况下,断言是关闭的。要通过-enableassertions或者-ea选项来运行程序以打开断言:
java –ea Myapp
打开或者关闭断言是类装载器的功能。当断言功能被关闭时,类装载器会跳过那些和断言相关的代码,因此不会降低程序运行速度,即它们没有任何副作用。
也可以对某个类或某个包打开断言功能,例如:
java –ea Myclass  –ea com.mydx.mylib…
       该命令打开类Myclass以及在com.mydx.mylib包中及其子包中全部类的断言功能(包名后的省略号是必须的)
可以使用-da选项来关闭特定类或包的断言功能:
java –da Myclass  –da:com.mydx.mylib…
–ea和-da选项对系统类(不通过类装载器而由JVM直接装载的类)无效,对系统类使用–esa和-dsa选项

使用断言

1 断言失败是致命的、不可恢复的错误。
2 断言检查仅仅用在程序开发和测试阶段。

语法如下:
1、assert  boolean条件   
或者
2、assert  boolean条件:详细信息
       这两个形式都会对“条件”进行判断, “条件”是一个布尔表达式。如果判断结果为假(false)则抛出AssertionError。在第二种形式中,“详细信息”会传进AssertionError的构造函数中并转成一个消息字符串。
“表达式”部分的唯一目的就是生成一个消息字符串。

例如,如果要进行如下的计算时:
double y=Math.sqrt(x);
          sqrt(x)是一个开平方运算,x必须为正才不会出错。为了检查传入的参数是否为正,可以使用如下的断言语句:
assert  x>=0;
double y=Math.sqrt(x);
或者
assert  x>=0:“ x>=0”;//将“ x>=0”传给AssertionError对象,从而可在出错时显示出来
double y=Math.sqrt(x);
        当x为负值时, assert语句将抛出AssertionError异常,你就可以根据异常信息对程序的其它部分进行检查。

暂无评论

登录后可以进行评论。没有账号?马上注册