首页 > 编程语言 >【Java】异常处理见解,了解,进阶到熟练掌握

【Java】异常处理见解,了解,进阶到熟练掌握

时间:2024-11-01 23:48:01浏览次数:5  
标签:Java 进阶 熟练掌握 System try println catch 异常 out

各位看官早安午安晚安呀

如果您觉得这篇文章对您有帮助的话

欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦

大家好我们今天来学习Java面向对象的的抽象类和接口,我们大家庭已经来啦~

目录

1.(throws和throw)我们不管这个异常,把这个异常抛出去

1.1:throws的注意事项

1.2:throw的注意事项

2:(try-catch)我们主动地去捕捉这个异常

2.1. try块内抛出异常位置之后的代码将不会被执行

2.2. 如果抛出异常类型与catch时异常类型不匹配

2.3. try中可能会抛出多个不同的异常对象

2.4:我们也可以有以下两种不推荐的捕获方式

3:关于 "调用栈"(e.printStackTrace())

关于 "调用栈"

4:finally

4:自定义异常类


Java 中,将程序执行过程中发生的不正常行为称为异常 。 要认识异常首先我们要认识一张图
从上图中可以看到: 1. Throwable 是异常体系的顶层类,其派生出两个重要的子类 , Error Exception 2. Error 指的是 Java 虚拟机无法解决的严重问题,比如: JVM 的内部错误、资源耗尽等 ,典型代表: StackOverflowError OutOfMemoryError ,一旦发生回力乏术。 3. Exception 异常产生后程序员可以通过代码进行处理,使程序继续执行。比如:感冒、发烧。我们平时所说的异常就是Exception 。
异常大概处理方案:

关于异常的一般处理方式:

异常的种类有很多, 我们要根据不同的业务场景来决定. 对于比较严重的问题(例如和算钱相关的场景), 应该让程序直接崩溃, 防止造成更严重的后果 对于不太严重的问题(大多数场景), 可以记录错误日志, 并通过监控报警程序及时通知程序猿 对于可能会恢复的问题(和网络相关的场景), 可以尝试进行重试 在我们当前的代码中采取的是经过简化的第二种方式. 我们记录的错误日志是出现异常的方法调用信息, 能很快速的让我们找到出现异常的位置. 以后在实际工作中我们会采取更完备的方式来记录异常信息
比如我们之前写代码时经常遇到的: 1: 算数异常
   System.out.println(10/0);
2.   数组越界异常
int [] arr = { 1 , 2 , 3 }; System . out . println ( arr [ 100 ]);
3:空指针异常
        int [] array = null;
        System.out.println(array.length);

这些异常,我们写代码的时候不会显示异常,但是程序运行时就会报错;

(这种异常我们称之为运行时异常也叫非受查异常)

既然有非受查异常,就自然会有受查异常

譬如:我们之前一直没明白的克隆方法,我们子类要用这个克隆方法,克隆之后肯定要用一个新的对象来接收,那就是Person person2 = person1.clone();但是clone()是Object类的方法,我们只能在类里通过super调用,所以子类要用就必须要重写这个方法。

但是重写之后还是会报异常CloneNotSupportedException

我们只要调用这个方法就会报异常,但是我们还是要用到这个方法

所以说现在摆在我们面前的有两条路:

1.我们不管这个异常,把这个异常抛出去

2:我们主动地去捕捉这个异常

1.(throws和throw)我们不管这个异常,把这个异常抛出去

抛出去这个异常就要引出一个关键字throws了

然后还有一个关键字throw和它很像(这个关键字也是抛出一个异常)

他俩的区别在于:

