第四阶段-异常

本文深入讲解Java中的异常处理机制,包括传统异常处理方法的局限性、异常的概念与分类、异常处理的具体实现方式(如try...catch...finally结构)、异常的声明与抛出,以及自定义异常的设计方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

异常

1.传统异常处理

import java.util.Scanner;

public class Test01 {
    public static void main(String[] args) {
        // 需求:从控制台输入两个整数,求他们的商
        Scanner sc = new Scanner(System.in);

        System.out.println("请输入第一个整数:");
        // int num1 = sc.nextInt();
        // a => InputMismatchException异常,输入不匹配异常
        
        if (sc.hasNextInt()){
            int num1 = sc.nextInt();

            System.out.println("请输入第二个整数:");
            int num2 = sc.nextInt();
            if (sc.hasNextInt()){
                num2 = sc.nextInt();
                if (0== num2 ){
                    System.out.println("除数不能为0");
                }else{
                    int r = num1 / num2;
                    System.out.println(r);
                }
            }
            int r = num1 / num2;
        }// num2 = 0; => ArithmeticException,数学计算异常
    }
}
传统不正常情况处理方案的缺点:
[1] 代码太长,增加了维护(review)的成本
[2] 判断逻辑和业务逻辑耦合

2.异常概念

异常(Exception)是指在程序运行过程中所发生不正常的事件,它会中断正在进行的程序

异常不是错误,更不是程序的逻辑错误!!!!!!!!!

程序预先设置好处理异常的方式=====>异常=====>程序中断

异常会导致程序中断运行.

java编程语言使用异常处理机制为程序提供了异常处理的能力
程序预先设置好处理异常的方式=====>异常=====>异常捕获,异常匹配分析,异常处理====>正常结束

异常处理机制可以保证程序出现异常后,继续向正确的方向运行.

3.异常处理

java异常处理包含两种代码块.一种是try…catch,一种是try…catch…finally.

1.try…catch

把有可能产生异常的代码放到try,如果产生异常由catch代码块处理.
    语法:
try{
    // 可能产生异常的代码块
}catch(异常类型 e){
    // 异常处理
}
try...catch执行有三种情况:

[1] 没有发生异常,正常执行
import java.util.Scanner;

public class Test01 {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        System.out.println("请输入被除数:");

        // 情况1: 没有发生异常
        int r = 0;
        try {
            // 可能存在异常的代码
            int num1 = sc.nextInt();
            System.out.println("请输入除数:");
            int num2 = sc.nextInt();
            r = num1 / num2;
            System.out.println(r);
        }catch (Exception e){
            // 异常处理
            System.out.println("程序出问题了!");

        }
        System.out.println(r);
        System.out.println("程序正常结束!");
    }
}

[2] 出现异常,catch 代码块捕获异常,并进行异常处理,处理完成后程序继续执行
    
import java.util.Scanner;

public class Test02 {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        System.out.println("请输入被除数:");

        // 情况2: 发生异常
        int r = 0;
        try {
            // 可能存在异常的代码
            int num1 = sc.nextInt();
            System.out.println("请输入除数:");
            int num2 = sc.nextInt();
            r = num1 / num2;
            System.out.println(r);
        }catch (Exception e){
            // 异常处理
            System.out.println("程序出问题了!");

        }
        System.out.println(r);
        System.out.println("程序正常结束!");
    }
}

// 总结:1.异常发生后,从异常发生的那句代码开始,程序不继续向下运行,立即进入异常处理

[3] 异常不匹配
    
import java.util.InputMismatchException;
import java.util.Scanner;

public class Test03 {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        System.out.println("请输入被除数:");

        // 情况3: 异常不匹配(多重catch来捕获)
        int r = 0;
        try {
            // 可能存在异常的代码
            int num1 = sc.nextInt();
            System.out.println("请输入除数:");
            int num2 = sc.nextInt();
            r = num1 / num2;
        }catch (InputMismatchException e){
            // 异常处理
            System.out.println("输入格式有问题,请输入整数");

        }catch (ArithmeticException e){
            System.out.println("除法不能为0");
        }catch (Exception e){
            System.out.println("未知错误");
        }
        System.out.println(r);
        System.out.println("程序正常结束!");
    }
}

// 总结: 1.异常不匹配,程序中断

2.多重catch

可以为try 代码书写多个catch用于捕获多个具体的异常.

书写格式:子类在上,父类在下

import java.util.InputMismatchException;
import java.util.Scanner;

public class Test04 {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        System.out.println("请输入被除数:");

        int r = 0;
        try {
            // 可能存在异常的代码
            int num1 = sc.nextInt();
            System.out.println("请输入除数:");
            int num2 = sc.nextInt();
            r = num1 / num2;
        }catch (InputMismatchException | ArithmeticException e){
            // 异常处理
            System.out.println("计算错误");
        }catch (Exception e){
            System.out.println("未知错误");
        }
        System.out.println(r);
        System.out.println("程序正常结束!");
    }
}

