Java编程思想学习(九) 异常处理
java的异常处理机制可以使程序有极好的容错性,让程序更加的健壮.所谓的异常,就是指的阻止当前方法或作用域继续执行的问题,,当程序运行时出现异常时,系统就会自动生成一个Exception对象来通知程序.这样就极大的简化了我们的工作.
当然java的异常对象有很多种,下面这幅图显示了java异常类的继承体系.
从图片中可以看到java将所有的非正常情况分成了两种: 异常(Exception)和错误(Error),他们都继承Throwable父类.Error错误一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误等.这种错误无法恢复或不可捕获,将导致应用程序中断,通常应用程序无法处理这些错误,因此也不应该试图用catch来进行捕获.而对于Exception我们是可以进行捕获并处理的.下面从几个方面对异常处理机制进行介绍.
一.异常处理的一般格式:
try{
///可能会抛出异常的代码
}
catch(Type1 id1){
//处理Type1类型异常的代码
}
catch(Type2 id2){
///处理type2类型异常的代码
}
try块中放置可能会发生异常的代码(但是我们不知道具体是哪种异常).如果异常发生了,try块抛出系统自动生成的异常对象,然后异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序,然后进行catch语句执行(不会在向下查找).看起来和switch语句比较类似.除了上面列出的那些异常类,其实我们也可以自己定义异常,然后处理他们.定义异常最重要的是异常类的名字,最好做到望名知义.另外异常处理中经常出现的throws和throw这两个关键字;throws表示的是当前程序不处理这个异常将其抛给调用这个程序的上一级程序执行,如果是main()方法则传给JVM虚拟机处理.throw表示的是显式的抛出一个异常对象.下面的代码中用到了上面提到的知识点.
///自己定义一个简单的异常类,只需要声明以下继承关系
///也可以自己编写构造器,但这不是必要的
public class SimpleException extends Exception{
///public SimpleException();
///public SimpleException(String str){
///super(str)};
} package lkl; import java.util.Scanner; public class ExceptionTest { ///声明该程序不处理SimpleException异常而是将其丢出
public void f() throws SimpleException{
System.out.println("Throws SimpleException from f()");
///丢出一个SimpleException异常对象
throw new SimpleException();
}
///多个try语句会依次执行,即使上一个try语句发生异常,下一个try语句任然会执行
public static void main(String[] args){
ExceptionTest et =new ExceptionTest();
try{
et.f();
}///处理try中丢出的SimpleException异常
catch (SimpleException sn){
System.out.println("Catch it");
//调用异常类的printStackTrace()方法,默认是打印到标准错误流的
///现在我们显示指定输出到标准输出流
///会打印从方法调用处直到异常抛出处的方法调用序列,栈顶是最后一次调用的而栈底是第一次掉用的
sn.printStackTrace(System.out);
}
try{
int[] a= new int[10];
Scanner sc = new Scanner(System.in);
System.out.println("请输入k: ");
int k=sc.nextInt();
sc.close();
System.out.println(a[k]);
}
///处理数组下标越界的异常
catch(ArrayIndexOutOfBoundsException ae){
System.out.println("Catch ArrayIndexOutOfBoundsException");
ae.printStackTrace(System.out);
}
}
}
二.打印异常信息
异常类的基类Exception中提供了一组方法用来获取异常的一些信息.所以如果我们获得了一个异常对象,那么我们就可以打印出一些有用的信息,最常用的就是void printStackTrace()这个方法,这个方法将返回一个由栈轨迹中的元素所构成的数组,其中每个元素都表示栈中的一帧.元素0是栈顶元素,并且是调用序列中的最后一个方法调用(这个异常被创建和抛出之处);他有几个不同的重载版本,可以将信息输出到不同的流中去.下面的代码显示了如何打印基本的异常信息:
package lkl; ///测试异常类Exception的常用方法
public class ExceptionMethods { public static void main(String[] args){
try{
throw new Exception("My Exception");
}///通过捕获异常类的基类Exception就可以捕获所有类型的异常
catch(Exception e){
System.out.println("Caught Exception");
System.out.println("getMessage(): "+e.getMessage());
System.out.println("getLocalizedMessage(): "+e.getLocalizedMessage());
System.out.println("toString(): "+e.toString());
System.out.println("printStackTrace(): ");
e.printStackTrace(System.out);
}
}
}
三.重新抛出异常与异常链
有时候需要将捕获的异常重新抛出,这样在语法上是很简单的.但问题是如果直接抛出的话,这个异常对象中信息将仍然是原来异常对象中的信息.如果我们想要跟新一下这个信息,我们可以调用fillInStackTrace()方法,这个方法返回一个Throwable对象,它是通过将当前调用栈信息填入原来那个对象而得到的.调用fillStackTrace()的那一行就成了异常的新的发生地.如果我们在捕获了异常之后抛出了另外一种异常,那么原来异常发生点的信息会丢失,得到与fillInStackTrace()一样的方法.
所谓的异常链指的是在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息给保留下来.java中的Throwable子类在构造器中都可以接受一个cause(因由)对象作为参数.这个参数其实就是原先的异常对象,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也可以通过异常链追踪到异常最初发生的位置.但是在Throwable的子类中,只对Error,Exception,RuntimeException这三个子类提供了带cause因子的构造器,如果要把其它类型的异常链接起来,就应该使用initCause()方法而不是构造器了.
四.使用finally进行清理
引入finally语句的原因是我们希望一些代码总是能得到执行,无论try块中是否抛出异常.这样异常处理的基本格式变成了下面这样:
try{
///可能会抛出异常的代码
}
catch(Type1 id1){
//处理Type1类型异常的代码
}
catch(Type2 id2){
///处理type2类型异常的代码
}
finally{
///总是会执行的代码
}
其实catch和finally语句也可以只有一个出现.下面的代码显示了finally语句总能运行
package lkl; public class FinallyTest extends Exception{ public static int count=0;
public static void main(String[] args){
while(true){
try{
//无论是不是抛出异常,finally语句都会执行
if(count++==0){
throw new MyException();
}
System.out.println("No exception");
}
catch(MyException me){
System.out.println("MyException");
}
finally{
System.out.println("In finally clause");
if(count==2) break;
}
}
}
}
下面这份代码说明就算在try中有return语句,finally语句仍会得到执行,看起来就像有多个返回点.
package lkl; public class MultipleReturns { public static void f(int i){
System.out.println("Initialization that requires cleanup");
try{///在函数reutrn之前,finally回先执行
System.out.println("Point 1");
if(i==1) return ;
System.out.println("Point 2");
if(i==2) return ;
System.out.println("Point 3");
if(i==3) return ;
System.out.println("Point 4");
if(i==4) return;
}
///在上面的return之前,会先执行finally语句
finally{
System.out.println("Perferming cleanup");
}
}
public static void main(String[] args){
for(int i=1;i<=4;i++)
f(i);
}
}
那么总是会执行的finally语句用来干嘛?在java中我们主要用来清理除内存外的其它一些资源,这些资源包括:已经打开的文件或网络连接,在屏幕上画的图像等.
五.异常处理与继承
最后一个问题是关于异常处理和继承的.当异常处理和继承一起发生的时候,我们需要关注的问题就是父类构造器抛出异常和子类构造器抛出异常的关系,在子类中重写父类方法是怎样声明异常等问题.总结一下其实也就是二个规则:
1).子类重写基类方法抛出的异常必须不比原基类方法抛出的异常类型更大.
2).虽然在语法上对子类构造器的异常声明没有规定,但是子类构造器的异常说明必须包括基类构造器的异常说明.(因为基类的构造器总是会被调用).
下面的代码对此进行了演示:
public class Foul extends BaseballException{
} public class PopFoul extends Foul{
} public class BaseballException extends Exception{
} public class Strike extends BaseballException{ } public class RainedOut extends StormException{ } public interface Storm { public void event() throws RainedOut;
public void rainHard() throws RainedOut;
} public abstract class Inning { public Inning() throws BaseballException{}
///定义抛出异常,但是实际上并没有
public void event() throws BaseballException{};
//声明抛出两个异常
public abstract void atBat() throws Strike ,Foul; public void walk() {}
} public class StormyInning extends Inning implements Storm{ public StormyInning() throws BaseballException,RainedOut{}
public StormyInning(String s)
throws Foul,BaseballException{}
//因为基类方法中没有抛出异常而子类覆盖后的方法抛出了
///所以编译报错
//void walk() throws PopFoul{} //接口不能给基类方法添加异常类型
//public void event() throws RainedOut{} ///重写的方法抛出和基类方法一样的异常是可以的
public void rainHard() throws RainedOut{} //基类的方法抛出了异常,而重写的方法不抛出也是可以的
public void event() {} //重写的方法抛出基类异常的子类也是可以的
public void atBat() throws PopFoul{} public static void main(String[] args) {
try{///下面的两句代码中构造器中可能抛出RainedOut,BaseballException异常
///atBat()方法可能抛出PopFoul异常.所以需要处理三种异常
StormyInning si = new StormyInning();
si.atBat();
}
catch(PopFoul e){
System.out.println("Pop Foul");
}
catch(RainedOut e){
System.out.println("Rained Out");
}
catch(BaseballException e){
System.out.println("Generic baseball");
}
try{//这段代码进行了向上转型,所以处理上面的三种情况外
///还必须处理基类atBat()方法抛出的Strike异常
Inning i = new StormyInning();
i.atBat();
}
catch(Strike e){
System.out.println("Strike");
}
catch(PopFoul e){
System.out.println("Pop Foul");
}
catch(RainedOut e){
System.out.println("Rained Out");
}
catch(BaseballException e){
System.out.println("Generic baseball");
}
}
}
转载:http://blog.csdn.net/acm_lkl/article/details/46422645
Java编程思想学习(九) 异常处理的更多相关文章
- [Java编程思想-学习笔记]第3章 操作符
3.1 更简单的打印语句 学习编程语言的通许遇到的第一个程序无非打印"Hello, world"了,然而在Java中要写成 System.out.println("He ...
- Java编程思想学习(八) 内部类
可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类的定义是简单的,但是它的语法确实很是复杂,让人不是很好理解.下面就内部类做一个小结. 一.内部类的分类 总的来讲内部类分为普通内部类,匿 ...
- java编程思想--学习心得
学习Java编程思想,需要了解语言特性,对于各种名词,能够借助项目代码,解释其含义,不借助搜索工具,明白其在什么样场景下使用,会带来什么样的问题,能否避免这类问题. 学习的过程,与软件开发相同,一样是 ...
- Java编程思想学习(一)----对象导论中多态的理解
1.1抽象过程 1)万物皆对象. 2)程序是对象的集合,他们通过发送消息来告知彼此所要求做的. 3)每个对象都有自己的由其他对象所构成的存储. 4)每个对象都拥有其类型. 5)某一特定类型的所有对象都 ...
- Java编程思想 学习笔记1
一.对象导论 1.抽象过程 Alan Kay曾经总结了第一个成功的面向对象语言.同时也是Java所基于的语言之一的Smalltalk的五个基本特性,这些特性表现了纯粹的面向对象程序设计方式 1)万物皆 ...
- [Java编程思想-学习笔记]第1章 对象导论
1.1 抽象过程 Java是一门面向对象的语言,它的一个优点在于只针对待解问题抽象,而不用为具体的计算机结构而烦心,这使得Java有完美的移植性,也即Java的口号"Write Once, ...
- Java编程思想学习(五)----第5章:初始化与清理
随着计算机革命的发展,“不安全”的编程方式已逐渐成为编程代价高昂的主因之一. C++引入了构造嚣(constructor)的概念,这是一个在创建对象时被自动调用的特殊方法.Java中也采用了构造器,并 ...
- Java编程思想学习(十) 正则表达式
正则表达式是一种强大的文本处理工具,使用正则表达式我们可以以编程的方法,构造复杂的文本模式,并且对输入的字符串进行搜索.在我看来,所谓正则表达式就是我们自己定义一些规则,然后就可以验证输入的字符串是不 ...
- Java编程思想 学习笔记12
十二.通过异常处理错误 Java的基本理念是“结构不佳的代码不能运行”. Java中的异常处理的目的在于通过使用少于目前数量的代码来简化大型.可靠的程序的生成,并且通过这种方式可以使你更加自信:你的 ...
随机推荐
- HDU 1251 统计难题
字典树又一基本题 代码: #include <iostream> #include <cstdio> #include <cstring> #include < ...
- 使用javascript获取url中的参数
方法一: //取url参数 var type = request("type") function request() { var query = location.search; ...
- 首次安装Pycharm出现No Python interpreter selected解决方法
刚装完Pycharm,新建Project的时候,出现了No Python interpreter selected.网上的教程里path interpret栏里应该选中python.exe,但是我搜遍 ...
- 大话redis/memcache缓存
通常情况下,随着业务量增加,对后端数据库的访问压力也会随之加大.当数据库访问压力渐渐增大时,除了升级数据库配置提高数据库本身的抗压能力外,我们也可以采用在应用服务器与数据库服务器之间架设数据库缓存服务 ...
- KeyBord事件分发和接收简要过程代码示例
step1:调用ViewRootImpl的内部类ImeInputStage的成员函数onProcess来判断输入法是否处于激活状态 final class ImeInputStage extends ...
- IC系统组成概论
IC系统是什么? 对算法工程师来说,IC系统是完成特定功能的硬件.对架构设计师来说,IC系统包括控制,运算,存储部分.电路设计工程师来说,IC系统是加法器,乘法器,与非门,运算放大器,开关电容等的搭配 ...
- 微软职位内部推荐-SW Engineer II for Skype
微软近期Open的职位: We are the Skype Beijing team. Skype division drives the communications strategy for Mi ...
- [转]redis 五种数据类型的使用场景
FROM : http://blog.csdn.net/gaogaoshan/article/details/41039581#t5 String 1.String 常用命令: 除了get.set.i ...
- Tasks.Parallel
.Net多线程编程-System.Threading.Tasks.Parallel System.Threading.Tasks.Parallel类提供了Parallel.Invoke,Paral ...
- OpenGL2.0及以上版本中glm,glut,glew,glfw,mesa等部件的关系
OpenGL2.0及以上版本中gl,glut,glew,glfw,mesa等部件的关系 一.OpenGL OpenGL函数库相关的API有核心库(gl),实用库(glu),辅助库(aux).实用工具库 ...