编程思想之异常处理

什么叫异常处理?

什么叫异常(Exception)?顾名思义就是非正常的情况,出现了不希望出现的意外,异常处理就是遇到这种意外时准备的对策和解决方案。比如您开着一辆劳斯莱斯在公路上行走,突然前面出现一个小孩,幸好您眼疾手快紧急刹车,而避免了一场交通事故。在这个例子中突然出现的小孩就是异常,紧急刹车就是异常处理(面对这种突发情况采取的解决方案)。

程序来源于现实,是现实的抽象和模拟,也会有异常,因此异常的处理就显示的极为重要。试想如果您的手机的某个应用使用两下就崩溃了,或都出现各种意想不到情况,您是不有种想扔手机的冲动,您还会再用这种App吗?

异常处理的解决方案

C++中的异常处理:

C++中的异常处理主要有两种实现方式:(1).返回错误码,(2).try...catch机制捕获异常。

返回错误码

返回错误码是传统的C语言的处理异常的方式。在一个函数中,如果发生某种不该发生的错误或异常,则直接返回一个错误码,函数的调用方在调用该函数的时候根据返回的错误码的类型进行相应的处理。由于C++的历史原因(由C发展而来),为了兼容性,现在的C++程序中仍能看到很多用错误码的方式向上抛出异常信息。

这种方式一般用于对性能的要求较高的地方,常用在系统库和接口的实现中。因为这种方式可以精确控制逻辑,让程序员只关注逻辑和性能,而程序的完整性和健壮性,交给上层调用方去处理。 其实现方式如下:

例【1】对两个浮点数进行除法运算,如果除数为0则抛出错误码。

1.定义错误码 RetCode.h

#ifndef RETCODE_H
#define RETCODE_Htypedef long ReturnCode;//成功
#define RT_OK                       0L
//失败
#define RT_FAILED                   1L//参数错误
#define RT_PARAM_ERROR              2L
//无法预知的错误
#define RT_UNEXPECTED_ERROR         3L
//空指针
#define RT_NULL_PTR                 4L
//分配内存错误
#define RT_ALLOCATE_MEMORY_FAILED   5L
//不支持的操作
#define RT_UNSUPPORT_OPERATE        6L#endif    /* RETCODE_H */

错误码的实现方式:

#include "RetCode.h"
#include <iostream>//判断一个浮点数是否为0
#define DEQUALZEOR(X)  ((X) <= 0.0001 && (X) > -0.0001) //除法运算
ReturnCode division(double dividend, double divisor, double& result)
{if(DEQUALZEOR(divisor))return RT_PARAM_ERROR;else{result = dividend / divisor;return RT_OK;}
}int main(int argc, char** argv)
{double r = 0;ReturnCode ret = division(5, 0, r);if(RT_PARAM_ERROR == ret){std::cout << "参数错误,检查是否除数为0。" << std::endl;} else{std::cout << r << std::endl;}return 0;
}

try...catch机制捕获异常

上面这种返回错误码的方式,可能有效地定义和控制各种错误和异常,但是每个调用都要检查错误值,极不方便,也容易让程序规模加倍。其实C++有一种专门的机制用于处理异常,那就是try...catch机制。

try

{

        // 抛出异常,或可能抛出异常的调用

} catch (ExceptioinObject e)

{

        // 处理异常

} catch (...)

{

        // 捕获所有类型的异常

}

说明:

1.try中的代码块用于抛出(throw)异常,或调用可能抛出异常的函数、对象;

2.throw关键字可用于抛出任意类型的对象,可以是类的对象,也可以是内置数据类型的对象(常称为变量),还可以是指针(指针本身就是一个对象,是一个特殊的对象,用于指向另外一个对象的地址);

3.第一个catch括号中的e表示异常对象,这个对象也可以是任意类型的对象。当throw出的对象类型与e的类型相同时,则捕获到异常,进行catch代码块中的异常处理。

