Java-异常处理

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、概述

在Java中,异常(Exception)和错误(Error)是两个不同的概念。错误分为语法错误、运行时错误、逻辑错误三类:

  • 语法错误(编译错误):指未按照Java语法规则书写代码而产生的错误,一般IDE均能提示和标注语法错误之处。
  • 运行时逻辑错误(语义错误):程序无语法错误,但运行时从外部获得不正确的数据从而导致错误,如将一个11位电话号码赋值给一个整形变量就会超出其数据范围而引发错误,这类错误会导致程序终止。
  • 逻辑错误:人为编码时导致的错误,如,a+b写成a-b,这类错误并不会导致程序终止,但不能得到正确结果。

和错误不同,异常是指运行环境正常的情况下遇到的运行时错误,是非致命的,但也会导致程序的非正常终止,Java可捕获和处理异常,而错误是无法被程序本身所处理的。事实上,sun公司会将这些错误封装成Error对象,供sun公司自己使用,并不在程序员的考虑范围之内。

在Java中,通常用Exception及其子类来封装异常,使得异常以类的形式存在,每个异常类都可以创建异常对象。例:

package org.example.SimpleCode;

public class Demo {
    public static void main(String[] args) {
        RuntimeException runtimeException=new RuntimeException();
        System.out.println(runtimeException);
    }
}

二、基本使用

(一)常见继承结构图

在这里插入图片描述
其中,Object有子类Throwable(可抛出的),意为,无论是错误还是异常,它们都是可抛出的,其子类包括Error(错误,不可处理,直接退出JVM)、Exception(异常,可处理)。异常的两个子类常见子类:

  • RuntimeException及其子类:运行时异常,编译阶段不会出现异常提醒,是指运行时出现的异常,即运行时才知道发生了异常,如,数组索引越界异常(java.lang.ArrayIndexOutOfBoundException)、空值对象的引用等(java.lang.NullPointerException)。
  • ExceptionSubClass(这不是一个真正的类,而是一个泛指):编译时异常(javac编译),是Exception的直接子类,要求程序员在编写程序阶段必须先对这些异常进行处理,即,需要手动处理才能运行,否则编译器会报错。

(二)常用方法

由于异常(Exception)继承于父类Throwable,故而它继承了一些常用的方法:
在这里插入图片描述

package org.example.SimpleCode;

public class Demo {
    public static void main(String[] args) {
        try {
            int i=2/0;
        }catch (ArithmeticException arithmeticException){
            System.out.println("运算出错");
            System.out.println(arithmeticException.getMessage());
            String string=arithmeticException.toString();
            System.out.println(string);
            arithmeticException.printStackTrace();
            System.out.println("执行完毕");
        }
    }
}

在这里插入图片描述

(三)异常处理

①JVM默认的处理方式

JVM会将异常的名称、原因及其出现的位置等信息输出在控制台中,且,程序会在异常处停止运行。

②捕获异常

目的:使得当代码出现异常时会继续向下运行,而不会退出JVM。

语法格式1
try{
    可能出现异常的代码;
}catch(异常类名 变量名){
    异常的处理代码;
}

执行顺序

  • 当try代码块中代码全部执行完且未遇见问题时,就不会执行catch代码块。
  • 当try代码块执行过程中出现异常时若该异常是catch中声明异常的子类时,则会被捕捉,转而执行catch内的代码。
    例:
package org.example.SimpleCode;

public class Demo {
    public static void main(String[] args) {
        try {
            System.out.println(2/0);
            //此处出现了异常,程序就会在此处创建一个ArithmeticException对象,即,new ArithmeticException();此时会拿着此对象和catch中进行对比,若括号中变量可接受,就表示异常被捕获(此处注意父子类继承关系,若catch中是Exception e,则任何异常都可被捕获),再执行catch中的代码,当try...catch中的代码都被执行完,便会执行体系外的代码.
            System.out.println("正在运行");
            //可见,此代码并未被执行
        }catch (ArithmeticException arithmeticException){
            System.out.println("运算出错");
        }
        System.out.println("执行完毕");
    }
}

在这里插入图片描述

语法格式2
try {  
    // 可能会发生异常的程序代码  
} catch (Type1 id1){  
    // 捕获并处置try抛出的异常类型Type1  
} catch (Type2 id2){  
     //捕获并处置try抛出的异常类型Type2  
}finally {  
    // 无论是否发生异常,都将执行的语句块  
}

执行顺序

  • 当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句。
  • 当try捕获到异常,catch语句块里没有处理此异常的情况:此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行。
  • 当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句(即,catch语句块最多只会执行其中的一个)。