throws是抛出已经出现的异常(一般放在方法参数列表之后)(也算是处理了,但不算是真正的处理,只是异常报告给抛出异常方法的调用者,由调用者处理

而throw是我们主动去抛出一个异常,一般是程序没出现我们为了提醒别人而抛出的异常

(但是这个异常迟早是要被捕捉的,不然异常一直存在,最后大家都处理,只能交给JVM去处理,最后程序就只能报出来这个异常)

我们throws出去这个异常

大家可以看到这个时候就不报异常了,但是我们现在调用它试试呢?

现在它又报错了,为啥呢?就像我上面所说的你只是抛出了这个异常(暂时解决了这个问题)实际上是把问题抛给了这个方法的调用者

然后我们还不想解决,那就,让main函数也把这个责任甩出去

按理说程序运行时,JVM会处理这个异常

但是我们实现了一个克隆接口(允许)该类的对象被拷贝

Cloneable接口是一个标记接口(即它没有任何方法定义)。当一个类实现了Cloneable接口,这表示这个类的对象支持克隆操作,而且Person类实现了Cloneable接口并且正确地重写了clone()方法,所以说最终没有抛出这个异常

1.1:throws的注意事项

处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助 throws 将异常抛给方法的调用者来处理。即当前方法不处理异常,提醒方法的调用者处理异常 。 注意:
1. throws 必须跟在方法的参数列表之后 2. 声明的异常必须是 Exception 或者 Exception 的子类 3. 方法内部如果抛出了多个异常, throws 之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可。
 
publicstaticvoidmain(String[]args)throwsArrayIndexOutOfBoundsException,ArithmeticException{}
可以替换成
public static void main(String[] args) throws Exception{
}

4:调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用 throws 抛出 (我们讲述的clone方法和main方法) 5: 将光标放在抛出异常方法上,ALT点击+enter快速处理

1.2:throw的注意事项

1.throw 必须写在方法体内部 2. 抛出的对象必须是 Exception 或者 Exception 的子类对象 3. 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给 JVM 来处理 4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译 throws继续抛出或者try-catch处理,最后try-catch不处理的话JVM处理,程序异常终止 5. 异常一旦抛出,其后的代码就不会执行

2:(try-catch)我们主动地去捕捉这个异常

就像如果我们捕捉了上面的克隆异常(但是他是一个异常,还是不会被执行,所以我们最好还是实现一个克隆接口,最终别抛出异常)

如果我们要捕捉异常

这时候就要引出一个组合了 try-catch捕获并处理(细节我们一会会具体将到)

try { // 将可能出现异常的代码放在这里 } catch ( 要捕获的异常类型 e ){ // 如果 try 中的代码抛出异常了,此处 catch 捕获时异常类型与 try 中抛出的异常类型一致时 ,或者是try中抛出异常的基类时,就会被捕获到 // 对异常就可以正常处理,处理完成后,跳出 try-catch 结构,继续执行后序代码 } finally { // 此处的语句无论是否发生异常,都会被执行到 } // 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行

示例:

2.1. try块内抛出异常位置之后的代码将不会被执行

 public static void main(String[] args){
       try{
           System.out.println(10/0);
           System.out.println("想你");
       }catch(ArithmeticException e){
           System.out.println("捕捉到了算数异常");
       }
    }

这里我们捕捉到了算数异常,但是“想你”并没有被打印(这是因为这里程序运行到这里已经发现了异常,就会去捕捉(因为我们用try-catch组合了)所以后面的代码就不会被执行了)

就像程序这里(哈哈打印了,想你没有打印)

2.2. 如果抛出异常类型与catch时异常类型不匹配

即异常不会被成功捕获,也就不会被处理,继续往外抛,直到 JVM收到后中断程序----异常是按照类型来捕获的

public static void main(String[] args){
       try{
           System.out.println(10/0);
           System.out.println("想你");
       }catch(NullPointerException e){
           System.out.println("捕捉到了算数异常");
       }
        System.out.println("想你");
    }

一个想你都没有打印

2.3. try中可能会抛出多个不同的异常对象

则必须用多个catch来捕获----即多种异常,多次捕获 ,并且捕获到了一个异常,就不会进入另一个catch了

  public static void main(String[] args){
       try{
           System.out.println(10/0);
           int[] arr = {1, 2, 3};
           System.out.println(arr[100]);
       }catch(NullPointerException e){
           System.out.println("捕获到了空指针异常");
       }
       catch(ArithmeticException e){
           System.out.println("捕捉到了算数异常");
       }
        System.out.println("想你");
    }

这时候就有小伙伴问了,那另一个异常去哪了?

其实我们上面就已经讲述过了,一个异常出现,我们用try-catch捕获处理(这个异常下面的代码就不会执行了,直接执行捕获后的catch里面的代码,以及处理完程序后面的代码)

2.4:我们也可以有以下两种不推荐的捕获方式

1.

catch ( ArrayIndexOutOfBoundsException | NullPointerException e ) { ... }
不过我们不推荐这种方式:太模糊了,到底是哪种异常 2.一次性捕获所有异常:
catch( Exception e ){    这是异常的父类 }
注意: 如果异常之间具有父子关系,一定是子类异常在前 catch ,父类异常在后 catch ,否则语法错误:(否则,相当于,你在前面就把水全部堵住了,异常根本流过来) 子类异常在前 catch ,父类异常在后 catch(还是有点作用的,防止程序异常终止嘛)

3:关于 "调用栈"(e.printStackTrace()

关于 "调用栈"

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

譬如上述代码:

 public static void main(String[] args){
       try{
           System.out.println(10/0);
           int[] arr = {1, 2, 3};
           System.out.println(arr[100]);
       }catch(NullPointerException e){
           System.out.println("捕获到了空指针异常");
       }
       catch(ArithmeticException e){
           System.out.println("捕捉到了算数异常");
           e.printStackTrace();
       }
        System.out.println("想你");
    }

4:finally

在写程序时, 有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源 :网络连接、数据库 连接,IO 流等, 在程序正常或者异常退出时,必须要对资源进进行回收 。 另外,因为 异常会引发程序的跳转,可能 导致有些语句执行不到 , finally 就是用来解决这个问题的。
 public static void main(String[] args){
       try {
           System.out.println(10 / 0);
           int[] arr = {1, 2, 3};
           System.out.println(arr[100]);
       }
       catch (ArrayIndexOutOfBoundsException e){
           System.out.println("捕获到了数组越界异常");
       }finally {
           System.out.println("不管是否捕获到了异常,finally后面的都会被执行");
       }
        System.out.println("想你");
    }

就是想你没有被执行(finally就是用来处理善后工作的)

譬如关闭输入流

大家可以想想如果没有finally,这个程序刚才没有捕捉到异常,就异常终止了,但是输入流没有没关闭(这时候就造成了资源的泄露)

注意:一般我们不建议在 finally 中写 return (被编译器当做一个警告).

public static void main(String[] args) {
System.out.println(func());
}
public static int func() {
try {
return 10;
} finally {
return 20;
}
}
A: 10 B: 20 C: 30 D: 编译失败

选择B

finally 执行的时机是在方法返回之前 (try 或者 catch 中如果有 return 会在这个 return 之前执行 finally). 但是如果 finally 中也存在 return 语句 , 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return. 一般我们不建议在 finally 中写 return ( 被编译器当做一个警告 ) 所以说我们这两个问题大家清楚了吗?
1. throw 和 throws 的区别? 2. finally 中的语句一定会执行吗?
异常处理流程总结 】 程序先执行 try 中的代码 如果 try 中的代码出现异常 , 就会结束 try 中的代码 , 看和 catch 中的异常类型是否匹配 . 如果找到匹配的异常类型 , 就会执行 catch 中的代码 如果没有找到匹配的异常类型 , 就会将异常向上传递到上层调用者 . 无论是否找到匹配的异常类型 , finally 中的代码都会被执行到 ( 在该方法结束之前执行 ). 如果上层调用者也没有处理的了异常 , 就继续向上传递 . 一直到 main 方法也没有合适的代码处理异常 , 就会交给 JVM 来进行处理 , 此时程序就会异常终止 .

4:自定义异常类

接下来我们实现一个登录系统

public class LogIn {
    private String userName = "admin";
    private String password = "123456";
    public void loginInfo(String userName, String password) {

        if (!this.userName.equals(userName)) {
            System.out.println("名字错误");
            return;
        }
        if (!this.password.equals(password)) {
            System.out.println("密码错误");
            return;
        }
        System.out.println("登陆成功");
    }
    public static void main(String[] args) {
        LogIn logIn = new LogIn();
        logIn.loginInfo("admin", "1234561");
    }
}

但是总是sout输出太俗了,不能有效的提醒,这个时候我们就可以自定义一个异常类

但是我们应该如何定义一个异常类呢?(并且运行时能给我们检测出来)我们不妨看看其他异常类是怎么定义的?譬如:算数异常

那么我们也这样写两个异常

然后我们的代码就可以改成这个样子

public class LogIn {
    private String userName = "admin";
    private String password = "123456";
    public void loginInfo(String userName, String password) throws UserNameException,UserPasswordException {

        if (!this.userName.equals(userName)) {
           throw new UserNameException("用户名错误");
        }
        if (!this.password.equals(password)) {
           throw new UserPasswordException("登录密码错误");
        }
        System.out.println("登陆成功");
    }
    public static void main(String[] args) {
        LogIn logIn = new LogIn();
        try{
            logIn.loginInfo("admin", "1234561");
        }catch(UserNameException e){
            System.out.println("");
        }
    }
}

登录密码错误

其实定义方式就是这个

1. 自定义异常类,然后继承自 Exception 或者 RunTimeException 2. 实现一个带有 String 类型参数的构造方法,参数含义:出现异常的原因

注意继承Exception时是编译异常 ,继承RunTimeException是受查时异常

所以说,继承RunTimeException时就不需要抛出异常了(写代码时也不会报异常),如果我们不处理,运行时JVM会帮我们处理(最后返回发生异常的位置)

大家可以看一下如果我继承Exception时不抛出异常,这个时候就会报错,因为继承Exception是编译时异常(受查异常)

总之,大家根据自己的需求去选择是用编译时异常还是运行时异常就好了

上述就是 Java异常处理了解,熟悉到进阶的全部内容了,能看到这里相信您一定对小编的文章有了一定的认可,异常的出现,让我们对于程序有了更多的认识~~~

有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正

您的支持就是我最大的动力​​​!!!!

标签:Java,进阶,熟练掌握,System,try,println,catch,异常,out
From: https://blog.csdn.net/2302_80639556/article/details/142962576

相关文章

  • Java实用类、工具类
    目录StringTokenizer和ScannerStringTokenizerScannerStringBuffer和StringBuilderStringBuilderStringBuffer时间与日期类Java8之前APIDateTimeZoneLocaleCalendarDateFormatSimpleDateFormat局限性Java8时间API表示日期和时间的类InstantLocalDateTime......
  • Java Web和Java有什么区别
    JavaWeb和Java都是Java编程语言的应用范畴,但它们有着根本的区别。主要包括:1.应用领域不同;2.技术栈和库的差异;3.开发工具和环境的不同;4.运行环境的区别;5.学习曲线的差异;6.与数据库的交互;7.安全性要求。Java主要关注核心编程,而JavaWeb专注于基于Java的网络应用开发。1、应用领......
  • Java 缓冲流详解
    在Java的I/O体系中,缓冲流(BufferedStreams)是对字节流和字符流的一种封装,通过在内存中开辟缓冲区来提高I/O操作的效率。Java提供了BufferedInputStream和BufferedOutputStream来实现字节流的缓冲,以及BufferedReader和BufferedWriter来实现字符流的缓冲。本文......
  • 基于java中的springboot框架实现旅游管理系统项目演示【内附项目源码+论文说明】
    基于java中的springboot框架实现旅游管理系统项目演示【内附项目源码+LW说明】摘要现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本旅游管理系统就是在这样的大环境下诞生,其可以帮助使用者在短时......
  • 基于java中的springboot框架实现经方药食两用服务平台项目演示【内附项目源码+论文说
    基于java中的springboot框架实现经方药食两用服务平台项目演示【内附项目源码+LW说明】摘要近年来,信息化管理行业的不断兴起,使得人们的日常生活越来越离不开计算机和互联网技术。首先,根据收集到的用户需求分析,对设计系统有一个初步的认识与了解,确定经方药食两用服务平台......
  • 基于java中的springboot框架实现旅游管理系统项目演示【内附项目源码+论文说明】
    基于java中的springboot框架实现旅游管理系统项目演示【内附项目源码+LW说明】摘要现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本旅游管理系统就是在这样的大环境下诞生,其可以帮助使用者在短时......
  • 二十六、Python基础语法(函数进阶-下)
    一、多值参数多值参数(可变参数、不定长参数):有的函数不确定参数有几个,在一个普通的参数前面加上一个*,这个参数就变为不定参数。可以接收任意多个位置传参的数据,类型为元组。deftest(name,*args,sex='男'):print(name,args,sex)#传递3个参数test('张三',25,se......
  • 基于Java中的SSM框架实现企业办公自动化系统项目【项目源码+论文说明】计算机毕业设计
    基于java中的SSM框架实现企业办公自动化系统演示【内附项目源码+LW说明】摘要互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对小型企业办公信息管理混乱,出错......
  • 基于Java中的SSM框架实现菜匣子优选系统项目【项目源码+论文说明】计算机毕业设计
    基于java中的SSM框架实现菜匣子优选系统演示【内附项目源码+LW说明】摘要随着科学技术的飞速发展,各行各业都在努力与现代先进技术接轨,通过科技手段提高自身的优势;对于菜匣子优选生鲜电商系统当然也不能排除在外,随着网络技术的不断成熟,带动了菜匣子优选生鲜电商系统,它彻底......
  • 基于Java中的SSM框架实现营业厅宽带系统项目【项目源码+论文说明】
    基于java中的SSM框架实现营业厅宽带系统演示【内附项目源码+LW说明】摘要现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本营业厅宽带系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理......