4.第二个catch括号中的”...”表示任意类型,可以捕获任意类型的异常。

5.一个try可以对应一个或多个catch,catch子句被检查的顺序与它们在try块之后排列顺序相同,一旦找到了一个匹配,则后续的catch子句将不再检查,按此规则,catch_all子句(catch(...){})处理表前面所列各种异常之外的异常。

6.catch子句可以包含返回语句(return),也可不包含返回语句。包含返回语句,则整个调用函数结束,后面的语句不再执行。而不包含返回语句,则执行catch列表之后的下一条语句。

异常处理,把正常逻辑和错误处理分离开来,由函数实现方抛出异常,由调用者捕获这个异常,调用者就可以知道程序函数调用出现错误了,并去处理,而是否终止程序就把握在调用者手里了。

我们将用上面的例子用try...catch...方式实现

例【2】对两个浮点数进行除法运算,如果除数为0则抛出异常。

#include <iostream>
//判断一个浮点数是否为0
#define DEQUALZEOR(X)  ((X) <= 0.0001 && (X) > -0.0001) using namespace std;//除数不为0异常
class DivisorZeorException{
public:DivisorZeorException(double value) : m_value(value){}void showInfo(){cout << "the divisor " << m_value << " is wrong." << endl;}
private:double m_value;
};double division(double dividend, double divisor)
{if ( DEQUALZEOR(divisor) ){throw DivisorZeorException(divisor);}return dividend / divisor;
}int main()
{try{double result = division(10, 0);cout << "result: " << result << endl;} catch (DivisorZeorException e){e.showInfo();} catch(...){cout << "all exception" << endl;}return 0;
}

异常的高级特性 

(1).异常规范

可在函数的后面用throw列出可能抛出的异常,并保证该函数不会抛出任何其他类型的异常。如上面的division函数改为:

double division(double dividend, double divisor) throw(DivisorZeorException)
{if ( DEQUALZEOR(divisor) ){throw DivisorZeorException(divisor);}return dividend / divisor;
}

如果在运行时,函数抛出了一个没有被列在它的异常规范中的异常(并且函数中所抛出的异常,没有在该函数内部处理),则系统调用C++标准库中定义的函数unexpected()。如果异常规范形式为throw(),则表示不得抛出任何异常。

(2).异常类的继承

异常类也可以继承,在catch捕获异常的时候应按照由子类到父类的顺序,因为atch子句被检查的顺序与它们在try块之后排列顺序相同,所以在catch子句列表中最特化的(匹配条件最严格的,即子类)catch子句必须先出现 。假设有三个异常类,ExceptionC是ExceptionB的子类,ExceptionB是ExceptionA的子类,try...catch...就写成:

try

{

    //可能抛出异常的语句

}

catch (ExceptionC c)

{

    //处理ExceptionC异常

}

catch (ExceptionB b)

{

    //处理ExceptionB异常

}

catch (ExceptionA a)

{

    //处理ExceptionA异常

}

参考资料:http://www.weixueyuan.net/view/5881.html

C++标准中的异常类

C++标准中已经定义了一套常用的异常类,它们之间的层次关系如下:

exception是所有异常类的父类,仅仅定义了拷贝构造函数、拷贝赋值运算符、一个虚析构函数和一个名为what的虚成员,what函数返回一个const char*,用于返回一些异常信息。

C++标准中定义的类虽然不多,但我们在定义自己的异常类的时候还是应该尽量利用已有的异常类,至少就继承自exception类,保持结构的统一性。

Java中的异常处理:

异常的类型

Java有一套非常完备的异常处理机制,使用起来简单而灵活。JDK把一些常见的异常都封装成了一个一个具体的类,java.lang.Throwable是所有异常类的父类。异常处理类的主要层次关系如下:

