异常描述

1.简介

    为了全面了解“异常”的概念,先来分析一个实例。假定要编写一个Java程序,该程序读取用户输入的一行文本,并在终端显示该文本。这里是一个演示Java语言I/O功能的简答回显(echo)程序。如果认为代码一定能正常运行,则可以编写一下程序。

import java.io.*;
public class EchoDemo{
public static void main(String[] args){
System.out.println("Enter text to echo");
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader inputReader = new BufferedReader(isr);
String inputLine = inputReader.readLine();
System.out.println("READ:\t"+inputLine);
}
}

分析这段代码,在EchoDemo类中,第3行声明了一个main方法;第4行提示用户输入文本;在第5、6行设置BufferReader对象连接到InputStreamReader,而InputStreamReader又连接到标准输入流System.in;第7行读入一行文本;第8行用标准输出流System.out显示出该文本。

    实际上EchoDemo类是完全有可能出现问题。要调用readLine方法的时候要正确读取输入,下面几种假设都必须成立:

  • 1.假定键盘有效,键盘能与计算机正常通信;
  • 2.假定键盘数据可从操作系统传输到Java虚拟机,又从Java虚拟机传输给inputReader。

 JDK执行结果

    readLine方法有事会产生IOException。编译器要求“捕获”或“声明”IOException。

  • “捕获(catch)”指当readLine方法产生错误时截获该错误,并处理或记录问题
  • “声明(declare)”指错误可能引发IOException,并通知调用该方法的任何代码:可能产生异常。

捕获异常

import java.io.*;
public class EchoDemo{
public static void main(String[] args){
System.out.println("Enter text to echo");
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader inputReader = new BufferedReader(isr);
try{
String inputLine = inputReader.readLine();
System.out.println("READ:\t"+inputLine);
}catch(IOException e){
System.out.println("Exception encountered:" + e);
} }
}

新添加的代码块包含关键字try和catch,表示如果要读取输入成功则正常运行,如果读取输入失败则捕获问题(由IOException对象表示),并采取相应的措施。

声明异常

import java.io.*;
public class EchoDemo{
public static void main(String[] args) throws IOException{
System.out.println("Enter text to echo");
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader inputReader = new BufferedReader(isr);
String inputLine = inputReader.readLine();
System.out.println("READ:\t"+inputLine);
}
}

此代码中未采取措施来处理readLine方法的可能故障,但声明:main方法可能产生IOException,调用该方法的任何代码都必须做好处理问题的准备。

 JDK执行结果

2.异常概念

    Java等语言的大多数应用程序始终在使用消息。在GUI中,消息经常被用作发送事件信息(用户与应用程序交互的一些相关数据类型,如单击按钮,或在文本框中输入文本)的方式。

    从某种意义上说,异常就是这样的一种消息:她传递一些系统问题、故障及未按规定执行的动作的小官信息。异常包含信息,以将信息从应用程序的一部分发送到另一部分。

3.异常类的层次结构

    在Java中,所有异常有一个共同祖先Throwable(可抛出)。Throwable指定代码中可用异常传播机制通过Java应用程序传输的任何问题的共性。

    Throwable有两个重要的子类:Exception(异常)和Error(错误),二者都是Java异常处理的重要子类,各自都包含大量子类。

  • “异常”是应用程序中可能的可预测、可恢复问题。Java API文档记录给出的定义是:“合理应用程序可能需要捕获的情况。”一般地,大多数异常表示轻度到中度的问题。
  • “错误”表示运行应用程序中的比较严重问题。Java API文档记录给出的定义是:“是Throwable的一个子类,代表严重问题,合理应用程序不应该试图捕获。大多数此类错误属反常情况。”

    Java中的异常可以分为两种异常类型--受检异常(checked exception)和非受检异常(unchecked exception)。非受检异常指的是java.lang.RuntimeException和java.lang.Error类及其子类,其他所有的异常类都被称之为受检异常。两种类型的异常在作用上没有差别,唯一的差别是使用受检异常时的合法性要在编译时刻由编译器来检测,因此受检异常在使用的时候需要比非受检异常更多的代码来规避编译错误。

    一直以来,关于在程序中到底是该使用受检异常还是非受检异常,开发者之间一直存在着争议,毕竟两者各有优缺点。受检异常的特点在于它强制要求开发者在代码中进行显式的声明和捕获,否则就会产生编译错误。这种限制从好的方面来说,可以防止开发者意外地忽视某些出错的情况,因为编译器不允许出现未被处理的受检异常;从不好的方面来说,受检异常对程序中的设计提出了更高的要求。不恰当的使用受检异常,会使代码中充斥着大量没有实际作用、只是为了通过编译而添加的代码。而非受检异常的特点是,如果不捕获异常,不会产生编译错误,异常会在运行时刻才被抛出。非受检异常的好处是可以去掉一些不需要的异常处理代码,而不好之处是开发者可能忽略某些应该处理的异常。

    目前的主流意见是,最好优先使用非受检异常

4.异常的处理或声明

4.1 处理异常:try、catch和finally

