Java面向对象之异常详解
Java面向对象之异常【一】
Java面向对象之异常【二】
往期回顾:上一篇我们大致总结了异常的继承体系,说明了Exception和Error两个大类都继承于顶级父类Throwable,又谈到编译时异常与运行时异常的区别,谈到异常的处理方式,以及处理方式中关于捕获方式的几种类型。
本篇承上启下,将从异常的其余部分进行总结,但是毕竟现在处于初学阶段,未必能够体会异常在真实场景中运用的便利之处,所以本文只是对目前所学内容的归纳整理,后续新的体会将会及时更新。
捕获异常的规则
- 在执行try块的过程中没有出现异常,那么很明显,没有异常当然就会跳过catch子句。
- 相反,如果抛出了一个异常,那么就会跳过try中的剩余语句,开始查找处理该异常的代码。以下是查找处理异常代码的具体过程:
- 从当前方法开始,沿着方法的调用链,按照异常的反向传播方向找到异常的处理代码。
- 从第一个到最后一个检查catch块,判断是否相匹配。如果是,那么恭喜!直接进入catch块中执行处理异常语句;如果不是,就将该异常传给方法的调用者,在调用者中继续执行相同步骤:匹配就处理,不匹配就向上传……
- 直到最后如果都没有找到的话,程序将会终止,并在打印台上打印出错信息。
如果是相对单一方法而言,其实是很简单的;如果方法层层嵌套呢,情况又是咋样的呢,咱们来验证一下以上内容:
//主方法
public static void main(String[] args) {
try{
//调用m1()
m1();
System.out.println("ExceptionMyDemo.main");
}catch (Exception e){
System.out.println("ExceptionMyDemo.main.catch");
}
}
//m1()
private static void m1(){
try{
//调用m2()
m2();
System.out.println("ExceptionMyDemo.m1");
}catch (NullPointerException e){
System.out.println("ExceptionMyDemo.m1.catch");
}
}
//m2()
private static void m2(){
String str = null;
System.out.println(str.hashCode());
}
//测试结果:
ExceptionMyDemo.m1.catch
ExceptionMyDemo.main
- 可以看到,m1中捕获了m2抛出匹配的空指针异常类型,直接处理,在main方法中就接收不到异常,也就正常执行。
- 假如我们把m1的catch的异常类型换成其他的类型,比如
catch (ArithmeticException e)
,这时的测试结果会是这个样子:
//更改之后的测试结果:
ExceptionMyDemo.main.catch
因为m2()抛出的异常在m1中并没有被合适地处理,所以向上抛出,在main方法中找到了处理方法,遂执行处理语句。
- 依据我们上面所说,如果在上面更改之后的基础上,再把main方法中的处理异常语句删去,那么程序运行的结果会是啥呢?哦,不出所料,是下面的结果:
因为抛出的异常没人处理,它就会在控制台上打印异常的栈轨迹,关于抛出的异常信息,我们接下来进行详细分析。
访问异常信息
我们提到,无论是虚拟机抛出异常还是我们主动抛出,异常的错误信息都包含其中,以便于我们得知并更好地处理异常,那么顺着上面所说,我们刚刚看到的就是异常的栈轨迹:
public void printStackTrace()
:默认将该Throwable对象及其调用栈的跟踪信息打印到标准错误流。public String getMessage()
:返回描述异常对象信息的字符串。public String toString()
:异常信息message为空就返回异常类的全名,否则返回全名:message
的形式。public StackTraceElement[] getStackTrace()
:返回栈跟踪元素的数组,表示和该异常对象相关的栈的跟踪信息。
异常对方法重写的影响
- 异常对方法重载没有影响。
- 方法重写时,子类中重写的方法抛出的编译时异常不能超过父类方法抛出编译时异常的范围。
finally详解
名言警句:无论异常是否会发生,finally修饰的子句总是会被执行。
于是我们进行了简单的尝试:
public static void m2(){
try{
System.out.println("0");
System.out.println(1/0);
System.out.println("1");
}
catch (Exception e){
System.out.println("2");
}
finally {
System.out.println("3");
}
System.out.println("4");
}
//测试结果
0 2 3 4 被打印在控制台上
可以看到1没有被打印,因为在执行
System.out.println(1/0);
时发生了异常,于是进入catch块,finally子句必会被执行,然后执行try语句后的下一条语句。想象以下:假如把接收异常的实例类型改为另外一个不匹配的类型的话,也就是说无法正常捕获,结果又会如何呢?结果如下:
很明显,这时候finally的效果就出来了,就算你出了异常,我finally块中的语句必须要执行,这个在现实场景中对于释放资源起了很关键的作用,但是具体来说,由于还没有学习后面的内容,就暂且不提了,有些东西还是体会之后会更加真实一些。
还有一个注意点就是4也没有被打印出来,是因为没有捕获到异常,将会把异常抛给调用者,所以不会执行
System.out.println("4");
。
但是,化名为几千万个为什么的我又开始疑惑了,我们直到return可以将方法直接返回,强制退出。那么如果在try中使用return语句,finally还会不会不忘初心,继续执行呢?
前方高能!各单位注意!!!
猜猜看,这四个方法执行结果是啥呢?
private static int m1(){
try{
return 1;
}catch(Exception e){
}
return 2;
}
private static int m2(){
try{
return 1;
}finally {
return 2;
}
//使用finally子句时可以省略catch块
}
private static int m3(){
try{
return 1;
}finally {
try{
return 2;
}finally {
return 3;
}
}
}
private static int m4(){
int i = 4;
try{
return i++;
}finally {
i++;
}
}
答案揭晓:分别是:1,2,3,4。你们猜对了吗?哈哈……
我想前三个答案应该是毋庸置疑的,但是这第四个就有点离谱了。不是说finally语句一定会执行吗,执行哪去了呢,你要是执行的话,你i难道不应该变成6了吗?
额……咳咳,这个嘛,我也有点迷惑,但是经过一番讨教,稍微懂了一些:
- 当执行try之前,如果后面有finally,会将try中的返回过程延迟,就是说把i=4放到结果区。
- 然后在计算区进行自增运算变为5,finally语句一定会执行,但是只是在计算区域自增为6了,结果区域还是原来的那个4。
- 不信的话,你可以在finally语句的i++后面看看i的值,它!就是6!所以说finally子句一定执行是毋庸置疑的的!
但是如果进行改变的是引用数据类型的变量时,那么就会随之改变了,人家村的是地址,改的就是本身。我在这边就稍微来个简单的例子奥:
public static Student m(){
Student s = new Student();
try{
s.age = 20;
s.name = "天乔";
return s;
}finally {
s.name = "巴夏";
s.age = 2;
}
}
//测试结果
//Student{age=2, name='巴夏'}
本文若有叙述不当之处,还望评论区批评指正哦!
Java面向对象之异常详解的更多相关文章
- java基础(十五)----- Java 最全异常详解 ——Java高级开发必须懂的
本文将详解java中的异常和异常处理机制 异常简介 什么是异常? 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常. Java异常的分类和类结构图 1.Java中的所 ...
- Java中的异常详解
一.异常定义 阻止当前方法或作用域继续执行的问题,称为异常 二.异常分析 所有不正常类都继承Throwable类,这个类主要有两个子类Error类和Exception类.Error指系统错误 ...
- JAVA面向对象-----main方法详解
JVM看不懂的可以跳过,这里不做过多解释,(^__^) 嘻嘻-- 主函数是静态的 public static void main(String[] args){ } 主函数是什么:主函数是一个特殊的函 ...
- Java面向对象12——static详解
static package oop.demon01.demon07; // static : public class Student { private static int a ...
- Java面向对象之异常【一】
目录 Java面向对象之异常[一] 异常的继承体系 Error Exception 异常是否受检 unchecked exceptions(不受检异常) checked exceptions(受检异常 ...
- java反射机制深入详解
java反射机制深入详解 转自:http://www.cnblogs.com/hxsyl/archive/2013/03/23/2977593.html 一.概念 反射就是把Java的各种成分映射成 ...
- java web.xml配置详解(转)
源出处:java web.xml配置详解 1.常规配置:每一个站的WEB-INF下都有一个web.xml的设定文件,它提供了我们站台的配置设定. web.xml定义: .站台的名称和说明 .针对环境参 ...
- Java编程配置思路详解
Java编程配置思路详解 SpringBoot虽然提供了很多优秀的starter帮助我们快速开发,可实际生产环境的特殊性,我们依然需要对默认整合配置做自定义操作,提高程序的可控性,虽然你配的不一定比官 ...
- java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock
原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...
随机推荐
- Math.abs( x )
Math.abs( x ) 下面是参数的详细信息: x : 一个数字 返回值: 返回一个数字的绝对值 <html> <head> <title>JavaScript ...
- yii框架不输出头文件和尾文件
控制器: public function actionCat(){ return $this->renderPartial('cat');} 在进行页面输出渲染的时候. 1.render 输出父 ...
- Vue进阶
组件深入 过渡&动画 可复用性&组合 工具&规模化&内在 ****************参考*************** vue官方教程
- H3C根桥的选举
- 读《Effect Java中文版》
读<Effect Java中文版> 译者序 序 前言 第1章引言 1 第2章创建和销毁对象 4 第1条:考虑用静态工厂方法代替构造函数 4 第2条:使用私有构造函数强化singleto ...
- dotnet 获取指定进程的输入命令行
本文告诉大家如何在 dotnet 获取指定的进程的命令行参数 很多的程序在启动的时候都需要传入参数,那么如何拿到这些程序传入的参数? 我找到两个方法,一个需要引用 C++ 库支持 x86 和 x64 ...
- Cannot destructure property `createHash` of 'undefined' or 'null'(next服务端渲染引入next-less错误).
next中引入@zeit/next-less因next版本过低(webpack4之前的版本)无法执行next-less内置的mini-css-extract-plugin mini-css-extra ...
- python调用另一个文件中的代码,pycharm环境下:同文件夹下文件(.py)之间的调用,出现红线问题
如何调用另一个python文件中的代码无论我们选择用何种语言进行程序设计时,都不可能只有一个文件(除了“hello world”),通常情况下,我们都需要在一个文件中调用另外一个文件的函数呀数据等等, ...
- 【Linux】grep笔记
Linux grep命令用于查找文件里符合条件的字符串. 参数: -a 或 --text : 不要忽略二进制的数据. -A<显示行数> 或 --after-context=<显示行数 ...
- 使用Git和Github来管理自己的代码和笔记
一.Github注册 1.先注册github.com的账号,官方网站: https://github.com/ 2.登录 3.创建仓库,仓库分公开的和私有的,公开的是免费的,私有的是收费的.我现在创建 ...