3.异常对象

异常对象是出现异常时的那条语句产生的(JVM自动创建)
异常在java类中通过Exception或其具体子类创建,大多数情况下都是由其子类
(命名方式:异常类型 + Exception)创建,Exception是所有异常类的父类

异常对象的方法

toString返回异常类型和异常信息
getMessage返回异常信息
printStackTrace打印堆栈信息(红色),位置不固定

异常堆栈信息:

  • 第一句: 表示异常类型和异常的Message构成
  • 最后一句: 包含具体发生异常的全路径类,方法,产生异常语句的行数。

4.try…catch…finally

try…catch用于捕获并处理异常, finally代码块用于处理异常后的收尾工作.不管是否发生异常, finally总执行

finally的收尾工作包含释放内存, 关闭文件, 关闭网络连接, 关闭数据库, 关闭…

import java.util.InputMismatchException;
import java.util.Scanner;

public class Test01 {
    /**
     * 未来进行DB操作
     * 1> 打开数据库
     * 2> 操作数据
     * 3> 关闭数据库
     */
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        System.out.println("请输入被除数:");

        int r = 0;

        try {
            // 可能存在异常的代码
            int num1 = sc.nextInt();
            System.out.println("请输入除数:");
            int num2 = sc.nextInt();
            r = num1 / num2;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("程序正常结束!");
        }

        System.out.println(r);

    }
}

finally 唯一不执行的情况: jvm正常退出

import java.util.InputMismatchException;
import java.util.Scanner;

public class Test05 {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        System.out.println("请输入被除数:");

        int r = 0;
        try {
            // 可能存在异常的代码
            int num1 = sc.nextInt();
            System.out.println("请输入除数:");
            int num2 = sc.nextInt();
            r = num1 / num2;
        }catch (Exception e){
            System.out.println(e.toString());
            // jvm正常退出
            System.exit(0);
        }finally{
            System.out.println("我是finally");
        }
        System.out.println("程序正常结束!");
    }
}

存在return的try…catch…finally

public class Test03 {
	public static int div(int a,int b) {
		int r = 0;
		try {
			r = a / b;
			return r;
		} catch (Exception e) {
			System.out.println(e.toString());
		} finally {
			System.out.println("我是 finally");
		}
		return r;
	}	
    
	public static void main(String[] args) {
		System.out.println(div(10,0));;
		System.out.println("程序正常结束!");
	}
}
总结:
[1] 存在returntry...catch...finally, finally 先执行, 然后执行 return
[2] return 总是最后执行

4.异常分类

1.异常继承体系

在Java中, Throwable 类是 Java 语言中所有错误(Error)或异常(Exception)的父类

2.Error

Error,表示代码运行时JVM(Java虚拟机)出现的问题。如果系统崩溃或内存溢出,不需要处理Error
常见的Error:
StackOverflowError:当应用程序递归太深而发生堆栈溢出是,抛出该错误。比如死循环或者没有出口的递				   归调用;
OutOfMemoryError:因为内存溢出或没有可用的内存提供给垃圾回收器是,Java 虚拟机无法分配一个对象,				   这时抛出该错误。比如 new了非常庞大数量的对象而没有释放。

3.Exception

Exception,表示程序在运行时出现的一些不正常情况,一般大多数表示轻度到中度的问题,属于可预测、可恢复问题。如 除数为0,数组索引越界等,这种情况,程序员通过合理的异常处理,确保程序的正常运行知道结束。

异常根据其是否必须要处理,分为运行时异常和检查时异常

1.运行时异常
运行时异常(RuntimeException):在程序运行过程中可处理,可不处理,父类是RuntimeException
    
   常见的运行时异常:
InputMismatchException : 输入不匹配异常
ArithmeticException : 数学计算异常 e.g:除数为0
IndexOutOfBoundsException : 下表/索引越界异常
ArrayIndexOutOfBoundsException : 数组下标越界异常 遍历数大于下标最大值
StringIndexOutOfBoundsException : 字符串下标越界异常
NullPointerException : 空指针异常  当一个对象为空,调用其成员方法会报
IllegalArgumentException : 方法接收到非法参数
ClassCastException : 强制类型转换异常
NumberFormatException : 数字格式转换异常
2.检查时异常
检查时异常(Checked Exception): 也称编译时异常,指的是编译期间检查程序可能存在不正常的情况,在程序运行过程中必须处理,否则编译不通过.
    
    常见的检查时异常