Throwable 类是 Java 语言中所有错误或异常的超类。Error是应用程序不应该试图捕获的严重问题,比如OutOfMemoryError、ThreadDeath等,在执行该方法期间,无需在其 throws 子句中声明可能抛出但是未能捕获的 Error 的任何子类,因为这些错误可能是再也不会发生的异常条件。Exception 是所有异常类的父类,程序本身可以处理的异常。Exception又分为运行时异常(发生在程序运行过程中,又叫uncheckException)和非运行时异常(发生在编译阶段,又称checkException)。

RuntimeException及其所有子类都属于运行时异常,这类异常在编译时不会被检测,只有在运行时才会发生。程序中可以选择捕获处理,也可以不处理(不捕获程序也能编译通过),但基于程序健壮性方面考虑,对有可能出现的异常一定要捕获处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

在Exception范围内,除了运行时异常的类都属于非运行时异常,又叫受检查异常类。这类异常在编译时会被检测,调用了会抛出这类异常的方法,如果调用方不捕获异常又不向上抛出异常,编译将会报错。这类异常一般是没有遵守Java语言规范的代码造成的,容易看的出来,并且较容易解决,常见的如IOException,SQLException。

我们再看看一些常见异常的继承关系,如下图

捕获异常的方法

抓取异常的方法(或说语法)是try、catch、finally,其结构如下:

try {

//1.可能会抛出异常的代码块

} catch (Throwable throwable) {

//2.异常发生时的处理方案

} finally {

//3.不管有没有捕获到异常(异常有没有发生),都会执行的代码

//此部分也可省略

}

说明:catch括号中的条件必须是Throwable或其子类的对象。try后面必须跟catch或finally(catch和finally必须至少有一个或两都有)。  finally括起来的代码不管有没有捕获到异常,都会执行,IO操作、数据库操作的close方法一般都放在这里,因为不管有没有发生异常,在程序退出前都需要释放资源。

异常处理的实现

1.用Java实现文件的拷贝,将制定文件的内容复制到另一个文件。

 public void copyFile(String source, String destination) {File file = new File(source);BufferedReader in = null;BufferedWriter out = null;try {if (file.exists() && file.isFile()) {in = new BufferedReader(new FileReader(file));out = new BufferedWriter(new FileWriter(new File(destination)));String line;StringBuffer str = new StringBuffer();while (null != (line = in.readLine())) {str.append(line + "\r\n");}out.write(str.toString());} else {throw new FileNotFoundException("此文件不存在或此象路径名表示的文件不是一个标准文件");}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {in.close();out.close();} catch (IOException e) {e.printStackTrace();}}}

2.自定义异常

需求:要从控制台上输入一串内容,但输入的必须是数字,如果输入的不是数字,抛出异常。

分析:JDK的库中好像并没有针对控制台输入的异常类,因此需要自己定义一个类来监控输入的异常。

import java.io.IOException;
import java.util.Scanner;//自定义异常类
class ConsoleInputException extends IOException {public ConsoleInputException() {super();System.err.println("The input content is not number.");}public ConsoleInputException(String message) {super(message);}
}public class Test {//控制台输入数字public static void inputFromConsole() throws ConsoleInputException{Scanner scanner = new Scanner(System.in);while(scanner.hasNext()) {String line = scanner.next();if(line.matches("\\d+")) {     //判断是否为数字System.out.println("contet:" + line);} else {throw new ConsoleInputException();}}scanner.close();}public static void main(String[] args) {try {inputFromConsole();} catch (ConsoleInputException e) {e.printStackTrace();}}
}

JavaScript中的异常处理:

JavaScript不像C++和Java一样,有专门的语法去定义和实现异常处理,JavaScript的异常处理主要体现在对页面输入内容的校验。JavaScript的异常处理的实现方式主要有两种,一种是alert弹出警告框,一种是在页面上给出错误提示。(可能还有其它方式,本人才疏学浅没掌握全,如果您知道请告诉我)

如下面这样一个登录页面,在点击登录按钮时,检查输入的用户名格式是否正确,用户名要求是邮箱格式。您可以有如下两种实现方式。