如果想要捕获异常,则必须在代码中添加异常处理器块。这种Java结构可能包含3部分,都有Java关键字。
示例:

import java.io.*;
public class EchoDemo{
public static void main(String[] args){
System.out.println("Enter text to echo");
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader inputReader = new BufferedReader(isr);
try{
String inputLine = inputReader.readLine();
System.out.println("READ:\t"+inputLine);
}catch(IOException e){
System.out.println("Exception encountered:" + e);
}finally{
System.out.println("My job here is done");
}
}
}

try块

    在将一个或者多个语句放入try时,则表示这些语句可能抛出异常。编译器知道可能要发生异常,于是用一个特殊结构评估块内所有语句。

catch块

    当问题出现时,一种选择是定义代码块来处理问题,catch块的目的便在于此。catch块是try块所产生异常的接受者。基本原理为:一旦生成异常,则try块的执行终止,JVM将查找相应的catch块。

finally块

    还可以定义这样一个代码块,无论试图运行try块代码的结果如何,该块一定运行。finally块的作用便在于此。在常见的所有环境中,finally块都将运行。无论try是否运行完,无论是否产生异常,也无论异常是否在catch块得到处理,finally都将运行。

4.2 try-catch-finally的规则

  • 必须在try之后添加catch或finally块。try块后可同时接catch和finally,且至少有一个块。
  • 必须遵守块的顺序:若代码同时使用catch和finally块,则必须将catch块放在try块之后。
  • try块与相应的catch或finally之间可能不存在语句。
  • catch块与特定异常的类型相关。
  • 一个try块可能有一个或者一个以上的catch块。如果是一个以上的catch块的话,将执行第一个匹配块。有一个经验法则:要按照从最具体到最一般的顺序组织处理块
  • 除下列情况,总将执行finally作为结束:

       a. JVM过早终止(调用System.exit(int))

       b. 在finlly块中抛出一个未处理的异常

       c. 计算机断电、失火,或遭遇病毒攻击
  • 可嵌套try-catch-finally结构。
  • 在try-catch-finally结构中可以重新抛出异常。

4.3 声明异常

若要声明,则必须将其添加到方法签名块的结束位置,即输入部分之后。
示例:

public void errorProneMethod(int input) throws java.io.IOException{
//Code for the method,including one or more method
//calls that may produce an IOException
}

这样,声明的异常将传给方法调用者,而且也通知了编译器:该方法的任何调用者必须遵守处理或声明规则。

4.4 声明异常的规则

  • 必须声明方法可能抛出的任何可检测异常(checked exception)。
  • 非检测异常(unchecked exception)不是必需的,可声明,也可不声明。
  • 调用方法必须遵守任何可检测异常的处理或声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。

4.5 异常的API

    实际上,Java的Exception和Errors的所有行为都集中在Throwable类中。
其代码(节选)如下

public class Throwable implements Serializable {
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -3042686055658047285L; /**
* The throwable that caused this throwable to get thrown, or null if this
* throwable was not caused by another throwable, or if the causative
* throwable is unknown. If this field is equal to this throwable itself,
* it indicates that the cause of this throwable has not yet been
* initialized.
*
* @serial
* @since 1.4
*/
private Throwable cause = this; /**
* Specific details about the Throwable. For example, for
* {@code FileNotFoundException}, this contains the name of
* the file that could not be found.
*
* @serial
*/
private String detailMessage;
}

说明:

  • Throwable类定义实现Serializable接口,这意味着,Java的每个Exception类和Error类也实现Serializable接口。
  • Throwable存储3个属性:Message(消息)、Stack Trace(栈跟踪)和Cause(原因)。
名称 描述 读-写
Message String 描述性文本.详细介绍异常 读(只能在构造函数中设置)
Stack Trace StackTraceElement[] 引发异常的所有方法调用的记录 读-写
Cause Throwable 产生此异常的起因 读-写(只能设置一次)

Message进一步描述与异常相关的问题,可在两个构造函数中设置,可调用getMessage方法检索它

Stack Trace提供与异常相关的调用栈的记录--应用程序代码中特定异常导致代码停止执行的一系列位置。栈跟踪详细记录在抛出异常时执行停止的应用程序各点。

Cause是JDK1.4引入的新特性。这样,可以将另外一个Throwable对象指定为当前异常的“起因”。getCause和initCause方法提供了获取和设置此属性的方法。

5.异常声明是API的一部分

     主要是针对受检异常的。在一个公开方法的声明中使用throws关键词来声明其可能抛出的异常的时候,这些异常就成为这个公开方法的一部分,属于开放API。在维护这个公开API的时候,这些异常有可能会对API的演化造成阻碍,使得编写代码时不能不考虑向后兼容性的问题。

     如果公开方法声明了会抛出一个受检异常,那么这个API的使用者肯定已经使用了try-catch-finally来处理这个异常。如果在后面的版本更新中,发现该API抛出这个异常是不合适的,也不能直接把这个异常的声明删除。因为这样会造成之前的API使用者的代码无法通过编译。

     因此,对于API的设计者来说,谨慎考虑每个公开方法所申明的异常是很有必要的。因为一旦加了异常声明,在很长的一段时间内部都无法甩掉它。这也是为什么推荐使用非受检异常的一个重要原因,非受检异常不需要声明就可以直接抛出。但是对于一个方法抛出的非受检异常,也需要在文档中进行说明。

