Java学习24--异常

异常

软件运行过程中的各种意料之外叫做Exception,比如要读取的文件找不到,准备联网发现没网,等着int参数来了个String

注意Error和exception不一样,error错的比较猛,一般是直接把JAVA整个搞崩了,比如内存空间异常等等,JVM只好终止线程,这一般都是大错了。Exception一般是程序员脑子暂时抽风了,修修改改程序还能救回来。

举例,method a 和b互相无限调用对方,陷入无限循环。运行程序,观察系统输出什么。

package exception;

public class Demo01 {
    public static void main(String[] args) {
 new Demo01().a();
    }
    public void a(){
        b();
    }
    public void b(){
        a();
    }
}

运行结果:

观察到系统输出了一句StackOverflowError这意思是stack内空间耗尽了,因为我们运行了一个没有终止条件的无限循环(记住做循环或者递归,一定要有可触发的程序终止条件)。

Exception in thread "main" java.lang.StackOverflowError
	at exception.Demo01.a(Demo01.java:8)
	at exception.Demo01.b(Demo01.java:11)
	at exception.Demo01.a(Demo01.java:8)
	at exception.Demo01.b(Demo01.java:11)
	N行循环

再举一个例子,除法分母是0,这在数学计算上本身就是不允许的。观察程序运行结果。

Demo01.java 复制代码
package exception;

public class Demo01 {
    public static void main(String[] args) {
        System.out.println(6 / 0);
    }
}

运行结果:

观察到程序出了关键字"ArithmeticException: / by zero"这句的意思就是,数学错误: 除以零。通过观察输出的字样,程序员可以快速找到程序出错的位置。

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

Process finished with exit code 1

异常处理的思维

Java异常处理是Java编程语言中用于处理程序中可能出现的错误或异常情况的一种机制。它提供了一种结构化的方式来响应和处理运行时错误,使程序能够在遇到问题时相对优雅的输出有效提示,而不是程序直接崩溃。

JAVA把异常当object处理,定义一个java.lang.Throwable 作为所有异常class的超类class

JAVA API已经提前定义了许多异常class,分为两大类:错误Error和异常Exception

如上图,错误error class:JAVA直接崩了,表示JVM无法处理或恢复的严重问题

  • 比如stack满了,compile一般检查不到,一般与"无限递归忘记写终止条件"或者"设置了一堆超大局部变量"或者"写了一大堆method互相调用"把stack空间占满了
  • 虚拟机运行错误 Virtual MachineError 比如当JVM不再有继续执行操作所需的内存资源,会报错OutOfMemoryError。
  • JVM运行时报错,比如 定义错误NoClassDefFoundError,链接错误LinkageError

如上图,Exception class一般还能救,一般都是程序员自己的锅:

Exception下分为unchecked exceptions 和 checked exception。

非检查异常unchecked exceptions:在compile阶段不需要catch或声明。并且是RuntimeException(运行时异常类)或它的子类,详细列出如下

  • RuntimeException(运行时异常)
    • ArrayIndexOutOfBoundsException数组下标越界
    • NullPointerException空指针异常
    • ArithmeticException算术异常
    • MissingResourceException丢失资源
    • ClassNotFoundException找不到类
    • 等等

检查异常checked exception :这类异常必须在方法签名中使用throws关键字声明,或者在方法体内部使用try-catch块进行处理。这意味着编译器要求这些method要么有程序块处理这些异常(使用try-catch块),要么在方法签名中使用throws关键字声明它们(等着这个class的父类try-catch)。比如程序想开文件 文件找不到了。检查异常类一般是指Exception下的IOException类或其子类。

Exception class存在的意义----让系统跑程序时候别崩溃,抛出一下这有个XXException,JVM不崩溃。

异常处理机制:抛出异常+捕获异常

五个关键字

try catch finally throw throws(注意throw和throws完全不一样)

try-catch

try-catch一般是成对使用。try块用于包含可能会抛出异常的代码。当try块中的代码抛出异常时,控制流将立即转移到与之匹配的catch块。catch块用于捕获并处理特定类型的异常。你可以有多个catch块来处理不同类型的异常。

