Java核心-异常处理

news2024/11/16 18:04:21

上一次学完了Java的核心类与API,这次来学习异常处理。我们都知道,Java语言具有健壮性和安全性,而异常处理机制就是其重要保证。如下
在这里插入图片描述

一、类型

错误(Error)和异常(Exception)。这里只讨论 Exception 类型的异常处理。

1、错误(Error)

Error 的异常通常是灾难性的致命错误,不是程序可以控制的。正常情况下不大可能出现,绝大部分的 Error 都会导致程序处于非正常、不可恢复状态。所以不需要被开发者捕获。常见错误类型如下

NoClassDefFoundError (找不到 class 定义异常)
StackOverflowError (深递归导致栈被耗尽而抛出的异常)
OutOfMemoryError (内存溢出异常)

2、异常(Exception)

Exception 是程序正常运行过程中可以预料到的意外情况,并且应该被开发者捕获并进行异常处理。异常又可分为运行时异常非运行时异常。常见异常类型如下
1)运行时异常

NullPropagation (空指针异常)
ClassCastException (类型强制转换异常)
IllegalArgumentException (传递非法参数异常)
IndexOutOfBoundsException (下标越界异常)
NumberFormatException (数字格式异常)等

2)非运行时异常

ClassNotFoundException (找不到指定 class 的异常)
IOException (IO 操作异常)等

二、解决

异常处理关键字:try、catch、throw、throws 和 finally。

try catch 语句用于捕获并处理异常
throw 语句用于拋出异常
throws 语句用于声明可能会出现的异常
finally 语句用于在任何情况下(除特殊情况外)都必须执行的代码。

1、异常的捕获与抛出

1.1 try catch捕获异常

捕获异常:找到合适的 catch 块,并把该异常对象交给该 catch 块处理。
1)几点注意

处理多种异常类型时,必须先捕获子类类型异常,后捕获父类类型异常,否则编译报错(最后捕获 Exception 类型异常,确保异常对象能被捕获到)

不管 try 块中的代码是否出现异常及catch 块是否被执行,甚至在 try 块或 catch 块中执行了 return 语句,finally 块总会被执行(除非在 try 块或会执行的 catch 块中调用退出 JVM 的相关方法)

异常处理语法结构中只有 try 块是必需的,catch 块和 finally 块都是可选的,但 catch 块和 finally 块至少出现其一,也可以同时出现(如try…catch、try…catch…finally、try…finally)

当程序执行 try 块、catch 块时遇到 return 或 throw 语句时,系统不会立即结束该方法,而是去寻找该异常处理流程中是否包含 finally 块,如果有 finally 块,系统立即开始执行 finally 块——只有当 finally 块执行完成后,系统才会再次跳回来执行 try 块、catch 块里的 return 或 throw 语句;如果 finally 块里也使用了 return 或 throw 等导致方法终止的语句,finally 块已经终止了方法,系统将不会跳回去执行 try 块、catch 块里的任何代码(有点绕,注意理解)

2)流程

  • try中发生异常,直接从异常处跳到catch语句进行捕获
  • try中没有异常,try块正常结束后跳过catch,执行catch后的语句(没有则结束)

3)输出异常信息方法

  • printStackTrace():指出异常的类型、性质、栈层次及出现在程序中的位置
  • getMessage():输出错误的性质。
  • toString():给出异常的类型与性质。

4)案例

public class ExceptionDemo {
    public static void main(String[] args){
        try { // 可能发生异常的语句
            int[] arr={1,3,5,8};
            System.out.println(arr[5]); //创建一个异常
        } catch (Exception e){ // 处理异常语句
            e.printStackTrace(); //跟踪异常,指出异常的类型、性质、栈层次及出现在程序中的位置
            System.out.println("数组下标越界");
        }finally { //无论是否发生异常,finally中的代码总会被执行
            System.out.println("over!");
        }
    }
}

运行结果

java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 4
    at test2.ExceptionDemo.main(ExceptionDemo.java:7)
数组下标越界
over!

再看一个多重捕获块(即多重catch语句)的示例

原则:存在父子,先子后父。

import java.util.InputMismatchException;
import java.util.Scanner;
public class ExceptionDemo {
    public static void main(String[] args){
        try {
            Scanner sc = new Scanner(System.in);
            System.out.println("请录入第一个数:");
            int num1 = sc.nextInt();
            System.out.println("请录入第二个数:");
            int num2 = sc.nextInt();
            System.out.println("商:" + num1 / num2);
        } catch (ArithmeticException e) {
            System.out.println("除数不能为0");
        } catch (InputMismatchException e) {
            System.out.println("本数不是int类型的数据!");
        } catch (Exception e) {
            System.out.println("对不起,你的程序出现异常");
        } finally {
            System.out.println("感谢您使用计算器!");
        }
    }
}

