首页 > 其他分享 >异常_演练

异常_演练

时间:2024-12-07 21:22:41浏览次数:6  
标签:读取 System catch sc 异常 演练 out

参考:

  • 韩顺平Java
  • Java程序设计教程(洪)
  • Java核心技术卷1
  • 廖雪峰的官方网站

异常(Exception)

异常对应的英文单词是Exception(一般情况以外的人(或事物);例外的事物)

内容

  1. 异常的概念
  2. 异常的层次结构(★)
  3. 非检查型异常与检查型异常(★)
  4. 捕获异常(★)
  5. 自定义异常与抛出异常

引入

课本P205例7.1A

import java.util.Scanner;

public class Example1 {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("整数a: ");
        int a = sc.nextInt();
        System.out.print("整数b: ");
        int b = sc.nextInt();

        System.out.println("a/b: " + a / b);

        System.out.println("程序继续运行......");
    }
}

理想状态下,用户输入的数据永远是符合规则的,例如,输入 10 除以 5 可以顺利得到 2,并且之后的代码也会顺利地执行。

但现实世界里,总是充斥着各种各样的例外。例如,输入一个 10 和 0 ,这段程序就会直接崩溃:

整数a: 10
整数b: 0
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at Example1.main(Example1.java:13)

 对该现象做一个分析:

        //1. 当执行到 a/b时 , 因为b=0 , 程序会出现一个异常(java中叫 抛出异常)
        // ps. 具体来说这个异常叫作 ArithmeticException (算数异常)本质上是一个代表异常的对象
        //2. 抛出异常后,程序会崩溃并退出 -> 后面的代码不再执行
        //3. 这样的程序好吗!? 仅因用户的一个输入错误导致整个系统崩溃 -> 显然不好!
        //4. java 提供了一套 异常处理机制 来解决这个问题
        // 当一段程序出现异常时,应该
        // 1 通知用户 -> 至少 给用户一个人性化的提示
        // 2 尽量不因为一个异常 而 导致整个系统崩溃( 但有时不可避免 )

解决方法

Java提供了一种叫作try/catch的语句块,可以帮助我们实现这两点。

怎么用try/catch?课本P207最上面的代码,对原始代码进行修改

  1. 找到可能出现异常的代码
  2. 把这段代码放到try语句块中
  3. catch子句中捕获可能出现的异常
import java.util.Scanner;

public class Example1 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("整数a: ");
        int a = sc.nextInt();
        System.out.print("整数b: ");
        int b = sc.nextInt();

        try {
            System.out.println("a/b: " + a / b); // 假如认为这段代码可能出现异常
        } catch (ArithmeticException e) {
            System.out.println("除数不能为0");
        }
        
        System.out.println("程序继续运行......");
    }
}

再次执行这段程序,就不会再因为除数为0而崩溃:

整数a: 10
整数b: 0
除数不能为0
程序继续运行......

异常简介

Java中,将程序执行中发生的不正常情况称为异常。(开发过程中的语法错误、逻辑错误不是异常)

例如,经常发现有同学不写main方法、单词拼错、大写的写成小写等等,都不是异常。

执行过程出现的异常可分为两大类:

  1. Error(错误):Java虚拟机无法解决的严重问题,如JVM内部错误、资源耗尽等。Error是致命的,会导致程序崩溃。
  2. Exception(异常):由于编程错误或偶然的外部因素导致的一般性的问题,可以使用针对性的代码进行处理。如除数为0、读取不存在的文件、网络连接中断等

如果把程序比作一个人,出现Error(错误),就相当于癌症晚期,没治了,只能挂掉,最多只能做到通知用户。

Exception可以类比成一些可以预防或处理的疾病。例如,在例7.1A中,除了可以通过try/catch语句块处理已抛出的异常,还可以通过事先检查避免抛出这个异常:

        if (b != 0) {
            System.out.println("a/b: " + a / b);
        } else {
            System.out.println("除数不能为0");
        } // 并不是一定要借助 java 异常处理机制!

异常的层次结构

每当发生异常时,会生成一个代表异常的对象,创建异常对象要基于现有的异常类。

Java中的所有异常类都是Throwable的子类。

在异常类的体系结构中,最顶层是Throwable类,下一层立即分成两个分支:ErrorException。(上文提到的)

Error是致命性的、无能为力的严重问题。编程中要重点关注的是可以预防或预先设置处理方法的Exception

Exception可以分为两个分支:RuntimeException(运行时异常)和其它异常。

