JavaSE进阶03-异常处理

异常的基本概念

在程序运行过程中出现的错误,称为异常。
Java中异常以类和对象的形式存在,有利于增强程序的健壮性。

异常的分类

异常的层次结构

在这里插入图片描述
Throwable下有两个分支:Error(不可处理,直接退出JVM)和Exception(可处理的)
Exception下有两个分支:
Exception的直接子类:编译时异常(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常。)。
RuntimeException:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管,都行。)

异常的分类

异常主要分为:错误、一般性异常(编译时异常、受控异常)、运行期异常(非受控异常)

  • 错误:应用程序出现Error将无法恢复,只能重新启动应用程序
  • 受控异常:该异常必须显示处理,不显示处理Java程序将无法编译通过
  • 非受控异常:该异常可以不用显示处理,如被0除异常。
    编译时异常和运行时异常均发生在运行阶段,因为异常的发生就是new异常对象,只有程序运行才可以new对象。

try、catch和finally

异常的捕获和处理需要采用try和catch处理,具体格式如下:

    try{
        // 可能产生异常的代码
    }catch(){
        // catch可以有多个,catch中是需要捕获的异常
    }catch(){
        // try中代码出现异常时,异常下面的代码不会执行,
        // 马上会跳转到相应的catch语块中,如果没有异常不会跳转到catch中
    }finally{
        // finally表示不管是否存在异常,此处的代码都将执行。
        // finally和catch可以分开使用,但必须与try一起使用。
    }

getMessage和printStackTrace()

取得异常对象的具体信息,常用的方法主要有两种:

  • 取得异常的描述信息:getMessage()
  • 取得异常的堆栈信息(适合于程序调试阶段):printStackTrace()
public class ExceptionTest {
    public static void main(String[] args) {
        int a=100;
        int b=0;
        try{
            int res=a/b;
        }catch(ArithmeticException ae){
            // ae是一个引用,指向堆中的ArithmeticException
            // 通过getMessage可以取得异常的描述信息
            // 通过printStackTrace可以打印栈结构
            System.out.println(ae.getMessage());
            ae.printStackTrace();
        }
    }
}
/ by zero
java.lang.ArithmeticException: / by zero
	at InterfaceTest.ExceptionTest.main(ExceptionTest.java:8)

受控异常

public class ExceptionTest {
    public static void main(String[] args) {
        FileInputStream fis =new FileInputStream("test.txt");
    }
}

此处会抛出异常:Error:(7, 30) java: 未报告的异常错误java.io.FileNotFoundException; 必须对其进行捕获或声明以便抛出。从此可以看出,程序无法编译,此类异常叫做“受控异常”。
处理FileNotFoundException异常:Alt+Enter选择捕捉(try catch)或抛出(throws)

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ExceptionTest {
    public static void main(String[] args) {
        try {
            FileInputStream fis =new FileInputStream("test.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

finally

finally在任何情况下都会执行,因此通常在finally关闭资源

public class ExceptionTest {
    public static void main(String[] args) {
        // fis作用域问题,需要放在try语句块外
        // 局部变量必须给定初始值
        FileInputStream fis=null;
        try{
            fis=new FileInputStream("test.txt");
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

只有Java虚拟机退出(System.exit(-1))不会执行finally,其他任何情况都会执行finally!

异常的处理

第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。
谁调用我,我就抛给谁。抛给上一级。

第二种方式:使用try…catch语句进行异常的捕捉。
这件事发生了,谁也不知道,因为我给抓住了。
Java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果即终止java程序的执行。

如何声明异常

在方法定义处采用throws声明异常,如果声明的异常为受控异常,那么调用方法必须处理该异常。

  • 声明受控异常
public class ExceptionTest {
    public static void main(String[] args){
        // throws FileNotFoundException,IOException{  //可以在此声明异常,交给Java虚拟机处理,不建议在此处声明
        // throws Exception{  //可以采用此种方式声明异常,因为Exception是两个异常的父类
        try {
            readFile();      // 声明异常后,调用者必须处理
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static void readFile() throws FileNotFoundException,IOException{
        FileInputStream fis=null;
        try {
            fis=new FileInputStream("test.txt");
        }finally {
            fis.close();
        }
    }
}
  • 声明非受控异常
public class ExceptionTest {
    public static void main(String[] args){
        // 不需要try catch,因为声明的是非受控异常
        // method1();
        
        // 也可以拦截非受控异常
        try {
            method1();
        }catch (ArithmeticException e){
            e.printStackTrace();
        }
    }

    private static void method1() throws ArithmeticException{
        int a=10;
        int b=0;
        int res=a/b;
        System.out.println(res);
    }
}

如何手动抛出异常

public class ExceptionTest {
    public static void main(String[] args) {
        try {
            int res = method(1000, 5);
            System.out.println(res);
        }catch (IllegalArgumentException e){
            e.printStackTrace();
        }

    }

    private static int method(int a,int b){
        if (b==0){
            // 手动抛出异常
            throw new IllegalArgumentException("除数为0");
        }
        if (!(a>0 && a<=100)){
            throw new IllegalArgumentException("被除数应介于1~100之间");
        }
        return a/b;
    }
}

throws和throw的区别?throws是声明异常,throw是抛出异常。
异常的捕获顺序是从小到大的,先截获子异常再截获父异常,因此catch时将子异常放到前面。

如何自定义异常

自定义异常通常继承于Exception或RuntimeException,具体视情况而定。

  • 自定义受控异常
public class ExceptionTest {
    public static void main(String[] args) {
        try {
            method(1000, 0);
        }catch (MyException e){
            // 必须拦截,拦截后给出处理
            // 否则属于吃掉了该异常,系统将不给任何提示,使程序调试变得困难
            e.printStackTrace();
        }

    }

    private static int method(int a,int b)
            throws MyException{ // 如果是受控异常必须声明
        if (b==0){
            throw new MyException("除数为0");
        }
        return a/b;
    }
}

// 自定义受控异常
class MyException extends Exception{
    public MyException(){
        // 调用父类的默认构造方法
        super();
    }

    public MyException(String message){
        // 手动调用父类的构造方法
        super(message);
    }
}
  • 自定义非受控异常
public class ExceptionTest {
    public static void main(String[] args) {
        method(100,0);
    }

    private static int method(int a,int b) {
        if (b==0){
            // 抛出非受控异常
            throw new MyException("除数为0");
        }
        return a/b;
    }
}

// 自定义非受控异常
class MyException extends RuntimeException{
    public MyException(){
        // 调用父类的默认构造方法
        super();
    }

    public MyException(String message){
        // 手动调用父类的构造方法
        super(message);
    }
}

方法覆盖与异常

子类方法不能抛出比父类方法更多的异常,但可以抛出父类方法异常的子异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值