,无论是否捕获异常,finally都会被执行,而当try块或catch中遇到return语句时,finally语句块会在方法返回之前被执行(若finally中有return语句,则try与catch中的return均不会被执行),只有以下四种情况finally代码块不会被执行:

  • finally中出现异常。
  • 前面代码使用了System.exit()退出了程序。
  • 程序所有进程死亡。
  • 关闭CPU。
    且,在try、catch、finally中声明的变量都是临时变量,而它们的作用域都被包括在方法的作用域之内。

③抛出异常

throws:写在方法定义处,表示声明异常,告诉调用者,使用本方法可能会有哪些异常。

public void 方法()throws 异常类名1,异常类名2...{
    ...
}

throw:写在方法内,用于结束方法,手动抛出异常对象,交给调用者,且,,抛出语句下面的代码不会再执行。

public void 方法(){
    throw new NullPointerException();
}

例:

package org.example.SimpleCode;

public class Demo {
    public static void main(String[] args) {
        int[] arr=null;
        int max=0;
        try{
            max=getMax(arr);
        }catch (NullPointerException nullPointerException){
            System.out.println("空指针异常,无最大值");
            System.exit(0);
        }catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException){
            System.out.println("索引越界异常");
            System.exit(0);
        }
        System.out.println("最大值为:"+max);
    }
    public static int getMax(int[] arr) throws NullPointerException,ArrayIndexOutOfBoundsException{
        if(arr==null){
            //手动创建一个异常对象,交给方法调用者处理,此时本方法将结束运行
            throw new NullPointerException();
        }
        if(arr.length==0){
            throw new ArrayIndexOutOfBoundsException();
        }
        int max=arr[0];
        for(int i=0;i<arr.length;i++){
            if(arr[i]>max)max=arr[i];
        }
        return max;
    }
}

注:JDK8新特性,catch()异常之间可自小到大用"|"分割,减少代码冗余,例:

try {
    //创建输入流
    FileInputStream fis = new FileInputStream("D:\\Download\\Javabean-addperson案例解析.docx");
    // 进行数学运算
    System.out.println(100 / 0); // 这个异常是运行时异常,编写程序时可以处理,也可以不处理。
} catch(FileNotFoundException | ArithmeticException | NullPointerException e) {
    System.out.println("文件不存在?数学异常?空指针异常?都有可能!");
}

(四)自定义异常

在实际开发过程中,可根据需求来自行定义异常。
步骤

  • 定义异常类
  • 声明继承关系
  • 空参构造
  • 带参构造

异常类NameFormatException.java,表示姓名格式出错

package org.example.MyException;

public class NameFormatException extends RuntimeException{
    /*
    * NameFormat:当前异常的名字,表示姓名格式化问题
    * Exception:表示当前类是一个异常类
    *
    * 运行时异常:继承RuntimeException
    * 编译时异常:继承Exception
    * */
    public NameFormatException(){
    }
    public NameFormatException(String message){
        super(message);
    }
}

异常类AgeOutOfBoundException.java,表示年龄范围越界

package org.example.MyException;

public class AgeOutOfBoundException extends RuntimeException{
    public AgeOutOfBoundException() {
    }


    public AgeOutOfBoundException(String message) {
        super(message);
    }
}

实体类Student.java

public void setName(String name) throws NameFormatException {
    int len=name.length();
    if(len<2||len>10){
        throw new NameFormatException(name+"格式有误,长度应在3~9之间");
    }
    Name = name;
}

public void setAge(int age) throws AgeOutOfBoundException{
    if(age<18||age>40){
        throw new AgeOutOfBoundException(age+"超出了范围");
    }
    this.age = age;
}

主函数

package org.example;
import org.example.MyException.AgeOutOfBoundException;
import org.example.MyException.NameFormatException;
import org.example.polo.Student;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        Student student=new Student();
        System.out.println("请输入学生姓名:");
        String name=scanner.nextLine();
        try{
            student.setName(name);
        }catch (NameFormatException nameFormatException){
            System.out.println(nameFormatException);
            System.out.println("输入姓名错误");
        }
        System.out.println("请输入学生年龄");
        String i=scanner.nextLine();
        Integer age=Integer.parseInt(i);
        try{
            student.setAge(age);
        }catch (AgeOutOfBoundException ageOutOfBoundException){
            System.out.println(ageOutOfBoundException);
            System.out.println("输入年龄错误");
        }
        System.out.println(student);
    }
}

在这里插入图片描述


三、源码分析

算是留一个坑,考研+技术同时抓的话还是要以推进度为主,这里的源码分析在我完成后端技术的复习之后再来填这个坑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值