Java——异常处理
1、java提供的异常不可能预见所有的问题,所以需要自己定义异常类,必须从已有的异常类继承,最好选择意思相近的异常类继承。
class MyException extends Exception{} public class Tree1 { public static void f() throws MyException{
System.out.println("throws MyException from f()");
throw new MyException();
} public static void main (String[] args) { try {
f();
}catch(MyException e){
System.out.println("caught it");
}
}
}
try块中的代码将被监控,catch将会接受来自try块的异常。这里的f()方法将会抛出一个 MyException类的异常,然后catch将会接收到这个异常,并输出caught it4
所以输出结果为:
throws MyException from f()
caught it
可以为异常类定义一个接受字符串参数的构造器:
class MyException extends Exception{
public MyException() {}
public MyException(String msg) {
super(msg);
}
}
public class Tree1 {
public static void f() throws MyException{
System.out.println("throws MyException from f()");
throw new MyException();
} public static void main (String[] args) {
try {
f();
}catch(MyException e){
e.printStackTrace(System.out);
}
}
}
这样的输出是:
throws MyException from f()
MyException
at Tree1.f(Tree1.java:11)
at Tree1.main(Tree1.java:16)
在异常类的定义中的第二个构造器中使用了super关键字明确调用了其基类构造器,它接受一个字符串作为参数。
在异常处理程序中,调用了Throwable类声明的printStackTrace()方法,就像输出中看到的这样,它将会打印“从方法调用处直到异常抛出处” 的方法调用序列,这里信息发送到了System.out,如果使用默认的e.printStackTrace();则信息将被输出到标准错误流。
2、Exception类型的方法
class MyException extends Exception{
public MyException() {}
public MyException(String msg) {
super(msg);
}
}
class MySecondException extends Exception{
public MySecondException() {}
public MySecondException(String msg) {
super(msg);
}
}
public class Tree1 {
public static void f() throws MyException {
System.out.println("throws MyException from f()");
throw new MyException("name");
} public static void g() throws MySecondException {
System.out.println("throws MySecondException from g()");
throw new MySecondException("name2");
} public static void main (String[] args){
try {
f();
// g();
}catch(Exception e){
System.out.println(e.getMessage());
System.out.println(e);
System.out.println(e.getLocalizedMessage());
}
}
}
这里定义了两个继承自Exception的异常类,分别是MyException、MySecondException,由两个方法f()、g()抛出,如果在try块中只有f();,输出结果为:
throws MyException from f()
name
MyException: name
name
Throwable类的getMessage()方法返回的是Exception的详细消息字符串,就是抛出这个异常时候传入的字符串,
而getLocalizedMessage()方法返回的是 Exception的本地化描述。
toString()方法返回 此对象的类的 name ": "(冒号和一个空格)
然后如果把上面代码g();的前面的//删掉,就是try块中将运行f()和g()两个方法,输出结果为:
throws MyException from f()
name
MyException: name
name
和刚才一样这是因为catch接收到异常后就会结束try块中的程序运行,所以g()方法并没有被运行,所以如果先运行g()的结果就是:
throws MySecondException from g()
name2
MySecondException: name2
name2
3、栈轨迹
public class Tree1 {
public static void f(){
try {
throw new Exception();
}catch(Exception e){
e.printStackTrace();
}
} public static void g() {f();}
public static void h() {g();} public static void main (String[] args){
f();
g();
h();
}
}
输出结果为:
java.lang.Exception
at Tree1.f(Tree1.java:17)
at Tree1.main(Tree1.java:27)
java.lang.Exception
at Tree1.f(Tree1.java:17)
at Tree1.g(Tree1.java:23)
at Tree1.main(Tree1.java:28)
java.lang.Exception
at Tree1.f(Tree1.java:17)
at Tree1.g(Tree1.java:23)
at Tree1.h(Tree1.java:24)
at Tree1.main(Tree1.java:29)
这里的调用的printStackTrace(),是Throwable类的方法,这个方法会将Throwable对象的栈轨迹信息打印到标准错误输出流上,第一行是异常类的tostring()方法输出的内容,后面几行的内容都是之前通过fillInStackTrace()方法保存的内容。
java.lang.Exception
at Tree1.f(Tree1.java:17)
at Tree1.main(Tree1.java:27)
在这个例子中,在方法f()中抛出异常,在main方法中捕获异常,并且打印栈轨迹信息。因此,输出依次展示了f—>main的过程。还打印了抛出错误,捕获错误的行数、类的名字。
java.lang.Exception
at Tree1.f(Tree1.java:17)
at Tree1.g(Tree1.java:23)
at Tree1.main(Tree1.java:28)
这里就是在方法f()中抛出异常,方法g()中调用了f(),然后和上一个一样了,第三个也同样。
public class Tree1 {
public static void f(){
try {
throw new Exception();
}catch(Exception e){
for(StackTraceElement ste: e.getStackTrace()) {
System.out.println(ste); }
}
} public static void g() {f();}
public static void h() {g();} public static void main (String[] args){
f();
System.out.println("------------------");
g();
System.out.println("------------------");
h();
}
}
这个例子中调用了getStackTrace()方法,这个方法返回StackTraceElement类的一个对象。其输出内容为:
Tree1.f(Tree1.java:17)
Tree1.main(Tree1.java:30)
------------------
Tree1.f(Tree1.java:17)
Tree1.g(Tree1.java:26)
Tree1.main(Tree1.java:32)
------------------
Tree1.f(Tree1.java:17)
Tree1.g(Tree1.java:26)
Tree1.h(Tree1.java:27)
Tree1.main(Tree1.java:34)
如果这样使用:
public class Tree1 {
public static void f(){
try {
throw new Exception();
}catch(Exception e){
for(StackTraceElement ste: e.getStackTrace()) {
System.out.print(ste.getMethodName() + " " );
System.out.println(ste.getLineNumber());
}
}
} public static void g() {f();}
public static void h() {g();} public static void main (String[] args){
f();
System.out.println("------------------");
g();
System.out.println("------------------");
h();
}
}
调用StackTraceElement中的方法getMethodName()和getLineNumber()就可以值获取栈轨迹中的方法 和行:
f 17
main 30
------------------
f 17
g 26
main 32
------------------
f 17
g 26
h 27
main 34
所以,其实StackTraceElement是一个栈轨迹元素的数组。将这些栈轨迹元素保存在一个数组中。每个元素对应栈的一个栈帧。数组的第一个元素保存的是栈顶元素,也就是上面的f。最后一个元素保存的栈底元素,也就是main。
public class Tree1 {
public static void f() throws Exception{
throw new Exception();
} public static void g() throws Exception {
try {
f();
}catch(Exception e){
e.printStackTrace();
throw e;
}
} public static void main (String[] args){
try {
g();
}catch(Exception e){
e.printStackTrace();
}
}
}
4、重新抛出异常
这里f()函数中抛出一个异常,然后g()方法中捕获了这个异常并打印异常栈轨迹Stack Trace,然后又再抛出了刚刚的异常,mai方法中捕获 了这个异常并打印异常栈轨迹Stack Trace,所以结果为:
java.lang.Exception
at Tree1.f(Tree1.java:16)
at Tree1.g(Tree1.java:21)
at Tree1.main(Tree1.java:30)
java.lang.Exception
at Tree1.f(Tree1.java:16)
at Tree1.g(Tree1.java:21)
at Tree1.main(Tree1.java:30)
也就是说,捕获到异常又立即抛出,在上级方法调用中再次捕获这个异常,打印的栈轨迹信息是一样的。额,其实只看代码也知道,因为这里只是把刚刚异常e又抛出一次,
public class Tree1 {
public static void f() throws Exception{
throw new Exception();
} public static void g() throws Exception {
try {
f();
}catch(Exception e){
e.printStackTrace();
throw (Exception)e.fillInStackTrace();
}
} public static void main (String[] args){
try {
g();
}catch(Exception e){
e.printStackTrace();
}
}
}
throw (Exception)e.fillInStackTrace();
这一行调用了Exception的fillInStackTrace()方法,首先要知道其实这个方法并不是来自于Exception类。Exception类本身除了定义了几个构造器之外,所有的方法都是从其父类继承过来的。而和异常相关的方法都是从java.lang.Throwable类继承过来的,所以其实fillInStackTrace()是Throwable类的方法,然后fillInStackTrace()方法就是将当前线程当前状态下的轨迹栈的状态保存进Throwabe中,就是更新的意思。
public Throwable fillInStackTrace()
这是这个方法的原型所以它的返回值是一个Throwable类对象,所以使用它更新的时候需要强制类型转换。
throw (Exception)e.fillInStackTrace();
所以输出内容为:
java.lang.Exception
at Tree1.f(Tree1.java:16)
at Tree1.g(Tree1.java:21)
at Tree1.main(Tree1.java:30)
java.lang.Exception
at Tree1.g(Tree1.java:24)
at Tree1.main(Tree1.java:30)
这次第二个打印的栈轨迹信息就没有f了。
5、异常链
class MyException1 extends Exception{} class MyException2 extends Exception{} public class Tree1 { public static void f() throws MyException1{
throw new MyException1();
} public static void g() throws MyException2{
try {
f();
} catch (MyException1 e) {
e.printStackTrace();
throw new MyException2();
}
} public static void main (String[] args){
try {
g();
}catch(Exception e){
e.printStackTrace();
}
}
}
这里定义了两个异常类,f()方法抛出了MyException1,g()方法捕获了这个异常并打印了异常的栈轨链,然后抛出了另一个异常MyException2也打印了异常的栈轨链,所以输出结果为:
MyException1
at Tree1.f(Tree1.java:16)
at Tree1.g(Tree1.java:21)
at Tree1.main(Tree1.java:31)
MyException2
at Tree1.g(Tree1.java:24)
at Tree1.main(Tree1.java:31)
这没什么毛病,但是常常想要在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,这就需要将原始异常的信息包装在新的异常中,这被称为异常链
class MyException1 extends Exception{} class MyException2 extends Exception{
MyException2(Throwable throwable){
super(throwable);
}
MyException2(){
super();
}
}
public class Tree1 { public static void f() throws MyException1{
throw new MyException1();
} public static void g() throws MyException2{
try {
f();
} catch (MyException1 e) {
e.printStackTrace();
throw new MyException2(e);
}
} public static void main (String[] args){
try {
g();
}catch(Exception e){
e.printStackTrace();
}
}
}
这样的输出结果就是:
MyException1
at Tree1.f(Tree1.java:16)
at Tree1.g(Tree1.java:21)
at Tree1.main(Tree1.java:31)
MyException2: MyException1
at Tree1.g(Tree1.java:24)
at Tree1.main(Tree1.java:31)
Caused by: MyException1
at Tree1.f(Tree1.java:16)
at Tree1.g(Tree1.java:21)
... 1 more
这样的定义:
public class MyException2 extends Exception{
//定义异常的原因
public MyException2(String message){
super(message);
} //定义异常原因,并携带原始的异常
public MyException2(String message,Throwable cause){
super(message,cause);
} //保留原始异常信息
publicMyException2(Throwable cause){
super(cause);
}
}
就能将原始异常传递给新异常
或是:
class MyException1 extends Exception{
} class MyException2 extends Exception{
MyException2(Throwable throwable){
super(throwable);
}
MyException2(){
super();
}
} class MyException3 extends Exception{
MyException3(Throwable throwable){
super(throwable);
}
MyException3(){
super();
}
} public class Tree1 { public static void f() throws MyException1{
throw new MyException1();
} public static void g() throws MyException2{
try {
f();
} catch (MyException1 e) {
// e.printStackTrace();
throw new MyException2(e);
}
} public static void h() throws MyException3{
try {
g();
} catch (MyException2 e) {
// e.printStackTrace();
throw new MyException3(e);
}
} public static void main (String[] args){
try {
h();
}catch(Exception e){
e.printStackTrace();
}
}
}
这里值输出了最后一个异常的栈轨迹
MyException3: MyException2: MyException1
at Tree1.h(Tree1.java:44)
at Tree1.main(Tree1.java:51)
Caused by: MyException2: MyException1
at Tree1.g(Tree1.java:35)
at Tree1.h(Tree1.java:41)
... 1 more
Caused by: MyException1
at Tree1.f(Tree1.java:27)
at Tree1.g(Tree1.java:32)
... 2 more
其中还包括了MyException1和MyException2 的信息
6、异常的限制
a、当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。
b、异常限制对构造器不起作用
c、当父类与接口具有相同的方法而且方法同时抛出不同的异常,这个时候是不允许的
d、当父类的构造方法抛出异常,子类必须有一个构造方法是抛出相同异常或者此异常的父类。
e、当父类方法没有抛出异常,子类覆盖的方法不能够抛出异常
f、当父类方法抛出异常,子类覆盖的方法可以不抛出异常
b和e有一点冲突,但是实际上是是因为父类构造器必须以这样或者那样的方式被调用,子类构造器的异常说明必须包含基类构造器的异常说明。
7、把“被检查的异常”转换为“不检查的异常”
import java.io.*; class WrapCheckedExcetion{
void thorwRuntimeExcetion(int type) {
try {
switch(type) {
case 0: throw new FileNotFoundException();
case 1: throw new IOException();
case 2: throw new RuntimeException("where an I");
default: return;
}
}catch(Exception e) {
throw new RuntimeException(e);
}
}
} class SomeOtherExcption extends Exception{}
public class Tree1 {
public static void main(String[] args) {
WrapCheckedExcetion wce = new WrapCheckedExcetion();
wce.thorwRuntimeExcetion(3);
for(int i = 0; i < 4; i++) {
try {
if(i < 3)
wce.thorwRuntimeExcetion(i);
else
throw new SomeOtherExcption();
}catch(SomeOtherExcption e) {
System.out.println("SomeOtherExcption:" + e);
}catch(RuntimeException re) {
try {
throw re.getCause();
}catch(FileNotFoundException e) {
System.out.println("FileNotFoundException:" + e);
}catch(IOException e) {
System.out.println("IOException:" + e);
}catch(Throwable e) {
System.out.println("Throwable:" + e);
}
}
}
}
}
输出:
FileNotFoundException:java.io.FileNotFoundException
IOException:java.io.IOException
Throwable:java.lang.RuntimeException: where an I
SomeOtherExcption:SomeOtherExcption
这里的WrapCheckedExcetion中的thorwRuntimeExcetion可以生成不同的异常,这些异常都被捕获并包装进了RuntimeExcetion对象中,所以它们都变成了运行时异常的“cause”了。在main中可以不用try块就可以调用thorwRuntimeExcetion(),因为它并没有抛出“被检查的异常”所以这个thorwRuntimeExcetion方法就实现了把“被检查的异常”转换为“不检查的异常”
Java——异常处理的更多相关文章
- 札记:Java异常处理
异常概述 程序在运行中总会面临一些"意外"情况,良好的代码需要对它们进行预防和处理.大致来说,这些意外情况分三类: 交互输入 用户以非预期的方式使用程序,比如非法输入,不正当的操作 ...
- java异常处理(父子异常的处理)
我当初学java异常处理的时候,对于父子异常的处理,我记得几句话“子类方法只能抛出父类方法所抛出的异常或者是其子异常,子类构造器必须要抛出父类构造器的异常或者其父异常”.那个时候还不知道子类方法为什么 ...
- Java 异常处理
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error:如果你用System.out ...
- 《转载》Java异常处理的10个最佳实践
本文转载自 ImportNew - 挖坑的张师傅 异常处理在编写健壮的 Java 应用中扮演着非常重要的角色.异常处理并不是功能性需求,它需要优雅地处理任何错误情况,比如资源不可用.非法的输入.nul ...
- JAVA 异常处理机制
主要讲述几点: 一.异常的简介 二.异常处理流程 三.运行时异常和非运行时异常 四.throws和throw关键字 一.异常简介 异常处理是在程序运行之中出现的情况,例如除数为零.异常类(Except ...
- Java异常处理和设计
在程序设计中,进行异常处理是非常关键和重要的一部分.一个程序的异常处理框架的好坏直接影响到整个项目的代码质量以及后期维护成本和难度.试想一下,如果一个项目从头到尾没有考虑过异常处理,当程序出错从哪里寻 ...
- 深入理解java异常处理机制
异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程.Java通 过API中Throwable类的众多子类描述各种不同的 ...
- Java提高篇——Java 异常处理
异常的概念 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error:如果你用Syst ...
- java异常处理的设计
有一句这样话:一个衡量Java设计师水平和开发团队纪律性的好方法就是读读他们应用程序里的异常处理代码. 本文主要讨论开发Java程序时,如何设计异常处理的代码,如何时抛异常,捕获到了怎么处理,而不是讲 ...
- java异常处理机制
本文从Java异常最基本的概念.语法开始讲述了Java异常处理的基本知识,分析了Java异常体系结构,对比Spring的异常处理框 架,阐述了异常处理的基本原则.并且作者提出了自己处理一个大型应用系统 ...
随机推荐
- Git使用小技巧之多个远程仓库
想要获取更多文章可以访问我的博客 - 代码无止境. 这是一个普通的工作日,小代正在勤勤恳恳的写代码.这时陈BOSS走到小代身边,跟小代说:"我们的代码需要同时推送到Github和码云两个仓库 ...
- c++学习书籍推荐《深度探索C++对象模型》下载
百度云及其他网盘下载地址:点我 百度云及其他网盘下载地址:点我 编辑推荐 如果你是一位C++程序员,渴望对于底层知识获得一个完整的了解,那么这本<深度探索C++对象模型>正适合你 作者简介 ...
- Spring Cloud Alibaba | Sentinel: 服务限流高级篇
目录 Spring Cloud Alibaba | Sentinel: 服务限流高级篇 1. 熔断降级 1.1 降级策略 2. 热点参数限流 2.1 项目依赖 2.2 热点参数规则 3. 系统自适应限 ...
- python基本用法
PYTHONPATH PYTHONPATH是python moudle的搜索路径.即import xxx会从$PYTHONPATH寻找xxx. 中文编码问题 #coding=utf-8 查看导入的包的 ...
- JS时间处理,获取天时分秒。以及浏览器出现的不兼容问题
//获取时间的天,小时,分钟,秒 function ToTime(second) { second = second / ; var result ; ) % ; ) % ; * )); ) { re ...
- 洛谷 P1635 跳跃
题目: 题目背景 NOIP即将迎来周年华诞.在这一个春秋的历程里,NOIP领导全国oier,建设高效.稳定.快捷.开放的社会主义现代化OI.在新的一年里,YZOJ将再接再厉,积极探寻成长之路,更好地为 ...
- 找bug的过程
关于昨天程序出差我找bug的过程记录 昨天才程序 https://www.cnblogs.com/pythonywy/p/11006273.html ├── xxxx │ ├── src.py │ └ ...
- [PTA] 数据结构与算法题目集 6-7 在一个数组中实现两个堆栈
//如果堆栈已满,Push函数必须输出"Stack Full"并且返回false:如果某堆栈是空的,则Pop函数必须输出"Stack Tag Empty"(其中 ...
- .NET 欢乐编程术之类型超级转换之术👍👍
准备工作:先确保 VS 版本大于 2017,且支持C# 7.0 语言版本.然后新建 .Net Core 项目,在 Nuget 包管理上引入微软霸霸官方包 System.Runtime.Compiler ...
- 【git】15分钟学会使用Git和远程代码库
Git是个了不起但却复杂的源代码管理系统.它能支持复杂的任务,却因此经常被认为太过复杂而不适用于简单的日常工作.让我们诚实一记吧:Git是复杂的,我们不要装作它不是.但我仍然会试图教会你用(我的)基本 ...