我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java 基础语法 异常处理

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java 基础语法 异常处理

前些章节的知识点有时会涉及到异常的知识,如果没有专门学习过异常的小伙伴可能看的有点疑惑。今天这节就是为了讲解异常,让我们来了解什么是异常,它的作用是啥,怎么使用异常。

1. 异常的背景

1.1 邂逅异常

大家在学习 Java 时,应该也遇见过一些异常了,例如

算术异常:


System.out.println(10 / 0);

结果为:Exception in thread "main" java.lang.ArithmeticException: / by zero

数组越界异常:


int[] arr = {1, 2, 3, 4, 5};
System.out.println(arr[100]);

结果为Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100

空指针异常:


int[] arr = null;
System.out.println(arr.length);

结果为:Exception in thread "main" java.lang.NullPointerException

那么什么是异常呢?

异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。

1.2 异常和错误

  • 异常被分为下面两种

运行时异常(非受查异常):

在程序运行(通过编译已经得到了字节码文件,再由 JVM 执行)的过程当中发生的异常,是可能被大家避免的异常,这些异常在编译时可以被忽略。

例如:算数异常、空指针异常、数组越界异常等等

编译时异常(受查异常):

编译时发生的异常,这个异常是大家难以预见的,这些异常在编译时不能被简单的忽略。

例如:要打开一个不存在的文件时,一个异常就发生了

除了异常我们我们也要了解下错误

错误:

错误不是异常,而是脱离程序员控制的问题,错误在代码中通常被忽略。

例如:当栈溢出时,一个错误就发生了,这是编译检查不到的


public static void func(){
    func();
}
public static void main(String[] args){
    func();
}

结果为:Exception in thread "main" java.lang.StackOverflowError

那么异常和错误的区别是什么呢?

出现错误必须由我们程序员去处理它的逻辑错误,而出现异常我们只要去处理异常就好了

如果有疑惑的伙伴通过后面的介绍你会逐渐了解它们的区别

1.3 Java 异常的体系(含体系图)

Java 中异常的种类是很多的,我将一些异常收集并归类如下

在这里插入图片描述

其中 Error 是错误,Exception 是异常。而异常中又分为了两种,黄色的是编译时异常,橙色的是运行时异常。

但是这张图不仅仅说明了上述的关系,我们还要知道

每个异常其实都是一个类,并且箭头代表了继承的关系

我们可以通过一个代码来理解


int[] arr = null;
System.out.println(arr.length);
// 结果为:Exception in thread "main" java.lang.NullPointerException

此时我们点击这个异常 NullPointerException 就转到了它的定义,我们会看到

在这里插入图片描述

我们可以得到以下结论:

  • NullPointerException 是一个类
  • 这个类继承了 RuntimeException 这个类

为了刨根究底,我们继续转到 RuntimeException 这个类看看

在这里插入图片描述

我们又得到了以下结论:

  • RuntimeException 是一个类
  • 这个类继承了 Exception 这个类

继续刨根究底,我们又可以看到

诶,此时我们再对照着体系图我们就可以理解清除这张图的所有意思,并且此时对异常又有了个全面对认识

而今天我们的主角是异常,即 Exception,接下来我将会对它进行解析。

1.4 异常的核心思想

作为一个程序员,我们经常都面对着

在这里插入图片描述

错误在代码中的存在我们不言而喻,因此就产生了两种主要针对错误的方式

方式一(LBYL):在操作之前就做充分的检查
方式二(EAFP):直接操作,有错误再解决

而异常的核心思想就是 EAFP

1.5 异常的好处

那么核心思想为 EAFP 的异常有什么好处呢?

我们可以随便举一个例子,比如你打一把王者,我们要进行登录、匹配、确认游戏、选择英雄等等的操作。

如果使用 LBYL 风格的代码,我们就要对每一步都做好充分的检查之后,再进行下一步,简单写个代码如下


boolean ret = false;
ret = log();
if(!=ret){
 // 处理登录游戏错误
 return;
}
ret = matching();
if(!=ret){
 // 处理匹配游戏错误
 return;
}
ret = confirm();
if(!=ret){
 // 处理确认游戏错误
 return;
}
ret = choose();
if(!=ret){
 // 处理选择游戏错误
 return;
}

而使用 EAFP 的风格,代码则是这样的


try{
    log();
    matching();
    confirm();
    choose();
}catch(登录游戏异常){
 // 处理登录游戏错误
}catch(匹配游戏异常){
 // 处理匹配游戏错误
}catch(确认游戏异常){
 // 处理确认游戏错误
}catch(选择游戏异常){
 // 处理选择游戏错误
}

