#Java异常处理

BitLunarLancer
• 阅读 2808

传统的语言如何处理

在一些传统的语言(如C语言中)

  • if语句来判断是否出现了例外

  • 全程变量ErrNo

但这有几个缺点

  • 正常处理与异常处理的代码同样处理

  • 可读性(readability)差

  • 每次调用一个方法时都进行错误检查

  • 可维护性( maintainability )差错误由谁处理不请

  • 职责不清

Java中的异常处理特点:

Java中处理异常包括

  1. 抛出(throw)异常

  2. 运行时系统在调用栈中查找

    • 从生成异常的方法开始进行回溯,直到找到:

  3. 捕获(catch) 异常的代码
    调用过程如下所示:

#Java异常处理

代码

例子一

没有使用Java异常处理机制

public class Test {
    public static void main(String [] args){
        for(int i = 0; i < 2;i++){
            System.out.print(i + " ");
            System.out.println(1/0);//throw an exception.
        }
    }
}

运行结果:

0 Exception in thread "main" java.lang.ArithmeticException: / by zero

1/0明显非法,所以产生了异常ArithmeticException对象,这个对象是Exception的子类。

例子二

以下开始用异常处理机制捕获该异常:

public class Test {
    public static void main(String [] args){
        for(int i = 0; i < 2;i++){
            System.out.print(i + " ");
            try{
                System.out.println(1/0);//An exception will throw from here.
            }
            catch(Exception ex){//在这里,这个Exception 其实就是ArithmeticException
            //这里用到了程序运行时的多态思想。
            //实际中应指定具体的作为ex的类型,更加有针对性。
                System.out.println("exception handling...");//
            }
        }
    }
}

运行结果:

0 exception handling...
1 exception handling...

这样确实能捕获到相应的异常对象。尽管什么也没做(只是打印字符串),却让编译器不再报告之前的异常。因为上述catch(type ex){...}就是负责处理异常。

例子三

当捕获到异常后,程序的运行过程会怎样?

public class Test2 {
    public static void main(String[] args){
        try{
            for (int i = 0; i< 2; i++){
                System.out.print(i + " ");
                System.out.println(1/0);//exception being thrown from here.Then it wil jump directly to the corresponding catch block.
                System.out.println("This line is supposed to be ignored");
            }
        }
        //handling the exception.
        catch(Exception ex){
            System.out.println("Exception handling...");
        }
        System.out.println("End of the main()");
    }
}

运行结果:

0 exception handling...
End of the main()

for不可能循环到i = 2;因为在i = 1时已经抛出了异常。只要产生了异常,转入对应的catch(type ex){...}catch(type ex)必须在参数里面说明捕获的对象是哪类型的。
throw语句就像一个调用函数,当程序运行中抛出了一个异常对象,就会调用对应的catch(type ex){}来处理。但它又不像调用函数。因为在调用完后,它不会返回到throw语句,而是接着catch之后的语句。所以System.out.println("This line is supposed to be ignored");这条语句被没有执行;for循环也相当于被中断了。

Java的异常处理语句

抛出异常

throw 异常对象;

捕获异常

try{
    语句组
}
catch(异常类名 异常形式参数名){
    异常处理语句组; 
}
catch(异常类名 异常形式参数名){
    异常处理语句组; 
}
finally{
    异常处理语句组;
}

其中,catch语句可以0至多个,可以没有finally语句

异常的分类

Throwable

  • Error: JVM的错误(一般来说,我们很难处理这里异常)

  • Exception: 异常(重点关注)
    注:一般所说的异常,是指Exception及其子类

#Java异常处理

Exception类

构造方法

public Exception();
public Exception(String message);
Exception(String message, Throwable cause) ;

方法

getMessage()
getCause()
printStackTrace()

代码

例子一

仍然是没有使用Java的异常处理机制:

import java.util.Scanner;
public class QuotientWithMethod {
    public static int quotient(int num1, int num2){
        if(num2 == 0){
            log("Divisor cannot be zero");
            System.exit(1);
        }
        return num1/num2;
    }
    public static void log(String s){
        System.out.println(s);
    }
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        //prompt user to enter two integers
        int num1 = input.nextInt();
        int num2 = input.nextInt();
        int result = quotient(num1,num2);//调用函数
        System.out.println(num1 + "/" + num2 + "is" + result );
    }
}

