第六章 异常机制
作者:互联网
第六章 异常机制
1 什么是异常(exception)
- 实际工作中,遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求;你的程序要打开某个文件,这个文件可能不存在或者文件格式不对;你要读取数据库的数据,数据可能是空的;程序在运行过程中,内存或硬盘可能满了等等;
- 异常指程序运行中出现的各种意外状况,如:文件找不到、网络连接失败、非法参数等;
- 异常发生在程序运行期间,它影响了正常的程序执行流程。
1.1 异常的简单分类
异常可大致分为三类:
-
一般性异常(受控异常):这种异常属于一般性异常,出现了这种异常必须显示的处理,不显示处理java程序将无法编译通过。编译器强制普通异常必须try…catch处理,或用
throws
声明继续抛给上层调用方法处理,所以普通异常也称为checked异常
。编译期就必须处理的,否则程序不能通过编译,就更不能正常运行了。 -
运行时异常(非受控异常):非受控异常也即是运行时异常(
RuntimeException
),这种系统异常可以处理也可以不处理,所以编译器不强制用try…catch处理或用throws
声明,所以系统异常也称为unchecked异常
。在编译期是不检查的,出现问题后,需要我们回来修改代码。此种异常可以不用显示的处理,例如被0除异常,java没有要求我们一定要处理, 当出现这种异常时,肯定是程序员的问题,也就是说,健壮的程序一般不会出现这种系统异常。
-
错误(ERROR):如果应用程序出现了Error,那么将无法恢复,只能重新启动应用程序,最典型的Error的异常是:
OutOfMemoryError
。
2 异常体系结构
- Java把溢出当作对象来处理,并定义一个基类
java.lang.Throwable
作为所有异常的超类。 - 在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception
2.1 Error
- Error 类对象由Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关;
- Java虚拟机运行错误(
Virtual MachineError
),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError
。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止; - 还有发生在虚拟机试图执行应用时,如类定义错误(
NoClassDefFoundError
)、链接错误(LinkageError
)这些错误都时不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。
2.2 Exception
-
在Exception分支中有一个重要的子类
RuntimeException
(运行时异常)-
ArrayIndexOutOfBoundsException
(数组下标越界) -
NullPointerException
(空指针异常) -
ArithmeticException
(算术异常) -
MissingResourceException
(丢失资源) -
ClassNotFoundException
(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;
-
-
Error和Exception的区别: Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
3 异常的处理
【注】代码块生成快捷键:选中要包裹代码块的代码,crtl+alt+t
为什么要自己处理异常
JVM默认的异常处理方案是遇到异常就结束程序的运行,而在实际的开发中,遇到了异常不应该影响后续的程序运行。
异常处理方式
运行时异常(系统异常)不需要预处理,通过规范的代码可以避免产生这种异常
受检异常(编译时异常)必须预处理,否则编译报错,有两种预处理方式 :
- 捕获处理(try-catch)
- 执行流程:
- 程序从try里面的代码开始执行
- 出现异常,会自动生成一个异常类对象,该异常对象将被提交给Java运行时系统
- 当Java运行时系统接收到异常对象时,会到catch中去找匹配的异常类,找到后进行异常的处理
- 执行完毕之后,程序还可以继续往下执行
- 执行流程:
- 抛出处理(throws)
3.1 异常捕获处理
3.1.1 try、catch和finally
异常的捕获和处理需要采用try和catch来处理,具体格式如下:
try {
} catch (OneException e) {
} catch (TwoException e) {
} finally {
}
try
中包含了可能产生异常的代码try
后面是catch
,catch
可以有一个或多个,catch
中是需要捕获的异常
当try
中的代码出现异常时,出现异常下面的代码不会执行,马上会跳转到相应的catch
语句块中,如果没有异常不会跳转到catch
中
finally
表示,不管是出现异常,还是没有出现异常,finally
里的代码都执行,finally
和catch
可以分开使用,但finally
必须和try
一块使用,如下格式使用finally
也是正确的
try{
}finally{
}
3.1.2 异常捕获处理
异常捕获处理详细说明:
private static void testException2() {
try {
//1、对可能产生异常的代码进行检视
//2、如果try代码块的某条语句产生了异常, 就立即跳转到catch子句执行, try代码块后面的代码不再执行
//3、try代码块可能会有多个受检异常需要预处理, 可以通过多个catch子句分别捕获
} catch (异常类型1 e1) {
//捕获异常类型1的异常, 进行处理
//在开发阶段, 一般的处理方式要么获得异常信息, 要么打印异常栈跟踪信息(e1.printStackTrace())
//在部署后, 如果有异常, 一般把异常信息打印到日志文件中, 如:logger.error(e1.getMessage());
} catch (异常类型2 e1) {
//捕获异常类型2的异常, 进行处理
//如果捕获的异常类型有继承关系, 应该先捕获子异常再捕获父异常
//如果没有继承关系, catch子句没有先后顺序
} finally {
//不管是否产生了异常, finally子句总是会执行
//一般情况下, 会在finally子句中释放系统资源
}
}
【代码示例】
若不进行try-catch处理,JVM的默认异常处理方法:
package com.exception.demo01;
public class ExceptionDemo01 {
public static void main(String[] args) {
System.out.println("开始");
method();
System.out.println("结束");
}
public static void method(){
int[] arr = {1,2,3};
System.out.println(arr[3]);
}
}
执行结果:结果只输出了“开始”没有输出“结束”,即JVM遇到异常默认结束程序运行
进行try-catch处理
package com.exception.demo01;
public class ExceptionDemo01 {
public static void main(String[] args) {
System.out.println("开始");
method();
System.out.println("结束");
}
public static void method(){
try {
int[] arr = {1,2,3};
System.out.println(arr[3]);//自动抛出了异常对象,相当于进行了new ArrayIndexOutOfBoundsException();操作,再把这个新创建的对象给到下面的e
} catch (ArrayIndexOutOfBoundsException e) {
//System.out.println("你访问的索引不存在");//实际开发不会直接输出一句话,而是给出一个页面提示
e.printStackTrace();//打印错误的栈信息
}
}
}
执行结果:结果输出了“开始”和“结束”,遇到异常程序正常运行
3.1.3 异常的捕获顺序
异常的捕获:一般按照由小到大的顺序,也就是先捕获子异常,再捕获父异常
package com.exception;
public class Demo01 {
public static void main(String[] args) {
int a = 1;
int b = 0;
try {//try监控区
System.out.println(a/b);
} catch (ArithmeticException ae){//catch用作捕获异常,格式:catch(想要捕获的异常类型)
System.out.println("程序出现异常,变量b不能为0");
} catch (Exception e){
System.out.println("Exception");
} catch (Throwable t){
System.out.println("Throwable");
} finally {//finally处理善后工作
System.out.println("finally");
}
/*finally可以不要,但try和catch一定要有,
可以捕获多个异常即catch可以有多个,捕获的异常要从小到大,也就是先截获子异常,再截获父异常*/
}
}
3.2 Throwable的成员方法
方法名 | 说明 |
---|---|
public String getMessage() |
返回此throwable的详细消息字符串 |
public String toString() |
返回此可抛出的简短描述 |
public void printStackTrace() |
把异常的错误信息输出在控制台 |
三个成员方法的输出示例:
代码部分:
package com.exception.demo01;
public class ExceptionDemo01 {
public static void main(String[] args) {
System.out.println("开始");
method();
System.out.println("结束");
}
public static void method(){
try {
int[] arr = {1,2,3};
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
//System.out.println(e.getMessage());
//System.out.println(e.toString());
//e.printStackTrace();
}
}
}
输出结果:
getMessage()
:返回此throwable的详细消息字符串
toString()
(返回异常的类名和异常原因):返回此可抛出的简短描述
printStackTrace()
(显示异常类名、异常原因、异常位置,异常信息最详细,所以一般都用这个方法来把异常信息显示在控制台):把异常的错误信息输出在控制台
3.2.1 getMessage()
和printStackTrace()
如何取得异常对象的具体信息,常用的方法主要有两种:
- 获取异常描述信息
使用异常对象的getMessage()
方法,通常用于打印日志时 - 取得异常的堆栈信息
使用异常对象的printStackTrace()
方法,比较适合于程序调试阶段
✳值得一提的是✳
getMessage()
的功能是返回异常原因,其返回的值是detaiMessage
也就是Throwable类
中有参构造中传递的message
Throwable
类中的方法getMessage()
的源码简单可概括为以下:
public class Throwable{
private String detailMessage;
public Throwable(String message){
detailMessage = message;
}
public String getmessage(){
return detailMessage;
}
}
可以看出getMessage()
的功能就是获取在初始化Throwable的某个Exception子类对象
时传给此子类构造器的message,此子类的有参构造器又会调用其父类的有参构造,直到将message
赋值给detailMessage
,也就是getMessage()
返回的detailMessage
。
3.3 throws抛出处理
虽然我们通过try-catch可以对异常进行处理,但是并不是所有的情况我们都有权限进行异常的处理,也就是说,有些时候可能出现的异常是我们处理不了的,这个时候该怎么办呢?
针对这种情况,Java提供了throws 的处理方案。抛出异常只是延迟了异常的处理,并没有解决异常。
格式:throws 类名
【注意】这个格式是跟在方法的括号后面的。
【小结】:
- 编译时异常必须要进行处理,两种处理方案:try-catch或者throws,将来谁调用谁处理
- 运行时异常可以不处理,因为出现问题后,需要我们回来修改代码,用try-catch处理异常没意义
在定义方法时,如果方法体中有受检(编译时)异常需要预处理,可以捕获处理,也可以抛出处理。
处理异常时,使用throws
抛出处理:
-
谁调用这个方法,谁负责用try-catch处理该异常
-
在定义方法时,把异常抛出就是为了提醒方法的使用者,有异常需要预处理
在处理异常时,是选择捕获处理还是抛出处理
-
一般情况下,在调用其他方法时,如果被调用的方法有受检(编译时)异常需要预处理,选择捕获处理,因为你调用了方法, 你负责处理该异常。
-
在定义方法时,如果方法体中有受检异常需要预处理,可以选择捕获 ,也可以选择抛出处理。如果方法体中通过
throw
语句抛出了一个异常对象,所在的方法应该使用throws
声明该异常。
4 自定义异常
为什么要学习自定义异常?
虽然Java提供了很多异常类供我们使用,但是实际开发中并不能满足我们所有的需求。
- 使用Java内置的异常类可以描述在编程时出现的大部分异常情况,除此之外,用户还可以自定义异常,用户自定义异常类,只需要继承
Exception类
或者RuntimeException类
即可; - 在程序中使用自定义异常类,大体可以分为一下几个步骤:
- 创建自定义异常类
- 在方法中通过throw关键字抛出异常对象
- 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作
- 在出现异常方法的调用者中捕获并处理异常
示例:
ScoreException
自定义异常类:
package com.exception.demo01;
public class ScoreException extends Exception {
public ScoreException(){}
public ScoreException(String message){
super(message);
}
}
Teacher
类:
package com.exception.demo01;
public class Teacher {
public void checkScore(int score) throws ScoreException{
if (score<0 || score>100){
throw new ScoreException("你给的分数有误,分数在0-100间");//自定义异常需要自己用throw抛出异常对象,给出提示信息,像是普通的异常会自动抛出异常对象。此处要是括号无内容即调用无参构造,在控制台就不会输出异常提示内容
}else {
System.out.println("输出正常");
}
}
}
main方法:
package com.exception;
import com.exception.demo01.ScoreException;
import com.exception.demo01.Teacher;
import java.util.Scanner;
public class Application {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入分数:");
int score = scanner.nextInt();
Teacher t = new Teacher();
try {
t.checkScore(score);
} catch (ScoreException e) {
e.printStackTrace();
}
}
}
4.1 throw与throws
throws | throw |
---|---|
用在方法声明后面,跟的是异常类名 | 用在方法体内,跟的是异常对象名 |
表示抛出异常,由该方法的调用者来处理 | 表示抛出异常,由方法体内的语句处理 |
表示出现异常的一种可能性,并不一定会发生这些异常(例如上文中Teacher类 中的checkScore() 方法若是0-100分就不会出现异常) |
执行throw一定抛出了某种异常(例如上文中Teacher类 中的checkScore() 方法只要执行了throw语句证明一定出现了异常) |
throw 和 throws 的区别?
-
throw
throw 语句用在方法体内,表示抛出异常,由方法体内的语句处理。throw是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行throw一定是抛出了某种异常。
throw一般用于抛出自定义异常。
-
throws
throws语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理。throws主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。
throws表示出现异常的一种可能性,并不一定会发生这种异常。
标签:处理,System,try,catch,第六章,机制,异常,public 来源: https://www.cnblogs.com/Ayisprite/p/16146334.html