两种方式的代码一对比,大家也可以看得出哪一种更好。EAFP 风格的就可以将流程和处理异常的代码分开,看起来更加舒服。而这也就是使用异常的好处之一。上述代码运用了异常的基本用法,后续会介绍。

2. 异常的基本用法

2.1 捕获异常

2.1.1 基本语法


try{
    // 有可能出现异常的语句
}[catch(异常类型 异常对象){
    // 出现异常后的处理行为
}...]
[finally{
    // 异常的出口
}]

  • try 代码块中放的是可能出现异常的代码
  • catch 代码块中放的是出现异常后的处理行为
  • finally 代码块中的代码用于处理善后工作,会在最后执行
  • 其中 catch finally 都可以根据情况选择加或者不加

2.1.2 示例一

首先我们看一个不处理异常的代码


int[] arr = {1, 2, 3};
System.out.println("before");
System.out.println(arr[100]);
System.out.println("after");

结果是:

在这里插入图片描述

我们分析一下这个结果,首先它告诉我们在 main 方法中出现了数组越界的异常,原因就是100这个数字。下面它又告诉我们了这个异常的具体位置。

并且通过这个结果我们知道,当代码出现异常之后,程序就中止了,异常代码后面的代码就不会执行了。

那么为什么这里抛出异常之后,后面的代码就不再执行了呢?

因为当没有处理异常的时候,一旦程序发生异常,这个异常就会交给 JVM 来处理。
而一旦交给了 JVM 处理异常,程序就会立即终止执行!

这也就是为什么我们会有自己处理异常这个行为

我们如果加上 try catch 自己处理异常


int[] arr = {1, 2, 3};
try {
    System.out.println("before");
    System.out.println(arr[100]);
    System.out.println("after");
}catch(ArrayIndexOutOfBoundsException e){
    System.out.println("数组越界!");
}
System.out.println("after try catch");

结果是:

在这里插入图片描述

我们发现 try 中出现了异常的语句,并且我们针对这个异常做出了处理的行为。而 try catch 后面的程序依然可以继续执行

我们在上述代码中处理异常时 catch 里面用的语句就是直接告诉它出现了什么问题,但是如果我们想要知道这是什么异常,在代码的第几行有问题的话,就可以再加一个调用栈。

什么是调用栈呢?

方法之间存在相互调用关系,这种调用关系可以用“调用栈”来描述。在 JVM 中有一块内存空间称为“虚拟机栈”,这是专门存储方法之间调用关系的。当代码中出现异常的时候,我们就可使用 e.printStackTrace(); 来查看出现异常代码的调用栈

2.1.3 示例二(含使用调用栈)


int[] arr = {1, 2, 3};
try {
    System.out.println("before");
    System.out.println(arr[100]);
    System.out.println("after");
}catch(ArrayIndexOutOfBoundsException e){
    System.out.println("数组越界!");
    e.printStackTrace();
}
System.out.println("after try catch");

结果是:

在这里插入图片描述

2.1.4 示例三(可以使用多个 catch 捕获不同的异常)


int[] arr = {1, 2, 3};
try {
    System.out.println("before");
    System.out.println(arr[100]);
    System.out.println("after");
}catch(ArrayIndexOutOfBoundsException e){
    System.out.println("数组越界!");
    e.printStackTrace();
}catch(NullPointerException e){
    System.out.println("空指针异常");
    e.printStackTrace();
}
System.out.println("after try catch");

这个代码里面有多个 catch,他会捕获到第一个出现异常的位置

2.1.5 示例四(可以使用一个 catch 捕获所有异常,不推荐)


int[] arr = {1, 2, 3};
try {
    System.out.println("before");
    System.out.println(arr[100]);
    System.out.println("after");
}catch(Exception e){
    e.printStackTrace();
}
System.out.println("after try catch");

其中我们使用了 Exception 这个类,我们知道它是所有异常的父类,因此可以用来捕获所有异常。但是这个方法是不推荐的,因为异常太多了,我们不容易定位问题

并且我们能得到一个结论

catch 进行类型匹配的时候,不光会匹配相同类型的异常,也能捕获目标异常类型的子类对象

2.1.6 示例五(使用 finally,它之间的代码将在 try 语句后执行)


int[] arr = {1, 2, 3};
try {
    arr = null;
    System.out.println(arr.length);
}catch(NullPointerException e){
    e.printStackTrace();
}finally{
    System.out.println("finally 执行啦!");
}
System.out.println("after try catch");

