5.2. try-catch-finally

一. 异常的两种处理方式

1. 异常上抛

在方法声明的位置上,使用 throws 关键字,抛给上一级

谁调用我,我就抛给谁,抛给上一级。

2. 异常捕捉

使用 try.. Catch 语句进行异常的捕捉

这件事发生了,谁也不知,因为我给抓住了。

举例:

我是某集团的一个销售员,因为我的失误,导致公司损失了 1000 块钱。”损失 1000 块”这可以看做一个异常发生了。我有两种处理方式。

  • 第一种方式:我把这件事告诉我的领导。【异常上抛】
  • 第二种方式:我把自己的掏腰包把这个钱补上。【异常的捕捉】

同样的异常发生之后,如果选择上抛,抛给了调用者,调用者需要对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。

注意:

Java 中异常发生之后如果一直上抛,最终抛给了 main 方法,main 方法继续向上抛,抛给了调用者 JVM,JVM 知道这个异常的发生,只有只有一个结果。终止 java 程序的执行。

二. 运行时异常编写程序时可以不处理

public class ExceptionTest01 {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        int c=a/b;
        /*
        程序执行到此处发生了ArithmeticException异常
        底层new一个ArithmeticException异常对象
        然后抛出了,由于是main方法调用了c=a/b
        所以这个异常ArithmeticException抛给main方法
        main方法没有处理,将这个异常自动抛给JVM
        JVM最终终止程序的执行

        ArithmeticException 继承 RuntimeException,属于运行时异常。
        在编写程序阶段不需要对这种异常进行预先的处理。
        
        */
        System. out.println(a + "/”+ b + "="+ c);

        //这里的HelloWorld没有输出,没有执行。
        System.out.println("Hello World!");//
    }
}

三. 方法声明上使用throws

以下代码报错的原因是什么?

==因为doSome()方法声明位置上使用了:throws ClassNotFoundException
而ClassNotFoundException是编译时异常。必须编写代码时处理,没有处理编译器报错。==

public class ExceptionText1 {
    public static void main(String[] args) {
        //main方法中调用doSome()方法
        //因为doSome()方法声明位置上有:throws ClassNotFoundException
        //我们在调用doSome()方法的时候必须对这种异常进行预先的处理。
        //如果不处理,编译器就报错
        //报错信息: 未报告的异常错误java.lang.ClassNotFoundException
        doSome();
    }

    /**
     * doSome方法在方法声明的位置上使用了:throws ClassNotFoundException
     * 这个代码表示doSome()方法在执行过程中,有可能会出现ClassNotFoundException异常。
     * 叫做类没找到异常。这个异常直接父类是: Exception,所以ClassNotFoundException属于编译异常。
     * @throws ClassNotFoundException
     */
    public static void doSome() throws ClassNotFoundException{
        System.out.println("doSome!!!");
    }
}
image.png

四. 异常处理的具体方式

第一种处理方式:在方法声明的位置上继续使用:throws,来完成异常的继续上抛。抛给调用者
上抛类似于推卸责任。(继续把异常传递给调用者)
这种处理异常的态度:上报

public class ExceptionText1 {
    public static void main(String[] args) throws ClassNotFoundException {
        doSome();
    }

    public static void doSome() throws ClassNotFoundException{
        System.out.println("doSome!!!");
    }
}

第二种方式:try ... catch进行异常捕捉
捕捉等于把异常拦下了,异常真正的解决了。(调用者不知道的)

public class ExceptionText1 {
    public static void main(String[] args) throws ClassNotFoundException {
        try {
        //尝试
            doSome();
        }catch (ClassNotFoundException e){
        //catch是捕捉异常之后走的分支
            e.printStackTrace;
        }
    }

    public static void doSome() throws ClassNotFoundException{
        System.out.println("doSome!!!");
    }
}

一般不建议在main方法上使用throws,因为这个异常如果真正发生了,一定会抛给JVM。JVM只有终止。一般采用 try...catch 进行捕捉。