ClassNotFoundException : class 没找到异常
FileNotFoundException : 未见未找到异常
IOException:IO 异常
SQLException : 数据库相关异常
UnsupportedEncodingException : 不支持的字符串编码异常

    
遇到不懂的异常类时,首先要分辨它属于检查时还是运行时异常。通过快捷键查看
IEDA : ctrl+h
Eclipse: ctrl + t

5.声明异常

1.throws

在java语言中通过 throws 声明某个方法可能抛出的各种异常.

当方法的定义者在定义方法时知道调用该方法时可能出现异常,定义者又不知道如何处理时,此 时方法定义者可以选择声明异常,使用 throws 关键字,可以声明多个异常,用**(,)**号分隔。

语法:
[修饰符] 返回值类型 方法名(形参列表) throws 异常1,异常2....{
    
}

声明异常时要根据异常的分类来确定是否外界(调用处)是否必须处理该方法产生的异常.

如果 需要外界必须处理,需要声明检查时异常,否则声明运行时异常。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class CalcUtils {

    public static int add(int num1,int num2){
        return num1 + num2;
    }

  /**  public static int divide(int num1,int num2){

        try {
            int r;
            r = num1 / num2;
            return r;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }

    }
    */



    // 当方法的定义者不知道如何更好的处理时,可以向外声明异常
    public  static int divide(int num1,int num2) throws ArithmeticException{
        int r;
        r = num1 / num2;
        return r;
    }


    public static Date parseDate(String dateStr) throws ParseException{
        SimpleDateFormat df = new SimpleDateFormat();
        return df.parse(dateStr);
    }
}



import java.text.ParseException;

public class Test01 {
    public static void main(String[] args) throws ParseException {
        try {
            CalcUtils.divide(10,0);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }


        // 如果调用处能处理,优先处理异常
        try {
            CalcUtils.parseDate("abc");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        // 异常的继续上抛
        CalcUtils.parseDate("abc");
    }
}

2.声明异常和重载的关系

无关

3.声明异常和重写的关系

声明异常和方法重写有关系!

如果父类声明运行时异常,子类可以声明运行时异常或者不声明如果父类声明检查时异常,子类可以声明检查时异常或者不声明或者运行时异常。

如果父类没有声明任何异常,子类要么不声明任意异常,要么可以声明运行时异常,但不能声明检查时异常。

一句话 : 子类方法声明的异常 <= 父类方法声明的异常

4.异常上抛

/*
在实际开发过程中,如果调用 A 类 showInfo 存在异常,调用处也不知如何处理时,可以继续向
上声明异常,我们把这个过程称为异常的上抛。
*/


public class Sub extends Parent{
	@Override
	public void showInfo() throws Exception{
	
    }
}

public class Test01 {
	public static void main(String[] args) throws Exception{
		Sub sub = new Sub();
		sub.showInfo();
		System.out.println("程序正常结束!");
	}
}


// 在声明异常的过程中,能内部处理有限处理,然后在选择上抛

6.手动抛出异常

在实际开发过程中,开发者也可以根据程序的需要,手动抛出异常,通过throw关键字.
当程序出现某种逻辑错误时,由程序员主动抛出某种特定类型的异常
public class GenderException extends Exception{
    public GenderException(){

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

    public void log(){
        System.out.println("我是日志");
    }
}


////////////////////////////////////////////////////////////////////////////////////

public class Student {
    private String name;
    private String gender;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

   /** public void setGender(String gender) throws Exception {
        if (gender.equals("男") || gender.equals("女")) {
            this.gender = gender;
        }else {

             Exception ex = new Exception("性别值不合法");
             throw ex;


            // throw new Exception("性别值不合法");


            throw new RuntimeException("性别值不合法");
        }
    }*/

   public void setGender(String gender) throws GenderException{
       if (gender.equals("男") || gender.equals("女")){
           this.gender = gender;
       }else{
           throw new GenderException("性别值不合法");
       }
   }
    public Student(String name, String gender) {
        this.name = name;
        this.gender = gender;
    }

    public Student() {
    }
}



/////////////////////////////////////////////////////////////////////

public class Test01 {
    public static void main(String[] args) {
        Student stu =new Student();

        // 手动抛出检查时异常
        /*try {
            stu.setGender("小姐姐");
        } catch (Exception e) {
            System.out.println(e.toString());
        }*/

        // 手动抛出运行时异常
        // stu.setGender("保密");

        try {
            stu.setGender("保密");
        } catch (GenderException e) {
            e.printStackTrace();
        }

    }
}

7.自定义异常

当JDk 中的异常类型不能满足程序的需要时(也即需要定义具体的业务异常时),可以自定义异常;

自定义异常的步骤:
[1] 确定异常类型(检查时异常/运行时异常)
[2] 继承异常的父类(检查时异常 继承 Exception/运行时异常 继承 RuntimeException)
[3] 声明构造方法
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值