一般规则是:由编程错误导致的异常属于RuntimeException;如果程序本身没问题,但由于一些不可避免的外部因素导致的异常属于其它异常。

RuntimeException

有句话叫:“如果出现 RuntimeException 异常,那么一定是你的问题”。

例如,上面例子中的ArithmeticException(算数异常)就属于运行时异常。也就是说,此类异常一般通过调整代码是可以避免的。出现这种异常就是提示你——代码写得有点问题!再比如,常见的数组越界异常ArrayIndexOutOfBoundsException也是运行时异常:

        int[] arr = new int[3];
        System.out.println(arr[5]);

出现该异常实际上是代码没写好,做了不该做的事情。

还有常见的空指针异常NullPointerException

public class Student {
    void study() {

    }
}
public class Test {
    public static void main(String[] args) {
        Student s = null;
        s.study();
    }
}

这是因为你尝试去使用一个没有创建的对象(null意味着对象不存在)。

非检查型异常与检查型异常

课本P206例7.1B

import java.io.File;
import java.util.Scanner;

public class Example2 {
    public static void main(String[] args) {
        File file = new File("hi.txt"); // 指定要读取的文件
        Scanner sc = new Scanner(file); // 创建文件扫描器,用于读取文件内容
        
        while (sc.hasNextInt()) { // 如果文件中还有整数
            int n = sc.nextInt(); // 读取下一个整数
            System.out.println("读取数字 " + n); // 打印读取的整数
        }
        
        sc.close(); // 关闭扫描器,释放资源
    }
}

PS. 将hi.txt文件放在项目的根目录中,也就是与src文件夹同级的位置

这段代码的功能是从文件 hi.txt 中读取所有整数并逐一打印。

当直接执行这段代码时,会产生一个编译错误:

java: 未报告的异常错误java.io.FileNotFoundException; 必须对其进行捕获或声明以便抛出

意思是:有一个可能的异常没有处理,该异常叫FileNotFoundException,即文件可能找不到,不能通过编译。

为何产生这个编译错误,这是Java独有的一种特色。

有一些异常是不能通过调整代码避免的,例如hi.txt是否存在,取决于外部环境,而不取决于你的代码。

针对这种情况,为了提高代码的健壮性,Java的设计者引入了检查型(checked)异常的概念,当程序中可能抛出检查型异常时,开发者必须使用try-catch捕获,要么使用throws声明抛出。FileNotFoundException就是一个典型的检查型异常,必须处理!

检查型异常的处理

要想让上面代码顺利编译、正常运行,有两种办法:

  1. try/catch
  2. throws

try/catch语句块:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class Example2 {
    public static void main(String[] args) {
        File file = new File("hi.txt"); // 指定要读取的文件
        Scanner sc = null; // 创建文件扫描器,用于读取文件内容
        try {
            sc = new Scanner(file);
        } catch (FileNotFoundException e) {
            
        }

        while (sc.hasNextInt()) { // 如果文件中还有整数
            int n = sc.nextInt(); // 读取下一个整数
            System.out.println("读取数字 " + n); // 打印读取的整数
        }

        sc.close(); // 关闭扫描器,释放资源
    }
}

通过throws声明这个方法可能抛出异常(如果可能抛出多个检查型异常,则需要列出所有的异常类,用逗号分隔):

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class Example2 {
    public static void main(String[] args) throws FileNotFoundException {
        File file = new File("hi.txt"); // 指定要读取的文件
        Scanner sc = new Scanner(file); // 创建文件扫描器,用于读取文件内容

        while (sc.hasNextInt()) { // 如果文件中还有整数
            int n = sc.nextInt(); // 读取下一个整数
            System.out.println("读取数字 " + n); // 打印读取的整数
        }

        sc.close(); // 关闭扫描器,释放资源
    }
}

需要强调的是,对于非检查型异常,例如算数异常、数组越界异常、空指针异常,也可以这样处理,但不是强制性的,即不这么做也能通过编译。

而且这种做法也不推荐。特别是对于上述的几个异常,应该多花时间修正这些错误,而不只是声明这些错误有可能发生。

Java异常层次结构中的检查型异常和非检查型异常

Java 语言规范将派生于 Error类或 RuntimeException类的所有异常称为非检查型(unchecked)异常,所有其他异常称为检查型(checked)异常。

如下图,红色的是检查型异常,绿色的是非检查型异常。

检查型异常是一个有争议的设计