运行结果

input two numbers:
1 0
Divisor cannot be zero

在没用使用异常处理机制的情况下,出现了异常情况的话,在被调用的函数中,处理异常情况——这里是直接退出了程序;

例子二

用异常处理机制的好处是职责分明:被调函数中抛出异常,主调函数中负责处理异常。

import java.util.Scanner;
public class QuotientWithException {
    public static int  quotient(int num1, int num2){
        if(num2 == 0){
            throw new ArithmeticException("Divisor cannot be zero");//用关键字throw抛出异常对象,这里是调用构造器来新建对象
        }//但不做处理
        return num1/num2;
    }
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        System.out.println("input two numbers:");
        int num1 = input.nextInt();
        int num2 = input.nextInt();
        boolean goOn = true;
        do {
            try {
                int result = quotient(num1, num2);
                System.out.println("num1" +"/" + "num2" + "is" +result);
                goOn = false;
            }
            //职责分明;一个负责抛出异常,一个负责处理异常.
            catch(ArithmeticException ex){
                //在主调函数中处理异常
                System.out.println("Exception occur:" + "Divisor cannot be zero. \n input again,please:");
                num1 = input.nextInt();
                num2 = input.nextInt();
                //int result = quotient(num1, num2);
                //System.out.println("num1" +"/" + "num2" + "is" +result);
            }
        }while(goOn);
        //处理后,进入catch{}后的语句.
        System.out.println("End of main().");
    }
}

运行结果

input two numbers:
1 0
Exception occur:Divisor cannot be zero. 
 input again,please:
1 1
num1/num2is1
End of main().

在被调函数中,只负责抛出异常:throw new ArithmeticException("Divisor cannot be zero");,主调函数中catch(ArithmeticException ex){...}指定将要捕获的对象的类型,并做相应的处理,这里要求重新输入。

多异常的处理

  • 子类异常要排在父类异常的前面,也就是先处理具体的异常,后处理抽象的异常。

  • finally语句,无论是否有异常都要执行,即使其中有break,return等语句

  • 在编译时,finally部分代码生成了多遍

Exception分两种

RuntimeException及其子类,可以不明确处理;否则,称为受检的异常(checked Exception)

RuntimeException, Error, and their subclasses are known as unchecked exceptions. All other exceptions are known as checked exceptions, meaning that the compiler forces the programmer to check and deal with them in a try-catch block or declare it in the method header.--Introduction to Java

对于受检的异常(checked Exception),要求明确进行语法处理:

  • 要么捕(catch)

  • 要么抛(throws):在方法的签名后面用throws xxxx来声明

    • 在子类中,如果要覆盖父类的一个方法,若父类中的方法声明了 throws异常,则子类的方法也可以throws异常

    • 可以抛出子类异常(更具体的异常),但不能抛出更一般的异常

自定义异常类

创建用户自定义异常时

  • 继承自Exception类或某个子Exception类

  • 定义属性和方法,或重载父类的方法

    • 这样getMessage(), toString(), printStackTrace()都会从Exception继承下来。

重抛异常及异常链接( Chained Exceptions)

对于异常,不仅要进行捕获处理,有时候还需要将此异常进一步传递给调用者,以 便让调用者也能感受到这种异常。这时可以在catch语句块或finally语句块中采取,以下三种方式:

  • 将当前捕获的异常再次抛出:
    throw e;

  • 重新生成一个异常,并抛出,如:
    throw new Exception("some message");

  • 重新生成并抛出一个新异常,该异常中包含了当前异常的信息,如:throw new Exception("some message",e);e.getCause() 来得到内部异常

