异常机制(Exception)
软件程序在运行过程中,非常可能遇到异常问题。常见的异常:
1、用户输入错误
2、设备错误
3、硬件问题,例如打印机关掉、服务器问题
4、物理限制:磁盘满了
Java是采用面向对象的方式来处理异常的。
处理过程
抛出异常:在执行一个方法是,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给JRE。
捕获异常:JRE得到该异常后,寻找相应的代码来处理该异常。JRE在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止。
JDK中定义了很多异常类,这些类对应了各种各样可能出现的异常时间,所有异常对象都是派生于Throwable类的一个实例。如果内置的异常类不能够满足需要,还可以创建自己的异常类。Error 错误类,不需要程序员管,是一个错误,关键在于Exception,需要我们程序员管。
Error
Error类层次描述了Java运行时系统内部错误和资源耗尽错误。这类错误是我们无法控制的,同时也是非常罕见的错误。所以在编程中,不去处理这类错误。Error表明系统JVM已经处于不可恢复的崩溃状态中。
Exception
所有异常类的父类,其子类对应了各种各样可能出现的异常事件。
一类特殊的异常,如被0除、数组下标超范围等,其产生比较频繁,处理麻烦,如果显式的声明或捕获将会对程序可读性和运行效率影响很大。因此由系统自动检测并将它们交给缺省的异常处理程序(用户可不必对其处理)
NullpointException
(空指针异常,遇到的最多的异常)
Checked Exception
这一类异常,我们必须捕获进行处理
异常的处理方法1(亲自动手捕获):
Try:try语句指定了一段代码,该段代码就是一次捕获并处理的范围。在执行过程中,当任意一条语句产生异常时,就会跳过该段中后面的代码。代码中可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句要分别对这些异常做相应的处理
一个try语句必须带有至少一个catch语句块或一个finally语句块。注意当异常处理的代码执行结束以后,是不会回到try语句去执行尚未执行的代码。
Catch:每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。Catch捕获异常时的捕获顺序:如果异常类之间由继承关系,在顺序安排上需注意。越是顶层的类,越放在下面。再不然就直接把多余的catch省略掉。
finally关键字
有些语句,不管是否发生了异常,都必须要执行,那么就可以把这样的语句放到finally语句块中。通常在finally中关闭程序块已打开的资源,比如:文件流、释放数据库连接等。
使用格式:
try{可能出现异常的代码}catch(异常 对象名){处理异常的代码-> 将来开发会将异常信息保存到日志文件中}finally{不管是否有异常,都会执行的代码}
try-catch-finally-return执行顺序
-
try中带有return
当try中带有return时,会先执行return前的代码,然后暂时保存需要return的信息,再执行finally中的代码,最后再通过return返回之前保存的信息。但如果finally通过地址改变了变量,还是会影响方法返回值的。 -
catch中带有return
catch中return与try中一样,会先执行return前的代码,然后暂时保存需要return的信息,再执行finally中的代码,最后再通过return返回之前保存的信息。 -
finally中带有return
当finally中有return的时候,try中的return会失效,在执行完finally的return之后,就不会再执行try中的return。这种写法,编译是可以编译通过的,但是编译器会给予警告,所以不推荐在finally中写return,这会破坏程序的完整性,而且一旦finally里出现异常,会导致catch中的异常被覆盖。
总结:
- finally中的代码总会被执行。
- 当try、catch中有return时,也会执行finally。return的时候,要注意返回值的类型,是否受到finally中代码的影响。
- finally中有return时,会直接在finally中退出,导致try、catch中的return失效。
异常的处理方法2(声明异常:throws子句)
当Checked Exception产生时,不一定立刻处理它,可以再把异常Throws出去。如果一个方法抛出多个已检查异常,就必须在方法的首部列出所有的异常,之间以逗号隔开。
方法重写中声明异常原则:子类声明的异常范围不能超过父类声明的范围。即1、父类没有声明异常,子类也不能;2、不可抛出原有方法抛出异常类的父类或上层类
自定义异常
在程序中,可能会遇到任何标准异常类都没有充分的描述清楚的问题,这种情况下可以创建自己的异常类,从Exception类或者它的子类派生一个子类即可。习惯上,定义的类应该包含2个构造器:一个是默认的构造器,另一个是带有详细信息的构造器。
- 定义一个类
- 如果继承Exception 就是编译时期异常
- 如果继承RuntimeException,就是运行时期异常
使用异常机制建议
- 要避免使用异常处理代替错误处理,这样会降低程序的清晰性,并且效率低下。
- 处理异常不可以代替简单测试(只在异常情况下使用异常机制)
- 不要进行小粒度的异常处理——应该将整个任务包装在一个Try语句块中
- 异常往往在高层处理
打印异常信息的三个方法
这些方法均继承于Throwable类:
- String toString() :输出异常类型和设置的异常信息
- String getMessage(): 输出设置的异常信息 ,只显示产生异常的原因,但不显示类名。
- void printStackTrace():打印异常信息是最全的:包括异常类型,信息,以及出现的行数等,常用来跟踪异常事件发生时堆栈的内容。
public class Exception {public static void main(String[] args) {//1.定义一个用户名,代表已经注册的用户String username = "root";//2.创建Scanner对象,录入用户名Scanner sc = new Scanner(System.in);System.out.println("请您输入要登录的用户名:");String name = sc.next();//3.判断用户名是否和已经存在的用户名一致if (name.equals(username)) {System.out.println("登录成功了");} else {try {throw new LoginUserException("登录失败了,用户名或者密码有问题");}catch (Exception e){System.out.println(e.toString()); //输出异常类型和设置的异常信息System.out.println(e.getMessage()); //输出设置的异常信息 ,只显示产生异常的原因,但不显示类名。e.printStackTrace(); //打印异常类型,信息,以及出现的行数}}}
}