运行结果

请录入第一个数:
1
请录入第二个数:
0
除数不能为0
感谢您使用计算器!

5)try…catch…finally

Java的垃圾回收机制不会回收任何物理资源,只回收堆内存中对象所占用的内存。为了确保一定能回收 try 块中打开的物理资源,异常处理机制提供了 finally 代码块。

Java 7 之后提供了自动资源管理(Automatic Resource Management)技术。

案例

import java.util.Scanner;
public class ExceptionDemo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("Windows 系统已启动!");
        String[] arr = { "记事本", "计算器", "浏览器" };
        try {
            for (int i = 0; i < arr.length; i++) { // 循环输出pros数组中的元素
                System.out.println(i + 1 + ":" + arr[i]);
            }
            System.out.println("是否运行程序:");
            String answer = sc.next();
            if (answer.equals("y")) {
                System.out.println("请输入程序编号:");
                int no = sc.nextInt();
                System.out.println("正在运行程序[" + arr[no - 1] + "]");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("谢谢使用!");
        }
    }
}

运行结果

Windows 系统已启动!
1:记事本
2:计算器
3:浏览器
是否运行程序:
y
请输入程序编号:
8
java.lang.ArrayIndexOutOfBoundsException: Index 7 out of bounds for length 3
    at test2.ExceptionDemo.main(ExceptionDemo.java:18)
谢谢使用!

总结

finally 与 try 语句块匹配的语法格式会导致异常丢失,所以不常见。

1.2 抛出异常

抛出异常:生成异常对象,并把它提交给运行时系统的过程。
throws :方法上声明要拋出的异常,表示此方法不处理异常,而交给方法调用处进行处理。
1)几点注意

任何方法都可以使用throws关键字声明异常类型,包括抽象方法。

子类重写父类中的方法,子类方法不能声明抛出比父类类型更大的异常。

使用了throws的方法,调用时必须处理声明的异常,要么使用try-catch,要么继续使用throws声明。

throw:方法内部拋出异常对象
1)几点注意

throw关键字用于显式抛出异常,抛出的是一个异常类的实例化对象。

throw用于方法体中,要么使用try/catch捕获异常,要么throws异常。

案例

import java.rmi.RemoteException;
public class ExceptionDemo {
    public void deposit() throws RemoteException {
        throw new RemoteException();
    }
    public static void main(String[] args) throws RemoteException {
        ExceptionDemo ec=new ExceptionDemo();
        ec.deposit();
    }
}

运行结果

Exception in thread "main" java.rmi.RemoteException
	at ExceptionDemo.deposit(ExceptionDemo.java:7)
	at ExceptionDemo.main(ExceptionDemo.java:11)

2、自定义异常

实现自定义异常类需要继承 Exception 类或其子类,如果自定义运行时异常类需继承 RuntimeException 类或其子类。
1)形式:

<自定义异常名>,规范上自定义异常名为XXXException(XXX表示异常的作用)

2)构造方法(两个)

  • 无参构造方法(默认)
  • String类型的构造方法(以字符串的形式接收一个定制的异常消息,并将该消息传递给超类的构造方法)

3)案例

//编写一个程序,对用户注册时的年龄进行验证,检测是否在 0~100 岁。
import java.util.InputMismatchException;
import java.util.Scanner;
class MyException extends Exception{ //自定义异常类MyException
    public MyException(){ //无参构造
        super();
    }
    public MyException(String str){ //String类型的构造方法
        super(str);
    }
}
public class MyExceptionDemo{ //测试类
    public static void main(String[] args){
        int age; //成员变量
        Scanner sc=new Scanner(System.in);
        System.out.println("请输入您的年龄:");
        try{
            age=sc.nextInt(); //获取年龄
            if(age<0){
                throw new MyException("您输入的年龄为负数!输入有误!");
            }else if(age>100){
                throw new MyException("您输入的年龄大于100!输入有误!");
            }else {
                System.out.println("您的年龄为:"+age);
            }
        } catch (InputMismatchException e1) { //捕获输入不匹配异常
            System.out.println("输入的年龄不是数字!");
        } catch (MyException e2){ //捕获自定义的异常
            System.out.println(e2.getMessage()); //调用getMessage()方法输出异常信息
        }
    }
}