结果为:

在这里插入图片描述

我们紧接着再看一个代码,我将异常给改正确


int[] arr = {1, 2, 3};
try {
    System.out.println(arr.length);
}catch(NullPointerException e){
    e.printStackTrace();
}finally{
    System.out.println("finally 执行啦!");
}
System.out.println("after try catch");

上述代码就没有错误了,但是结果是

在这里插入图片描述

我们就得出了这个结论

无论 catch 是否捕获到异常,都要执行 finally 语句

finally 是用来处理善后工作的,例如释放资源是可以被做到的。如果大家对于使用 finally 释放资源有疑惑,可以先看示例八,因为在 finally 中加入 Scanner close 方法就是释放资源的一种例子

2.1.7 示例六(finally 引申的思考题)


public static int func(){
    try{
        return 10;
    }catch(NullPointerException e){
        e.printStackTrace();
    }finally{
        return 1;
    }
}
public static void main(String[] args) {
    int num = func();
    System.out.println(num);
}

结果为:1

因为 finally 块永远是最后执行的。并且你也无法在这个代码之后执行其他语句,因为不管有没有捕获到异常都要执行 finally 中的 return 语句去终止代码

2.1.8 示例七(使用 try 负责回收资源)

在演示代码前要先补充一个关于 Scanner 的知识

我们知道使用 Scanner 类可以帮助我们进行控制台输入语句,但是 Scanner 还是一种资源,而资源使用完之后是需要回收的,就像是我们打开了一瓶水喝了点还要盖上它。故用完后我们可以加上 close 方法来进行回收,如


Scanner reader = new Scanner(System.in);
int a = reader.nextInt();
reader.close();

而 try 有一种写法可以在它执行完毕后自动调用 Scanner close 方法


