异常
客观角度:不符合现实生活的各种情况,都可以理解为是异常
Java语言角度:在代码的运行过程中,出现的各种错误导致程序停止运行,那么这些错 误就是异常。
-
注意:异常在程序种是通过一个个对象来表示
-
和异常相关的类型:
Throwable 该类型是所有异常类的父类
Error:错误 一般表示比较严重的问题,一旦出现该问题,无法通过代码解决
Exception:异常 一般表示比较轻微的问题,如果程序种出现异常,可以通过代码来处理或者解决该异常。
编译时异常:除了运行时异常,其他类型都是编译时异常
运行时异常:RunTimeException 类型以及 它的子类类型
编译型异常和运行时异常
-
编译时异常:在代码编译阶段,系统会检查代码的语法格式等情况,如果在检查的过程中出现了问题,就提示一个错误,这些问题就属于编译时异常。
-
运行时异常:在代码编译阶段不对代码进行检查,但是在代码运行阶段,如果出现了一些导致程序意外终止的问题,这些问题就属于运行时异常。
-
注意: 不管是编译时异常还是运行时异常,都只会在运行阶段出错。
JAVA虚拟机默认处理异常的方式
- 如果在代码中的某个方法内出现了错误情况,系统会将这个错误发生的原因,发生异常类型,发生的路径封装到异常对象中。
- 如果当前方法中没有处理这个异常对象,就将异常往上抛出,抛给调用该方法的方法。
- 如果调用的方法也没有处理异常,那么就一层一层往上抛出,直到抛给main方法,main方法再抛给虚拟机
- 虚拟机将当前异常对象通过标准错误流,打印到控制台,并结束自己。
代码:
手动处理异常的方式
- 异常声明
- 异常捕获
异常声明
如果在某个方法中出现了编译时异常,可以在当前方法上声明这个异常的类型,声明之后编译时异常就会消失。
- 声明异常的格式:
修饰符 返回值类型 方法名称 (参数列表)throws 异常类型1,异常类型2…{
方法体语句;
}
-
注意事项:
-
异常的声明,不能从本质上解决问题,只能在编译阶段不检查这段代码
如果后续传入一些错误的数据,在运行阶段也可能会发生错误。
-
如果方法1中进行了异常的声明,方法2调用了方法1,那么方法2需要对该异常进行捕获或者处理。
-
在声明异常的时候,尽量声明小的异常类型
-
public static void main(String[] args) throws ParseException{
test();
}
public static void test() throws ParseException {
SimpleDateFormat sim = new SimpleDateFormat();
Date date = sim.parse("2022年2月28号");
System.out.println(date);
}
异常的捕获
如果代码的某个位置会出现了错误情况,可以使用特定的格式,捕获这个 错误,捕获之后可以按照自己定义的方式去处理异常。
-
格式:
try … catch
try … catch … finally
try … finally
try … catch
try{
可能会出现错误的代码
}catch(异常类型 异常对象名称){
处理异常的方式
}
-
执行流程:
- 先执行try中的代码,检测是否出现异常
- 如果try中的代码没有出现问题,trycatch直接结束,代码正常执行trycatch后面的代码。
- 如果try中出现了异常,程序立即跳转到catch中查看出现异常所属的类型和catch中声明的类型是否一样,如果一样,就捕获该异常按照指定的方式去处理异常,处理之后,trycatch结束
- 如果try中出现了catch中没有声明的异常类型,就不能捕获该异常,这时虚拟机来处理这个异常(默认处理方式)。
-
注意事项:
如果在某行代码中,出现了异常,立即去catch块中去匹配异常类型,出现错误的代码后面的代码就不能执行了。
代码:
public static void main(String[] args) {
Scanner sc =new Scanner(System.in);
System.out.println("请输入日期");
String s =sc.nextLine();
SimpleDateFormat sim = new SimpleDateFormat("yyyy年MM月dd日");
try {
Date da = sim.parse(s);
System.out.println(da);
}catch (ParseException parseException){
System.out.println("格式输入有问题!");
}
}
try … catch格式的多种异常情况
- 格式:
try{
可能出现错误的代码
}catch(异常类型1 对象名1){
异常1的处理方式
}catch(异常类型2 对象名2){
异常2的处理方式
}...
-
流程:
- 先执行try中的代码,检测是否出现异常
- 如果出现了异常, 就先和异常类型1匹配,如果能匹配上就执行异常1的处理方 式,处理之后,直接结束整个try…catch语句,执行之外的代码。
- 如果不能和异常类型1匹配,就继续和异常类型2匹配,如果能匹配上,就执行异常类型2的处理方式,之后结束整个try…catch语句,执行之外的代码。
- 如果异常类型2不能匹配,依次类推,往后匹配。
- 如果出现的异常,catch中的类型都不能匹配,虚拟机默认处理
-
注意事项:
- 如果定义的多个catch块中的类型,有子父类的关系,不能将父类的类型定义在子类的类型前面。
- 如果ctach块中定义的多个异常类型,要使用同一种处理方式,可以使用||来定义异常类型。
代码:
public static void main(String[] args) {
try {
int[] i = new int[5];
System.out.println(i[4]);
String s ="20200412";
SimpleDateFormat sim = new SimpleDateFormat();
Date da = sim.parse(s);
System.out.println(da);
}catch (ArrayIndexOutOfBoundsException indexOut){
System.out.println("索引越界");
}catch (ParseException | NullPointerException Exception ){
System.out.println("格式不匹配或者空指针异常");
}
}
try … catch…finally
-
格式:
try{ 可能会发生错误的代码 }catch(异常类型1 异常对象1){ 异常1的处理方式 }catch(异常类型2 异常对象2){ 异常2的处理方式 … }finally{ 一定需要执行的代码 }
-
finally 使用原因:
- 如果有某些代码一定要执行,将代码放在try中或者catch中或者trycatch外都有可能执行不到
- 将这段代码放在finally块中,不管遇到什么情况,系统都会去执行finally中的内容。
-
注意:
- finally关键字不能单独使用,一般是用来定义一个代码块
- finally代码块也不能单独使用,一定是和try代码块一起使用
try … finally
-
格式:
try{ 第一段代码 }finally{ 第二段代码 }
-
作用:
如果在程序中,有多段代码,多段代码都需要有执行的机会,那么可以将这多段代码,分别放在try中和finally中,这样,多段代码之间就会互不影响,都有执行的机会。用来分隔代码,让代码之间互不影响,都有执行的机会。
代码:
异常类型中的方法
在异常的体系中,顶层父类Throwable中定义了一下操作异常对象方法
常用方法:
-
getMessage() :返回发生异常的原因
getCause() :返回引起调用者异常对象发生的另一个异常对象
toString() :返回该异常对象发生的原因及所属的类型
printStackTrace() :返回该异常发生的路径,原因及类型
public static void main(String[] args) {
try {
int i = 10 / 0;
System.out.println(i);
} catch (ArithmeticException ae) {
//返回异常发生的原因
String str = ae.getMessage();
System.out.println("该异常发生的原因:"+str);
//返回引发该异常发生的异常对象
System.out.println(ae.getCause());
//打印除该异常的类型、原因、位置
ae.printStackTrace();
//返回异常的类型以及原因
System.out.println(ae.toString());
}
}
/*
该异常发生的原因:/ by zero
null
java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at note.ExceptionMothed.main(ExceptionMothed.java:8)
*/
异常中的构造方法
- Throwable() :创建一个没有任何属性值的异常对象
//学生类
public int getAge() {
if (age<0){
//创建一个有原因为异常对象
RuntimeException exp = new RuntimeException();
throw exp;
}
return age;
}
//测试类
Student s = new Student();
s.setAge(-1);
try {
System.out.println(s.getAge());
}catch (RuntimeException exp){
//获取异常出现的原因
System.out.println(exp.getMessage());
System.out.println("重新录入学生年龄:");
s.setAge(new Scanner(System.in).nextInt());
}
//null
//重新录入学生年龄:
- Throwable(String message) :创建一个有原因的异常对象
//学生类
public int getAge() {
if (age<0){
//创建一个有原因为异常对象
RuntimeException exp = new RuntimeException("年龄不能小于零");
throw exp;
}
return age;
}
//测试类
Student s = new Student();
s.setAge(-1);
try {
System.out.println(s.getAge());
}catch (RuntimeException exp){
//获取异常出现的原因
System.out.println(exp.getMessage());
System.out.println("重新录入学生年龄:");
s.setAge(new Scanner(System.in).nextInt());
}
//年龄不能小于零
//重新录入学生年龄:
throw关键字
- throw:抛出异常
- 使用场景:如果在某个方法中出现了和正常生活不符合的情况,开发人员可以在该方法中创建一个异常对象,但是创建的异常对象自己不会自动抛出,需要使用throw关键字抛出异常。
- 注意事项:
- 在创建一个异常对象之后,一定要主动抛出,如果不抛出,该异常对象就不起作用。
- 如果抛出的是一个运行时异常,在编译阶段不做检查,在运行阶段如果传入的数据是错误的,就会执行该异常。
- 如果抛出的是一个编译时异常,在抛出之后,编译期间就会出现这个异常,可以声明也可以捕获处理。
throw和throws关键字的区别
-
throw关键字是用来抛出一个创建的异常对象
throws关键字使用来声明一个异常类型
-
throw关键字在方法中使用
throws关键字在方法的声明上(参数列表的后面)使用
-
throw一次只能抛出一个异常对象
throws可以一次抛出多个异常类型(抛出的多个类型之间使用逗号分隔)
自定义异常类型
-
使用原因:
在异常的体系中官方定义了很多异常类型,但是大多类型都没有自己特殊的方法和属性,目的就是为了使用不同的类型来区分各种问题,当看到对应的类型时,就知道代码 中出现了什么问题,方便去开发者分析代码并解决问题。
-
步骤:
-
创建一个类型以Exception结尾
-
将这个异常类型继承Exception或者RunTimeException
如果继承了Exception类型,当前定义的类型就是一个编译时异常
如果继承了RuntimeException类型,当前定义的类型就是一个运行时异常类型
-
在该类中调用父类的构造方法,用于让子类创建对象
-
使用原因:
在异常的体系中官方定义了很多异常类型,但是大多类型都没有自己特殊的方法和属性,目的就是为了使用不同的类型来区分各种问题,当看到对应的类型时,就知道代码 中出现了什么问题,方便去开发者分析代码并解决问题。
-
步骤:
-
创建一个类型以Exception结尾
-
将这个异常类型继承Exception或者RunTimeException
如果继承了Exception类型,当前定义的类型就是一个编译时异常
如果继承了RuntimeException类型,当前定义的类型就是一个运行时异常类型
-
在该类中调用父类的构造方法,用于让子类创建对象
-