概述
1.异常是一个体系,具有自己的体系结构。
2.用于表达程序中特殊情况的一种手段
这句话是什么意思呢?
1.在程序中,可能会出现某些情况,例如给用户的年龄赋值的时候,可能会错误录入,(比如负数,符号等)出现这种情况是不符合生活的正常状态的时候就认为是一个异常,
2.碰到异常情况之后,就可以使用异常对象的方式,描述异常信息。
可以将异常抛出,实现程序的结束或者跳转
3.是用于处理特殊情况的一种机制。
异常体系
Throwable //异常体系的顶级父类
Error //正常程序,不应该捕获处理,严重问题
Exception //异常(例外),正常程序,应该试图捕获处理,轻微的问题
RuntimeException //运行时异常
jvm默认处理异常的方式
1.当某个方法出现异常情况时,会将异常封装成一个异常对象。
2.异常对象抛出给调用者,一层一层往上抛,最终到达main方法,主方法也没有办法处理,就抛出给jvm虚拟机
3.jvm虚拟机有没有得到指令处理异常,只能将异常的信息通过标准错误流(System.err)打印到控制台,结束自己。
示例代码:
package Exception;
import java.util.Scanner;
public class Demo1_异常的处理 {
public static void main(String[] args) {
getNum(new Scanner(System.in).nextInt());//当输入0的时候会出现异常
}
private static void getNum(int i){
System.out.println(10 / i);
}
}
这里报了一个异常叫做
ArithmeticException: / by zero 算数异常 0不能作为除数。
在我们平时书写代码的时候也会经常的遇见这些异常,下面就来说一下异常的处理方式。
手动处理异常
概述:
1.通过写代码来对异常的处理方案做干预。
(一般如果在代码中出现异常了,没有合适的方法处理会导致代码向上抛出异常,一层一层最后到jvm虚拟机来在控制台抛出异常信息,我们要做的就是对这个异常进行捕获和干预)
2.分类:
对异常进行声明(说明白了会出问题,你要么想办法处理,要么别怪我)
对异常进行捕获解决
3.解决的办法
try…catch
try…catch…finally
try…finally
捕获解决的第一种格式:
1.格式:
try {
可能出现异常的代码
} catch (可能出现的异常类型 异常的变量名称) {
针对该异常类型的处理办法
}
2.说明:
1.try ,尝试,可能会失败,用于对异常的检测。
2.catch:抓住,捕获
小括号,捕获定义的这种异常类型的异常,变量名称表示一个异常对象的引用。
大括号,针对这种异常的处理方案。
3.执行流程:
(1)执行try语句。
(2)如果没有异常,当try执行完毕时就算完成了。
(3)如果在执行try语句的时候,出现了异常,就和catch中声明的异常进行匹配(catch括号里面的),如果匹配成功,就执行针对这种异常的处理方案;如果没有匹配成功,就按照jvm默认的处理方式来处理。
示例代码:
package Exception;
import java.util.Scanner;
public class Demo2_异常的处理的第一种格式 {
public static void main(String[] args) {
try {
getNum(new Scanner(System.in).nextInt());//当输入0的时候会出现异常
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
}
}
private static void getNum(int i) {
System.out.println(10 / i);
}
}
捕获解决的第一种格式的多种异常情况
1、一段代码在一次运行过程中,只能发生一个异常,但是可能发生的异常种类有很多
2、针对多种可能出现的意外情况,就需要准备针对每种异常情况做方案
3、格式:
try {
可能出现异常的代码
} catch (异常类型1 异常名称1) {
针对异常类型1的处理方案
} catch (异常类型2 异常名称2) {
针对异常类型2的处理方案
}
...
} catch (异常类型n 异常名称n) {
针对异常类型n的处理方案
}
4、执行流程:
1、执行try中的内容,如果执行成功,就当前语句块就结束了
2、如果在try中发生了异常,就跳转到catch块中,从上到下依次匹配这些异常类型,匹配到哪个类型,就执行哪个类型对应的处理方案
3、当某种方案执行完成,整个try…catch就算执行完了,并且catch执行完之后,就相当于法异常没有发生,后续代码继续执行
5、注意事项:
1、如果在多个catch块中,可能出现的异常类型有子父类的关系,必须子类异常在上面,父类异常在下面
2、如果有多种异常,需要有相同的处理方案,可以将多种异常进行“逻辑或”运算,具体格式:(jdk1.7出现)
catch(可能出现的异常类型1 | 可能出现的异常类型2 异常对象名称) {
两种异常出现之后的处理方案
}
代码示例:
package Exception;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Demo2_异常的处理的第一种格式 {
public static void main(String[] args) {
while (true) {
try {
System.out.println("输入除数");
getNum(new Scanner(System.in).nextInt());//当输入0的时候会出现异常,和输入不匹配异常。
int[] arr2 = {1, 2, 3, 4, 5};
System.out.println(arr2[20]);//下标越界异常。
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
} catch (InputMismatchException e) {
System.out.println("输入不匹配");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("下标越界异常");
}
}
}
private static void getNum(int i) {
System.out.println(10 / i);
}
}
捕获异常的第二种方式
1、格式:
try {
可能出现异常的代码
} catch (可能出现的异常类型 异常名称) {
这种异常的处理方案
} finally {
一定会执行的代码
}
2、finally:
1、没有发生异常,finally会执行
2、发生了可以捕获的异常,finally会执行
3、发生了无法捕获的异常,finally也会执行
3、finally的作用
一般用于关闭资源
示例代码:
package Exception;
import java.util.Scanner;
public class Demo2_异常的处理的第一种格式 {
public static void main(String[] args) {
while (true) {
try {
System.out.println("输入除数");
getNum(new Scanner(System.in).nextInt());//当输入0的时候会出现异常,和输入不匹配异常。
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
} finally {
System.out.println("必须执行的代码");
}
}
}
private static void getNum(int i) {
System.out.println(10 / i);
}
}
捕获解决异常的第三种格式
1、说明:
第三种格式只能检测异常,不能捕获和解决异常
2、格式:
try {
可能出现异常的代码
} finally {
一定要执行的代码
}
3、作用:
将try中的代码和finally中的代码,分离开,不会因为try中代码的执行失败,影响到finally中的代码的执行机会
这个和try catch finally差不多,这里就不在写代码示例了。
编译时异常和运行时异常
1、强调:
1、无论是编译时异常,还是运行时异常,都是只能发生在运行阶段的异常
2、编译阶段只能有“编译成功”(会出现.class的字节码文件),“编译失败”(无法生成.class的字节码文件)
3、编译失败出现的报错信息(javac.exe编译java源代码时,将错误了打印)
2、区别:
1、继承体系区别
编译时异常,都是Exception或者Exception的子类(RuntimeException除外)
运行时异常,都是RuntimeException以及RuntimeException的子类
2、处理方式区别
运行时异常,在编译阶段不强制要求给出解决方案的异常,可以在编译阶段,认为将来没有异常发生,因此可以对该异常不做任何处理,还可以使用非异常处理的方式,对异常进行避免
编译时异常,在编译阶段强制要求给出出现异常之后的解决方案,如果没有给出,就编译报错,不允许运行。没有办法通过非异常处理的手段进行避免异常。
示例代码:
package Exception;
import java.io.FileInputStream;
import java.util.Scanner;
public class Demo3_编译时和运行时异常 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
if (num != 0) {
int result = 10 / num;
System.out.println(result);
}
FileInputStream fis = new FileInputStream("a.txt");//这一句就是编译时异常,不解决无法编译成功。
}
Throwable中的常用方法
1、说明:
在整个异常体系中,几乎所有的成员方法都定义在Throwable整个顶层父类中,在各种子类中,没有特有的成员方法;构造方法也是都在访问Throwable中的构造方法
2、构造方法:用于将异常情况封装成对象
Throwable()
创建一个没有描述的异常对象
Throwable(String message)
创建一个带有消息描述的异常对象
Throwable(Throwable cause)
创建一个带有原因异常对象的异常对象
3、成员方法:所有的异常都可以调用以下方法
getMessage()
获取异常对象中的详细消息字符串
toString()
获取异常对象中的简短描述
getCause()
获取异常对象中的原因异常对象
printStackTrace()
打印当前异常对象的调用栈轨迹
栈轨迹:调用的轨迹,可以很清晰的表示出异常出现和抛出的路径,有了这个信息, 可以查找出具体出现问题的地方
代码示例:
package Exception;
import java.util.Scanner;
public class Demo2_异常的处理的第一种格式 {
public static void main(String[] args) {
try {
System.out.println("输入除数");
getNum(new Scanner(System.in).nextInt());//当输入0的时候会出现异常,和输入不匹配异常。
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
System.out.println(e.getMessage());
System.out.println(e.toString());
System.out.println(e.getCause());
}
}
private static void getNum(int i) {
System.out.println(10 / i);
}
}
throw关键字
1、作用:
将一个异常对象抛出,实现程序的跳转或者结束
2、使用场景:
当程序运行到了,程序员认为不正常的情况时(不符合生活的正常状态、不符合当前业务逻辑的时候),就不需要让程序继续按照正常的逻辑运行下去,就在这个条件下抛出一个异常对象
3、注意事项:
1、如果抛出的是一个运行时异常,那么在编译阶段就相当于没有异常
2、如果抛出的是一个编译时异常,那么就需要对该异常进行处理(声明或者解决
代码示例:
package Exception;
public class Demo4_throw关键字 {
public static void main(String[] args) {
getNum(0);
}
private static void getNum(int i) {
if (i == 0){
throw new RuntimeException("0不能作为除数");
}else{
System.out.println(10 / i);
}
}
}
这次报的是运行时异常而不是算术异常。
throws关键字
1、作用:
用于给一个方法声明可能出现的异常类型
2、格式:
修饰符 返回值类型 方法名称(参数列表) throws 异常类型1, 异常类型2... {
}
3、说明:
1、用于表达可能出现的异常类型
2、即使没有出现这种异常,也可以声明
4、注意事项:
1、运行时异常不需要声明,声明了也相当于没声明
2、尽量声明小的、少的异常
代码示例:
package Exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Demo5_throws关键字 {
public static void main(String[] args) throws FileNotFoundException {
FileInputStream fis = new FileInputStream("a.txt");
}
}
自定义异常
1、jdk中提供了很多的异常类型,都没有什么特有的方法
2、为什么要提供那么多的异常类型?
1、方便辨别出现的异常情况,代码中可能出现各种例外情况,如果有不同类型的异常对象,就可以非常快速的根据异常类型,定位出现异常情况的位置和原因
2、可以根据不同的异常类型,判断出不同的异常情况,方便在多个catch块中,做不同的处理方案
3、为什么要自定义异常?
在自己公司、当前项目中,可能出现一些特有的例外情况,没有被定义在jdk中,就需要自己定义这个类型,方便处理和辨认。
3、自定义异常的步骤:
1、定义一个类,以Exception结尾
2、继承异常类型
如果是编译时异常,就继承Exception
如果是运行时异常,就继承RuntimeException
3、自己写出构造方法
Alt + shift + s c
示例代码:
代码1:
package Exception;
public class Person {
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
if(age < 0){
throw new IllegalAgeException("年龄非法: " + age);//抛出一个自定义类型的异常,也是我觉得不符合常理的情况
}else {
this.age = age;
}
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
代码2:
package Exception;
public class IllegalAgeException extends RuntimeException{
public IllegalAgeException() {
}
public IllegalAgeException(String message) {
super(message);
}
}
//自定义的异常类型
代码3:
//测试类
package Exception;
public class Demo5_自定义异常 {
public static void main(String[] args) {
Person p = new Person(12, "小明");
try {
p.setAge(-2);
} catch (IllegalAgeException e) {
System.out.println(e.getMessage());
}
System.out.println(p);
}
}
好了异常的不分就暂时介绍到这里。