在非常个别情况下,也有try-finally模块一起用,一般是文件调用,网络连接,数据库连接的method结束时被释放的情况下。

比如下面的例子,

try块内int r= m/n;是可能会抛出异常的代码,当异常被捕捉到,立刻转到catch块,如果直接碰到throw语句,那么后续代码就不看了,因为因为throw语句会立即结束当前方法的执行并将异常传递给调用者。如果throw旁边还有代码(包括System.out.println这种语句),建议写在catch块内throw关键字前面。

try块内可以写多个可能会抛出异常的代码,但一次只会触发一个,触发了一个就直接走catch模块了。

catch模块可以存在多个,是按顺序catch的,不要把大的父类放在第一个catch,那样后面的catch模组都不触发了。

package exception.demo02;

public class mytest {
    public static void main(String[] args) {
        int m = 10;
        int n = 0;
        try {
            int r= m/n;
        } catch (ArrayIndexOutOfBoundsException e) {System.out.println("发现ArrayIndexOutOfBounds报错");}
        catch (ArithmeticException e){System.out.println("发现Arithmetic报错");}
        catch (RuntimeException e){System.out.println("发现Runtime报错");}
    }
}

运行结果

发现Arithmetic报错

throw关键字

在Java中,throw是一个关键字,不是method。当你想在代码中抛出一个异常时,你使用throw关键字并跟随一个异常object。这个异常对象可以是Java标准库中的异常类的一个实例object,也可以是你自定义的异常类的一个实例object。

一旦异常被抛出,程序的正常执行流程将被中断,并且控制权将转移到最近的异常处理代码,通常是catch块。

简单而言 throw后面跟着一个object,是继承于系统(或者自定义)Throwable class的object(见上图分类),触发后立刻终止程序,跳到最近的(或父级们)能处理该异常的catch模块,直到被处理或程序中止。

比如这句throw e;用在catch模块里,直接throw了异常object e,那么运行后我们会直接获得系统默认输出的这类Exception提示信息。这个提示或许不够清晰明确,有时程序员会将throw e;换成类似这样写 throw new ArithmeticException("啊啊啊你为什么要除以零");,用来输出自己想要的Exception提示信息。

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at exception.demo02.mytest.main(mytest.java:8)

整体代码见下面的例子:

先用简单的throw e;

package exception.demo02;

public class mytest {
    public static void main(String[] args) {
        int m = 10;
        int n = 0;
        try {
            int r= m/n;
        } catch (ArrayIndexOutOfBoundsException e) {System.out.println("发现ArrayIndexOutOfBounds报错"); throw e;}
        catch (ArithmeticException e){System.out.println("发现Arithmetic报错");throw e;}
        catch (RuntimeException e){System.out.println("发现Runtime报错");throw e;}
    }
}

运行结果

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at exception.demo02.mytest.main(mytest.java:8)
发现Arithmetic报错

再将throw后面换成新建object new Exception_class("用户自定义提示信息") ;

package exception.demo02;

public class mytest {
    public static void main(String[] args) {
        int m = 10;
        int n = 0;
        try {
            int r= m/n;
        } catch (ArrayIndexOutOfBoundsException e) {System.out.println("发现ArrayIndexOutOfBounds报错"); throw new ArrayIndexOutOfBoundsException("数组下标啊大哥大姐");}
        catch (ArithmeticException e){System.out.println("发现Arithmetic报错");throw new ArithmeticException("你除数是零啊!牛逼");}
        catch (RuntimeException e){System.out.println("发现Runtime报错");throw new RuntimeException("我点炒蘑菇没让你从种蘑菇开始啊");}
    }
}

程序运行结果

发现Arithmetic报错
Exception in thread "main" java.lang.ArithmeticException: 你除数是零啊!牛逼
	at exception.demo02.mytest.main(mytest.java:10)

Process finished with exit code 1

printStackTrace()

除了在catch模块里直接throw异常object e使用语句throw e;有时程序员也选择使用printStackTrace(); printStackTrace()是Java中的一个异常处理方法,用于在控制台或日志文件中打印异常的堆栈跟踪信息。通过调用printStackTrace()方法,可以将这些Exception信息打印到控制台中。

package exception.demo02;

public class mytest {
    public static void main(String[] args) {
        int m = 10;
        int n = 0;
        try {
            my_div(m,n);
        }

        catch (ArrayIndexOutOfBoundsException e) {e.printStackTrace();}
 

        catch (ArithmeticException e){e.printStackTrace();}
    

        catch (RuntimeException e){e.printStackTrace();}

    }
    public static int my_div(int a, int b) throws ArithmeticException {
        return a/b;
    }
}

运行结果:

java.lang.ArithmeticException: / by zero
	at exception.demo02.mytest.my_div(mytest.java:21)
	at exception.demo02.mytest.main(mytest.java:8)

Process finished with exit code 0

throws

Java允许程序员在method的声明中使用throws关键字来声明可能会抛出的异常。throws的位置写在method名称那行的最后面+可能出现的Exception种类,可写多个exception种类,用逗号隔开),比如public void doSomething() throws IOException,RuntimeException,ArithmeticException

如果一个方法的声明中使用了throws关键字来声明可能会抛出的异常,那么调用这个方法的代码不一定需要使用try-catch块来捕获这个异常。有几种处理异常的方式:

  • 使用try-catch块:这是最常见的处理方式。调用者可以使用try-catch块来捕获并处理可能抛出的异常。
  • 使用finally块:finally块可以用于在try块之后执行一些清理代码,无论是否发生异常都会执行。但请注意,finally块不能用来处理异常,它仅仅是用来执行清理任务。

以下是伪处理方式:(实际没有真的处理异常,而是丢给了别人或者直接摆烂)

  • 继续向上抛出异常:如果调用者不想在当前方法中处理异常,它可以选择不捕获该异常,这样异常会继续向上抛出,直到被某个上层方法捕获或者最终由JVM处理(通常会导致程序终止)。
  • 声明方法也抛出异常:如果调用者是一个方法,并且它也不想处理异常,那么它可以在自己的方法签名中使用throws关键字来声明同样的异常。这样,异常会继续向上传递,直到最终有某个方法处理了它。
  • 忽略异常:在某些特殊情况下,调用者可能会选择忽略异常。这是一种非常不推荐的做法,因为它可能会隐藏问题,导致程序在后续执行中出现不可预测的行为。

下面这个例子中,my_div method声明了它可能会抛出ArithmeticException,RuntimeException,于是在main模块try-catch程序对这些exception统统进行了涵盖,完成了异常(们)的处理。

package exception.demo02;

public class mytest {
    public static void main(String[] args) {
        int m = 10;
        int n = 0;
        try {
            my_div(m,n);
        }

        catch (ArrayIndexOutOfBoundsException e) {e.printStackTrace();}

        catch (ArithmeticException e){e.printStackTrace();}
        
		catch (RuntimeException e){e.printStackTrace();}
    }
	
    public static int my_div(int a, int b) throws ArithmeticException, RuntimeException{
        return a/b;
    }
}


java.lang.ArithmeticException: / by zero
	at exception.demo02.mytest.my_div(mytest.java:21)
	at exception.demo02.mytest.main(mytest.java:8)

Process finished with exit code 0

throws和throw关键字经常一起使用来处理异常,但它们也可以单独使用。可以只使用throws,表明方法不处理某些异常,让调用者去处理。也可以只使用throw,在方法内部遇到错误时直接抛出异常,并不在方法签名中声明任何异常。

下面这个例子,doSomething方法声明了它可能会抛出IOException,但方法内部没有使用throw关键字来实际抛出异常。调用这个方法的代码需要处理或继续传递这个异常。

public class Example {  

    public void doSomething() throws IOException {  
        // 这里可能会抛出IOException  
        FileInputStream fis = new FileInputStream("file.txt"); 
        // ...  
    }  
}

如果声明了可能抛出的异常,但层层调用它时最终到了main都没人处理这个异常(没人用try-catch模块处理),编译器会报错。

public class mytest {  

    public static void main(String[] args) {  

//下面这个try-catch模块要是不存在,直接调用mymethod(),绝对报错!
        try {  

            mymethod();  

        } catch (Exception e) {  

            e.printStackTrace();  

        }  

    }  

  

    public static void mymethod() throws Exception {  

        throw new Exception("An exception occurred");  

    }  

}

特殊的情况是,如果method声明了可能抛出的异常,层层调用它时最终到了main没人用try-catch模块处理,都在一层一层throws向上抛,main也摆烂直接用了throws向上抛,这种编译器不会报错。而是会在编译器输出Exception的具体内容。

下面这个例子就相当于层层向上抛,最后main也抛,编译器是不会报错的。

public class mytest {  

    public static void main(String[] args) throws Exception {  

        mymethod();  

    }  

    public static void mymethod() throws Exception {  

        throw new Exception("An exception occurred");  

    }  

}

知识补充

快速生成try catch finally结构块:

IDEA有一个功能可以快速生成try catch finally结构块,这个功能叫做Surround with,快捷键是ctrl+alt+t

由于它和ubuntu open terminal重复了,我们可以用下面的方法,将快捷键更改,改为alt+c,下次使用时,将待检验模块高亮,然后alt+c,在里面选择 try catch finally即可。

Tips: IntelliJ IDEA 中如何更改快捷键设置?

 - 打开 IntelliJ IDEA,在顶部菜单栏选择 "File",然后在下拉菜单中选择 "Settings"(对于 macOS 用户,选择 "IntelliJ IDEA" -> "Preferences")。
 - 在弹出的 "Settings" 对话框中,在左侧的导航栏选择 "Keymap"。
 -  在 "Keymap" 界面,你可以看到所有的快捷键设置。你可以通过搜索功能找到你想要更改的特定快捷键。在搜索框中输入你想要更改的功能名称,然后在下方的列表中找到它。
 - 找到你想要更改的快捷键后,右键点击它,然后在弹出的菜单中选择 "Remove" 来删除原有的快捷键。
 - 接着,右键点击你刚刚删除快捷键的功能,然后在弹出的菜单中选择 "Add Keyboard Shortcut"。
  - 在弹出的 "Enter Keyboard Shortcut" 对话框中,按下你想要设置的新快捷键,然后点击 "OK"。
  - 最后,点击 "Apply" 按钮应用更改,然后点击 "OK" 按钮关闭 "Settings" 对话框。

throw和throws关键字的用法:

助记

主动throw exception object inside method block using throw

主动throws (potential) exception class on method title using throws

举例,分母参数为零的情况下,将算式写完,用alt+c快捷键打开surround with选择try catch finally快速生成exception抛出模块

package exception;

public class test2 {
    public static void main(String[] args) {
        int a = 5;
        int b = 0;
        int c = 7;
        try {
            System.out.println(a+c/b);
        } catch (Exception e) {
            //e.printStackTrace();//做法1:打印错误的stack信息
            //System.exit(0); //做法2:直接退出 里面的参数可以写0或者1
            throw new RuntimeException(e); //做法3:直接抛出错误
        } finally {
        }
    }
}

运行结果

Exception in thread "main" java.lang.RuntimeException: java.lang.ArithmeticException: / by zero
	at exception.test2.main(test2.java:13)
Caused by: java.lang.ArithmeticException: / by zero
	at exception.test2.main(test2.java:9)
相关推荐
繁依Fanyi几秒前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
慧都小妮子11 分钟前
Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图
java·pdf·.net
m512715 分钟前
LinuxC语言
java·服务器·前端
IU宝20 分钟前
C/C++内存管理
java·c语言·c++
湫ccc20 分钟前
《Python基础》之pip换国内镜像源
开发语言·python·pip
瓜牛_gn21 分钟前
依赖注入注解
java·后端·spring
hakesashou22 分钟前
Python中常用的函数介绍
java·网络·python
佚先森31 分钟前
2024ARM网络验证 支持一键云注入引流弹窗注册机 一键脱壳APP加固搭建程序源码及教程
java·html
菜鸟的人工智能之路31 分钟前
极坐标气泡图:医学数据分析的可视化新视角
python·数据分析·健康医疗
菜鸟学Python32 分钟前
Python 数据分析核心库大全!
开发语言·python·数据挖掘·数据分析