alert弹出警告框

<script language="javascript" type="text/javascript">//验证邮箱function isEmail(text) {var reg = /^([\w-.])+@([\w-])+((\.[\w-]{2,3}){1,2})$/;return reg.test(text);}function checkForm() {//username为用户名表单的IDvar usertName = document.getElementById("username").value;if(!isEmail(usertName)) {alert("请输入正确的邮箱!");} else {var form = document.getElementById("loginForm");form.submit();}}
</script>

调用语句如下:

<button type="button" onClick="checkForm()" class="btn btn-primary">登录</button>

效果如下:

页面上给出错误提示

上面这种方式虽然能实现功能,但每次都弹出提示框,是不很难看,且很不友好?其实我们可以有更优雅的提示方式,就是在页面上专门预留一个位置来提示用户错误的操作。如下

function checkForm() {//username为用户名表单的IDvar usertName = document.getElementById("username").value;if(!isEmail(usertName)) {document.getElementById( "showTipsInfo").innerHTML = "请输入正确的邮箱!"document.getElementById("infoTips").style.display = "block";} else {document.getElementById("infoTips").style.display = "none";var form = document.getElementById("loginForm");form.submit();}
}

网页HTML:(这里使用了第三方库bootstrap,如果有些样式看不懂请忽略这些样式)

<div class="well span5 center login-box"><div class="alert alert alert-error" id="infoTips" style="display:none"><span id="showTipsInfo">tips info</span></div><form class="form-horizontal" action="./std_index.html"  method="post" id="loginForm"><fieldset><div class="input-prepend" title="用户名" data-rel="tooltip"><span class="add-on"><i class="icon-user"></i></span><input autofocus class="input-large" name="username" id="username" type="text" value="admin" /></div><div class="clearfix"></div><div class="input-prepend" title="密码" data-rel="tooltip"><span class="add-on"><i class="icon-lock"></i></span><input class="input-large" name="password" id="password" type="password" value="admin123456" /></div><div class="clearfix"></div><div class="input-prepend" title="用户类型" data-rel="tooltip"><span class="add-on"><i class="icon-list"></i></span><!--<input type="text"/ class="input-large span10">--><select class="input-large" name="select" id="select"><option value="1">学生</option><option value="2">教师</option><option value="4">校级工作人员</option>--><option value="5">管理员</option></select></div><div class="clearfix"></div><div class="input-prepend"><label class="remember" for="remember"><input type="checkbox" id="remember" />记住密码</label></div><p class="center span5"><button type="button" onClick="checkForm()" class="btn btn-primary">登录</button></p></fieldset></form>
</div>

当输入正确时,什么也不提示,保持页面整洁:

当输入错误时,显示错误信息:

异常处理的作用

1.使程序更稳定,更健壮;

2.友好提示,改善用户体验;

3.给出错误信息,方便程序维护;

4.可以处理一些原子性的操作

(比如交易等数据操作时,如果发生错误,可在捕获到异常时重置成操作之前的状态,即叫回滚,数据库中的概念在特定的情况下也可以由代码来实现)

如果您有什么疑惑和想法,请在评论处给予反馈,您的反馈就是最好的测评师!由于本人技术和能力有限,如果本博文有错误或不足之处,敬请谅解并给出您宝贵的建议!

========================编程思想系列文章回顾========================

编程思想之正则表达式

编程思想之迭代器

编程思想之递归

编程思想之回调

