异常体系结构;异常处理机制;编译时异常和运行时异常;Throwable常用方法;自定义异常;异常关键字(Java Day17)
一,异常
- 概述:字面意思就是不正常。在java中代码在编译或者运行的过程中出现了不能编译或运行结果受影响的情况都属于异常。
- 体现:类 使用类对各种不正常的现象进行描述【异常的原因、位置、类型、描述等属性】
- 使用:类对象 抛出一个异常类的对象,对象中包含了该异常的产生原因、位置、类型、具体描述等具体的数据。
-
异常的体系
- 体系:
- Throwable
- Error
- Exception
- RuntimeException
- 解释:
- Throwable: 是所有的异常的顶层父类,抽取了所有异常的共性的属性和行为
- Error: 错误;属于异常中到的一大分支,指的那些程序员不能够进行捕获处理的异常。比如:内存溢出、栈溢出
- Exception: 异常;属于异常中的另一大分支,指的是我们可以使用代码捕获异常对象并进行相关处理的异常。比如:空指针异常、除数为零异常等异常
- RuntimeException: 运行异常,指的是那些编译没有问题,运行会发生异常的现象。 比如:空指针异常 除数为零异常等
-
异常的处理
- 异常的处理:就是我们异常的对象的解决方式
- 处理方式:手动处理,jvm处理
-
手动处理:需要我们自己对代码进行相关异常的处理方式。
-
声明异常 :遇到异常的时候自己不处理,把异常的类型声明出去,要别人来处理【把异常扔出去】使用关键字throws在方法的声明的后面声明异常的类型
-
场景:方法的方法体中有了异常的发生的可能
- 格式:方法名(参数列表)throws 异常的类型 {方法体}
- 注意:
- 声明异常不代表异常对象一定会产生,产生了就进行抛出处理,不产生就不处理,该声明照样声明【声明了有你就处理没有算我多此一举】
- 声明异常没有控制台输出等的效果,只有一个声明【告诉别人有异常】异常的效果往外扔的效果【扔的的抽象的类型】
- 声明异常异常产生如果抛出去后别人没有处理或者是被jvm处理了;下面的代码执行不了,如果别人把异常处理了,下面的代码继续执行。
代码示例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Demo_Exception {
public static void main(String[] args) throws FileNotFoundException {
//创建对象的时候使用了构造方法,构造方法扔出来一个FileNotFoundException异常类型
//谁使用它就扔给谁了 main方法的方法体在使用 方法体中就有了这个异常 需要处理
//main方法不想处理 也可以继续往外扔 采用声明异常
FileInputStream fis = new FileInputStream("a\\b.txt");
//异常声明了 方法体中一定会产生这个异常吗?
//工程中没有a\\b.txt 运行的时候报错 意味着异常对象产生了
//工程中有a\\b.txt 运行的时候不报错 意味着异常对象没有产生
}
}
-
捕获异常处理: 当方法体中可能出现异常,方法体自己去捕获这个异常对象,并对异常对象内容进行相关处理,不会抛给别人【自己惹得祸自己处理】
- 格式:
- try....catch
- try...catch..finally
- try...finally
-
第一种格式:try...catch
- 具体格式:try { 可能出现异常的代码段 } catch (异常类型 变量名) {异常解决的代码段}
- try: 试试的意思。尝试执行有可能有异常的代码
- catch: 捕获的意思,如果try中发生异常了,捕获该异常的对象并进行处理
- ( ): 书写catch捕获到的异常对象
- { }: 书写处理异常的代码
- 执行流程:
- try 当中的代码没有发生异常,代码自上而下来执行,执行过程中跳过catch块【不起作用】
- try 当中的代码发生异常;触发catch块到发生异常的地方进行异常对象的捕获,代码执行顺序就会从异常出直接跳转到 catch 块中执行,完毕继续执行catch块下面的代码。
- catch 话括号中如果出现异常,可以声明处理也可以捕获处理
代码示例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Demo_Exception02 {
public static void main(String[] args) {
System.out.println("开始执行");//1
//这行代码有异常 进行处理 自己惹来的祸自己处理 try...catch
try {
//文件是存在的不会发生异常
FileInputStream fis = new FileInputStream("a\\b.txt");
System.out.println("无异常执行");
//文件是不存在的会发生异常 catch就会到这里捕获异常,代码执行跳转到catch中
FileInputStream fis1 = new FileInputStream("b\\b.txt");
System.out.println("尝试执行过程中");//2 有异常不执行了 无异常就执行
} catch (FileNotFoundException e) {
System.out.println("这是一个文件无法找到的异常,已经处理");//4 捕获异常并执行
//catch中代码产生了异常 该怎们处理就怎么处理 可以声明也可以捕获
try {
FileInputStream fis = new FileInputStream("a\\b.txt");
} catch (FileNotFoundException e1) {
System.out.println("异常处理了");
}
}
System.out.println("代码执行完毕!");//3
}
}
- 变形格式: 多个 catch 块格式
try {有可能有异常的代码段} catch(异常对象1){解决异常1的方案} catch(异常对象2){解决异常2的方案}....catch(异常对象n){解决异常n的方案}
- 注意事项:【掌握的面试的时候经常被问到里面的相关知识点】
- 多个 catch 块,执行的时候只会执行其中之一或者一个也不执行
- 多个异常有了同时触发的条件,实际以第一个被触发的异常为准,其他的异常不会被触发,对应执行的是第一个异常的 catch 块,其他 catch 块不管【不执行】
- catch 块中不可以写 break,但是可以写 return 提前【异常处理完】结束方法
- catch 的多个异常对象之间如果有子父类关系,catch 异常对象的顺序有要求:自上而下遵循由小到大【子类类型对象在前,父类类型对象在后】
- 捕获的异常对象处理的时候选择catch块时遵循:
- 优先匹配自己类型的对象的catch块
- 没有自己类型的对象catch块,就近执行最近的父类的对象的catch块
- 自己和父类对象的catch块都没有,那就报错
- jdk1.7之后,异常捕获时异常类型可以使用|【或】表示【多种不同的异常想要用相同的处理方式的时候可以使用|】
- 多种异常类型对象,可以使用相同的处理方式
- 异常类型是相互独立没有关系的类型【不能有继承关系】
- 格式:catch(异常类型1 | 异常类型2 |异常类型3 对象名){ }
代码示例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Scanner;
public class Demo_Exception03 {
public static void main(String[] args) {
System.out.println("开始执行");//1
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数:");
int num = sc.nextInt();
//这行代码有异常 进行处理 自己惹来的祸自己处理 try...catch
try {
//文件是存在的不会发生异常 //异常1 FileNotFoundException
FileInputStream fis = new FileInputStream("a\\b.txt");
System.out.println("无异常执行");
//文件是不存在的会发生异常 catch就会到这里捕获异常,代码执行跳转到catch中
FileInputStream fis1 = new FileInputStream("b\\b.txt");
System.out.println("尝试执行过程中");//2 有异常不执行了 无异常就执行
//异常2:除数为0的异常 ArithmeticException
System.out.println(100/num);//第一个被触发了
} catch (FileNotFoundException | ArithmeticException | NullPointerException e) {
System.out.println("这是一个文件无法找到的异常,已经处理");//4 捕获异常并执行
//break;
//return ;//结束方法的
}/*catch (ArithmeticException e) {
System.out.println("这是一个文件无法找到的异常,已经处理");
//System.out.println("这是一个除数为0异常");
}*//*catch (IOException e) {//他是捕获到的异常对象的父类
System.out.println("这是一个io的异常,已经处理");//4 捕获异常并执行
}*//*catch (Exception e) {//爷爷类 执行爷爷类
System.out.println("这是一个编译异常,已经处理");//4 捕获异常并执行
}*/
System.out.println("代码执行完毕!");//3
}
}
-
第二种:try...catch...finally
- 具体格式: try {有可能产生异常代码} catch(异常对象){对异常对象的解决方案} finally {无论有无都必须要执行的代码}
- 特点:finally 里面的代码不管try 当中的代码是否会发生异常它肯定会执行的。
- 使用场景:io 流对象关闭使用 finally。
- 注意事项:
- 中间的 catch 块可以有多个
- finally 不可以单独使用,必须跟在 try 代码块后面
代码示例
import java.io.FileInputStream;
public class Demo_Exception04 {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("b\\b.txt");
//写在这没有异常可以打印输出 有异常输出不了
//System.out.println(123);
} catch (Exception e) {
System.out.println("异常已处理");
//没有异常 输出不了 有异常输出了
//System.out.println(123);
return;
}finally {
System.out.println(123);//百分百的保证代码执行
}
//必须要在控制台输出一个123
//如果catch块中产生异常或者有return 有可能输出不了
//System.out.println(123);
}
}
-
第三种处理方式【不处理异常,纯粹的为了finally】
- 格式:try { 有可能出现异常的代码 } finally { 必须要执行的代码 }
- 不怎么用;这种格式并不是处理异常的,单纯就是为了finally的使用,异常根本就没有被处理
代码示例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Demo_Exception05 {
public static void main(String[] args) /*throws FileNotFoundException*/ {
try {
try {
FileInputStream fis = new FileInputStream("b\\b.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//FileInputStream fis = new FileInputStream("b\\b.txt");
}/* catch (FileNotFoundException e) {
e.printStackTrace();
}*/ finally {//finally不能单独使用,必须需要try
System.out.println("nihaoha,bingdu");
}
}
}
- 处理异常使用格式注意事项:
- try 代码块可以单独使用【单独使用没有意义】变换也不能单独使用
- catch 代码块不能单独使用,不能离开 try 代码块。
- finally 不能单独使用,要结合 try 代码块使用。
二,jvm的异常处理方式
- jvm 是作为执行者,调用的 main 方法;其他方法都需要通过main方法直接或间接的调用,异常一旦产生,自己又不想自己解决采用声明的方式来抛出异常,不停抛抛来抛去最终抛到 jvm,jvm 最后的一个调用者抛不了必须要自己处理。
- jvm 的处理方式:先把程序给停止 然后捕获异常处理【把异常对象中的相关信息进行打印】。
- jvm 异常的处理和捕获处理的区别?
- 捕获处理异常:catch 块下面的代码可以继续执行出效果【jvm没有停止】
- jvm 处理异常:产生异常代码处后面的所有代码都不能够执行了【jvm直接停止】
三,异常分类
- 编译异常:在代码编译过程中会对 相关代码进行格式和语法的检查,如果不满足就会直接报错提示,不会将错误放到运行去。编译异常都属于Exception的子类,必须要进行处理才可能继续执行
- 运行异常:在编译的时候不会对代码进行一些相关的检查,只有在运行的时候才会发现有问题的异常。运行异常都是RuntimeException的子类,可以处理也可以不处理【交给jvm来处理】一般的运行异常选择不处理
四,异常体系的常用方法
- 异常体系的方法全部定义在 Throwable 类中,其他异常类没有定义任何的方法,都是继承 Throwable 中的方法。
- 构造方法:
- Throwable(): 创建一个没有任何异常信息的异常对象
- Throwable(String message): 创建一个只有异常信息描述的对象
- Throwable(Throwable t): 创建一个有异常原因的异常对象
- Throwable(String message,Throwable t): 创建一个有异常信息和异常原因的异常对象
- 普通方法:
- getCause: 获取异常对象的异常原因
- getMessage: 获取异常的提示详细信息【常用】
- toString: 获取异常对象的详细属性信息
- printStackTrace: 打印异常的调用地址信息【常用】
代码示例
public class Demo {
public static void main(String[] args) {
Throwable t1 = new Throwable();
Throwable t2 = new Throwable("这是一个角标越界异常");
Throwable t3 = new Throwable(t2);
Throwable t4 = new Throwable("这是一个类型转换异常",t1);
System.out.println(t1.getCause());
System.out.println(t1.getMessage());
System.out.println(t1.toString());
t1.printStackTrace();
System.out.println("===============");
System.out.println(t2.getCause());
System.out.println(t2.getMessage());
System.out.println(t2.toString());
t2.printStackTrace();
System.out.println("===============");
System.out.println(t3.getCause());
System.out.println(t3.getMessage());
System.out.println(t3.toString());
t3.printStackTrace();
System.out.println("================");
System.out.println(t4.getCause());
System.out.println(t4.getMessage());
System.out.println(t4.toString());
t4.printStackTrace();
}
}
五,异常关键字
- 异常的关键字
- throws:抛出 抛出的是异常的类型
1.1,主要使用在异常处理的声明异常中,主要是在方法的参数列表后面抛出异常的类型【一般针对的是编译异常】
1.2,异常可能有异常对象产生,也可能没有产生。
2. throw:抛出 抛出的是异常的对象。主要使用在方法体中用来产生真正的异常对象,一定是产生了异常对象
- 自定义异常
- 概述:java开发中大部分使用的异常jdk都给大家提前写好了,直接拿来用,但是一些公司开发中jdk提供的异常类不能满足自己的业务需求,就得自己书写一个异常类为自己所用。
- 自定义异常分为:
- 编译异常:
- 步骤:
- 创建一个类,类名默认要求以 Exception 结尾【必须】
- 要这个类继承 Exception
- 提供所有的空参构造
- 运行异常:
- 步骤:
- 创建一个类,类名默认要求以 Exception 结尾【必须】
- 要这个类继承 RuntimeException
- 提供所有的空参构造
代码示例
//自定义编译异常
public class MyAgeException extends Exception{
// 空参构造
public MyAgeException() {
super();
}
public MyAgeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public MyAgeException(String message, Throwable cause) {
super(message, cause);
}
public MyAgeException(String message) {
super(message);
}
public MyAgeException(Throwable cause) {
super(cause);
}
}
//自定义运行异常public class MyAgeRuntimeException extends RuntimeException{
public MyAgeRuntimeException() {
super();
}
public MyAgeRuntimeException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public MyAgeRuntimeException(String message, Throwable cause) {
super(message, cause);
}
public MyAgeRuntimeException(String message) {
super(message);
}
public MyAgeRuntimeException(Throwable cause) {
super(cause);
}
}
//自定义的使用:public class Gril {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
if (age<0) {
throw new MyAgeRuntimeException("年龄不合法,是负数了");
}
this.age = age;
}
//有参构造
public Gril(int age)/* throws MyAgeException*/ {
super();
if (age>200) {
try {
throw new MyAgeException("年龄太大了,都成老妖婆了");
} catch (MyAgeException e) {
e.printStackTrace();
}
}
this.age = age;
}
public Gril() {
super();
}
}
//测试:public class Gril_Test {
public static void main(String[] args) {
Gril gril = new Gril();
gril.setAge(10);
//gril.setAge(-78);
//Gril中没有处理异常抛出来
/*try {
Gril gril2 = new Gril(20);
} catch (MyAgeException e) {
e.printStackTrace();//异常体系的常用方法 把对象e中的地址信息打印出来
}*/
Gril gril2 = new Gril(208);
}
}