java中的异常处理

四、异常处理

概述

异常: 就是指程序出现了不正常的情况

用来封装错误信息的对象。

组成结构:类型,提示,行号。

异常的继承结构

查看Java的API文档可知, Java中所有错误和异常的顶级父类是Throwable类

Throwable类下有两个子类, 分别是Error和Exception, 两者的区别是:

  1. Error: 是指不需要捕获的严重问题, 通常是java程序以外的问题, 比如硬件问题或者内存不足导致的问题等

因此, 如果java程序中出现了Error, 我们无需处理。

  1. Exception: 称为异常类, 它表示程序本身可以处理的问题

Exception下有很多异常子类, 其中有一个异常子类是RuntimeException类, 这里还可以将异常分为两大类:

1)编译时异常:

其他异常类以及不是RuntimeException子类的异常类都是检查异常(也叫编译时异常)

在编写完程序后, Java编译器会对其进行检查, 如果检查出此类异常, 就必须要显式处理, 否则程序将无法进行编译。

例如:ClassNotFoundException、

FileNotFoundException、

SQLException等都是编译时异常。

2)运行时异常:

RuntimeException以及子类被称为未经检查的异常(也叫运行时异常)

这类异常通常在编写完程序后没有问题, 但是运行程序才出现异常, 需要我们回来修改代码进行解决的异常

这类异常无需显式处理, 当然也可以像编译时异常一样处理

例如:IndexOutOfBoundsException、

ArithmeticException、

NullPointerException、

ClassCastException 等都是运行时异常。

判断一个异常是不是运行时异常, 可以通过检查这个异常类是不是RuntimeException的子类, 或者检查这个异常是否只有在程序运行时才会出现!

虚拟机的默认处理方式

如果程序在运行时出现了问题,而我们又没有处理该问题,最终虚拟机会做默认的处理,而这种默认处理方式为:

  1. 将异常的名称(类型)、异常的原因以及异常出现的位置等信息输出在了控制台(Console窗口)

  2. 将程序停止运行(这意味着,出现异常的代码后面的代码将不会再执行)

异常示例-1:

package exception;

/**
 * 异常示例
 */
public class ExceptionDemo {
    public static void main(String[] args) {
        //没有报错,表示没有编译期异常
        //执行代码,报错了,说明有运行时异常
        int i = 1/0;
        System.out.println("hahaha");
    }
}

运行结果:

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at exception.ExceptionDemo.main(ExceptionDemo.java:10)

异常示例-2:

package exception;

/**
 * 异常示例
 */
public class ExceptionDemo2 {
    public static void main(String[] args) {
        //定义一个数组
        int[] arr = {1,2,3,4,5,6};
        System.out.println(arr[0]);
        System.out.println(arr[1]);
        System.out.println(arr[2]);
        System.out.println(arr[3]);
        System.out.println(arr[4]);
        System.out.println(arr[5]);
        System.out.println(arr[6]);

    }
}

运行结果:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6
	at exception.ExceptionDemo2.main(ExceptionDemo2.java:16)

如果程序出现了异常, 需要我们自己处理, 有两种方案:

  1. 使用try…catch…进行处理(捕获异常)

  2. 使用throws进行处理(抛出异常)

异常处理之try…catch…处理

try…catch处理异常的格式为:

try{
  可能出现异常的代码;
}catch(异常类型 变量名){
  异常处理代码; 
  //当try中的代码出现了异常并且这个异常能和catch中的异常类型匹配上, 才会执行catch
  //反之, 如果出现的异常和catch中的异常类型不匹配, 就不会执行catch
}

执行流程为:

  1. 程序执行到try{}中的代码时, 如果出现了异常, 将会自动产生一个异常对象, 该异常对象将会被提交给java运行时系统;

  2. 当Java运行时系统接收到异常对象时, 回到catch()中寻找匹配的异常类型, 找到后就进入catch{}中进行异常的处理;

  3. 执行完毕后, 程序还可以继续执行try…catch之后的代码

代码案例1

package exception;

/**
 * JAVA异常处理机制中的try-catch
 * 语法:
 * try{
 *     可能会出现异常的代码
 * }catch(XXXException e){
 *     当try代码块中出现了XXXException后的解决方法
 * }
 * try{
 *     看病的你
 * }catch(发烧 e){
 *     多喝热水
 * }
 * try代码块不能独立存在,必须后面要跟catch或者finally
 */