try(Scanner sc = new Scanner(System.in){
 int num = sc.nextInt();
}catch(InputMismatchException e){
 e.printStackTrace();
}

而这种方式的代码风格要比使用 finally 中含有 close 方法要好些

2.1.9 示例八(本方法中没有合适的处理异常方式,就会沿着调用栈向上传递)


public static void func(){
    int[] arr = {1, 2, 3};
    System.out.println(arr[100]);
}
public static void main(String[] args){
    try{
        func();
    }catch(ArrayIndexOutOfBoundsException e){
        e.printStackTrace();
    }
}

结果为:

在这里插入图片描述

由于我们写 func 方法时出现了异常没有及时处理,但我们在 main 方法中调用它了,所以就经过方法之间互相的调用关系,我们一直到了 main 方法被调用的位置,并且此时有合适的处理异常的方法

若最终没有找到合适的异常处理方法,最终该异常就会交给 JVM 处理,即程序就会终止

2.1.10 异常处理流程总结

  • 程序先执行 try 中的代码
  • 如果 try 中的代码出现异常,就会结束 try 中异常之后的代码,并查看该异常和 catch 中的异常类型是否匹配
  • 如果匹配,就会执行 catch 中的代码
  • 如果没有匹配的,就会将异常向上传递到上层调用者
  • 无论是否找到匹配类型,finally 中的代码都会被执行
  • 如果上层调用者没有处理异常的方法,就会继续向上传递
  • 一直到 main 方法也没有合适的代码处理异常,就会交给 JVM 来处理,此时程序就会终止

2.2 抛出异常

以上我们介绍的都是 Java 内置的类抛出的一些异常,除此之外我们也可以使用关键字 throw 手动抛出一个异常,如


public static int divide(int x, int y) { 
    if (y == 0) { 
        throw new ArithmeticException("抛出除 0 异常"); 
    }
}
public static void main(String[] args) { 
 System.out.println(divide(10, 0)); 
} 

该代码就是我们手动抛出的异常,并且手动抛出的异常还可以使用自定义的异常,后面将会介绍到

2.3 异常说明

我们在处理异常时,如果有一个方法,里面很长一大段,我们其实是希望很简单的就知道这段代码有可能会出现哪些异常。故我们可以使用关键字 throws,把可能抛出的异常显示的标注在方法定义的位置,从而提醒使用者要注意捕获这些异常,如


public static int divide(int x, int y) throws ArithmeticException{ 
    if (y == 0) { 
        throw new ArithmeticException("抛出除 0 异常"); 
    }
}

注意:

如果我们将 main 方法抛出一个异常说明,而 main 方法的调用者是 JVM,所以如果在 main 函数上抛出异常的话,就相当于 JVM 来处理这个异常了

3. 自定义异常类

Java 中虽然有丰富的异常类,但是实际上肯定还要一些情况需要我们对这些异常进行扩展,创建新的符合情景的异常。

那怎么创建自定义异常呢?首先我们就可以去看看原有的那些异常是怎么做的

在这里插入图片描述

在这里插入图片描述

两异常

我们发现这两个异常都是继承在 RuntimeException 这个类的,并且都构造了两个构造方法,分别是不带参数和带参数

而我模拟了一个登录账号的代码


public class TestDemo { 
    private static String userName = "root"; 
    private static String password = "123456"; 
    public static void main(String[] args) { 
        login("admin", "123456"); 
    } 
    public static void login(String userName, String password) { 
        if (!TestDemo.userName.equals(userName)) { 
            // 处理用户名错误
        } 
        if (!TestDemo.password.equals(password)) { 
            // 处理密码错误
        } 
        System.out.println("登陆成功"); 
    } 
}

通过这个模拟的场景,我们可以针对运行时账号和密码是否正确写一个异常


class UserException extends RuntimeException{
    public UserException(){
        super();
    }
    public UserException(String s){
        super(s);
    }
}
class PasswordException extends RuntimeException{
    public PasswordException(){
        super();
    }
    public PasswordException(String s){
        super(s);
    }
}

紧接着我们再手动抛出异常


public class TestDemo { 
    private static String userName = "root"; 
    private static String password = "123456"; 
    public static void main(String[] args) { 
        login("admin", "123456"); 
    } 
    public static void login(String userName, String password) { 
        if (!TestDemo.userName.equals(userName)) { 
            throws new UserException("用户名错误");
        } 
        if (!TestDemo.password.equals(password)) { 
            throws new PasswordException("密码错误");
        } 
        System.out.println("登陆成功"); 
    } 
}

所以我们创建新的异常时,就是先思考这是哪种类型的异常,再照猫画虎。但是可能有疑惑,如果我们新建异常时统一继承 Exception 不就行吗?

No!由于 Exception 分为编译时异常和运行时异常,使用 Exception 的话默认是编译时异常(即受查异常),而一段代码可能抛出受查异常则必须显示进行处理。

故如果我们将上述新建的异常继承 Exception 的话,就要再对代码中的异常进行处理,否则会直接报错

到此这篇关于Java 基础语法 异常处理的文章就介绍到这了,更多相关Java 异常处理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java 基础语法 异常处理

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java基础之异常处理详解

异常可能是在程序执行过程中产生的,也可能是程序中throw主动抛出的。本文主要给大家介绍了Java中异常处理的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
2023-05-17

Python基础:异常处理

如何处理异常  无论发生什么情况,finally block 中的语句都会被执行,哪怕前面的 try 和 excep block 中使用了 return 语句。import systry: f = open('file.txt', '
2023-01-31

【C#基础】C# 异常处理操作

序号系列文章6【C#基础】C# 常用语句讲解7【C#基础】C# 常用数据结构8【C#基础】C# 面向对象编程 文章目录 前言1,异常的概念2,处理异常3,自定义异常4,编译器异常结语 前言 🌷大家好,我是wr
2023-08-18

python基础学习15----异常处理

异常处理,是编程语言或计算机硬件里的一种机制,用于处理软件或信息系统中出现的异常状况(即超出程序正常执行流程的某些特殊条件)。1.异常的类型异常的类型多种多样,常见的异常有:AttributeError 试图访问一个对象没有的属性,比如fo
2023-01-30

Python基础教程之异常处理详解

Python的异常处理能力是很强大的,它有很多内置异常,可向用户准确反馈出错信息,下面这篇文章主要给大家介绍了关于Python基础教程之异常处理的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
2023-05-14

java基础中异常及包归纳整理

1 异常异常的体系 ThrowableError通常出现重大问题如:运行的类不存在或者内存溢出等。不编写针对代码对其处理Exception在运行时运行出现的一起情况,可以通过try catch finallyException和Error的
2023-05-31

Python中的异常处理相关语句基础学习笔记

异常是指因为程序出现了错误而在正常控制流以外采取的行动,其分为两个阶段,第一阶段是引发异常的错误,当系统检测到错误并且意识到异常条件,解释器(也可以是程序员引发异常)会引发一个异常通知前段控制流有错误发生,对异常的处理发生在第二阶段,异常引
2022-06-04

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录