【Java】异常处理 之 Java的异常

Java的异常

在计算机程序运行的过程中,总是会出现各种各样的错误。

有一些错误是用户造成的,比如,希望用户输入一个int类型的年龄,但是用户的输入是abc

// 假设用户输入了abc:
String s = "abc";
int n = Integer.parseInt(s); // NumberFormatException!

程序想要读写某个文件的内容,但是用户已经把它删除了:

// 用户删除了该文件:
String t = readFile("C:\\abc.txt"); // FileNotFoundException!

还有一些错误是随机出现,并且永远不可能避免的。比如:

网络突然断了,连接不到远程服务器;
内存耗尽,程序崩溃了;
用户点“打印”,但根本没有打印机;
……
所以,一个健壮的程序必须处理各种各样的错误。

所谓错误,就是程序调用某个函数的时候,如果失败了,就表示出错。

调用方如何获知调用失败的信息?有两种方法:

方法一:约定返回错误码。

例如,处理一个文件,如果返回 0,表示成功,返回其他整数,表示约定的错误码:

int code = processFile("C:\\test.txt");
if (code == 0) {// ok:
} else {// error:switch (code) {case 1:// file not found:case 2:// no read permission:default:// unknown error:}
}

因为使用int类型的错误码,想要处理就非常麻烦。这种方式常见于底层C函数。

方法二:在语言层面上提供一个异常处理机制。

Java内置了一套异常处理机制,总是使用异常来表示错误。

异常是一种class,因此它本身带有类型信息。异常可以在任何地方抛出,但只需要在上层捕获,这样就和方法调用分离了:

try {String s = processFile(C:\\test.txt”);// ok:
} catch (FileNotFoundException e) {// file not found:
} catch (SecurityException e) {// no read permission:
} catch (IOException e) {// io error:
} catch (Exception e) {// other error:
}

因为Java的异常是class,它的继承关系如下:

在这里插入图片描述

从继承关系可知:Throwable是异常体系的根,它继承自ObjectThrowable有两个体系:ErrorExceptionError表示严重的错误,程序对此一般无能为力,例如:

  • OutOfMemoryError:内存耗尽
  • NoClassDefFoundError:无法加载某个Class
  • StackOverflowError:栈溢出而Exception则是运行时的错误,它可以被捕获并处理。

某些异常是应用程序逻辑处理的一部分,应该捕获并处理。例如:

  • NumberFormatException:数值类型的格式错误
  • FileNotFoundException:未找到文件
  • SocketException:读取网络失败

还有一些异常是程序逻辑编写不对造成的,应该修复程序本身。例如:

  • NullPointerException:对某个null的对象调用方法或字段
  • IndexOutOfBoundsException:数组索引越界

Exception又分为两大类:

  • RuntimeException以及它的子类;
  • 非RuntimeException(包括IOException、ReflectiveOperationException等等)

Java规定:

必须捕获的异常,包括Exception及其子类,但不包括RuntimeException及其子类,这种类型的异常称为Checked Exception

不需要捕获的异常,包括Error及其子类,RuntimeException及其子类。

注意:编译器对RuntimeException及其子类不做强制捕获要求,不是指应用程序本身不应该捕获并处理RuntimeException。是否需要捕获,具体问题具体分析。

捕获异常

捕获异常使用try...catch语句,把可能发生异常的代码放到try {...}中,然后使用catch捕获对应的Exception及其子类:

// try...catch
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Main {public static void main(String[] args) {byte[] bs = toGBK("中文");System.out.println(Arrays.toString(bs));}static byte[] toGBK(String s) {try {// 用指定编码转换String为byte[]:return s.getBytes("GBK");} catch (UnsupportedEncodingException e) {// 如果系统不支持GBK编码,会捕获到UnsupportedEncodingException:System.out.println(e); // 打印异常信息return s.getBytes(); // 尝试使用用默认编码}}
}

如果我们不捕获UnsupportedEncodingException,会出现编译失败的问题:

// try...catch
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Main {public static void main(String[] args) {byte[] bs = toGBK("中文");System.out.println(Arrays.toString(bs));}static byte[] toGBK(String s) {return s.getBytes("GBK");}
}

编译器会报错,错误信息类似:unreported exception UnsupportedEncodingException; must be caught or declared to be thrown,并且准确地指出需要捕获的语句是return s.getBytes("GBK");。意思是说,像UnsupportedEncodingException这样的Checked Exception,必须被捕获。

这是因为String.getBytes(String)方法定义是:

public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {...
}

在方法定义的时候,使用throws Xxx表示该方法可能抛出的异常类型。调用方在调用的时候,必须强制捕获这些异常,否则编译器会报错。

在toGBK()方法中,因为调用了String.getBytes(String)方法,就必须捕获UnsupportedEncodingException。我们也可以不捕获它,而是在方法定义处用throws表示toGBK()方法可能会抛出UnsupportedEncodingException,就可以让toGBK()方法通过编译器检查:

// try...catch
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Main {public static void main(String[] args) {byte[] bs = toGBK("中文");System.out.println(Arrays.toString(bs));}static byte[] toGBK(String s) throws UnsupportedEncodingException {return s.getBytes("GBK");}
}

上述代码仍然会得到编译错误,但这一次,编译器提示的不是调用return s.getBytes("GBK");的问题,而是byte[] bs = toGBK("中文");。因为在main()方法中,调用toGBK(),没有捕获它声明的可能抛出的UnsupportedEncodingException

修复方法是在main()方法中捕获异常并处理:

// try...catch
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Main {public static void main(String[] args) {try {byte[] bs = toGBK("中文");System.out.println(Arrays.toString(bs));} catch (UnsupportedEncodingException e) {System.out.println(e);}}static byte[] toGBK(String s) throws UnsupportedEncodingException {// 用指定编码转换String为byte[]:return s.getBytes("GBK");}
}

可见,只要是方法声明的Checked Exception,不在调用层捕获,也必须在更高的调用层捕获。所有未捕获的异常,最终也必须在main()方法中捕获,不会出现漏写try的情况。这是由编译器保证的。main()方法也是最后捕获Exception的机会。

如果是测试代码,上面的写法就略显麻烦。如果不想写任何try代码,可以直接把main()方法定义为throws Exception

// try...catch
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Main {public static void main(String[] args) throws Exception {byte[] bs = toGBK("中文");System.out.println(Arrays.toString(bs));}static byte[] toGBK(String s) throws UnsupportedEncodingException {// 用指定编码转换String为byte[]:return s.getBytes("GBK");}
}

因为main()方法声明了可能抛出Exception,也就声明了可能抛出所有的Exception,因此在内部就无需捕获了。代价就是一旦发生异常,程序会立刻退出。

还有一些童鞋喜欢在toGBK()内部“消化”异常:

static byte[] toGBK(String s) {try {return s.getBytes("GBK");} catch (UnsupportedEncodingException e) {// 什么也不干}return null;```
这种捕获后不处理的方式是非常不好的,即使真的什么也做不了,也要先把异常记录下来:
```java
static byte[] toGBK(String s) {try {return s.getBytes("GBK");} catch (UnsupportedEncodingException e) {// 先记下来再说:e.printStackTrace();}return null;

所有异常都可以调用printStackTrace()方法打印异常栈,这是一个简单有用的快速打印异常的方法。

小结

Java使用异常来表示错误,并通过try ... catch捕获异常;

Java的异常是class,并且从Throwable继承;

Error是无需捕获的严重错误,Exception是应该捕获的可处理的错误;

RuntimeException无需强制捕获,非RuntimeException(Checked Exception)需强制捕获,或者用throws声明;

不推荐捕获了异常但不进行任何处理。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

React diff 根据相对位置的 diff 算法

文章目录 diff 算法没有 key 时的diff通过 key 的 diff查找需要移动的节点移动节点添加新元素移除不存在的元素缺点 diff 算法 没有 key 时的diff 根据新旧列表的长度进行 diff 公共长度相同的部分直接patch新列表长度>旧列表长度则添加,否则删除 function pa…

九、Spring 声明式事务学习总结

文章目录 一、声明式事务1.1 什么是事务1.2 事务的应用场景1.3 事务的特性(ACID)1.4 未使用事务的代码示例1.5 配置 Spring 声明式事务学习总结 一、声明式事务 1.1 什么是事务 把一组业务当成一个业务来做;要么都成功,要么都失败…

ros tf

欢迎访问我的博客首页。 tf 1. tf 命令行工具1.1 发布 tf1.2 查看 tf 2.参考 1. tf 命令行工具 1.1 发布 tf 我们根据 cartographer_ros 的 launch 文件 backpack_2d.launch 写一个 tf.launch,并使用命令 roslaunch cartographer_ros tf.launch 启动。该 launch 文件…

了解JavaSpring

什么是Spring? Spring开发方向:分布式,微服务,网站 Spring技术(全家桶):Spring Framework、Spring boot、Spring Cloud Spring Framework(4.x) 是spring体系中最基础…

powerdesigner各种字体设置;preview字体设置;sql字体设置

1.设置左侧菜单: 步骤如下: tools —> general options —> fonts —> defalut UI font ,选择字体样式及大小即可,同下图。 2.设置preview字体大小(sql预览) 步骤如下: tools —> general o…

选读SQL经典实例笔记19_Any和All

1. Any 1.1. 任意一个 1.2. 选修了任意一门课程的学生 1.2.1. 找出选修了至少一门课程的学生 1.3. 比任何火车都快的飞机 1.3.1. 找出比所有火车都快的飞机 2. All 2.1. 全部 2.2. 吃所有蔬菜的人 2.2.1. 没有任何一种蔬菜他们不吃 3. 问题12 3.1. 选修了全部课程的…

DARPA TC-engagement5数据集解析为json格式输出到本地

关于这个数据集的一些基本信息就不赘述了,参考我之前的博客。DARPA TC-engagement5数据集官方工具可视化 官方给的工具是将解析的数据存到elasticsearch的,但是数据集的解压增长率非常恐怖,对空间要求很高。因此针对这个问题,我对…

FreeRTOS源码分析-10 互斥信号量

目录 1 事件标志组概念及其应用 1.1 事件标志组定义 1.2 FreeRTOS事件标志组介绍 1.3 FreeRTOS事件标志组工作原理 2 事件标志组应用 2.1 功能需求 2.2 API 2.3 功能实现 3 事件标志组原理 3.1 事件标志组控制块 3.2 事件标志组获取标志位 3.3 等待事件标志触发 3.4…

如何离线安装ModHeader - Modify HTTP headers Chrome插件?

如何离线安装ModHeader - Modify HTTP headers Chrome插件? 1.1 前言1.2 打开Chrome浏览器的开发者模式1.3 下载并解压打包好的插件1.4 解压下载好的压缩包1.5 加载插件1.6 如何使用插件? 1.1 前言 ModHeader 是一个非常好用的Chrome浏览器插件,可以用…

SQL 数据科学:了解和利用联接

推荐:使用 NSDT场景编辑器助你快速搭建可编辑的3D应用场景 什么是 SQL 中的连接? SQL 联接允许您基于公共列合并来自多个数据库表的数据。这样,您就可以将信息合并在一起,并在相关数据集之间创建有意义的连接。 SQL 中的连接类型…

Java中常用的API概览及示例解析

文章目录 1. java.lang包1.1 String类1.2 StringBuilder类 2. java.util包2.1 ArrayList类2.2 HashMap类 3. java.io包3.1 File类3.2 FileInputStream和FileOutputStream类 Java作为一门广泛应用于软件开发的编程语言,拥有丰富的类库和API(Application P…

PHP实践:用openssl打造安全可靠的API签名验证系统

🏆作者简介,黑夜开发者,全栈领域新星创作者✌,阿里云社区专家博主,2023年6月csdn上海赛道top4。 🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。 🏆本文已…