运行结果(3种情况)

-----------
请输入会员注册时的年龄:
acr
输入的年龄不是数字!
-----------
请输入您的年龄:
-1
您输入的年龄为负数!输入有误!
-----------
请输入您的年龄:
101
您输入的年龄大于100!输入有误!

注:因为自定义异常继承自 Exception 类,因此自定义异常类中包含父类所有的属性和方法。(可以调用)

3、断言(assert)

3.1 几点注意
  • java断言assert是jdk1.4引入的。
  • jvm断言默认是关闭的。(要手动开启)
    • 开启:在vm虚拟机中输入参数-ea
    • 关闭:输入-da,或删除-ea
  • 断言可以局部开启的,如:父类禁止断言,而子类开启断言,所以一般说“断言不具有继承性”。

在这里插入图片描述
在这里插入图片描述

3.2 作用及使用注意

断言主要使用在代码开发和测试时期,用于对某些关键数据的判断,如果这个关键数据不是程序所预期的数据,程序就提出警告或退出。

断言一般用于程序执行结构的判断,千万不要让断言处理业务流程。

3.3 使用方法

1)assert<boolean表达式>

表达式为true,程序继续执行,为false,抛出AssertionError,并终止执行。

2)assert<boolean表达式><msg> (msg为错误信息)

为true同上,为false,抛出AssertionError,输出错误信息并终止程序。

3.4 案例

1)示例1

public class AssertTest {
    public static void main(String[] args) {
        assert true; //断言1为true,则继续往下执行
        System.out.println("断言1没有问题,Go!");
        System.out.println("-----------------");
        assert false : "断言失败,此表达式的信息将会在抛出异常的时候输出!"; //断言2为false,抛出AssertionError,输出错误信息并终止程序。
        System.out.println("断言2没有问题,Go!"); //不会执行
    }
}

运行结果

断言1没有问题,Go-----------------
Exception in thread "main" java.lang.AssertionError: 断言失败,此表达式的信息将会在抛出异常的时候输出!
    at test2.AssertTest.main(AssertTest.java:8)

2)示例2

public class AssertTest {
    public static void main(String[] args) {
        assert true; //断言1为true,则继续往下执行
        System.out.println("猜猜我叫什么名字?");
        String name="zhangsan"; //初始化name
        assert name=="lishi":"断言错误,我不叫lishi";//该断言为false,抛出AssertionError,输出错误信息并终止程序。
        System.out.println("断言正确,我叫zhangsan"); //不会执行
    }
}

运行结果

猜猜我叫什么名字?
Exception in thread "main" java.lang.AssertionError: 断言错误,我不叫lishi
    at test2.AssertTest.main(AssertTest.java:8)
3.5 assert陷阱总结(尽量少用)

1)优点
可以帮助我们在开发和测试中提示哪部分的代码有问题,使用断言时需按需求设置好一个表达式,才能在我们放松警惕时提示“你这代码有问题”。
2)陷阱(了解)

assert关键字需要在运行时候显式开启才能生效,否则断言就没有任何意义。而现在主流的 Java IDE工具默认都没有开启-ea断言检查功能。意味着如果使用 IDE工具编码,调试运行时候会有一定的麻烦。并且,对于 Java Web应用,程序代码都是部署在容器里面,没法直接去控制程序的运行,如果一定要开启 -ea的开关,则需要更改Web容器的运行配置参数。这对程序的移植和部署都带来很大的不便。

用assert代替 if是陷阱之二。assert的判断和 if语句差不多,但两者的作用有着本质的区别:assert关键字本意上是为测试调试程序时使用的,但如果不小心用 assert来控制了程序的业务流程,那在测试调试结束后去掉 assert关键字就意味着修改了程序的正常的逻辑。

assert断言失败将面临程序的退出。这在一个生产环境下的应用是绝不能容忍的。一般都是通过异常处理来解决程序中潜在的错误。但是使用断言就很危险,一旦失败系统就挂了。

3.5 assert反思
  • 使用更好的测试 JUint(单元测试),用 assert只是为了调试测试程序用,不在正式生产环境下用。
  • 尽量避免在 Java中使用 assert关键字,除非哪天 Java默认支持开启 -ea的开关。

4、logging记录日志类

4.1 概述

日志用来记录程序的运行轨迹,方便查找关键信息和快速定位解决问题。

logging是JDK自带的记录日志类,目的是为了取代System.out.println()