五. 异常捕捉和上报的联合使用

import java.io.FileInputStream;

public class ExceptionText2 {
    public static void main(String[] args) {
        System.out.println("main begin ");
        m1();
        System.out.println("main over");
    }

    private static void m1(){
        System.out.println("m1 begin");
        m2();
        System.out.println("m1 over");
    }

    private static void m2(){
        System.out.println("m2 begin");
        m3();
        System.out.println("m3 over");
    }
    private static void m3(){
        //调用SUN jdk中某个类的构造方法
        //这个类是IO流的
        //创建一个输入流对象,该流指向一个文件。
        new FileInputStream("C:\\Users\\Administrator\\Desktop\\Java自学\\学习.txt");
    }
}
image.png

编译报错的原因:

  • 第一:这里调用了一个构造方法:FileInputStream(String name)
  • 第二:这个构造方法的声明位置上有:throws FileNotFoundException
  • 第三:通过类的继承结构看到:FileNotFoundException 父类是 IOException,IOException 的父类是 Exception。

最终得知,FileNotFoundException 是编译时异常。编译时异常要求程序员编写阶段必须对它进行处理,不处理编译器就报错

进行异常处理后

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ExceptionText2 {
    public static void main(String[] args) {
        System.out.println("main begin ");
        m1();
        System.out.println("main over");
    }

    private static void m1(){
        System.out.println("m1 begin");
        try {
            m2();
        } catch (FileNotFoundException e) {
        //这个分支中可以使用e引用,e引用保存的内存地址是new出来异常对象的内存地址
        //catch是捕捉异常之后走的分支
            System.out.println("文件不存在,可能路径错误,也可能文件被删除了!");
        }
        System.out.println("m1 over");
    }

   //也可以    throws Exception
    private static void m2() throws FileNotFoundException {
        System.out.println("m2 begin");
        //编译器报错的原因是:m3()方法声明位置上有:throws FileNotFoundException
        //我们这里调用m3()没有对异常进行预处理,所以编译报错
        m3();
        System.out.println("m3 over");
    }
    private static void m3() throws FileNotFoundException {
        //调用SUN jdk中某个类的构造方法
        //这个类是IO流的
        //创建一个输入流对象,该流指向一个文件。
        new FileInputStream("C:\\Users\\Administrator\\Desktop\\Java自学\\学习.txt");
    }
}

上抛异常也可以 throws Exception

注意:只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。
另外需要注意,try语句块中的某一行出现异常,该行后面的代码不会执行。

六. 上报和捕捉怎么选择?

  • 如果希望调用者来处理,选择throws上报。
  • 其他情况可以使用捕捉的方式

七. 深入try catch

1. catch后面的小括号的类型可以是具体的异常类型,也可以是该异常类的父类型

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ExceptionText2 {
    public static void main(String[] args) {
        try{
            FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\Java自学\\学习.txt");
            System.out.println("以上出现异常,这里的代码无法执行!");
        }catch(FileNotFoundException e){
            System.out.println("文件不存在!");
        }

        System.out.println("hello world");
    }

}
image.png|425

也可以写为多态:

try{
    //创建输入流
    FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\Java自学\\学习.txt");
}catch(IOException e){ //多态:IOException e = new FileNotFoundException();
    System.out.println("文件不存在!");
}

try{
          FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\Java自学\\学习.txt");
      }catch(Exception e){ //多态:Exception e = new FileNotFoundException();
          System.out.println("文件不存在!");
      }

2. catch可以写多个

建议catch的时候,精确的一个一个处理。这样有利于程序的调试

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionText2 {
    public static void main(String[] args) {
        try{
            //创建输入流
            FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\Java自学\\学习.txt");
            //读文件
            fis.read();
        }catch(FileNotFoundException e){ //所有异常都走这个分支
            System.out.println("文件不存在!");
        }catch(IOException e){
            System.out.println("读文件报错了!");
        }
    }
}

