18. 异常处理篇
1、异常的定义
异常是不正常的事件,不是错误
异常是指程序运行的过程中,发生某些意外的事情,比如10除以0, 文件不存在等。
public class ExcptionTest {
public void test(){
System.out.println("====运行前====");
int a=10/0;
System.out.println("====运行后====");
}
public static void main(String[] args) {
ExcptionTest et= new ExcptionTest();
et.test();
}
}
在运行期我们int型的a 被除以0 那么 这样的事件称之为不正常事件,将发生异常,异常不是错误,是不正常的事情,错误往往是跟源代码的BUG 或者内部环境有关,如内存泄漏等。
2、错误的定义
错误是很难处理的,比如内存溢出等,不能够通过异常处理机制来解决。 异常是程序中发生的不正常事件流,通过处理程序依然可以运行下去。但是错误是无法控制的,程序肯定要中断。 应用软件与操作系统或其他应用软件发生不兼容造成应用程序发生错误。
3、API中异常结构及常见异常
所谓异常处理,就是把一些不正常的事件,用类标记出来。
当发生某种不正常事件时,抛出这个类的对象,JVM的异常处理机制能够识别这种对象,并进行处理。
API中根据需要,定义了一些类,来标记各种不正常的事件。我们把API中定义的这些异常类称为标准异常。 另外,需要理解的是,API中定义的异常类型,除了运行期异常是运行时检测到的一些异常,编译期异常都是用来标记API中其他类中的一些事件,比如IO处理的文件找不到等。
3.1 异常的体系
在Java中,Throwable是所有错误和异常的父类,Throwable类有两个直接子类:Exception类、Error类。
Error表示Java程序运行错误,可能是编译期错误或者系统错误,往往程序中并不处理,如果程序出现Error,则系统能做的工作也只能有记录错误的成因和安全终止。
Exception表示Java程序运行异常,即运行中的程序发生了人们不期望发生的事件,可以被Java异常处理机制处理。是所有异常类的父类,是程序员所关心的。
3.2 Exception的分类
异常分为运行期异常和编译期异常两种。
(1)运行期异常(RuntimeException):指在 Java 虚拟机正常运行期间抛出的异常,是可以被捕获处理的,如果出现RuntimeException异常,那么一定是程序发生错误导致的。
- NullPointerException —— 空指针异常
- ClassCastException —— 类型转换异常
- ArrayIndexOutOfBundsException —— 数组下标越界 ……
(2)编译期异常(CheckedException):指在编译阶段 Java 编译器会检查 CheckedException 异常并强制程序捕获和处理此类程序,即要求程序在可能出现异常的地方通过 try catch 语句块捕获并处理异常。
-
IOException —— 输入输出异常(打开错误文件)
-
SQLExeption —— SQL 语法错误
-
ClassNotFoundException —— 类不存在
……
3.3 常见异常
Exception 异常层次结构的根类
RuntimeException 所有运行期异常的基类
ArithmeticException 算术错误情形,如以零作除数
IllegalArgumentException 方法接收到非法参数
ArrayIndexOutOfBoundsException 数组大小小于或大于实际的数组大小
NullPointerException 尝试访问 null 对象成员
ClassNotFoundException 不能加载所需的类
NumberFormatException 例如字符串到 float 型数字的转换无效
IOException I/O 异常的根类
FileNotFoundException 找不到文件
EOFException 文件结束
InterruptedException 线程中断
4、使用 try catch 捕获并处理异常
4.1 try的使用和定义
检测不安全的代码块(发现异常) try块中任何一条语句发生了异常,下面的代码将不会被执行,程序将跳转到异常处理代码块中,即catch块。因此,不要随意将不相关的代码放到try块中,因为随时可能会中断执行。
try{ 可能会发生异常的代码 }
4.1 catch的使用和定义
把抓到的类型匹配的异常捕获,保证程序能继续运行下去。 catch语句必须紧跟着try语句之后,称为捕获异常,也就是异常处理函数,一个try后面可以写多个catch,分别捕获不同类型的异常,要从子类往父类的顺序写,否则有编译错误。
catch(异常类型 引用名){ 异常处理代码 }
4.1 finally的使用和定义
finally该内容总是会执行的,只能有一个finally语句
finally{ 必须执行的逻辑 }
4.4 try/catch/finally基本语法
try{
可能会发生异常的代码
}catch(异常类型 引用名){
异常处理代码
}finally{
必须执行代码
}
(1)运行流程(无异常情况)
try{
System.out.println("try");
}catch(Exception e) {
System.out.println("Exception");
}
finally{
System.out.println("Finally");
}
结果:
try
Finally
(2)运行流程(有异常情况)
try{
String demo = null;
demo.split("a");
System.out.println("try");
}catch(Exception e) {
System.out.println("Exception");
e.printStackTrace();
}
finally{
System.out.println("Finally");
}
结果:
java.lang.NullPointerException
Exception
at com.company.project.test.t1201.Demo.main(Demo.java:9)
Finally
4.5 finally与return
在try中没有异常的情况下try、catch、finally的执行顺序 try --- finally
如果try中有异常,执行顺序是try --- catch --- finally
如果try中没有异常并且try中有return这时候正常执行顺序是try ---- finally --- return
如果try中有异常并且try中有return这时候正常执行顺序是try----catch---finally--- return
如果try有异常,相应catch中有return,顺序是try---catch---finally---return 总之 finally 永远执行!
除非,关掉Java虚拟机,System.exit(0),finall将不会执行。
5、抛出异常(throw与throws )
遇到异常时不进行具体处理,而是将异常抛给调用者,由调用者根据情况处理。有时候是直接捕获并处理,也有可能是继续向上抛出异常。
抛出异常有三种形式:throw、throws、系统自动抛出异常。
其中,throws 作用在方法上,用于定义方法可能抛出的异常;throw 作用在方法内,表明抛出一个异常。他们两个共同处理编译器异常,而运行期异常通常是系统自动抛出异常。
系统自动抛出异常:前面我们学习过,异常使用一些特殊类标记的,所谓发生异常,就是抛出一个异常类对象,JVM的异常机制能够识别这样的对象,并进行处理。 运行期异常都是JVM在运行时自行抛出的,而编译期异常都是程序员在编写代码时,通过语句自行抛出的。
5.1 throw
关键字throw用于显式抛出异常,抛除异常后处理。 使用try-catch捕获异常
5.1.1语法: throw new 异常类构造方法; 例如:throw new Exception();
5.1.2 为什么要throw异常呢? 主要是要用这种异常类型标记一些不正常的事件,而且希望这些事件发生时,一定能被调用者关注并处理。 throw抛出异常后,可以有两种方式处理,一般使用第二种,即throws。 因为如果throw后使用try catch自行处理,意义不大。异常只被处理一次,调用者就没法处理该异常。 如果throw后使用了try catch,往往也会在catch中再使用throw继续抛出。 利用抛出的异常来标记不正常的事件,从而对事件流统一处理。
5.1.3 throw的使用
(1)一般配合try-catch 使用
格式如下:
public class ThisDemo06{
public static void main(String args[]){
try{
throw new Exception("自己抛着玩的。") ;// 抛出异常的实例化对象
}catch(Exception e){
System.out.println(e) ;
}
throw e 实现了在try-catch块中对异常的上抛,catch块中不再对异常进行处理 在catch块中,throw e后不能再跟代码 ,且cathc块外,后面的代码不被执行。
(2)也可以不配合try-catch,需要配合throws
static int avg(int n1,int n2)throws Exception{
if (n1<0||n2<0) { //判断方法参数是否满足条件
throw new Exception("不能使用负数");//错误信息
}
if (n1>100||n2>100) {
throw new Exception("数值太大了");
}
return (n1+n2)/2; // 将参数的平均值返回
}
5.2 throws
这里的throws抛出的是一个异常类对象,这个对象带有所发生异常的信息。我们可以直接使用JAVA中已经封装好的异常信息类,也可以自己去定义一个异常类。
用了throws关键字,相当于给一个方法加上了 不安全 标签。 使得调用这个方法的方法必须处理这些类型的异常,否则将出现编译错误。 当然,处理的方法也依然有两种,即try catch或者继续throws。
任何方法都可以无条件的使用throws声明抛出任意类型的异常,抽象方法也可以。 可以说方法使用throws声明异常,在语法上没有任何要求。如果子类覆盖父类方法,子类的方法不能声明抛除比父类方法更多的异常类型。 就想你的年龄肯定没有你老爸的年龄大一样。
5.2.1 throws 使用地方
用于方法声明处,指出方法引发的异常。
可以声明多种异常类型,用逗号分开即可。
5.2.2 格式
public void test throws 异常1,异常2,异常3{
}
5.2.3 throw的使用
(1)调用系统的异常信息类
public class Test {
int age;
public void Abnormal() throws IOException{
int i=0;
int x=5/i;
System.out.println(x);
}
//主函数入口
public static void main(String[] args) throws IOException {
Test t=new Test();
t.Abnormal();
}
}
(2)自定义异常类
1.自定义异常就是自己定义的异常类,也就是API中的标准异常类的直接或间接的子类。
自定义异常类中往往不写其他方法,只重载需要使用的构造方法
2.作用:用自定义异常标记业务逻辑的异常,避免与标准异常混淆。
3.基本语法:
public class 异常类名 extends Exception{
public 异常类名(String msg){
super(msg);
}
}
例如:
//自定义异常类
public class AgeException extends Exception{
public AgeException() {
}
public AgeException(String msg) {
super(msg);
}
}
//测试类
public class Test1 {
public static void main(String[] args) {
User u = new User();
try {
u.setAge(-1);
}catch(AgeException e){
System.out.println(e.getMessage());
}
System.out.println(u);
}
}
class User {
private int age;
public User() {
}
public int getAge() {
return age;
}
public void setAge(int age) throws AgeException {
if (age < 0 || age > 150) {
throw new AgeException("年龄输入有误");
}
this.age = age;
}
@Override
public String toString() {
return "User [age=" + age + "]";
}
}
5.3 throws 和 throw 的区别
- 位置不同:throws 是作用在方法上,后面跟着的是异常的类;而 throw 作用在方法内,后面跟着的是异常的对象。
- 功能不同:throws 用来声明方法在运行过程中可能出现的异常,以便调用者根据不同的异常类型预先定义不同的处理方式;throw 用来抛出封装了异常信息的对象,程序在执行到 throw 时后续的代码将不再执行,而是跳转到调用者,并将异常信息抛给调用者。也就是说,throw 后面的语句将不再执行 ( finally 语句块除外)。
6、总结
throw/throws就是一个甩手掌柜,它只会把异常一层层地往上抛,直到有人去处理它。而try...catch就是那个劳苦工人,负责获取相应的异常并对它进行处理。
遇到异常,有两种处理方式,第一种自己处理,第二种向上抛,交给调用自己的时候再做处理。
共有 0 条评论