4.2 优点
  • 可以设置输出样式,避免自己每次都写"ERROR: " + var;
  • 可以设置输出级别,禁止某些级别输出。例如,只输出错误日志;
  • 可以被重定向到文件,这样可以在程序运行结束后查看日志;
  • 可以按包名控制日志级别,只输出某些包打的日志;等等。
4.3 方法
  • log():指定级别,logger.log(Level.FINE, message);
  • setLevel():设置级别,logger.setLevel(Level.FINE);
  • Level.ALL():开启所有级别的记录。
  • Level.OFF():关闭所有级别的记录。

示例

import java.util.logging.Logger; //导包
public class LoggingTest {
    private static Logger log=Logger.getLogger(LoggingTest.class.toString());
    public static void main(String[] args) {
        //级别依次升高,后面的日志级别会屏蔽之前的级别,默认显示最高的3个
        //使用日志级别的好处在于,调整级别,就可以屏蔽掉很多调试相关的日志输出。
        log.finest("finest");   //最好
        log.finer("finer");     //较好
        log.fine("fine");       //良好
        log.config("config");   //配置
        log.info("info");       //信息
        log.warning("warning"); //警告
        log.severe("server");   //严重
    }
}

运行结果

116, 2023 10:59:31 上午 test2.LoggingTest main
信息: info
116, 2023 10:59:31 上午 test2.LoggingTest main
警告: warning
116, 2023 10:59:31 上午 test2.LoggingTest main
严重: server

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

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

相关文章

浏览器卡顿解决办法 —— DNS设置

1.Windows - 设置 2.网络和Internet 3.以太网 - 更改适配器选项 4.点击你现在用的网络 (WLAN或以太网)&#xff0c;右键属性 5.双击IPv4协议&#xff0c;DNS地址首选114.114.114.114 南京信风网络科技有限公司&#xff0c;成立于2000年&#xff0c;是一家位于中国南京的高…

LeetCode215.数组中的第K个最大元素

题目 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 输入: [3,2,1,5,6,4], …

基于springboot + vue实现的前后端分离-在线旅游网站系统(项目 + 论文)

项目介绍 本旅游网站系统采用的数据库是MYSQL &#xff0c;使用 JSP 技术开发&#xff0c;在设计过程中&#xff0c;充分保证了系统代码的良好可读性、实用性、易扩展性、通用性、便于后期维护、操作方便以及页面简洁等特点。 技术选型 后端: SpringBoot Mybatis 数据库 : MyS…

大模型日报|今日必读的6篇大模型论文

大家好&#xff0c;今日必读的大模型论文来啦&#xff01; 1.中科院、国科大新研究&#xff1a;进行自我感知、更接近人类的AI “机器能思考吗&#xff1f;”这个问题和评估机器是否能达到人类智能水平的图灵测试&#xff0c;是人工智能&#xff08;AI&#xff09;的核心问题…

Stable Diffusion 模型分享:Realistic Stock Photo(真实的库存照片)

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八 下载地址 模型介绍 条目内容类型大模型基础模型SDXL 1.0来源CIVITAI作者PromptSharingSamaritan文件名称reali…

java 获取package下的所有类名

1、获取指定包的路劲 2、根据路劲获取所有实体类的.class文件 3、我们使用ClassLoader来获取资源的URL&#xff0c;然后根据URL获取对应的文件夹。最后&#xff0c;我们使用过滤器来筛选出所有以".class"结尾的文件 获取entity包下所有的实体进行映射 1.包的路劲…

android程序员面试笔试宝典,Android开发社招面试总结

部分面试常问的面试专题 一、Java篇 1.多线程并发&#xff1b; sleep 和 wait 区别join 的用法线程同步&#xff1a;synchronized 关键字等线程通信线程池手写死锁 2.Java 中的引用方式&#xff0c;及各自的使用场景 3.HashMap 的源码 4.GC(垃圾回收)是什么&#xff1f;如何…

浅谈 Linux 网络编程 - Server 端模型、sockaddr、sockaddr_in 结构体

文章目录 前言前置知识Server 端核心模型 【重点】相关函数 【重点】socket 函数bind 函数listen 函数accept 函数close 函数 sockaddr 数据结构 【重点】 前言 本文主要是对 Linux 网络编程中&#xff0c;Server 端的模型、相关函数 以及 sockaddr、sockaddr_in 结构体做介绍…

用node写后端环境运行时报错Port 3000 is already in use

解决方法:关闭之前运行的3000端口,操作如下 1.WindowR输入cmd确定,打开命令面板 2.查看本机端口详情 netstat -ano|findstr "3000" 3.清除3000端口 taskkill -pid 41640 -f 最后再重新npm start即可,这里要看你自己项目中package.joson的启动命令是什…