C++、Java、JavaScript中的异常处理(Exception)相关推荐

  1. javascript中的异常处理

    简单来说,可以用如下的语句,来处理javascript中的异常. function message() {     try {         adddlert("Welcome guest ...

  2. java多线程中的异常处理

    2019独角兽企业重金招聘Python工程师标准>>> 在java多线程程序中,所有线程都不允许抛出未捕获的checked exception,也就是说各个线程需要自己把自己的che ...

  3. IDEA java编译中出现了Exception in thread “main java.lang.UnsupportedClassVersionError

    这个问题确实是由较高版本的JDK编译的java class文件试图在较低版本的JVM上运行产生的错误. 在idea中需要修改的有两区,四个地方 1. 修改项目编译器 Crtl+Shift+A 进入如下 ...

  4. 【转】Java中关于异常处理的十个最佳实践

    原文地址:http://www.searchsoa.com.cn/showcontent_71960.htm 导读:异常处理是书写强健Java应用的一个重要部分,Java许你创建新的异常,并通过使用 ...

  5. java 异常处理发生异常_Java中的异常处理

    java 异常处理发生异常 Exception Handling in Java is a very interesting topic. Exception is an error event th ...

  6. 快速而优雅的处理 JavaScript 中的错误与异常(一键 get 新技能)

    古之立大事者,不惟有超世之才,亦必有坚忍不拔之志.--苏轼 写在前面 在我们的实际编程中,抛出异常(代码报错)是最正常不过的,但是怎么处理异常这就要因人而异的,有的人遇到异常,通常会通过某种办法解决这 ...

  7. crytojs加密 java解密,使用CryptoJS在Javascript中加密并在Java中解密

    我试图使用谷歌的 https://code.google.com/p/crypto-js/#AES加密JavaScript,就像它的例子一样.问题是,我试图用Java解密它的结果是不同的.我可以看到的 ...

  8. Java中的异常 Exception

    Java中的异常 Exception java.lang.Exception类是Java中所有异常的直接或间接父类.即Exception类是所有异常的根类. 比如程序: public class Ex ...

  9. 第八节:详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架

    前言 大家好,给大家带来详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架的概述,希望你们喜欢 JAVA 异常 try...catch...finally结构的使用方法 class Tes ...

最新文章

  1. 科学家发联合声明:强烈谴责首例免疫艾滋病基因编辑
  2. uicontrol图形对象用户界面的用法
  3. 七、Mosquito 集群搭建
  4. 解决ios10以上H5页面手势、双击缩放问题
  5. CIDR地址块及其子网划分
  6. Java 8 StampedLocks与ReadWriteLocks和同步
  7. LeetCode 11. 盛最多水的容器(双指针)
  8. Parts of a URL
  9. error: ‘VPX_IMG_FMT_RGB32’ undeclared (first use in this function); did you mean ‘VPX_IMG_FMT_NV12’?
  10. 教之初计算机考试试题,教之初题库管理系统操作教程-考题处理
  11. 【线性代数】1.6矩阵的特征值和特征向量
  12. 闲置商标转让怎样管理最好?
  13. 从0到1的电商架构应该怎么做?有哪些坑?
  14. 简述与机器学习相关的十大常用Python库,极简化算法编程
  15. 现在开一间网吧还能挣钱么?
  16. RT-Thread— 知识点总结(RTT认证+面试题汇总)
  17. 达梦数据库查询模式名,表名,字段名
  18. 如何破解Aspose.word带水印问题
  19. python将考勤表中同一个人,同一日期的多行出勤时间转换成同一列显示的方法
  20. Redis基础(二)—— 基本命令与数据类型

热门文章

  1. 电子技术——反馈对放大器极点的影响
  2. 牛客练习赛31 C 无畏死灵术士莉莲娜与锁链面纱(dfs + 期望dp)
  3. 基于python的验证码自动识别系统设计与实现
  4. 2015070306 - EffactiveJava笔记 - 第55条 谨慎地进行优化(2)
  5. Design with the User in Mind--从用户角度进行设计
  6. 关注新技术,打破自满
  7. PATA1138_偷鸡大法
  8. 计算机组装信息化教学,全国信息化计算机应用技术水平教育培训试卷(计算机组装与维护)二...
  9. 小桥加加的英语学习博客
  10. Github每日精选(第56期):支持多语言的文字识别EasyOCR