Java学习笔记--异常描述的更多相关文章

  1. Java学习笔记--异常机制

    简介 在实际的程序运行过程中,用户并不一定完全按照程序员的所写的逻辑去执行程序,例如写的某个模块,要求输入数字,而用户却在键盘上输入字符串:要求打开某个文件,但是文件不存在或者格式不对:或者程序运行时 ...

  2. java学习笔记 --- 异常

    异常 (1)程序出现的不正常的情况. (2)异常的体系   Throwable    |--Error  错误,严重问题,我们不处理.    ·    |--Exception 异常     |--R ...

  3. 0016 Java学习笔记-异常-如果try-catch-finally中都存在return语句会怎样?

    上午在搜索"System.runFinalization"的时候,搜到 http://www.cnblogs.com/Skyar/p/5962253.html ,其中有关于try- ...

  4. JAVA学习笔记—review基本知识[反射与异常]

    JAVA学习笔记—review基本知识[反射与异常] 1.异常: 1.1异常的分类: Java会将所有的异常封装成对象,其根本父类为Throwable. Throwable有两个子类:Error 和E ...

  5. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  6. Java学习笔记4

    Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...

  7. java学习笔记5--类的方法

    接着前面的学习: java学习笔记4--类与对象的基本概念(2) java学习笔记3--类与对象的基本概念(1) java学习笔记2--数据类型.数组 java学习笔记1--开发环境平台总结 本文地址 ...

  8. 20145230《java学习笔记》第九周学习总结

    20145230 <Java程序设计>第9周学习总结 教材学习内容 JDBC JDBC简介 JDBC是用于执行SQL的解决方案,开发人员使用JDBC的标准接口,数据库厂商则对接口进行操作, ...

  9. java学习笔记之OOP(二)

    java学习笔记二.面向对象[OOP]Object Oriented Programming 一.三大特性: 1.封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用 ...

随机推荐

  1. 用Python设计一个经典小游戏

    这是关于Python的第9篇文章,介绍如何用Python设计一个经典小游戏:猜大小. 在这个游戏中,将用到前面我介绍过的所有内容:变量的使用.参数传递.函数设计.条件控制和循环等,做个整体的总结和复习 ...

  2. Day3 Pyhon的六大数据类型

    Python3 中有六个标准的数据类型: Number(数字) String(字符串) List(列表) Tuple(元组) Sets(集合) Dictionary(字典) Number(数字) Py ...

  3. 关于JS面向对象中原型和原型链以及他们之间的关系及this的详解

    一:原型和原型对象: 1.函数的原型prototype:函数才有prototype,prototype是一个对象,指向了当前构造函数的引用地址. 2.函数的原型对象__proto__:所有对象都有__ ...

  4. Spring Boot 之构建Hello Word项目

    1.创建一个maven项目 如下步骤: (第一步) (第二步) (第三步) 2.配置pom.xml文件 加载一些依赖包.字符集.指定jdk.编译插件. <project xmlns=" ...

  5. 看完48秒动画,让你不敢再登录HTTP网站(附完整示例代码)

    在我的 单点登录SSO示例代码 一文中,强烈不建议部署HTTP的SSO服务站点. 在此写个基于网络包嗅探的HTTP会话劫持程序,给大家一个直观的危害性展示. 示例中,我在一台Mac上登录58同城,被另 ...

  6. Dockerfile 常用指令 - 每天5分钟玩转 Docker 容器技术(16)

    是时候系统学习 Dockerfile 了.下面列出了 Dockerfile 中最常用的指令,完整列表和说明可参看官方文档. FROM指定 base 镜像. MAINTAINER设置镜像的作者,可以是任 ...

  7. 省市联动_简单的Demo,适用于各种二级菜单联动

    最近搞了一个功能,是查询页面需要用到二级菜单联动,获取到选中的属性value传入到后台. 平常都是用AJAX或者JQuery ,通过XML或者JSON的方式,这样的话需要调用数据库,像典型得到省市联动 ...

  8. Python正则表达式完全指南(上)

    正则表达式处理文本有如疾风扫秋叶,绝大部分编程语言都内置支持正则表达式,它应用在诸如表单验证.文本提取.替换等场景.爬虫系统更是离不开正则表达式,用好正则表达式往往能收到事半功倍的效果. 介绍正则表达 ...

  9. 虚拟机安装Android最详细教程

    虚拟机想必大家都听说过,有些同学还用过.虚拟机可以模拟出一个操作系统,基于物理机创建.可以模拟常见的 Windows,ubuntu等等. 在使用虚拟机的过程中,想必大家都遇到过一些棘手的问题,尤其是安 ...

  10. js的event事件

    一 .  焦点:使浏览器能够区分区分用户输入的对象,当一个元素有焦点的时候,那么他就可以接收用户的输入. 我们可以通过一些方式给元素设置焦点 1.点击 2.tab   3.js 不是所有元素都能够接受 ...