VBA_MF系列技术资料1-390

MF系列VBA技术资料1-390 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-0…

阿里云能处!2024年ta真降价呀!附服务器优惠价格表

阿里云能处&#xff0c;关键时刻ta真降价啊&#xff01;2024新年伊始阿里云带头降价了&#xff0c;不只是云服务器&#xff0c;云数据库和存储产品都降价&#xff0c;阿里云新老用户均可购买99元服务器、199元服务器&#xff0c;续费不涨价&#xff0c;阿里云百科aliyunbaike.c…

新闻网站封锁AI爬虫 AI与新闻媒体博弈继续

随着ChatGPT等新兴AI模型的兴起&#xff0c;它们所依赖的网络爬虫正面临来自全球主流新闻网站的大规模封锁。Richard Fletcher博士团队对十个国家主流新闻网站的统计发现&#xff0c;到2023年底&#xff0c;48%的网站屏蔽了OpenAI的爬虫&#xff0c;24%屏蔽了Google的爬虫。那么…

【总结】对大量函数进行trace调用流程+国际AIS3题

现在混淆的主要目的之一就有让逆向分析人员不清楚函数的调用流程&#xff0c;给你一堆函数&#xff0c;加了高强度的OLLVM&#xff0c;更不能看了。那么Trace跟踪技术就显得很重要的&#xff0c;如果清楚了函数调用流程&#xff0c;那么逐个分析&#xff0c;距离成功不就很快了…

方格分割644--2017蓝桥杯

1.用dfs解决&#xff0c;首先这题的方格图形就很像一个走迷宫的类型&#xff0c;迷宫想到dfs&#xff0c;最中心点视为起点&#xff0c;起点有两个小人在这个方格里面对称行动&#xff0c;直到走出迷宫&#xff08;一个人走出来了另一个人就也走出来了&#xff0c;而走过的点会…

亚信安慧AntDB:打破数据孤岛,实现实时处理

AntDB数据库以其独特的创新能力在分布式数据库领域引领潮流。其中&#xff0c;融合统一与实时处理是其两大核心创新能力&#xff0c;为其赢得广泛关注与赞誉。融合统一意味着AntDB能够将多种不同类型的数据库融合为一体&#xff0c;实现数据的统一管理与处理&#xff0c;极大地…

NC65 rest接口 开发 NC65接口开发

一、在对应模块META-INF下编写 xxx.rest 文件,也要放在Home里对应的目录下。 二、开发接口&#xff0c;继承extends AbstractUAPRestResource&#xff0c;&#xff08;有的项目会继承别的方法如&#xff1a;AbstractNCCRestResource&#xff0c;MTFRestResource&#xff1b;有…

公司招嵌入式开发岗位,为什么感觉一年比一年难?

最近看到一个问题&#xff1a; 是一个HR在吐槽招不到嵌入式开发的人才。 这句话&#xff0c;难免会误导一些想入行嵌入式的同学&#xff0c;卧槽&#xff0c;这么缺人?赶紧冲&#xff01; 哼次哼次学完一堆技术栈&#xff0c;一投简历&#xff0c;一个面试机会都没有。 这就是…

详解字符串函数<string.h>(上)

1. strlen函数的使用和模拟实现 size_t strlen(const char* str); 1.1 函数功能以及用法 字符串长度 strlen函数的功能是计算字符串的长度。在使用时&#xff0c;要求用户传入需要计算长度的字符串的起始位置&#xff0c;并返回字符串的长度。 #include <stdio.h> #…

CC攻击与DDoS攻击有什么区别?如何进行有效防护?

CC攻击的前身是一个名为Fatboy攻击程序&#xff0c;而之所以后来人们会成为CC&#xff0c;是因为DDoS攻击发展的初期阶段&#xff0c;绝大部分DDoS攻击都能被业界熟知的“黑洞”&#xff08;collapsar&#xff0c;一种安全防护产品&#xff09;所抵挡&#xff0c;CC攻击的诞生就…

NFTScan NFT API 在 Web3 钱包追踪器上的开发应用

Web3 钱包追踪器是通过整合区块链数据 API&#xff0c;为加密资产投资者提供全面的钱包分析和追踪工具。用户可以利用钱包追踪器跟踪特定钱包地址的资产总额和交易情况&#xff0c;分析历史交易发现交易趋势&#xff0c;设置资产价格警报&#xff0c;生成钱包报告&#xff0c;同…