有如下事实:

  • 在主流的编程语言中,只有 Java 实现了检查型异常(Checked Exception)的机制
  • 其他大多数编程语言(C++、、C#、Python、JavaScript、Go、Kotlin等)都选择不支持检查型异常
  • Go语言中,甚至没有传统意义上的异常处理机制(即try/catch语句块),而是采用了一种更简单、直接的方式——通过函数的返回值来传递错误信息。

以下是一些反对检查型异常的观点:

  • 代码冗长和复杂: 检查型异常要求在方法签名中声明可能抛出的异常,或者在代码中捕获并处理。这可能导致代码变得冗长,降低可读性。
  • 破坏封装性: 当方法签名中包含检查型异常时,方法的实现细节暴露给了调用者,违反了封装原则。
  • 没有显著提高代码质量: 实践表明,受检异常并没有显著提高代码的质量,反而带来了一些问题。因此,许多语言选择不支持受检异常。
  • 不信任开发者:只有Java会强制开发者进行try/catchthrows
  • 存在更优雅的处理方式: 引入了如 OptionResult等数据结构,或者使用函数式编程的方式处理异常,更加优雅。

小结

在Java中,有些异常不处理是可以的,有些异常必须处理。

可以不处理的异常即非检查型异常,必须处理的异常即检查型异常。

检查型异常有两种处理办法:一,try/catch。二,通过throws声明这个方法可能抛出异常。

捕获异常

try/catch解析

如果发生了某个异常,但没有在任何地方捕获这个异常,程序就会终止。

上文已经使用了最简单的try语句块:

        try {
            // 
        } catch (异常类型 e) {
            // 针对该类型异常的处理代码
        }

如果try中任何代码抛出了catch中指定的一个异常类,那么程序会

  1. 跳过try中的其余代码
  2. 执行对应catch中的代码

如果没抛出任何异常则跳过catch子句,如果抛出了但不是catch指定的异常,那么这个方法会立即退出。

实际上,在一个try语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理:

public class MultiCatchExample {
    public static void main(String[] args) {
        try {
            int result = 10 / 0; // ArithmeticException
            String str = null;
            System.out.println(str.length()); // NullPointerException
        } catch (ArithmeticException e) {
            System.out.println("数学错误:不能除以零!");
        } catch (NullPointerException e) {
            System.out.println("空指针错误:对象未初始化!");
        } catch (Exception e) {
            System.out.println("其他错误:" + e.getMessage());
        }
    }
}

需要注意的是catch语句块是从上往下执行的,一旦匹配到一个就不会再往下执行。这意味着必须先捕获子类异常再捕获父类异常。(课本P209)

finally子句

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class Example2 {
    public static void main(String[] args) {
        File file = new File("hi.txt"); // 指定要读取的文件
        Scanner sc = null; // 创建文件扫描器,用于读取文件内容
        try {
            sc = new Scanner(file);

            while (sc.hasNextInt()) { // 如果文件中还有整数
                int n = sc.nextInt(); // 读取下一个整数
                System.out.println("读取数字 " + n); // 打印读取的整数
            }
        } catch (FileNotFoundException e) {
            System.out.println("文件没找到");
        } finally {
            sc.close();
            System.out.println("关闭扫描器,释放资源");
        }
    }
}

不管是否捕获到异常,finally子句中的代码都会执行。

分析代码下面代码:

work方法什么情况下返回 0?什么情况返回 1?

如果文件存在finally中的代码是在return前执行还是return后执行?

    private static int work() {
        File file = new File("hi.txt"); // 指定要读取的文件
        Scanner sc = null; // 创建文件扫描器,用于读取文件内容
        try {
            sc = new Scanner(file);

            while (sc.hasNextInt()) { // 如果文件中还有整数
                int n = sc.nextInt(); // 读取下一个整数
                System.out.println("读取数字 " + n); // 打印读取的整数
            }

            return 0;
        } catch (FileNotFoundException e) {
            System.out.println("文件没找到");
        } finally {
            if (sc != null) {
                sc.close();
            }
            System.out.println("关闭扫描器,释放资源");
        }

        return 1;
    }
    
    public static void main(String[] args) {
        System.out.println(work());
    }

自定义异常与抛出异常

自定义异常参考异常层次结构。重点是区分检查型异常和非检查型异常。

可以通过throw关键字抛出异常:

throw new RuntimeException();

练习

教材练习

《Java程序设计教程(洪)》第一版,P217

一:1~8、10

三:2、3、5

其它

在异常处理中,释放资源、关闭文件等应由________语句块处理。

代码

先理解课本例7.1A与7.1B及它们的改进方式

熟悉try/catchfinally(实际开发经常会用)

懂得thowsthrow

自定义异常首先要熟悉异常的层次结构,在Java中要特别区分检查型异常和非检查型异常

标签:读取,System,catch,sc,异常,演练,out
From: https://www.cnblogs.com/xkxf/p/18574039

相关文章

  • 异常--C++
    文章目录一、异常的概念及使用1、异常的概念2、异常的抛出和捕获3、栈展开4、查找匹配的处理代码5、异常重新抛出6、异常安全问题7、异常规范二、标准库的异常一、异常的概念及使用1、异常的概念异常处理机制允许程序中独立开发的部分能够在运行时就出现的问题进行......
  • 代码中如果遇到未定义的变量,会抛出异常吗?程序还会不会继续往下走?
    在前端开发中,如果遇到未定义的变量,JavaScript会抛出ReferenceError异常。程序在遇到这个异常后,默认情况下会停止在当前代码块的执行,并且不会继续往下执行。更具体地说:未声明变量的引用:如果你尝试使用一个从未使用var、let或const声明过的变量,JavaScript引擎会抛出Re......
  • 异常处理try...except的应用
    '''try...except语法应用出现问题一般两种提示:1.Error(错误):一般是语法导致的问题,运算逻辑出现问题,都会在控制台以Error形态展示。可以通过Error在控制台的相关信息快速去排查定位缺陷的所在点2.Exception(异常):一般是程序运行时,由于环境导致的问题。由于数据传递出......
  • 14. 异常处理
    一、什么是异常  程序在运行过程之中,不可避免的出现一些错误,比如:使用了没有赋值的变量、使用了不存在的索引、除0等等。这些错误在程序中,我们称之为异常。程序运行过程中,一旦出现异常将会导致程序立即终止,异常以后的代码全部都不会执行。二、异常的传播  当在函数......
  • MySQL数据库写入异常,主库内存溢出,扩容+清理buff/cach!很开门
    MySQL数据库写入异常,主库内存溢出,扩容+清理buff/cach!很开门最近数据出现了两次写入异常报错如下:org.springframework,jdbc.UncategorizedsQlException:PreparedstatementCallback;uncategzed50LExcention[sql语句]TheMysOlserverisrunningwiththe--read-onlyoption......
  • List接口介绍和题目演练
    List接口介绍、定义及特点在Java中, List 接口是 java.util 包中的一部分,它继承自 Collection 接口。一、定义和特点定义和特点1. 有序集合- List 中的元素是有序的,这意味着可以通过索引(位置)来访问元素,索引从0开始。例如,在一个 List 中添加元素的顺序是 a 、 b......
  • 3. 手机话费异常(cqupt)
    3.手机话费异常【问题描述】定义了一个接口PayAble,包含计算电话话费的方法pay()。在手机类定义中增加计算话费异常,如果话费小于0则抛出异常。要点提示:1)  自定义一个异常类,表示话费小于0的异常;2) 计算话费时如果小于0则抛出异常,在测试类中处理异常。【输入形式】无【......
  • 水仓水位异常识别智慧矿山一体机提升机运行状态识别:4G网络摄像机小知识
    在现代科技的快速发展下,4G网络摄像机已经成为我们生活中不可或缺的一部分,它们不仅提高了我们的安全保障,还为远程监控和管理提供了极大的便利。本文将详细介绍4G网络摄像机的安装、运行以及它们在水仓水位异常识别智慧矿山一体机中的应用,以及这些设备如何通过技术创新提升矿山安全......
  • 新能源智慧充电桩建设:如何利用视频与AI技术进行充电异常/火灾远程告警?
    随着智慧城市建设的不断深入,智慧充电桩管理方案已经成为新能源汽车充电基础设施中不可或缺的一部分。这种方案通过集成先进的信息技术,如物联网(IoT)、云计算、大数据分析和移动通信技术,实现了对充电桩的远程监控、故障诊断、性能优化和用户交互等功能。特别是在远程告警与消息推送......
  • C# WinForm 添加全局统一的异常捕获并追踪到异常发生位置
    1.应用程序级别的异常处理​使用AppDomain.CurrentDomain.UnhandledException事件,使用StackTrace获取到错误的堆栈跟踪信息{///<summary>///应用程序的主入口点。///</summary>[STAThread]staticvoidMain()......