public class TryCatchDemo {
    public static void main(String[] args) {
        System.out.println("程序开始了");
        try{//可能会出现异常的代码
            String str = "";
            //这里会出现空指针异常,虚拟机会实例化空指针异常示例并在此处抛出
            System.out.println(str.length());
            //charAt(0) 获取字符串指定的下标位置的字符
            //0表示下标0,代表的是第一个字符
            System.out.println(str.charAt(0));
        }catch (NullPointerException e){//进行对应异常的捕获,如果发生该异常,执行内部代码
            System.out.println("出现了空指针,并在这里得到了解决!");
            //catch可以定义多个,针对不同的异常可以有不同的处理方案
        }catch (StringIndexOutOfBoundsException e){
            System.out.println("出现了字符串下标越界的异常,并在这里得到了解决!");
        }
        System.out.println("程序结束了");
    }
}

代码案例2

package exception;

/**
 * JAVA异常处理机制中的try-catch
 * 语法:
 * try{
 *     可能会出现异常的代码
 * }catch(XXXException e){
 *     当try代码块中出现了XXXException后的解决方法
 * }
 * try{
 *     看病的你
 * }catch(发烧 e){
 *     多喝热水
 * }
 * try代码块不能独立存在,必须后面要跟catch或者finally
 */
public class TryCatchDemo2 {
    public static void main(String[] args) {
        System.out.println("程序开始了");
        try{//可能会出现异常的代码
            String str = null;
            //这里会出现空指针异常,虚拟机会实例化空指针异常示例并在此处抛出
            System.out.println(str.length());
            //charAt(0) 获取字符串指定的下标位置的字符
            //0表示下标0,代表的是第一个字符
            System.out.println(str.charAt(0));
            //如果多个异常有同样的处理方案,可以合并捕获异常
        }catch (NullPointerException|StringIndexOutOfBoundsException e){
            System.out.println("多喝热水");
        }
        System.out.println("程序结束了");
    }
}

代码案例3

package exception;

/**
 * JAVA异常处理机制中的try-catch
 * 语法:
 * try{
 *     可能会出现异常的代码
 * }catch(XXXException e){
 *     当try代码块中出现了XXXException后的解决方法
 * }
 * try{
 *     看病的你
 * }catch(发烧 e){
 *     多喝热水
 * }
 * try代码块不能独立存在,必须后面要跟catch或者finally
 */
public class TryCatchDemo3 {
    public static void main(String[] args) {
        System.out.println("程序开始了");
        try{//可能会出现异常的代码
            String str = "a";
            //这里会出现空指针异常,虚拟机会实例化空指针异常示例并在此处抛出
            System.out.println(str.length());
            //charAt(0) 获取字符串指定的下标位置的字符
            //0表示下标0,代表的是第一个字符
            System.out.println(str.charAt(0));
            //将字符串转换为整数
            System.out.println(Integer.parseInt(str));
            //如果当我们不知道这段程序会发生什么异常时,可以捕获一个异常的超类
        }catch(NullPointerException e){
            System.out.println("如果发生空指针怎么办");
        }catch (Exception e){
            System.out.println("不知道是什么病,反正多喝热水就对了");
        }
        System.out.println("程序结束了");
    }
}

finally块

作用就是确保一定要执行某些代码

代码案例

package exception;

/**
 * finally块
 * finally是异常处理机制中的最后一块,它可以直接跟在try语句块之后,或者最后一个catch之后
 * finally语句块的特点:
 * 只要是程序可以执行到try中,无论是否会出现异常,最终都会执行finally块的代码
 * 因此我们只需要将释放资源等代码放在finally里,就可以确保执行,比如IO操作后的close方法的调用
 */
public class FinallyDemo {
    public static void main(String[] args) {

        System.out.println("程序开始了");
        //快速生成try-catch代码块
        // 1.选中要包在try中的代码
        // 2.crtl+alt+T
        // 3.选择try相关选项,生成对应的代码结构
        try {
            String str = "null";
            System.out.println(str.length());
            return;//方法实际返回前,也要先执行完finally中的代码
        } catch (Exception e) {
            System.out.println("出错了!");
        } finally {
            System.out.println("finally中的代码执行了!");
        }
        System.out.println("程序结束了");
    }
}

代码案例2

package exception;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 异常处理机制在IO操作中的应用
 */