点赞
收藏
评论区
推荐文章
如何优雅的处理异常
Java语言按照错误严重性,从throwale根类衍生出Error和Exception两大派系。本文从异常的定义、处理异常的方式、如何优雅的抛出异常以及处理异常等方面来聊聊如何异常这件事
Wesley13 Wesley13
3年前
java多线程之停止线程
在多线程开发中停止线程是很重要的技术点。停止线程在Java语言中并不像break语句那样干脆,需要一些技巧性的处理。一、 异常法采用异常法来停止一个线程,首先我们需要了解一下两个方法的用法:1、interrupt()方法publicclassMyTh
Stella981 Stella981
3年前
Spring Cloud Gateway 全局通用异常处理
为什么需要全局异常处理在传统SpringBoot应用中,我们@ControllerAdvice来处理全局的异常,进行统一包装返回//摘至springcloudalibabaconsole模块处理@ControllerAdvicepublicclassConsol
Wesley13 Wesley13
3年前
ulua,slua,tolua,xlua 等跨语言C#Lua 接口崩溃原因
使用ulua等unitylua脚本接口工具时,经常会出现各种崩溃这些崩溃本质上有一个共同模式:C调用Lua,Lua调用c,接着C出现异常也就是跨语言异常处理上存在严重问题;ulua,slua等库都抄袭了一个叫做LuaInterface的库,而这个异常传递导致崩溃的问题,就是LuaInterface自身设计的问题
Wesley13 Wesley13
3年前
03.Android崩溃Crash库之ExceptionHandler分析
目录总结00.异常处理几个常用api01.UncaughtExceptionHandler02.Java线程处理异常分析03.Android中线程处理异常分析04.为何使用setDefaultUncaughtExceptionHandler前沿上一篇整体介绍了crash崩溃
Wesley13 Wesley13
3年前
初探 Objective
作者:Cyandev,iOS和MacOS开发者,目前就职于字节跳动0x00前言异常处理是许多高级语言都具有的特性,它可以直接中断当前函数并将控制权转交给能够处理异常的函数。不同语言在异常处理的实现上各不相同,本文主要来分析一下ObjectiveC和C这两个语言。为什么要把ObjectiveC和
Wesley13 Wesley13
3年前
Java 异常处理
完善的异常处理有利于程序稳定。不要不停的catch异常。什么是异常??定义:异常是一个事件,它发生在程序的执行过程中,会破坏程序的正常执行在一个错误发生会在一个方法时,创建一个Exception对象来处理来保证程序能继续执行下去。当异常发生时,JVM会搜索调用栈上的所有方法,若没有找到合适的异常处理方法,JVM将会终
Wesley13 Wesley13
3年前
Java异常处理的最佳实践
Java异常处理的最佳实践为什么要有最佳实践我们在写程序是不可避免的要对代码进行异常处理,但是有时对异常的处理会使我们的程序变的更加糟糕,这是我们所不想看到的。所以,我们再进行异常处理时需要遵循一定的套路,来降低异常处理对我们程序的影响。异常产生的原因一般来说,java中的异常会
小万哥 小万哥
1年前
C++ 异常处理机制详解:轻松掌握异常处理技巧
C异常处理C异常处理机制允许程序在运行时处理错误或意外情况。它提供了捕获和处理错误的一种结构化方式,使程序更加健壮和可靠。异常处理的基本概念:异常:程序在运行时发生的错误或意外情况。抛出异常:使用throw关键字将异常传递给调用堆栈。捕获异常:使用
一站式统一返回值封装、异常处理、异常错误码解决方案—最强的Sping Boot接口优雅响应处理器
1.前言统一返回值封装、统一异常处理和异常错误码体系的意义在于提高代码的可维护性和可读性,使得代码更加健壮和稳定。统一返回值封装可以避免每一个接口都需要手工拼装响应报文;统一异常处理可以将异常处理的逻辑集中到一个地方,避免代码中出现大量的trycatch语
一站式统一返回值封装、异常处理、异常错误码解决方案—最强的Sping Boot接口优雅响应处理器
1.前言统一返回值封装、统一异常处理和异常错误码体系的意义在于提高代码的可维护性和可读性,使得代码更加健壮和稳定。统一返回值封装可以避免每一个接口都需要手工拼装响应报文;统一异常处理可以将异常处理的逻辑集中到一个地方,避免代码中出现大量的trycatch语
BitLunarLancer
BitLunarLancer
Lv1
有理的想着说,没理的抢着说。
文章
3
粉丝
0
获赞
0