3. catch后面异常中可以加 | 号(JDK8新特性,7不支持)

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionText2 {
    public static void main(String[] args) {
        try{
            //创建输入流
            FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\Java自学\\学习.txt");

            System.out.println(100/0);//这个异常是运行时异常,编写程序时可以处理,也可以不处理

        }catch(FileNotFoundException | ArithmeticException | NullPointerException e){ //所有异常都走这个分支
            System.out.println("文件不存在?数字异常?空指针异常?都有可能!");
        }
    }
}

4. catch写多个的时候,从上到下,必须遵守从小到大

八. 关于try…catch中的finally子句

1. 在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现了异常

  • finally子句必须和try一起出现,不能单独编写。

2. finally语句通常使用在哪些情况下呢?

通常在finally语句块中完成资源的释放/关闭
因为finally中的代码比较有保障
即使try语句块中的代码出现异常,finally中的代码也会正常执行

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionText3 {
    public static void main(String[] args) {
        FileInputStream fis = null;//声明位置放到try外面,这样在finally中才能用
        try{
            //创建输入流对象
            fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\学习.txt");
            //开始读文件...

            String s = null;
            //这里一定会出现空指针异常!
            s.toString();
            System.out.println("hello world!");

            //流使用完需要关闭,因为流是占用资源的
            //即使以上程序出现异常,流也必须要关闭
            //放在这里有可能流关不了
            //fis.close();
        }catch(FileNotFoundException e){
            e.printStackTrace();
        }catch(IOException e) {
            e.printStackTrace();
        }catch(NullPointerException e){
            e.printStackTrace();
        }finally {
            System.out.println("hello 浩克");
            //流的关闭放在这里比较保险
            //finally中的代码是一定会执行的
            //即使try中出现了异常!
            if(fis != null) {//避免空指针异常
                try {
                    //close方法有异常采用捕捉的方式
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        System.out.println("hello kitty!");
    }
}

fis.close 未被注释前:

image.png

修改后:

image.png

3. try和finally,也可以没有catch

  • try不能单独使用。
  • try 可以和finally联合使用。

以下代码的执行顺序:

先执行try…
再执行finally
最后执行return(return语句只要执行方法必然结束)

public class ExceptionText4 {
    public static void main(String[] args) {
        try {
            System.out.println("try...");
            return;
        }finally {
            //finally中的语句会执行。能执行到。
            System.out.println("finally...");
        }

        //这里不能写语句,因为这个代码是无法执行到的。
        //System.out.println("Helloword");
    }
}

运行结果:

image.png

4. 退出JVM,finally语句不执行

public class ExceptionText5 {
    public static void main(String[] args) {
        try {
            System.out.println("try..");
            //退出JVM
            System.exit(0);//退出JVM之后,finally语句中的代码就不执行了!
        }finally {
            System.out.println("finally..");
        }
    }
}

运行结果:

image.png|375

5. 关于finally的一道面试题

public class ExceptionText5 {
    public static void main(String[] args) {
        int result = m();
        System.out.println(result);//结果为100
    }

    public static int m(){
        int i = 100;
        try {
        //这行代码出现在int i = 100的下面,所以最终结果必须是返回100
        //return语句还必须保证是最后执行的。一但执行,整个方法结束。
            return i;
        }finally {
            i++;
        }
    }
}

最后的结果为 100

image.png|575

解释: Java语法中有这样一条规则:

  • 方法体中的代码必须遵循自上而下顺序依次逐行执行(亘古不变的语法!)

Java中还有一条语法规则:

  • return语句一但执行,整个方法必须结束(亘古不变的语法!)

反编译之后的结果:

public static int m(){
    int i = 100;
    int j = i;
    i++;
    return j;
    Exception exception;
    exception;
    i++;
    throws exception;    
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