1 -基本概念
异常,指不正常,在Java中异常处理机制能让程序在异常情况发生时,按照实现设定好的异常逻辑有针对性的处理出现的异常。
java.lang.Throwable类是Java中所有错误和异常的超类,已知直接子类有:Error类和Exception类。
异常的层次结构
2 - 异常的主要分类
java.lang.Exception类是所有异常类的超类
名称 | 说明 |
---|---|
RuntimeException | 运行时异常类,也叫作非检测性异常 |
IOException | IO异常类,也叫作检测性异常 |
RuntimeException类的主要子类
异常类型 | 说明 |
---|---|
AritmeticException | 算数异常 |
ArrayIndexOutofBoundsException | 数组下标越界异常(间接子类) |
NullPointException | 空指针异常 |
ClassCastException | 类型转换异常 |
NumberFormatException | 数字格式异常(间接子类) |
注意:
当程序执行中产生异常并没有手动处理时,则采用默认处理方法,默认处理方法的方式就是:打印异常名称、异常原因、异常位置等相关信息,并终止程序,导致后续代码无法执行。
运行时异常的处理
绝大多数的运行时异常/非检测性异常都可以采用if判断的方式避免发生。
/**
* @author Mr.乐
* @data 2022/8/5 23:03
*/
public class Demo01 {
public static void main(String[] args) {
//运行时异常通常可以通过if判断的方式避免掉
int a = 10;
int b = 0;
if(b != 0){
System.out.println(a / b); //java.lang.ArithmeticException
}
int[] arr = new int[5];
int pos = 5;
if(pos < 5 && pos >= 0){
System.out.println(arr[pos]);//java.lang.ArrayIndexOutOfBoundsException
}
String s1 = null;
if(s1 != null){
System.out.println(s1.length());//java.lang.NullPointerException
}
Exception e = new Exception();
if (e instanceof IOException){//判断e是否属于IOException的类型
IOException io = (IOException) e; //java.lang.ClassCastException
}
String s2 = "123abc";
if(s2.matches("\\d+")){//判断字符串内部是否是数字格式
System.out.println(Integer.parseInt(s2));//java.lang.NumberFormatException
}
}
}
3 - 异常的捕获格式
语法格式:
try{
编写可能会产生异常的语句块;
}catch(异常的类型 引用){
编写针对该异常的处理语句;
}finally{
无论是否发生异常都需要执行的语句块;
}
4 - 异常执行流程
/**
* @author Mr.乐
* @data 2022/8/5 23:47
*/
public class Demo02 {
public static void main(String[] args) {
try{
System.out.println("a");
FileInputStream fis = new FileInputStream("./aa.txt");
System.out.println("b");
}catch (FileNotFoundException e){
System.out.println("c");
// System.out.println("异常真的出现了,我的处理办法是,按兵不动!");
e.printStackTrace();
}finally {
System.out.println("d");
}
System.out.println("e");
//没有异常情况时: a b d e
//有异常情况时: a c d e
}
}
finally中通常编写无论是否异常发生都会执行的代码,因此通常用来做善后处理,比如:关闭流,关闭数据库连接。
当多个catch同时出现的情况下,需要将小的异常类型放在打的类型的上面。
/**
* @author Mr.乐
* @data 2022/8/6 0:25
*/
public class Demo03 {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("./a.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
}
5 - 手动抛出异常
在某些特殊场合中,对于出现的异常无法直接处理或者不便于处理时,就可以选择将异常转移给调用方法这,这种形式叫做异常的抛出。
/**
* @author Mr.乐
* @data 2022/8/6 0:25
*/
public class Demo04 {
public static void show() throws FileNotFoundException {
FileInputStream fis = new FileInputStream("./a.txt");
}
public static void main(String[] args) {
try {
Demo04.show();//抛出的异常会抛给方法的调用者
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//异常一旦抛到了主方法中,建议进行捕获,如果不捕获(处理)的情况下,选择抛出就会抛给JVM,会增加JVM的负担
}
}
在子类中,如果父类被重写的方法抛出了异常,那么子类中重写的方法可以抛出更小的异常、一样的异常,但是不可以抛出更大的异常。
/**
* @author Mr.乐
* @data 2022/8/6 0:28
*/
public class Demo05 {
public void show() throws IOException {
System.out.println("我是Demo05中的show方法");
}
/*
在子类中,如果父类被重写的方法抛出了异常,那么子类中重写的方法可以抛出更小的异常、一样的异常,
但是不可以抛出更大的异常。
* */
}
--------------------------------------------------------------------------------
/**
* @author Mr.乐
* @data 2022/8/6 0:38
*/
public class Demo05Sub extends Demo05 {
//重写的父类方法会将方法中的异常类型一同重写到子类方法中
@Override
public void show() throws IOException {
super.show();
}
// @Override
// public void show() throws FileNotFoundException {//子类可以抛出比父类更小的异常
// }
// @Override
// public void show() throws Exception {//子类不可以抛出比父类更大的异常
// }
}
6 - 自定义异常
自定义异常类的方式
自定义类去继承于Exception类或者其子类,提供两个版本的构造方法,一个是无参的,一个是String类型的有参构造。
public class AgeException extends Exception{
//提供无参构造
public AgeException() {
}
//提供有参,并且是String类型参数的构造方法
public AgeException(String message) {
super(message);//将参数传递给父类的构造方法,目的是打印传递的参数
}
}
---------------------------------------------------------------------------
/**
* @author Mr.乐
* @data 2022/8/6 0:52
*/
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
// this.name = name;
// this.age = age;
setName(name);
//在构造方法中将异常捕获
try {
setAge(age);
} catch (AgeException e) {
e.printStackTrace();
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) throws AgeException {//188
//做学生年龄的合理判断,如果年龄不合理,则抛出自定义异常
if (age < 0 || age > 150){
//抛出自定义年龄异常
throw new AgeException("年龄不合理");//异常抛出
}else{
this.age = age;//年龄合理则进行正常的赋值
}
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
--------------------------------------------------------------------
public class TestStudent {
public static void main(String[] args) {
//Student类初始化
Student stu = new Student("Smith", 188);
System.out.println(stu);//打印对象引用,调用的是Student类中重写的toString方法,打印内容
}
}
7 - final、finally、finalize的区别
方法名 | 说明 |
---|---|
final | 修饰的变量不可修改、方法不可重写、类不能被继承 |
finally | 异常处理机制中的一个关键字(用作善守处理) |
finalize | 垃圾收集器执行之前被自动调用的方法 |
/**
* @author Mr.乐
* @data 2022/8/6 1:10
*/
public class Demo06 {
public final static String name = "Andy";//使用final定义变量
public /*final*/ void show(){//不可被重写
System.out.println("我是被final修饰的方法");
}
@Override//垃圾收集器启动前,自动调用的方法
protected void finalize() throws Throwable {
System.out.println("我是finalize方法,被自动调用了");
}
public static void main(String[] args) {
// name = "Smith";//常量不可被修改
Demo06 dd = new Demo06();
dd = null;//把引用类型复制为null类型
System.gc();//启动垃圾回收机制
}
}