一、什么是异常
指程序在运行时可能出现的错误或非正常情况。Java提供了大量的异常类,这些异常类都继承自java.lang包下的Throwable类。
下图是Throwable类的继承体系
Error代表程序中产生的错误,表示运行时系统内部错误或者资源耗尽的错误,仅靠修改程序本身是不能恢复执行的。如使用java命令运行一个不存在的类;
Exception代表程序中产生的异常,表示程序本身可以处理的错误。
Throwable类常用的方法
String getMessage() 返回异常的消息字符串;
String toString() 返回异常的简单信息描述;
void printStackTrace() 获取异常类名和异常信息出现在程序中的位置,把信息输出到控制台
二、运行时异常与编译时异常
1、编译时异常
在Exception类中,除了RuntimeException类以外,其他子类都是编译时异常。
两种处理方法
使用try...catch语句对异常进行捕获处理;
使用throws关键字声明抛出异常,由调用者对异常进行处理
2、运行时异常
RuntimeException类及其子类都是运行时异常;运行时异常是在程序运行时由Java虚拟机自动进行捕获处理的,Java编译器不会对异常进行检查。
三、异常处理及语法
1、处理异常的关键字
try:放置可能出现异常的代码块
catch:后面对应异常类型和一个代码块,该代码块用于处理这种类型的异常
finally:主要用于回收在try代码块里面打开的物理资源,如数据库连接、网络链接和磁盘文件。异常机制保证finally代码块总是被执行。
throw:用于抛出一个实际的异常。可以单独作为语句来抛出一个具体的异常对象
throws:用在方法签名中,用于声明该方法可能抛出的异常
2、try...catch语句
为了使异常发生后的程序代码正常运行,防止异常导致程序立即中止,程序需要捕获异常并进行处理。
语法格式:try{
可能出现异常的代码块
}catch(ExceptionType e){
针对异常进行处理的代码块
};
注意:try代码块是必需的;catch代码块可以有多个,但捕获父类异常的catch代码块必须位于捕获子类异常的catch代码块后面
示例:
public class practise {
public static void main(String[] args) {
try {
int result = divide(4,0);//调用divide()方法
System.out.println(result);
}catch (Exception e){ //对异常进行处理
System.out.println("捕获的异常信息为:"+e.getMessage());
}
System.out.println("程序继续向下执行了");
}
public static int divide(int x,int y){
int result = x/y;
return result;
}
}
运行结果:
如果执行try代码块中的语句没有出现异常,则直接跳出try...catch语句块。如果出现了异常,则程序自动跳转到catch代码块中匹配对应的异常类型。若匹配成功则执行catch代码块中的代码,执行后程序将继续往下执行;若catch代码块中匹配不到对应的异常,则程序中断执行。
3、finally语句
无论程序是否发生异常,finally代码块中的代码都会被执行。
finally代码块只能出现在try...catch或try代码块之后,不能单独出现。若程序发生异常,但是异常没有被捕获,在执行完finally代码块中的代码之后,程序会中断。
语法格式:
try{
可能出现异常的代码块
}catch(ExceptionType e){
针对异常进行处理的代码块
}finally{代码块}
示例:
public class practise {
public static void main(String[] args) {
try {
int result = divide(4,0);//调用divide()方法
System.out.println(result);
}catch (Exception e){ //对异常进行处理
System.out.println("捕获的异常信息为:"+e.getMessage());
return; //用于结束当前语句
}finally {
System.out.println("进入finally代码块");
}
System.out.println("程序继续向下执行了");
}
public static int divide(int x,int y){
int result = x/y;
return result;
}
}
运行结果:
注意: 如果在try...catch中执行了System.exit(0)语句,那么finally代码块不再执行。System.exit(0)表示退出当前Java虚拟机,虚拟机停止了,任何代码都无法执行了。
四、抛出异常
在编译过程中,有些异常暂时不需要处理,此时可以将异常抛出,让该类的调用者处理。Java提供了throws关键字和throw关键字用于抛出异常。
1、throws关键字
当定义的方法用throws关键字声明了该方法可能抛出的异常后,方法的调用者必须在调用时对抛出异常进行处理,否则发生编译报错。如可以将可能出现异常的调用者代码块放入try...catch语句中。
当不知道如何处理声明抛出的异常时,可以使用throws关键字继续将异常抛出,以此类推,让上一级的调用者处理或者继续向上抛出,直到最终系统接收到异常中止程序执行。
格式:修饰符 返回值类型 方法名 (参数1,参数2,...)throws 异常类1,异常类2,...{方法体}
public class practise {
public static void main(String[] args) throws Exception {
int result = divide(4,0);//调用divide()方法
System.out.println(result);
System.out.println("程序执行1");
}
public static int divide(int x,int y) throws Exception{
int result = x/y;
return result;
}
}
运行结果:
2、throw关键字
用于方法体内,抛出的是一个异常实例,并且每次只能抛出一个异常实例。
格式:throw ExceptionInstance;
在方法中,通过throw关键字抛出异常后,还需要使用throws关键字或try...catch语句对异常进行处理。但如果throw抛出的是Error、RuntimeException或它们的子类异常对象,则不需要。
当抛出异常是编译时异常时,有两种处理办法:在try代码块里面使用throw关键字抛出异常,通过try代码块捕获异常;在一个throws声明的方法中使用throw关键字抛出异常,把异常交给方法的调用者处理。
当抛出异常是运行时异常时,既可以使用try...catch语句捕获处理也可以不理会,将该异常交给方法的调用者处理。
public class practise {
public static void printAge(int age) throws Exception{
if(age <= 0){
throw new Exception("输入年龄有误,必须大于零");
}else {
System.out.println("年龄为:"+age);
}
}
public static void main(String[] args) {
int age = -1;
try{
printAge(age);
}catch (Exception e){
System.out.println("捕获的异常信息为:"+e.getMessage());
}
}
}
运行结果:
3、自定义异常类
Java允许用户自定义异常类,但是自定义异常类必须继承Exception类或其子类。
自定义的异常类需要用到throw关键字。使用throw关键字在方法中声明异常的实例对象,语法格式:throw Exception 异常对象
class error extends Exception{
public error(){
super();//调用Exception的无参构造方法
}
public error (String message){
super(message);//调用Exception的有参构造方法
}
}
public class practise {
public static void main(String[] args) {
//使用try...catch语句捕获异常
try {
int result = divide(4,-2);
System.out.println(result);
}catch (error e){ //处理异常
System.out.println(e.getMessage());//打印异常
}
}
//定义相除方法,使用throws关键字声明抛出自定义异常
public static int divide(int x, int y) throws error{
if (y<0){
throw new error("除数不能是负数");
}
int result = x/y;
return result;
}
}
运行结果: