Java异常处理--异常处理的方式1

news2024/11/16 6:56:12

文章目录

  • 一、异常处理概述
  • 二、方式1:捕获异常(try-catch-finally)
    • (1)抓抛模型
    • (2)try-catch-finally基本格式
      • 1、基本语法
      • 2、整体执行过程
      • 3、try和catch
        • 3.1 try
        • 3.2 catch (Exceptiontype e)
    • (3)catch中异常处理的方式
      • 1、处理方式
      • 2、案例1
      • 3、案例2
      • 4、案例3
      • 5、案例4
      • 6、整体代码
    • (4) finally使用及举例
      • 1、finally介绍
      • 2、举例
      • 3、笔试题
        • 3.1 题1
        • 3.2 题2
        • 3.3 题3
        • 3.4 题4
      • 4、面试题
      • 5、对finally的理解
    • (5)异常处理的体会

一、异常处理概述

在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行x/y运算时,要检测分母为0数据为空输入的不是数据而是字符等。

过多的if-else分支会导致程序的代码加长臃肿可读性差,程序员需要花很大的精力“堵漏洞”。

因此采用异常处理机制。

【Java异常处理】

Java采用的异常处理机制,是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。

异常处理中的“异常”是Exception,因为Error不编写针对性的代码进行处理。

Java异常处理的方式:

方式一:try-catch-finally

方式二:throws + 异常类型

二、方式1:捕获异常(try-catch-finally)

(1)抓抛模型

Java提供了异常处理的抓抛模型

  • 前面提到,Java程序的执行过程中如出现异常,会生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常
  • 如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处理。如果异常没有在调用者方法中处理,它继续被抛给这个调用方法的上层方法。这个过程将一直继续下去,直到异常被处理。这一过程称为捕获(catch)异常
  • 如果一个异常回到main()方法,并且main()也不处理,则程序运行终止。

方式一(抓抛模型):try-catch-finally

  • 过程1:“抛”-- 产生异常的对象
    • 程序在执行的过程当中,一旦出现异常,就会在出现异常的代码处,生成对应异常类的对象,并将此对象抛出。(目前先看自动创建异常的对象并抛出)
    • 一旦抛出,此程序就不执行其后的代码了。
  • 过程2:“抓”-- 捕获处理
    • 针对于过程1中抛出的异常对象,进行捕获处理。此捕获处理的过程,就称为抓。
    • 一旦将异常进行了处理,代码就可以继续执行。

(2)try-catch-finally基本格式

1、基本语法

捕获异常语法如下:

try{
	......	//可能产生异常的代码
}
catch( 异常类型1 e ){
	......	//当产生异常类型1型异常时的处置措施
}
catch( 异常类型2 e ){
	...... 	//当产生异常类型2型异常时的处置措施
}  
finally{
	...... //无论是否发生异常,都无条件执行的语句
}

try后面有一对大括号,大括号里面包裹的就是可能产生异常的代码(可能有多行)。

若是此代码在执行的过程当中,没有出现异常,那么就不会执行下面catch代码。(先不管finally

若是此代码在执行的过程当中,出现了异常,那么就会“抛”出异常。

比如现在try里面有4行代码执行,在第2行出现了异常,那么第2行就会抛出一个对象,第3行和第4行就不执行了。

接下来需要捕获对象,它会根据这个对象的类型依次匹配下面的多个catch。若第一个catch不匹配,就继续与下面的catch匹配,若成功匹配,就执行catch里面的代码,catch里面是对异常处理的代码。执行结束,下面的catch就不执行了。

try{
	......	//可能产生异常的代码
}
catch( 异常类型1 e ){	//不匹配
	......	//当产生异常类型1型异常时的处置措施
}
catch( 异常类型2 e ){	//匹配,执行
	...... 	//当产生异常类型2型异常时的处置措施
} 
...	//异常处理结束,这里的代码可以继续执行,try里面有4行代码,第3行和第4行没有机会执行了。

finally是try-catch里面可以放的结构,里面放的代码比较特别。就是不管前面什么情况,这里面的代码一定执行。暂时不说它。

2、整体执行过程

当某段代码可能发生异常,不管这个异常是编译时异常(受检异常)还是运行时异常(非受检异常),我们都可以使用try块将它括起来,并在try块下面编写catch分支尝试捕获对应的异常对象。

  • 如果在程序运行时,try块中的代码没有发生异常,那么catch所有的分支都不执行
  • 如果在程序运行时,try块中的代码发生了异常,根据异常对象的类型,将从上到下选择第一个匹配的catch分支执行。此时try中发生异常的语句下面的代码将不执行,而整个try…catch之后的代码可以继续运行
  • 如果在程序运行时,try块中的代码发生了异常,但是所有catch分支都无法匹配(捕获)这个异常,那么JVM将会终止当前方法的执行,并把异常对象**“抛”给调用者**。如果调用者不处理,程序就挂了。

image.png

【使用细节】

  • 将可能出现异常的代码声明在try语句中。一旦代码出现异常,就会自动生成一个对应异常类的对象。并将此对象抛出。
  • 针对于try中抛出的异常类的对象,使用之后的catch语句进行匹配。一旦匹配上,就进入catch语句块进行处理。一旦处理结束,代码就可继续向下执行。
  • 如果声明了多个catch结构,不同的异常类型在不存在子父类关系的情况下,谁声明在上面,谁声明在下面都可以。如果多个异常类型满足子父类的关系,则必须将子类声明在父类结构的上面。否则,报错。

3、try和catch

3.1 try
  • 捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的业务逻辑代码放在try语句块中。
3.2 catch (Exceptiontype e)
  • catch分支,分为两个部分,catch()中编写异常类型和异常参数名,{}中编写如果发生了这个异常,要做什么处理的代码。
  • 如果明确知道产生的是何种异常,可以用该异常类作为catch的参数;也可以用其父类作为catch的参数。
    比如:可以用ArithmeticException类作为参数的地方,就可以用RuntimeException类作为参数,或者用所有异常的父类Exception类作为参数。但不能是与ArithmeticException类无关的异常,如NullPointerException(catch中的语句将不会执行)。
  • 每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
  • 如果有多个catch分支,并且多个异常类型有父子类关系,必须保证小的子异常类型在上,大的父异常类型在下。否则,报错。
  • catch中常用异常处理的方式
    • public String getMessage():获取异常的描述信息,返回字符串
    • public void printStackTrace():打印异常的跟踪栈信息并输出到控制台。包含了异常的类型、异常的原因、还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace()。

image.png

(3)catch中异常处理的方式

1、处理方式

java.lang.Throwable 类是Java程序执行过程中发生的异常事件对应的类的根父类

Throwable类是异常的根父类,它继承于Object类。

Throwable中的常用方法:

  • public void printStackTrace():打印异常的详细信息。
    包含了异常的类型、异常的原因、异常出现的位置、在开发和调试阶段都得使用printStackTrace。
  • public String getMessage():获取发生异常的原因。 返回值是字符串。

【catch中异常处理的方式】

① 自己编写输出的语句。

printStackTrace():打印异常的详细信息。 (推荐

getMessage():获取发生异常的原因。

try中声明的变量,出了try结构之后,就不可以进行调用了。

try-catch结构是可以嵌套使用的。

2、案例1

处理方式:自己编写输出的语句。

以如下代码为例:

public class ExceptionHandleTest {
    @Test
    public void test1(){
        //InputMismatchException 输入类型不一致
        Scanner scanner=new Scanner(System.in);
        int num= scanner.nextInt(); //若输入的不是int类型,则会报错
        System.out.println(num);
    }
}

接下来将可能出现异常的代码用try包裹,假如这几行都可能有异常,都包裹起来,如下:

public class ExceptionHandleTest {
    @Test
    public void test1(){
        //InputMismatchException 输入类型不一致
        try{
            Scanner scanner=new Scanner(System.in);
            int num= scanner.nextInt(); //若输入的不是int类型,则会报错
            System.out.println(num);
        }
    }
}

然后将可能出现的异常写在catch里面(这里可能出现的异常是InputMismatchException),如下:

public class ExceptionHandleTest {
    @Test
    public void test1(){
        //InputMismatchException 输入类型不一致
        try{
            Scanner scanner=new Scanner(System.in);
            int num= scanner.nextInt(); //若输入的不是int类型,则会报错
            System.out.println(num);
        }catch (InputMismatchException e){

        }
    }
}

比如随便写一句话:

package yuyi02;

import org.junit.Test;

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

public class ExceptionHandleTest {
    @Test
    public void test1(){
        //InputMismatchException 输入类型不一致
        try{
            Scanner scanner=new Scanner(System.in);
            int num= scanner.nextInt(); //若输入的不是int类型,则会报错
            System.out.println(num);
        }catch (InputMismatchException e){
            System.out.println("出现了InputMismatchException的异常");
        }

        System.out.println("异常处理结束,代码继续执行");
    }
}

🍺运行

①无异常

运行看一下,比如输入了10:

image.png

此时意味着在try里面输出了10,catch就不执行。然后将try-catch后面的代码继续执行了。

②有异常

再次运行,输入abc:

image.png

此时在try里面第2行出现了InputMismatchException的异常,它会自动创建这个类型的对象,这个对象一抛出,会被相匹配的catch语句捕获,然后进行异常处理。最后将try-catch后面的代码继续执行。(try里面第3行代码没有机会执行了,因为第2行抛出了异常)


☕补充

当然,我们也可以有多个catch,比如:

public class ExceptionHandleTest {
    @Test
    public void test1(){
        //InputMismatchException 输入类型不一致
        try{
            Scanner scanner=new Scanner(System.in);
            int num= scanner.nextInt(); //若输入的不是int类型,则会报错
            System.out.println(num);
        }catch (InputMismatchException e){
            System.out.println("出现了InputMismatchException的异常");
        }catch (NullPointerException e){    //空指针异常
            System.out.println("出现了NullPointerException的异常");
        }catch (RuntimeException e){  //运行时异常
            System.out.println("出现了RuntimeException的异常");
        }

        System.out.println("异常处理结束,代码继续执行");
    }
}

比如在代码执行过程中,可能会有上面几种异常,若是第1个catch没有进去,就是没有匹配成功,那么就会继续匹配后面的catch。

若是都没有进去,那就会报异常。相当于和没有处理一样。

InputMismatchExceptionNullPointerException是两个并列的类,没有继承关系,可以颠倒。如下:

image.png

RuntimeExceptionInputMismatchExceptionNullPointerException的父类,应当写在下面。

若写在上面就会报错,因为后面两个就没有机会执行(会说该异常已经被处理了),如下:

image.png

InputMismatchException e中,InputMismatchException是变量类型,e是变量名。因为上面抛出来的异常对象没有名字。

在catch里面可以使用e这个变量名。

有效范围是大括号内,所以各个catch都能用e来当变量名。

3、案例2

处理方式:printStackTrace():打印异常的详细信息。 (推荐

以下面代码为例:

package yuyi02;

import org.junit.Test;

public class ExceptionHandleTest {

    @Test
    public void test2(){
        //NumberFormatException 数据格式化异常
        String str="123";
        str="abc";
        int i=Integer.parseInt(str);
        System.out.println(i);
    }
}

假如这四行代码可能出现异常,将它们用try包裹起来,如下:

@Test
public void test2(){
    //NumberFormatException 数据格式化异常
    try {
        String str="123";
        str="abc";
        int i=Integer.parseInt(str);
        System.out.println(i);
    }

}

然后写上可能出现的异常:

@Test
public void test2(){
    //NumberFormatException 数据格式化异常
    try {
        String str="123";
        str="abc";
        int i=Integer.parseInt(str);
        System.out.println(i);
    }catch (NumberFormatException e){
        e.printStackTrace();    //打印堆栈信息
    }
    System.out.println("程序结束");
}

🍺输出

image.png

此时程序正常结束了,异常也处理了,这里只不过是输出了异常信息而已。

把相应的哪个方法里面,调用哪个了,情况都给罗列出来了。

☕注意

有人就问了,这里和没有处理的显示一样,那么有啥意义呢?

若是没有写try-catch语句,此时程序出现了异常,如下:

image.png

其实真正在开发当中,像这种异常可以考虑不去处理

因为处理、不处理报出来的结果一样。这个处理其实就是预防万一出问题,就要按照指定的方式去执行。

但是归根结底,这个代码还是有问题的。

所以对于这种运行时异常,平时就是不处理。若是出现了异常,那就直接去改代码。

try-catch的目的是,提前预知了可能有问题的代码,并给一种方案。不至于让程序挂掉。

比如支付某个订单的时候出现了错误,界面不会出现一堆乱码,而是出现一个友好的提示,这其实就是一种呈现异常的解决方式。应用程序还可以正常运行,只是支付功能没有实现而已。最终还是要改代码啦。

上面的例子其实是编译时异常,对于运行时异常,没有必要处处加上try-catch,只要出现了就回去改代码。


【开发体会】

  • 对于运行时异常

    • 开发中,通常就不进行显示的处理了。
    • 一旦在程序执行中,出现了运行时异常,那么就根据异常的提示信息修改代码即可。
  • 对于编译时异常:(try-catch主要处理的是编译时异常)

    • 一定要进行处理。否则编译不通过。

4、案例3

处理方式:getMessage():获取发生异常的原因。

以下面代码为例:

package yuyi02;

import org.junit.Test;

public class ExceptionHandleTest {

    @Test
    public void test2(){
        //NumberFormatException 数据格式化异常
        try {
            String str="123";
            str="abc";
            int i=Integer.parseInt(str);
            System.out.println(i);
        }catch (NumberFormatException e){
            System.out.println(e.getMessage());
        }
        System.out.println("程序结束");
    }
}

这里返回字符串,所以需要主动输出一下。

🍺输出

image.png

这里表达的意思是:因为写了“abc”不满足要求,所以出现异常。

☕注意

在这个地方其实不能打印str了,如下:

image.png

这是因为,在try里面声明的变量,作用域只在try里面,出了try就看不到了。

try中声明的变量,出了try结构之后,就不可以进行调用了。

try-catch结构是可以嵌套使用的。

5、案例4

以下面代码为例:

@Test
public void test3() {
    File file=new File("D:\\hello.txt");

    //可能报FileNotFoundException 文件找不到异常
    FileInputStream fis=new FileInputStream(file);  //流直接操作文件

    //把文件内容直接读到内存中输出
    int data=fis.read();    //可能报IOException 输入输出异常
    while(data!=-1){    //data为-1的时候退出,就是读完了
        System.out.print((char)data);
        data=fis.read();  //可能报IOException 输入输出异常
    }

    //资源关闭
    fis.close();  //可能报IOException 输入输出异常
}

其实这个文件是存在的,不会出现错误。但此时编译就是不让过,如下:

image.png

此时必须要处理一下。

将可能出现异常的代码用try包裹起来,如下:

@Test
public void test3() {
    try {
        File file=new File("D:\\hello.txt");

        //可能报FileNotFoundException 文件找不到异常
        FileInputStream fis=new FileInputStream(file);  //流直接操作文件

        //把文件内容直接读到内存中输出
        int data=fis.read();    //可能报IOException 输入输出异常
        while(data!=-1){    //data为-1的时候退出,就是读完了
            System.out.print((char)data);
            data=fis.read();  //可能报IOException 输入输出异常
        }

        //资源关闭
        fis.close();  //可能报IOException 输入输出异常
    }

}

然后针对可能出现的异常写catch,如下:

@Test
public void test3() {
    try {
        File file=new File("D:\\hello.txt");

        //可能报FileNotFoundException 文件找不到异常
        FileInputStream fis=new FileInputStream(file);  //流直接操作文件

        //把文件内容直接读到内存中输出
        int data=fis.read();    //可能报IOException 输入输出异常
        while(data!=-1){    //data为-1的时候退出,就是读完了
            System.out.print((char)data);
            data=fis.read();  //可能报IOException 输入输出异常
        }

        //资源关闭
        fis.close();  //可能报IOException 输入输出异常
    }catch (FileNotFoundException e){
        e.printStackTrace();
    }catch (IOException e){
        e.printStackTrace();
    }

}

将光标放在FileNotFoundException上,按Ctrl+h,可以看到继承关系,如下:

image.png

可以看到,FileNotFoundException继承于IOException,这里不是继承于RuntimeException,所以是编译时异常。

因为它们是子父类的关系,所以不能颠倒写的顺序。

☕注意

这里我们写的比较细,两种可能出现的异常都写出来了,处理方案都一样。

这种情况下,若将上面一个删了也是可以的,如下:

image.png

即使这里抛的是一个FileNotFoundException,这里也能够捕获。

其次,IOExceptionFileNotFoundException的父类,它们的printStackTrace()方法的方法体不一样,是多态。

所以完全可以替换。

🍺输出

这个例子在上一篇文章讲过,文件里面的内容就是如下显示的内容:

image.png

在整个程序运行过程中,没有出现任何异常,也没有捕获过。

若此时找不到文件,比如将路径更改一下:

image.png

再次运行:

image.png

出现的异常是在FileInputStream fis=new FileInputStream(file);这个位置,产生了一个FileNotFoundException的对象,这个对象直接出了try的花括号,try里面剩下的语句都无法执行了。

然后就到了catch里面,找到相匹配的处理,打印相应的信息。最后执行try-catch后面的语句即可。

如下:

image.png

可以调试看一下过程:

PixPin_2024-01-10_16-07-14.gif

编译时异常虽然处理了,但是在运行的时候还可能会出现。

可以理解为,把一个编译时异常延后到运行时出现了。在运行的时候报出来。

编译时异常必须要处理,若是不处理就不能运行。

【开发体会】

  • 对于运行时异常

    • 开发中,通常就不进行显示的处理了。
    • 一旦在程序执行中,出现了运行时异常,那么就根据异常的提示信息修改代码即可。
  • 对于编译时异常:(try-catch主要处理的是编译时异常)

    • 一定要进行处理。否则编译不通过。

所以平常我们写代码,写完一行发现出红线了,就看一下什么原因,既然是编译时异常,那么就将它用try-catch解决一下即可。

6、整体代码

🌱代码

package yuyi02;

import org.junit.Test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.InputMismatchException;
import java.util.Scanner;

/**
 * ClassName: ExceptionHandleTest
 * Package: yuyi02
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2024/1/10 0010 9:39
 */
public class ExceptionHandleTest {
    @Test
    public void test1(){
        //InputMismatchException 输入类型不一致
        try{
            Scanner scanner=new Scanner(System.in);
            int num= scanner.nextInt(); //若输入的不是int类型,则会报错
            System.out.println(num);
        }catch (NullPointerException e){    //空指针异常
            System.out.println("出现了NullPointerException的异常");
        }catch (InputMismatchException e){
            System.out.println("出现了InputMismatchException的异常");
        }catch (RuntimeException e){  //运行时异常
            System.out.println("出现了RuntimeException的异常");
        }

        System.out.println("异常处理结束,代码继续执行...");
    }

    @Test
    public void test2(){
        //NumberFormatException 数据格式化异常
        try {
            String str="123";
            str="abc";
            int i=Integer.parseInt(str);
            System.out.println(i);
        }catch (NumberFormatException e){
            System.out.println(e.getMessage());
        }
        System.out.println("程序结束...");
    }

    @Test
    public void test3() {
        try {
            File file=new File("D:\\hello1.txt");

            //可能报FileNotFoundException 文件找不到异常
            FileInputStream fis=new FileInputStream(file);  //流直接操作文件

            //把文件内容直接读到内存中输出
            int data=fis.read();    //可能报IOException 输入输出异常
            while(data!=-1){    //data为-1的时候退出,就是读完了
                System.out.print((char)data);
                data=fis.read();  //可能报IOException 输入输出异常
            }

            //资源关闭
            fis.close();  //可能报IOException 输入输出异常
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
        System.out.println("读取数据结束...");
    }
}

🍺输出结果

image.png

(4) finally使用及举例

1、finally介绍

捕获异常语法如下:

try{
	......	//可能产生异常的代码
}
catch( 异常类型1 e ){
	......	//当产生异常类型1型异常时的处置措施
}
catch( 异常类型2 e ){
	...... 	//当产生异常类型2型异常时的处置措施
}  
finally{
	...... //无论是否发生异常,都无条件执行的语句
}

两种情况:

image.png

☕注意

  • 因为异常会引发程序跳转,从而会导致有些语句执行不到。而程序中有一些特定的代码无论异常是否发生,都需要执行。例如,数据库连接、输入流输出流、Socket连接、Lock锁的关闭等,这样的代码通常就会放到finally块中。所以,我们通常将一定要被执行的代码声明在finally中
    • 唯一的例外,使用 System.exit(0) 来终止当前正在运行的 Java 虚拟机。这个操作会让程序强行结束。(也就是说,除非让当前Java虚拟机运行的进程强行结束,除此之外,finally中的代码一定会被执行)
  • 不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch语句中是否有return,finally块中的语句都会被执行。
  • finally语句和catch语句是可选的,但finally不能单独使用。 之前说switch-case的时候,default是可选的,包括if-else中的else是可选的,可选的意思就是可写可不写。

【try-catch】

try{
	......	//可能产生异常的代码
}
catch( 异常类型1 e ){
	......	//当产生异常类型1型异常时的处置措施
}

//没有finally

【try-finally】

对于运行时异常,通常不做显示的处理,就根据异常提示信息修改代码即可。

对于编译时异常,一定要处理,否则编译不通过。

若是某一些代码可能会有运行时异常,不用显示处理,所以不要catch了;但是这个代码万一出现了运行时异常怎么办呢?在finally里面写上一定要被执行的代码即可。

try{
     
}finally{
    
}

//没有catch

可以没有catch,仅有finally;可以没有finally,仅有catch。

但是不能仅仅只有一个try。

2、举例

以如下代码为例:

package yuyi02;

import org.junit.Test;

public class FinallyTest {
    @Test
    public void test1(){
        //NumberFormatException 数据格式化异常
        try {
            String str="123";
            str="abc";
            int i=Integer.parseInt(str);
            System.out.println(i);
        }catch (NumberFormatException e){
            e.printStackTrace();
        }
        System.out.println("程序结束...");
    }
}

此时代码表达的意思如下:

image.png

再看一下输出:

image.png

可以看到此时确实出现异常了,而且这个异常在catch里面被捕获了。

try-catch下面的代码可以继续执行。

🚗那么写在try-catch下面的代码一定会执行,为啥要刻意写一个finally呢?

看一下下面两种写法:

image.png

此时感觉不到两者的区别,因为执行结果都一致。

现在改一下代码,在catch中出现了异常,而此时没有地方给它处理。

public class FinallyTest {
    @Test
    public void test1(){
        //NumberFormatException 数据格式化异常
        try {
            String str="123";
            str="abc";
            int i=Integer.parseInt(str);
            System.out.println(i);
        }catch (NumberFormatException e){
            e.printStackTrace();
            System.out.println(10/0);   //在catch中存在异常
        }
        System.out.println("程序结束...");
    }

    @Test
    public void test2(){
        //NumberFormatException 数据格式化异常
        try {
            String str="123";
            str="abc";
            int i=Integer.parseInt(str);
            System.out.println(i);
        }catch (NumberFormatException e){
            e.printStackTrace();
            System.out.println(10/0);   //在catch中存在异常
        }finally {
            System.out.println("程序结束...");
        }
    }
}

🗳️分析

test1中,执行过程如下:

image.png

看一下输出结果:

image.png

此时没有打印"程序结束…"。

再看一下test2,打印了"程序结束…",如下:

image.png

所以,如果有一定要执行的代码,就放到finally里面。

3、笔试题

3.1 题1

下面代码输出结果是?

🌱代码

package yuyi02.interview;

/**
 * ClassName: FinallyTest1
 * Package: yuyi02.interview
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2024/1/11 0011 9:41
 */
public class FinallyTest1 {
    public static void main(String[] args) {
        int result = test("12");
        System.out.println(result);
    }

    public static int test(String str){
        try{
            Integer.parseInt(str);
            return 1;
        }catch(NumberFormatException e){
            return -1;
        }finally{
            System.out.println("test结束");
        }
    }
}

🍺输出结果

image.png

🎲分析

image.png

即使有return,finally也一定会执行。

若是没有finally,最后一行代码还无法存在,如下:

image.png

因为此时在try和catch中都有return,最后一行输出语句没有机会执行,但要是写在finally里面就不一样啦。

3.2 题2

下面代码输出结果是?

🌱代码

package yuyi02.interview;

/**
 * ClassName: FinallyTest2
 * Package: yuyi02.interview
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2024/1/11 0011 9:42
 */
public class FinallyTest2 {
    public static void main(String[] args) {
        int result = test("a");
        System.out.println(result);
    }

    public static int test(String str) {
        try {
            Integer.parseInt(str);
            return 1;
        } catch (NumberFormatException e) {
            return -1;
        } finally {
            System.out.println("test结束");
        }
    }
}

🍺输出结果

image.png

🎲分析

image.png

不管try和catch里面有没有执行语句,finally一定会被执行。

3.3 题3

下面代码输出结果是?

🌱代码

package yuyi02.interview;

/**
 * ClassName: FinallyTest3
 * Package: yuyi02.interview
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2024/1/11 0011 9:43
 */
public class FinallyTest3 {
    public static void main(String[] args) {
        int result = test("a");
        System.out.println(result);
    }

    public static int test(String str) {
        try {
            Integer.parseInt(str);
            return 1;
        } catch (NumberFormatException e) {
            return -1;
        } finally {
            System.out.println("test结束");
            return 0;
        }
    }
}

🍺输出结果

image.png

🎲分析

image.png

3.4 题4

下面代码输出结果是?

🌱代码1

package yuyi02.interview;

/**
 * ClassName: FinallyTest4
 * Package: yuyi02.interview
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2024/1/11 0011 9:43
 */
public class FinallyTest4 {
    public static void main(String[] args) {
        int result = test(10);
        System.out.println(result);
    }

    public static int test(int num) {
        try {
            return num;
        } catch (NumberFormatException e) {
            return num--;
        } finally {
            System.out.println("test结束");
            return ++num;
        }
    }
}

🍺输出结果

image.png

🎲分析

image.png


🌱代码2

package yuyi02.interview;

/**
 * ClassName: FinallyTest4
 * Package: yuyi02.interview
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2024/1/11 0011 9:43
 */
public class FinallyTest4 {
    public static void main(String[] args) {
        int result = test(10);
        System.out.println(result);
    }

    public static int test(int num) {
        try {
            return num;
        } catch (NumberFormatException e) {
            return num--;
        } finally {
            System.out.println("test结束");
            ++num;
        }
    }
}

🍺输出结果

image.png

🎲分析

image.png

方法都是一个栈帧,num是一个局部变量,局部变量存在于栈帧里面。

栈帧又分为好几个部分,其中最重要的有局部变量表(数组,用来存放局部变量)、操作数栈(存放临时运算的数据)。

画个图看一下:

image.png

4、面试题

🚗final 、 finally 、finalize 的区别?

final:最终的

finally:一定会被执行的代码

finalize:方法名

5、对finally的理解

【finally的理解】

  • 我们将一定要被执行的代码声明在finally结构中。
  • 更深刻的理解:无论try中或catch中是否存在仍未被处理的异常,无论try中或catch中是否存在return语句等,finally中声明的语句都一定要被执行。
  • finally语句和catch语句是可选的,但finally不能单独使用。

🗳️什么样的代码我们一定要声明在finally中呢?

我们在开发中,有一些资源(比如:输入流、输出流,数据库连接、Socket连接等资源),在使用完以后,必须显式地进行关闭操作,否则,GC(垃圾回收器)不会自动地回收这些资源,进而导致内存的泄漏。

为了保证这些资源在使用完以后,不管是否出现了未被处理的异常的情况下,这些资源能被关闭。我们必须将这些操作声明在finally中!


【举例】

以如下代码为例:

//实际开发中,finally的使用
@Test
public void test3() {
    try {
        File file=new File("D:\\hello.txt");

        //可能报FileNotFoundException 文件找不到异常
        FileInputStream fis=new FileInputStream(file);  //流直接操作文件

        //把文件内容直接读到内存中输出
        int data=fis.read();    //可能报IOException 输入输出异常
        while(data!=-1){    //data为-1的时候退出,就是读完了
            System.out.print((char)data);
            data=fis.read();  //可能报IOException 输入输出异常
        }

        //资源关闭
        fis.close();  //可能报IOException 输入输出异常

    }catch (FileNotFoundException e){
        e.printStackTrace();
    }catch (IOException e){
        e.printStackTrace();
    }
    System.out.println("读取数据结束...");
}

这里面涉及资源的关闭fis.close();,但是处理的不太好。

如下:

image.png

☕注意

既然test3是一个方法,执行的时候它的栈帧进入栈空间,代码走完栈帧不是弹出去了吗?栈帧弹出去了,那么栈帧里面定义的fis也因该销毁了啊?

fis指向堆空间造的一个流对象,既然指针没有了,那GC为啥不能回收呢?

注意,test3方法执行完,栈帧确实弹出了,变量也没有了。GC去回收它的时候,由于现在是个,流需要跟物理磁盘上的文件打交道,包括网络资源也是一样。它还有其他的引用指向。

所以GC在回收的时候,他发现还有其他的指针指向,所以没有办法回收。

实际上,此时没有变量名指向它,也用不了。

从我们的角度来看是垃圾,但是GC发现还有别的引用指向,它就没有回收。

image.png

此时必须调用fis.close()将其他连接断掉,这时候GC才能够回收,否则就可能有泄露问题。

既然fis.close()的操作必须要被执行,而若是有其他风险的话可能执行不了,就需要将它放入finally里面。

如下:

image.png

此时发现,fis报红,这是因为在try里面声明的变量出了try就看不见了。

所以就需要将相关代码也一起拿出去,但是此时可能报的异常怎么办?如下:

image.png

这时候需要将处理异常的部分还放在try里面。既然变量会看不见,那就不在try里面声明。

在外面声明,在try里面赋值即可。如下:

image.png

此时既在try里面处理了异常,又可以在finally里面看见了。


现在还有一个问题,close()也可能有异常,所以也需要处理一下。

如下:

image.png

可以看到,try-catch里面是可以嵌套使用的。

现在的程序就可以解决刚才的泄露问题:

image.png

其实还可以让程序健壮性更好,若fis=new FileInputStream(file); 这里出现异常,流并没有创建成功,处理异常之后,还要执行finally,fis.close();就会出现空指针。

所以可以在finally里面,可以先加一个if判断。如下:

image.png

🌱代码

//实际开发中,finally的使用
@Test
public void test3() {
    FileInputStream fis=null;   //在try外面声明,让finally里面也可以使用
    try {
        File file=new File("D:\\hello.txt");
        //可能报FileNotFoundException 文件找不到异常
        fis=new FileInputStream(file);  //流直接操作文件

        //把文件内容直接读到内存中输出
        int data=fis.read();    //可能报IOException 输入输出异常
        while(data!=-1){    //data为-1的时候退出,就是读完了
            System.out.print((char)data);
            data=fis.read();  //可能报IOException 输入输出异常
        }
    }catch (FileNotFoundException e){
        e.printStackTrace();
    }catch (IOException e){
        e.printStackTrace();
    }finally {
        //重点:将流资源的关闭操作放在finally当中。
        try {
            if (fis!=null){ //预防流没有创建成功,会出现空指针的问题
                //资源关闭  防止读文件的时候出异常,导致资源无法关闭,所以写在finally里面,必须执行
                fis.close();  //可能报IOException 输入输出异常
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    System.out.println("读取数据结束...");
}

看一下前后对比:

image.png

重点:将流资源的关闭操作放在finally当中。

(5)异常处理的体会

  • 前面使用的异常都是RuntimeException类或是它的子类,这些类的异常的特点是:即使没有使用try和catch捕获,Java自己也能捕获,并且编译通过 ( 但运行时会发生异常使得程序运行终止 )。所以,对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
  • 如果抛出的异常是IOException等类型的非运行时异常,则必须捕获,否则编译错误。也就是说,我们必须处理编译时异常,将异常进行捕捉,转化为运行时异常。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1376833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

系列二、Spring Security中的核心类

一、Spring Security中的核心类 1.1、自动配置类 UserDetailsServiceAutoConfiguration 1.2、密码加密器 1.2.1、概述 Spring Security 提供了多种密码加密方案,官方推荐使用 BCryptPasswordEncoder,BCryptPasswordEncoder 使用 BCrypt 强哈希函数&a…

element-ui 如何修改el-popconfirm的样式

改造之前效果 改造之后效果 代码&#xff1a; <style lang"scss"> .my-popconfirm {width: 92% !important;height: 130px !important;padding: 14px !important;font-size: 28px !important;.el-popper {font-size: 28px !important;}.el-popconfirm__main {…

ChatGPT:人工智能与人类交流的桥梁

在人工智能的浪潮中&#xff0c;ChatGPT以其独特的交流能力成为了一个亮点。作为一个基于强大的GPT-4模型的聊天机器人&#xff0c;ChatGPT不仅仅是技术的展示&#xff0c;它更是人工智能与人类交流的桥梁。 人工智能的语言理解革命 ChatGPT的出现标志着人工智能在语言理解和…

怎么挑选一体化污水处理设备

选择一体化污水处理设备是一个关键决策&#xff0c;它直接影响到污水处理系统的效能和运行成本。随着环保意识的日益提高&#xff0c;各种污水处理设备也不断地涌现出来。那么&#xff0c;在众多选项中&#xff0c;如何挑选一体化污水处理设备&#xff1f;本文将为您提供一些建…

了解PyTorch中的缩放点积注意力及演示

torch.nn.functional.scaled_dot_product_attention 函数在 PyTorch 框架中用于实现缩放点积注意力&#xff08;Scaled Dot-Product Attention&#xff09;。这是一种在自然语言处理和计算机视觉等领域常用的注意力机制。它的主要目的是通过计算查询&#xff08;query&#xff…

详解动态网页数据获取以及浏览器数据和网络数据交互流程-Python

前言 动态网页是一种在用户浏览时实时生成或变化的网页。与静态网页不同&#xff0c;后者通常是预先编写好的HTML文件&#xff0c;直接由服务器传送给浏览器&#xff0c;内容在服务端生成且固定不变&#xff0c;获取静态数据的文章课查阅博主上一篇文章&#xff1a;详解静态网…

小区楼盘3D场景可视化:重塑您对家的想象

随着科技的飞速发展&#xff0c;我们的生活正经历着前所未有的变革。曾经&#xff0c;我们只能通过文字和二维图片来了解楼盘的布局和设计&#xff1b;而今&#xff0c;3D技术的引入&#xff0c;让这一切变得栩栩如生。今天&#xff0c;就让我们一起走进小区楼盘3D场景可视化&a…

虹科新闻丨LIBERO医药冷链PDF温度计完成2024年航空安全鉴定,可安全空运!

来源&#xff1a;虹科环境监测技术 虹科新闻丨LIBERO医药冷链PDF温度计完成2024年航空安全鉴定&#xff0c;可安全空运&#xff01; 原文链接&#xff1a;https://mp.weixin.qq.com/s/XHT4kU27opeKJneYO0WqrA 欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; 虹科LIBE…

回归预测 | Matlab实现RIME-HKELM霜冰算法优化混合核极限学习机多变量回归预测

回归预测 | Matlab实现RIME-HKELM霜冰算法优化混合核极限学习机多变量回归预测 目录 回归预测 | Matlab实现RIME-HKELM霜冰算法优化混合核极限学习机多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现RIME-HKELM霜冰算法优化混合核极限学习机多变…

华为网络设备 通过路由器子接口 Dot1q终结子接口实现跨VLAN通信

(二层交换机直接跳过三层交换价接入路由器时才使用该配置。推荐使用三层交换机建立VLANIF配置更简洁明了。如果VLAN较少可直接配置&#xff1b;路由器接口&#xff0c;一个物理接口一个VLAN) S1配置 vlan batch 2 to 3interface GigabitEthernet0/0/1port link-type trunkpor…

基础篇_面向对象(什么是对象,对象演化,继承,多态,封装,接口,Service,核心类库,异常处理)

文章目录 一. 什么是对象1. 抽取属性2. 字段默认值3. this4. 无参构造5. 抽取行为 二. 对象演化1. 对象字段演化2. 对象方法演化3. 贷款计算器 - 对象改造4. 静态变量5. 四种变量 三. 继承1. 继承语法2. 贷款计算器 - 继承改造3. java 类型系统4. 类型转换1) 基本类型转换2) 包…

AMEYA360报导:瑞萨宣布收购Transphorm,大举进军GaN

全球半导体解决方案供应商瑞萨电子与全球氮化镓(GaN)功率半导体供应商Transphorm, Inc.(以下“Transphorm”)于今天宣布双方已达成最终协议&#xff0c;根据该协议&#xff0c;瑞萨子公司将以每股5.10美元现金收购Transphorm所有已发行普通股&#xff0c;较Transphorm在2024年1…

修改vscode内置Vue VSCode Snippets(代码片段)

打开插件文件夹 文件夹名是 "作者名.vscode-插件名-版本号"组成的. C:\Users\Administrator\.vscode\extensions\sdras.vue-vscode-snippets-3.1.1\snippets 打开vue.json "prefix": "vbase" 就是代码块的关键词,输入vbase就会提示代码块 …

一文教你用Python写网络爬虫,内容详尽讲解细致,手把手教会你

什么是网络爬虫&#xff1f; 网络爬虫是一个自动提取网页的程序&#xff0c;它为搜索引擎从万维网上下载网页&#xff0c;是搜索引擎的重要组成。传统爬虫从一个或若干初始网页的URL开始&#xff0c;获得初始网页上的URL&#xff0c;在抓取网页的过程中&#xff0c;不断从当前…

报表生成器FastReport .Net用户指南:数据源与“Data“窗口

FastReport .Net是一款全功能的Windows Forms、ASP.NET和MVC报表分析解决方案&#xff0c;使用FastReport .NET可以创建独立于应用程序的.NET报表&#xff0c;同时FastReport .Net支持中文、英语等14种语言&#xff0c;可以让你的产品保证真正的国际性。 FastReport.NET官方版…

血泪教训!Java项目的路径中一定不要包含中文~

今天通过应用类加载器获取某个目录下的文件时&#xff0c;控制台一直没有输出&#xff0c;但是没有任何的报错&#xff0c;代码如下所示 ClassLoader classLoaderwjrApplicationContext.class.getClassLoader();//appURL url classLoader.getResource("com/wjr/service&qu…

Alphafold2蛋白质结构预测AI工作站配置推荐

AlphaFold2计算特点 蛋白质三维结构预测是一项计算量非常巨大的任务&#xff0c;科学家多年的探索研究&#xff0c;形成了X射线晶体学法、核磁共振法、冷冻电镜等。 2021年底&#xff0c;谷歌的DeepMind团队的采用人工智能方法的AlphaFold2算法在生物界引起了极大的轰动…

antd时间选择器,设置显示中文

需求 在实现react&#xff0c;里面引入antd时间选择器&#xff0c;默认显示为英文 思路 入口处使用ConfigProvider全局化配置&#xff0c;设置 locale 属性为中文来实现。官方文档介绍全局化配置 ConfigProvider - Ant Design 代码 import React from react; import { Prov…

慢 SQL 的优化思路

分析慢 SQL 如何定位慢 SQL 呢&#xff1f; 可以通过 slow log 来查看慢SQL&#xff0c;默认的情况下&#xff0c;MySQL 数据库是不开启慢查询日志&#xff08;slow query log&#xff09;。所以我们需要手动把它打开。 查看下慢查询日志配置&#xff0c;我们可以使用 show …

【数据库学习】ClickHouse(ck)

1&#xff0c;ClickHouse&#xff08;CK&#xff09; 是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。 1&#xff09;特性 按列存储&#xff0c;列越多速度越慢&#xff1b; 按列存储&#xff0c;数据更容易压缩&#xff08;类型相同、区分度&#xff09;&#xff1b…