public class FinallyDemo2 {
    public static void main(String[] args) {
        //声明为全局变量,全局才能使用
        FileOutputStream fos = null;
        try {
            //FileNotFoundException 文件找不到异常 继承IOException
            //为全局变量fos赋值
            fos = new FileOutputStream("./DAY03/fos.txt");
            //IOException IO流异常
            fos.write(1);
        } catch (IOException e) {
            System.out.println("出错了,多喝热水!!!");
        } finally {
            try {
                //卡了
                if (fos!=null){
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

代码案例3

package exception;

import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 异常处理机制在IO操作中的应用
 * JDK7之后,java推出了自动关闭的特性
 * 使得我们都在源代码中异常处理机制在IO应用得到了简化
 */
public class AutoCloseableDemo {
    public static void main(String[] args) {
        try(
                /*
                    只有实现了AutoCloseable的接口的类才能在这里定义并初始化
                    并且编译器在编译时,自动将定义在这里的变量在finally中调用close方法
                    最终编译器会将之前在FinallyDemo2中的写的代码呈现在这里
                 */
            FileOutputStream fos = new FileOutputStream("./DAY03/fos.txt");
                ){
            fos.write(1);
        }catch (IOException e){
            System.out.println("出错了");
        }
    }
}

throw关键字

当程序发生错误而无法处理的时候,会抛出对应的异常对象,除此之外,在某些时刻,您可能会想要自行抛出异常,例如字异常处理结束后,再将异常抛出,让下一层异常处理块来捕捉,若想要自行抛出异常,您可以使用"throw"关键字,并生成执行的异常对象后抛出.

例如:throw new ArithmeticException();

代码案例

Person
package exception;

/**
 * 使用当前类测试异常的抛出
 */
public class Person {
    private int age;//年龄
    //alt+insert 生成get和set方法
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if(age<0 || age>100){
            //主动抛出异常
            throw new RuntimeException("年龄不合法");
        }
        this.age = age;
    }
}
ThrowDemo
package exception;

/**
 * 异常的抛出
 * throw关键字可以主动的对外抛出一个异常
 * 通过在什么情况下会主动抛出异常呢?
 * 1.当前代码片段出现了异常,但是当前异常不应该在当前代码片段中被解决,可以将其抛出
 * 2.程序可以运行,但是不满足业务要求时,可以对外抛出一个异常告知(满足语法但是不满足业务)
 */
public class ThrowDemo {
    public static void main(String[] args) {
        Person person = new Person();
        //此处很典型的满足语法,但是不满足业务需求
        try {
            //此处可能会发生异常,所以我们需要在此处处理异常
            person.setAge(10000);
        } catch (RuntimeException e) {
            System.out.println("出错了");
            person.setAge(20);
        }
        System.out.println("此人年龄"+person.getAge());
    }
}

异常处理之throws处理

程序中会声明许多的方法,这些方法中可能会因某些错误而引发异常,但是不希望直接在这个方法中处理这些异常,而希望调用这个它的方法来统一处理,这时候可以使用throws关键字来声明这个方法将会抛出的异常

throws 处理异常的格式为:

...方法名() throws 异常类名 {
  方法体
}

代码案例

Person
package exception;

/**
 * 使用当前类测试异常的抛出
 */
public class Person {
    private int age;//年龄
    //alt+insert 生成get和set方法
    public int getAge() {
        return age;
    }

    /**
     * java中除了RuntimeException之外,其他异常抛出编译要求的
     * 必须要在方法上使用throws关键字声明该异常被抛出
     */
    public void setAge(int age) throws Exception{
        if(age<0 || age>100){
            //主动抛出异常
            throw new Exception("年龄不合法");
        }
        this.age = age;
    }
}
ThrowDemo
package exception;

/**
 * 异常的抛出
 * throw关键字可以主动的对外抛出一个异常
 * 通过在什么情况下会主动抛出异常呢?
 * 1.当前代码片段出现了异常,但是当前异常不应该在当前代码片段中被解决,可以将其抛出
 * 2.程序可以运行,但是不满足业务要求时,可以对外抛出一个异常告知(满足语法但是不满足业务)
 */
public class ThrowDemo {
    public static void main(String[] args) throws Exception {
        Person person = new Person();
        //此处很典型的满足语法,但是不满足业务需求
        //此处可能会发生异常,所以我们需要在此处处理异常
        person.setAge(10000);
        System.out.println("此人年龄"+person.getAge());
    }
}

throws的重写规则

代码案例

package exception;

import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;

import java.awt.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;

/**
 * 子类重写超类含有throws关键字声明异常抛出的方法时,重写规则
 */
public class ThrowsDemo {
    //抛时,可能会抛多个异常
    public void doSome() throws IOException, AWTException {}
}
//子类继承超类
class SubClass extends ThrowsDemo{
    //1.第一种情况,父类抛什么异常,子类就抛什么异常
    //public void doSome() throws IOException,AWTException{ }
    //2.第二种情况,允许抛出任意部份异常
    //public void doSome() throws IOException{}
    //3.第三种情况,允许不抛出任何异常
    //public void doSome(){}
    //4.第四种情况,允许抛出超类方法抛出异常的子类的异常类型
    //FileNotFoundException 继承自 IOException
    //public void doSome() throws FileNotFoundException {}
    //5.第五种情况,不允许抛出额外异常
    //public void doSome() throws SQLException{}
    //6.不允许抛出超类方法抛出异常的超类型异常
    //public void doSome() throws Exception{ }
}

异常常用方法

代码案例

package exception;

/**
 * 异常常用的方法
 */
public class ExceptionApiDemo {
    public static void main(String[] args) {
        try {
            String str = "abc";
            System.out.println(Integer.parseInt(str));
        } catch (NumberFormatException e) {
            System.out.println("abc字符串不能转成数字");
            //输出程序的异常信息在控制台,方便程序员debug
            e.printStackTrace();//打印异常
            //返回异常的提示信息
            String msg = e.getMessage();
            System.out.println(msg);
            //返回异常的名称与详细信息字符串
            String s = e.toString();
            System.out.println(s);
        }
    }
}

自定义异常

代码案例

IllegalAgeException
package exception;

/**
 * 自定义异常
 * 通常使用自定义异常用来表达业务错误
 * 自定义异常应该做到以下几点:
 * 1.类名要见明知义
 * 2.需要继承Exception(直接或者间接继承)
 * 3.提供超类中所有的构造器
 */
public class IllegalAgeException extends Exception{
    //alt+insert
    //1.提供无参构造
    public IllegalAgeException(){
    }
    //2.提供包含错误提示信息的构造器
    public IllegalAgeException(String message){
        super(message);
    }
    //3.提供包含错误提示信息和错误原因的构造器
    public IllegalAgeException(String message,Throwable cause){
        super(message,cause);
    }
    //4.包含错误原因的构造器
    public IllegalAgeException(Throwable cause){
        super(cause);
    }
    public IllegalAgeException(String message,Throwable cause,
                               boolean enableSuppression,boolean writableStackTrace){
        super(message,cause,enableSuppression,writableStackTrace);
    }
}
Person
package exception;

/**
 * 使用当前类测试异常的抛出
 */
public class Person {
    private int age;//年龄
    //alt+insert 生成get和set方法
    public int getAge() {
        return age;
    }
    /**
     * java中除了RuntimeException之外,其他异常抛出编译要求的
     * 必须要在方法上使用throws关键字声明该异常被抛出
     */
    public void setAge(int age) throws IllegalAgeException{
        if(age<0 || age>100){
            //主动抛出异常
            throw new IllegalAgeException("年龄不合法"+age);
        }
        this.age = age;
    }
}
ThrowDemo
package exception;

/**
 * 异常的抛出
 * throw关键字可以主动的对外抛出一个异常
 * 通过在什么情况下会主动抛出异常呢?
 * 1.当前代码片段出现了异常,但是当前异常不应该在当前代码片段中被解决,可以将其抛出
 * 2.程序可以运行,但是不满足业务要求时,可以对外抛出一个异常告知(满足语法但是不满足业务)
 */
public class ThrowDemo {
    public static void main(String[] args) throws Exception {
        Person person = new Person();
        //此处很典型的满足语法,但是不满足业务需求
        //此处可能会发生异常,所以我们需要在此处处理异常
        try {
            person.setAge(10000);
        } catch (IllegalAgeException e) {
            e.printStackTrace();
        }
        System.out.println("此人年龄"+person.getAge());
    }
}

总结: 什么时候需要try…catch异常, 什么时候需要throws异常?

  1. 如果这个异常是方法内部的代码造成的异常, 而不是因为调用者的传参导致的异常(也就是说这个异常和调用者没有关系), 通常需要我们try…catch异常

  2. 如果这个异常是调用者的传参导致的异常, 则将异常throws抛出(就是将异常抛给调用者)

  3. 不要在main方法上throws抛出异常, 因为这样会将异常抛给虚拟机, 而虚拟机是不会帮我们处理异常的!(虚拟机会按照默认方式处理:输出异常信息以及终止程序执行!)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值