近些年直接被一个题材所苦恼,就是写出来的先后老是出新无故崩溃,有的地点和谐通晓或者有标题,可是部分地方又历来不可能知道有啥难点。更苦逼的业务是,大家的程序是急需7×24劳动客户,纵然不必要实时精准零差错,可是总不可以冒出断线丢失数据状态。故刚好通过拍卖该难题,找到了一些缓解方案,怎么捕获做客不合规内存地址或者0除以一个数。从而就蒙受了这么些结构化分外处理,今就简短做个介绍认识下,方便大家遇到相关题材后,首先知道难题由来,再就是何许化解。废话不多说,上面进入正题。

转自:

异常是指程序运行时(非编译时)所发生的畸形意况或不当,当程序违反了语义规则时,JVM就会将现出的荒谬表示为一个老大并抛出。这一个万分可以在catch程序块中开展捕获,然后开展处理。

JAVA非凡机制

一、基本概念
在Java中那种在程序中运行时可能出现的一部分谬误称为卓殊。Java处理卓殊时,如若某个方法抛出更加,既可以在现阶段格局中展开捕捉,然后处理该更加,也得以将分外向上抛出,由艺术调用者来拍卖。分外爆发后,借使不做其余处理,程序就会被终止。
二、十分捕获和拍卖
1、Java至极处理涉嫌到三个重大字,分别是:try、catch、finally、throw、throws。
Try:可能暴发格外的Java语句
Catch:激发被抓走的百般
finally:方法重临前总要执行的代码.
throw:用于抛出一个要命对象
throws:在宣称方法时,用于指定该办法或者抛出的不得了。
2、try、catch、finally三个语句块应注意的题材(笔试首要)
首先、try、catch、finally五个语句块均不可以独立选拔,三者可以组成
try…catch…finally、try…catch、try…finally二种结构,catch语句可以有一个或多少个,finally语句最多一个。
第二、try、catch、finally七个代码块中变量的效用域为代码块内部,分别独立而不可能相互走访。借使要在多个块中都可以访问,则必要将变量定义到这几个块的外场。
其三、多少个catch块时候,只会协作其中一个丰硕类并执行catch块代码,而不会再实施其他catch块,并且匹配catch语句的各样是由上到下。
第四、无论程序是不是有相当,并且无论之间try-catch是不是顺遂推行完结,都会履行finally语句。在偏下特殊意况下,finally块不会执行:在finally语句块中生出更加;在眼前代码中选择了System.exit()退出程序;程序所在线程长逝;关闭cpu。
第五、当程序执行try块,catch块时遇上return语句或者throw语句,这七个语句都会促成该措施马上终止,所以系统并不会立时施行那三个语句,而是去搜寻该尤其处理流程中的finally块,要是没有finally块,程序及时施行return语句或者throw语句,方法终止。如果有finally块,系统及时开首进行finally块,唯有当finally块执行到位后,系统才会再度跳回来执行try块、catch块里的return或throw语句,要是finally块里也利用了return或throw等导致方法终止的话语,则finally块已经甘休了措施,不用再跳回去执行try块、catch块里的其它代码了。
3、throw与throws
1、throws出现在章程函数头;而throw出现在函数体。throws代表出现非凡的一种可能,并不一定会生出那个极度;throw则是抛出了老大,执行throw则必然抛出了某种非凡对象。
三、很是结构
Throwable类派生了四个子类。Error类用来描述Java运行系统中的内部错误以及资源耗尽的失实;Exception类为非致命性类,可以透过捕捉处理使程序继续执行。Exception类依据错误爆发的缘故分为运行时那么些和自我批评很是。如图所示。

1、检查格外
自我批评分外是RuntimeException以外的要命(IOException、SQLException等),必须出示的介乎理否则无从编译通过。处理方法有二种,一是用try…catch捕捉分外,二是选拔throws评释抛出该更加。
2、运行时格外
运作时非凡的表征是Java编译器不会检查它,程序中可以接纳捕获处理,也得以不处理。
3、错误
Error(错误):是先后不能处理的谬误,表示代码运行时 JVM(Java
虚拟机)出现的标题。例如,Java虚拟机运行错误(Virtual MachineError),当
JVM 不再有继续执行操作所需的内存资源时,将现出 OutOfMemoryError。
四、自定义极度
自定义杰出只需编写一个类继承Exception类(Throwable及其子类均可)即可。

怎么是结构化十分处理

结构化卓殊处理(structured exception
handling
,下文简称:SEH),是当做一种系统编制引入到操作系统中的,本身与语言无关。在大家团结的主次中应用SEH可以让我们集中精力开发主要功用,而把程序中所可能出现的那些举行联合的处理,使程序显得越发简洁且增加可读性。

使用SHE,并不表示可以完全忽视代码中可能出现的荒唐,可是大家得以将软件工作流程和软件十分情状处理举办分离,先集中精力干紧要且急迫的活,再来处理那一个或许会遇上种种的一无所能的最主要不紧迫的难点(不急迫,但相对紧要)

当在先后中选择SEH时,就改成编译器相关的。其所造成的承担首要由编译程序来负责,例如编译程序会生出一些表(table)来支撑SEH的数据结构,还会提供回调函数。

注:
不用混淆SHE和C++ 极度处理。C++
格外处理再模式上显现为运用主要字catchthrow,这么些SHE的花样不一致等,再windows
Visual C++中,是经过编译器和操作系统的SHE举办落到实处的。

在所有 Win32
操作系统提供的建制中,使用最普遍的未公开的编制可能就要数SHE了。一提到SHE,可能就会令人想起
*__try__finally* 和 *__except*
之类的台词。SHE事实上包括两方面的听从:终止处理(termination
handing)
极度处理(exception handing)

导读: 
从本篇小说起头,将完美讲演__try,__except,__finally,__leave万分模型机制,它也即是Windows种类操作系统平台上提供的SEH模型。主人公阿愚将在此处与我们享受SEH(
结构化非凡处理)的学习进度和经验总计。 长远通晓请参阅<<windows
要旨编程>>第23, 24章.

丰裕处理的目标就是为着进步程序的安全性与健壮性。

停止处理

停下处理程序确保不管一个代码块(被敬服代码)是何许退出的,其余一个代码块(终止处理程序)总是能被调用和实施,其语法如下:

__try
{
    //Guarded body
    //...
}
__finally
{
    //Terimnation handler
    //...
}

**__try __finally**
关键字标记了甘休处理程序的五个部分。操作系统和编译器的协同工作有限支撑了随便保护代码部分是何等退出的(无论是正常退出、照旧越发退出)终止程序都会被调用,即**__finally**代码块都能进行。

SEH实际蕴涵四个举足轻重作用:截至处理(termination
handling)和尤其处理(exception handling) 

澳门金沙国际 1

try块的正常化退出与有失水准退出

try块或者会因为returngoto,分外等非当然退出,也说不定会因为成功施行而当然退出。但不论是try块是何等退出的,finally块的情节都会被实施。

int Func1()
{
    cout << __FUNCTION__ << endl;
    int nTemp = 0;
    __try{
        //正常执行
        nTemp = 22;
        cout << "nTemp = " << nTemp << endl;
    }
    __finally{
        //结束处理
        cout << "finally nTemp = " << nTemp << endl;
    }
    return nTemp;
}

int Func2()
{
    cout << __FUNCTION__ << endl;
    int nTemp = 0;
    __try{
        //非正常执行
        return 0;
        nTemp = 22;
        cout << "nTemp = " << nTemp << endl;
    }
    __finally{
        //结束处理
        cout << "finally nTemp = " << nTemp << endl;
    }
    return nTemp;
}

结果如下:

Func1
nTemp = 22  //正常执行赋值
finally nTemp = 22  //结束处理块执行

Func2
finally nTemp = 0   //结束处理块执行

如上实例可以阅览,通过拔取终止处理程序可以幸免过早执行return不行处理,__leave分外模型机制。语句,当return言语视图退出try块的时候,编译器会让finally代码块再它此前实施。对于在三三十二线程编程中经过信号量访问变量时,出现格外意况,能顺风是或不是信号量,那样线程就不会直接占有一个信号量。当finally代码块执行完后,函数就回来了。

为了让所有机制运作起来,编译器必须生成一些卓殊代码,而系统也必须举行一些十分工作,所以理应在写代码的时候幸免再try代码块中行使return语句,因为对应用程序性能有影响,对于简易demo难点不大,对于要长日子不间断运行的次序如故悠着点好,下文子禽提到一个要害字**__leave**根本字,它可以帮忙大家发现有一部分进展成本的代码。

一条好的经验法则:决不再截至处理程序中带有让try块提前退出的言语,那代表从try块和finally块中移除return,continue,break,goto等语句,把那一个话语放在终止处理程序以外。这样做的便宜就是不用去捕获哪些try块中的提前退出,从而时编译器生成的代码量最小,提升程序的周转功效和代码可读性。

每当你建立一个try块,它必须跟随一个finally块或一个except块。

1. Error&Exception

####finally块的清理功用及对程序结构的震慑

在编码的长河中必要参加须求检测,检测功效是或不是中标施行,若成功的话执行这么些,不成功的话需求作一些额外的清理工作,例如释放内存,关闭句柄等。如若检测不是许多的话,倒没关系影响;但若又很多检测,且软件中的逻辑关系相比复杂时,往往必要化很大精力来兑现繁琐的检测判断。结果就会使程序看起来结构比较复杂,大大下降程序的可读性,而且程序的体积也不绝于耳增大。

对应以此题材自己是深有体会,过去在写通过COM调用WordVBA的时候,需求层层获取对象、判断目的是否收获成功、执行有关操作、再自由对象,一个流程下来,本来一两行的VBA代码,C++
写出来就要好几十行(那还得看操作的是多少个怎么着目的)。

下边就来一个方法让大家看看,为啥有些人喜爱脚本语言而不欣赏C++的缘故吧。

为了更有逻辑,更有层次地操作 OfficeMicrosoft
把应用(Application)按逻辑成效划分为如下的树形结构

Application(WORD 为例,只列出一部分)
  Documents(所有的文档)
        Document(一个文档)
            ......
  Templates(所有模板)
        Template(一个模板)
            ......
  Windows(所有窗口)
        Window
        Selection
        View
        .....
  Selection(编辑对象)
        Font
        Style
        Range
        ......
  ......

惟有询问了逻辑层次,大家才能科学的操纵
Office。举例来讲,假设给出一个VBA语句是:

Application.ActiveDocument.SaveAs "c:\abc.doc"

那么,大家就领会了,这么些操作的过程是:

  1. 第一步,取得Application
  2. 第二步,从Application中取得ActiveDocument
  3. 第三步,调用 Document 的函数
    SaveAs,参数是一个字符串型的公文名。

那只是一个最简便的的VBA代码了。来个稍微复杂点的如下,在选中处,插入一个书签:

 ActiveDocument.Bookmarks.Add Range:=Selection.Range, Name:="iceman"

此间流程如下:

  1. 获取Application
  2. 获取ActiveDocument
  3. 获取Selection
  4. 获取Range
  5. 获取Bookmarks
  6. 调用方法Add

收获每个对象的时候都急需判定,还须求提交错误处理,对象释放等。在此就交付伪码吧,全写出来篇幅有点长

#define RELEASE_OBJ(obj) if(obj != NULL) \
                        obj->Realse();

BOOL InsertBookmarInWord(const string& bookname)
{
    BOOL ret = FALSE;
    IDispatch* pDispApplication = NULL;
    IDispatch* pDispDocument = NULL;
    IDispatch* pDispSelection = NULL;
    IDispatch* pDispRange = NULL;
    IDispatch* pDispBookmarks = NULL;
    HRESULT hr = S_FALSE;

    hr = GetApplcaiton(..., &pDispApplication);
    if (!(SUCCEEDED(hr) || pDispApplication == NULL))
        return FALSE;

    hr = GetActiveDocument(..., &pDispDocument);
    if (!(SUCCEEDED(hr) || pDispDocument == NULL)){
        RELEASE_OBJ(pDispApplication);
        return FALSE;
    }

    hr = GetActiveDocument(..., &pDispDocument);
    if (!(SUCCEEDED(hr) || pDispDocument == NULL)){
        RELEASE_OBJ(pDispApplication);
        return FALSE;
    }

    hr = GetSelection(..., &pDispSelection);
    if (!(SUCCEEDED(hr) || pDispSelection == NULL)){
        RELEASE_OBJ(pDispApplication);
        RELEASE_OBJ(pDispDocument);
        return FALSE;
    }

    hr = GetRange(..., &pDispRange);
    if (!(SUCCEEDED(hr) || pDispRange == NULL)){
        RELEASE_OBJ(pDispApplication);
        RELEASE_OBJ(pDispDocument);
        RELEASE_OBJ(pDispSelection);
        return FALSE;
    }

    hr = GetBookmarks(..., &pDispBookmarks);
    if (!(SUCCEEDED(hr) || pDispBookmarks == NULL)){
        RELEASE_OBJ(pDispApplication);
        RELEASE_OBJ(pDispDocument);
        RELEASE_OBJ(pDispSelection);
        RELEASE_OBJ(pDispRange);
        return FALSE;
    }

    hr = AddBookmark(...., bookname);
    if (!SUCCEEDED(hr)){
        RELEASE_OBJ(pDispApplication);
        RELEASE_OBJ(pDispDocument);
        RELEASE_OBJ(pDispSelection);
        RELEASE_OBJ(pDispRange);
        RELEASE_OBJ(pDispBookmarks);
        return FALSE;
    }
    ret = TRUE;
    return ret;

那只是伪码,即便也足以因此goto压缩代码行,不过goto用得不佳就出错了,上边程序中稍不留神就goto到不应该取得地点了。

BOOL InsertBookmarInWord2(const string& bookname)
{
    BOOL ret = FALSE;
    IDispatch* pDispApplication = NULL;
    IDispatch* pDispDocument = NULL;
    IDispatch* pDispSelection = NULL;
    IDispatch* pDispRange = NULL;
    IDispatch* pDispBookmarks = NULL;
    HRESULT hr = S_FALSE;

    hr = GetApplcaiton(..., &pDispApplication);
    if (!(SUCCEEDED(hr) || pDispApplication == NULL))
        goto exit6;

    hr = GetActiveDocument(..., &pDispDocument);
    if (!(SUCCEEDED(hr) || pDispDocument == NULL)){
        goto exit5;
    }

    hr = GetActiveDocument(..., &pDispDocument);
    if (!(SUCCEEDED(hr) || pDispDocument == NULL)){
        goto exit4;
    }

    hr = GetSelection(..., &pDispSelection);
    if (!(SUCCEEDED(hr) || pDispSelection == NULL)){
        goto exit4;
    }

    hr = GetRange(..., &pDispRange);
    if (!(SUCCEEDED(hr) || pDispRange == NULL)){
        goto exit3;
    }

    hr = GetBookmarks(..., &pDispBookmarks);
    if (!(SUCCEEDED(hr) || pDispBookmarks == NULL)){
        got exit2;
    }

    hr = AddBookmark(...., bookname);
    if (!SUCCEEDED(hr)){
        goto exit1;
    }

    ret = TRUE;
exit1:
    RELEASE_OBJ(pDispApplication);
exit2:
    RELEASE_OBJ(pDispDocument);
exit3:
    RELEASE_OBJ(pDispSelection);
exit4:
    RELEASE_OBJ(pDispRange);
exit5:
    RELEASE_OBJ(pDispBookmarks);
exit6:
    return ret;

此处仍旧经过SEH的终止处理程序来重新该方法,那样是否更清晰明了。

BOOL InsertBookmarInWord3(const string& bookname)
{
    BOOL ret = FALSE;
    IDispatch* pDispApplication = NULL;
    IDispatch* pDispDocument = NULL;
    IDispatch* pDispSelection = NULL;
    IDispatch* pDispRange = NULL;
    IDispatch* pDispBookmarks = NULL;
    HRESULT hr = S_FALSE;

    __try{
        hr = GetApplcaiton(..., &pDispApplication);
        if (!(SUCCEEDED(hr) || pDispApplication == NULL))
            return FALSE;

        hr = GetActiveDocument(..., &pDispDocument);
        if (!(SUCCEEDED(hr) || pDispDocument == NULL)){
            return FALSE;
        }

        hr = GetActiveDocument(..., &pDispDocument);
        if (!(SUCCEEDED(hr) || pDispDocument == NULL)){
            return FALSE;
        }

        hr = GetSelection(..., &pDispSelection);
        if (!(SUCCEEDED(hr) || pDispSelection == NULL)){
            return FALSE;
        }

        hr = GetRange(..., &pDispRange);
        if (!(SUCCEEDED(hr) || pDispRange == NULL)){
            return FALSE;
        }

        hr = GetBookmarks(..., &pDispBookmarks);
        if (!(SUCCEEDED(hr) || pDispBookmarks == NULL)){
            return FALSE;
        }

        hr = AddBookmark(...., bookname);
        if (!SUCCEEDED(hr)){
            return FALSE;
        }

        ret = TRUE;
    }
    __finally{
        RELEASE_OBJ(pDispApplication);
        RELEASE_OBJ(pDispDocument);
        RELEASE_OBJ(pDispSelection);
        RELEASE_OBJ(pDispRange);
        RELEASE_OBJ(pDispBookmarks);
    }
    return ret;

那多少个函数的成效是均等的。可以见见在InsertBookmarInWord中的清理函数(RELEASE_OBJ)各处可见,而InsertBookmarInWord3中的清理函数则整个会聚在finally块,要是在读书代码时只需看try块的情节即可了然程序流程。那四个函数本身都很小,可以细细咀嚼下那七个函数的区分。

一个try 块之后不可以既有finally块又有except块。但足以在try –
except块中嵌套try – finally块,反过来
也可以。

1.1 Error

Error代表程序在运行时期出现了格外沉痛的一无可取,并且该错误是不可恢复生机的,由于那属于JVM层次的严重错误,所以那种不当是会促成程序终止执行的。

别的,编译器不会检查Error是不是被拍卖,因而,在先后中不引进去捕获Error类型的十分,主要原因是运行时万分多是由于逻辑错误造成的,属于应该解决的谬误。当极度暴发时,JVM一般会挑选将线程终止。

关键字 __leave

try块中使用**__leave一言九鼎字会使程序跳转到try块的最终,从而自然的进入finally块。
对此上例中的InsertBookmarInWord3try块中的return完全可以用
__leave**
来替换。两者的分别是用return会引起try过早退出系统会展开一些进展而充实系统开发,若使用**__leave**就会自然退出try块,开支就小的多。

BOOL InsertBookmarInWord4(const string& bookname)
{
    BOOL ret = FALSE;
    IDispatch* pDispApplication = NULL;
    IDispatch* pDispDocument = NULL;
    IDispatch* pDispSelection = NULL;
    IDispatch* pDispRange = NULL;
    IDispatch* pDispBookmarks = NULL;
    HRESULT hr = S_FALSE;

    __try{
        hr = GetApplcaiton(..., &pDispApplication);
        if (!(SUCCEEDED(hr) || pDispApplication == NULL))
            __leave;

        hr = GetActiveDocument(..., &pDispDocument);
        if (!(SUCCEEDED(hr) || pDispDocument == NULL))
            __leave;

        hr = GetActiveDocument(..., &pDispDocument);
        if (!(SUCCEEDED(hr) || pDispDocument == NULL))
            __leave;

        hr = GetSelection(..., &pDispSelection);
        if (!(SUCCEEDED(hr) || pDispSelection == NULL))
            __leave;

        hr = GetRange(..., &pDispRange);
        if (!(SUCCEEDED(hr) || pDispRange == NULL))
            __leave;

        hr = GetBookmarks(..., &pDispBookmarks);
        if (!(SUCCEEDED(hr) || pDispBookmarks == NULL))
            __leave;

        hr = AddBookmark(...., bookname);
        if (!SUCCEEDED(hr))
            __leave;

        ret = TRUE;
    }
    __finally{
        RELEASE_OBJ(pDispApplication);
        RELEASE_OBJ(pDispDocument);
        RELEASE_OBJ(pDispSelection);
        RELEASE_OBJ(pDispRange);
        RELEASE_OBJ(pDispBookmarks);
    }
    return ret;
}

__try  __finally关键字用来标注截至处理程序两段代码的概况

1.2 Exception

Exception代表可复原的越发,是编译器可以捕捉到的。它包含两类:检查十分和周转时极度。

那个处理程序

软件极度是我们都不乐意见见的,不过错误如故时常有,比如CPU捕获类似不合规内存访问和除0那样的标题,一旦侦查到那种指鹿为马,就抛出相关非凡,操作系统会给咱们应用程序一个翻看非常类型的火候,并且运行程序自己处理那些越发。相当处理程序结构代码如下

  __try {
      // Guarded body
    }
    __except ( exception filter ) {
      // exception handler
    }

在意关键字**__except**,任何try块,前面总得更一个finally代码块或者except代码块,但是try后又无法同时有finallyexcept块,也不可以同时有两个finnalyexcept块,可是足以并行嵌套使用

任由尊崇体(try块)
是哪些退出的。不论你在珍爱体中使用return,仍然goto,或者是longjump,停止处理程序
(finally块)都将被调用。

1. 检讨非凡

反省十分是在程序中最常常碰到的十分,所有继续自Exception并且不是运作时极度的至极都是检查极度,如IO非凡或SQL非凡等。对于那种卓殊,都发出在编译阶段,Java编译器强制程序去捕获此类非常。

  • 卓殊的暴发并不会导致程序的失误,举办处理后得以继续执行后续的操作;
  • 程序敬服于不有限援救的外部规范

越发处理中央流程

int Func3()
{
    cout << __FUNCTION__ << endl;
    int nTemp = 0;
    __try{
        nTemp = 22;
        cout << "nTemp = " << nTemp << endl;
    }
    __except (EXCEPTION_EXECUTE_HANDLER){
        cout << "except nTemp = " << nTemp << endl;
    }
    return nTemp;
}

int Func4()
{
    cout << __FUNCTION__ << endl;
    int nTemp = 0;
    __try{
        nTemp = 22/nTemp;
        cout << "nTemp = " << nTemp << endl;
    }
    __except (EXCEPTION_EXECUTE_HANDLER){
        cout << "except nTemp = " << nTemp << endl;
    }
    return nTemp;
}

结果如下:

Func3
nTemp = 22  //正常执行

Func4
except nTemp = 0 //捕获异常,

Func3try块只是一个简练操作,故不会造成非凡,所以except块中代码不会被实施,Func4try块视图用22除0,导致CPU捕获那么些事件,并抛出,系统稳定到except块,对该尤其举行处理,该处有个非常过滤表明式,系统中有三该定义(定义在Windows的Excpt.h中):

1. EXCEPTION_EXECUTE_HANDLER:
    我知道这个异常了,我已经写了代码来处理它,让这些代码执行吧,程序跳转到except块中执行并退出
2. EXCEPTION_CONTINUE_SERCH
    继续上层搜索处理except代码块,并调用对应的异常过滤程序
3. EXCEPTION_CONTINUE_EXECUTION
    返回到出现异常的地方重新执行那条CPU指令本身

面是三种为主的行使方法:

  • 措施一:直接运用过滤器的五个重返值之一

__try {
   ……
}
__except ( EXCEPTION_EXECUTE_HANDLER ) {
   ……
}
  • 办法二:自定义过滤器

__try {
   ……
}
__except ( MyFilter( GetExceptionCode() ) )
{
   ……
}

LONG MyFilter ( DWORD dwExceptionCode )
{
  if ( dwExceptionCode == EXCEPTION_ACCESS_VIOLATION )
    return EXCEPTION_EXECUTE_HANDLER ;
  else
    return EXCEPTION_CONTINUE_SEARCH ;
}

在try使用__leave关键字会引起跳转到try块的末尾

2. 运行时万分

对此运行时那些,编译器没有强制对其举办捕获并拍卖。即使不对那种很是举办处理,当出现那种更加时,会由JVM来拍卖。在Java语言中,最常见的周转时非凡有:空指针极度、数据存储非凡、类型转换相当、数组越界非凡、缓冲区溢出非常、算术非凡等。

出现运行时这个后,系统会把那么些直白往上层抛出,直到碰各处理代码为止。
如果没有拍卖快,则抛到最上层;假诺是八线程就由Thread.run()方法抛出,固然是单线程,就被Main()方法抛出。

抛出后,若是是其他线程,那个线程也就淡出了。若是是主程序抛出的要命,那么万事程序也就淡出了。

设若不对运作时非常进行拍卖,后果是很严重的。
一旦发送,要么线程中止,要么程序终止。

.NET4.0中捕获SEH异常

在.NET
4.0之后,CLR将会有别于出一部分非常(都是SEH至极),将那个更加标识为破坏性非常(Corrupted
State
Exception)。针对这么些格外,CLR的catch块不会捕捉那几个十分,一下代码也没有主意捕捉到那一个更加。

try{
    //....
}
catch(Exception ex)
{
    Console.WriteLine(ex.ToString());
}

因为并不是所有人都急需捕获那个分外,若是您的次第是在4.0底下编译并运行,而你又想在.NET程序里捕捉到SEH非凡的话,有八个方案得以尝试:

  • 在托管程序的.config文件里,启用legacyCorruptedStateExceptionsPolicy这一个特性,即简化的.config文件类似上面的公文:

App.Config

<?xml version="1.0"?>
<configuration>
 <startup>
   <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
 </startup>
    <runtime>
      <legacyCorruptedStateExceptionsPolicy enabled="true" />
    </runtime>
</configuration>

其一设置告诉CLR 4.0,整个.NET程序都要使用老的分外捕捉机制。

  • 在需求捕捉破坏性很是的函数外面加一个HandleProcessCorruptedStateExceptions属性,那个特性只控制一个函数,对托管程序的其余函数没有影响,例如:

[HandleProcessCorruptedStateExceptions]
try{
    //....
}
catch(Exception ex)
{
    Console.WriteLine(ex.ToString());
}

 SEH有两项相当有力的效率。当然,首先是不行处理模型了,因而,那篇作品首先深刻阐释SEH提供的不得了处理模型。此外,SEH还有一个特意强劲的效率,这将在下一篇小说中举办详尽介绍。

2. Java丰裕处理体制

try-except入门
  SEH的老大处理模型紧要由try-except语句来成功,它与正统C++所定义的那一个处理模型分外相近,也都是可以定义出受监督的代码模块,以及定义卓殊处理模块等。如故老方法,看一个例证先,代码如下: 
//seh-test.c

2.1 try/catch

运用 try 和 catch 关键字可以捕获相当。try/catch
代码块放在极度或者暴发的地方。try/catch代码块中的代码称为爱惜代码,使用
try/catch 的语法如下:

try
{
   // 程序代码
}catch(ExceptionName e1)
{
   //Catch 块
}

Catch 语句包涵要捕获卓殊类型的扬言。当保安代码块中暴发一个可怜时,try
后边的 catch 块就会被检查。

借使暴发的极度蕴涵在 catch 块中,极度会被传送到该 catch
块,那和传递一个参数到点子是同一。

澳门金沙国际 2

2.2 finally关键字

finally 关键字用来创造在 try
代码块后边推行的代码块。无论是不是暴发更加,finally
代码块中的代码总会被实施。在 finally
代码块中,可以运行清理项目等收尾善后性质的言辞。

finally 代码块出现在 catch 代码块最终,语法如下:

try{
  // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}finally{
  // 程序代码
}

瞩目上面事项:

  • catch 不可以独立于 try 存在。
  • 在 try/catch 前边添加 finally 块并非强制性要求的。
  • try 代码后不可以既没 catch 块也没 finally 块。
  • try, catch, finally 块之间不可以添加其余代码。
void main()
{
    // 定义受监控的代码模块
    __try
    {
        puts("in try");
    }
    //定义异常处理模块
    __except(1)
    {
        puts("in except");
    }
}

2.3 throws/throw 关键字

一旦一个方法没有捕获一个检查性极度,那么该办法必须运用 throws
关键字来声称。throws 关键字放在方法签名的底部。

也可以动用 throw 关键字抛出一个格外,无论它是新实例化的照旧刚抓获到的。

下边方法的扬言抛出一个 RemoteException 十分:

import java.io.*;
public class className
{
  public void deposit(double amount) throws RemoteException
  {
    // Method implementation
    throw new RemoteException();
  }
  //Remainder of class definition
}

澳门金沙国际 3

3. 丰富流程处理

  1. finally语句不被实践的唯一情形是先实施了用来终止程序的System.exit()方法
  2. return语句用于退出本办法
  3. 提出并非在finally代码块中选拔return或throw
  4. 在运行时环境,并不会有别卓殊的档次,所以程序员自己要听从非凡的实践标准,否则Java格外处理机制就会被误用。
  5. finally代码块总是会在章程重临或措施抛出卓殊前履行,而try-catch-finally代码块前边的代码就有可能不会再实施。
  6. try代码块肯定必要要有一个catch代码块或finally代码块(二者取其一就行)。
  7. catch处理器的优先级比申明格外语句要高。
  8. 若是多处抛出非常,finally代码块里面的老大会控制其他更加。

  9. 广大难点

 呵呵!是或不是很粗略,而且与C++至极处理模型很一般。当然,为了与C++至极处理模型相差异,VC编译器对重大字做了有限改观。首先是在每个重点字加上多个下划线作为前缀,那样既保持了语义上的一致性,其它也尽最大可能来幸免了举足轻重字的有可能造成名字冲突而引起的分神等;其次,C++非凡处理模型是运用catch关键字来定义万分处理模块,而SEH是行使__except关键字来定义。并且,catch关键字背后往往好像接受一个函数参数一样,可以是各系列型的格外数据对象;不过__except关键字则差异,它背后跟的却是一个表明式(可以是各种类型的表明式,后边会尤其分析)。

4.1 throw与throws的比较

1、throws出现在艺术函数头;而throw出现在函数体。
2、throws代表出现分外的一种可能,并不一定会发出那些至极;throw则是抛出了丰富,执行throw则肯定抛出了某种非凡对象。
3、两者都是失落处理极度的不二法门(那里的无所作为并不是说这种方法不佳),只是抛出或者可能抛出万分,可是不会由函数去处理相当,真正的处理万分由函数的上层调用处理。

try-except进阶
  与C++卓殊处理模型很相似,在一个函数中,可以有多少个try-except语句。它们得以是一个平面的线性结构,也足以是分支的嵌套结构。例程代码如下:

4.2 final、finally、finalize的区别

  1. final修饰符(关键字)。被final修饰的类,就意味着不可以再派生出新的子类,不可能当做父类而被子类继承。因而一个类不可以既被abstract表明,又被final注解。将变量或措施声明为final,可以有限帮衬他们在动用的长河中不被改动。被声称为final的变量必须在宣称时提交变量的上马值,而在其后的引用中不得不读取。被final表明的格局也同等只可以动用,不可能重载。

  2. finally是在丰富处理时提供finally块来进行其它清除操作。不管有没有尤其被抛出、捕获,finally块都会被实施。try块中的内容是在无相当时进行到截止。catch块中的内容,是在try块内容发生catch所申明的格外时,跳转到catch块中履行。finally块则是不管很是是还是不是暴发,都会实施finally块的始末,所以在代码逻辑中有要求无论暴发怎么着都必须履行的代码,就足以放在finally块中。

  3. finalize是办法名。java技术允许利用finalize()方法在垃圾收集器将对象从内存中排除出去之前做要求的清理工作。那个主意是由垃圾收集器在规定那个目标没有被引述时对这一个目的调用的。它是在object类中定义的,因而有所的类都继承了它。子类覆盖finalize()方法以盘整系统资源或者被实施其它清理工作。finalize()方法是在垃圾堆收集器删除对象之前对那些目标调用的。


// 例程1
// 平面的线性结构

参考

  1. Java
    很是处理
  2. Java中final、finally和finalize的区别

澳门金沙国际 4

void main()
{
    __try
    {
        puts("in try");
    }
    __except(1)
    {
        puts("in except");
    }


    // 又一个try-except语句
    __try
    {
        puts("in try1");
    }
    __except(1)
    {
        puts("in except1");
    }
}

澳门金沙国际 5

// 例程2
// 分层的嵌套结构

澳门金沙国际 6

void main()
{
    __try
    {
        puts("in try");
        // 又一个try-except语句
        __try
        {
            puts("in try1");
        }
        __except(1)
        {
            puts("in except1");
        }
    }
    __except(1)
    {
        puts("in except");
    }
}

澳门金沙国际 7

// 例程3
// 分层的嵌套在__except模块中

澳门金沙国际 8

void main()
{
    __try
    {
        puts("in try");
    }
    __except(1)
    {
        // 又一个try-except语句
        __try
        {
            puts("in try1");
        }
        __except(1)
        {
            puts("in except1");
        }

        puts("in except");
    }
}

澳门金沙国际 9

 1. 受监督的代码模块被执行(也即__try定义的模块代码);
  2.
只要地点的代码执行进度中,没有出现相当的话,那么控制流将转入到__except子句之后的代码模块中;
  3.
否则,即使出现非凡的话,那么控制流将进入到__except前面的表明式中,也即首先计算这几个说明式的值,之后再按照那么些值,来控制做出相应的拍卖。那么些值有二种景况,如下:
  EXCEPTION_CONTINUE_EXECUTION (–1)
万分被忽视,控制流将在尤其出现的点过后,继续上涨运转。
  EXCEPTION_CONTINUE_SEARCH (0)
相当不被识别,也即眼前的这么些__except模块不是以此尤其错误所对应的科学的越发处理模块。系统将一而再到上一层的try-except域中继承查找一个极度的__except模块。
  EXCEPTION_EXECUTE_HANDLER (1)
卓殊已经被识别,也即眼前的那几个那多少个错误,系统现已找到了并可以认同,那么些__except模块就是天经地义的可怜处理模块。控制流将进入到__except模块中。
 
try-except深入
  上边的情节中已经对try-except举行了周密的刺探,然而有一些还并未解说到。那就是何等在__except模块中得到丰盛错误的相干新闻,那极度关键,它实际上是展开更加错误处理的前提,也是对十分进行分层分级别处理的前提。总之,如果没有这一个起码的新闻,分外处理怎么着进展?因而收获极度音信分外的重大。Windows提供了八个API函数,如下:
 

LPEXCEPTION_POINTERS GetExceptionInformation(VOID);
DWORD GetExceptionCode(VOID);

  其中GetExceptionCode()再次来到错误代码,而GetExceptionInformation()重返更完美的音讯,看它函数的扬言,再次回到了一个LPEXCEPTION_POINTERS类型的指针变量。那么EXCEPTION_POINTERS结构如何呢?如下,
 

typedef struct _EXCEPTION_POINTERS { // exp 
PEXCEPTION_RECORD ExceptionRecord; 
PCONTEXT ContextRecord; 
} EXCEPTION_POINTERS;

 

  呵呵!仔细瞅瞅,那是还是不是和上一篇文章中,用户程序所注册的格外处理的回调函数的五个参数类型一样。是的,的确没错!其中EXCEPTION_RECORD类型,它记录了有的与丰硕相关的音信;而CONTEXT数据结构体中著录了老大暴发时,线程当时的上下文环境,主要包罗寄存器的值。由此有了那几个音信,__except模块便足以对那多少个错误进行很好的归类和復苏处理。可是更加须要注意的是,那五个函数只好是在__except前面的括号中的表明式成效域内卓有成效,否则结果也许没有管教(至于为什么,在后头深切解析非常模型的落实时候,再做详细演说)。看一个例程吧!代码如下:

澳门金沙国际 10

int exception_access_violation_filter(LPEXCEPTION_POINTERS p_exinfo)
{
    if(p_exinfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
    {
        printf("存储保护异常\n");
        return 1;
    }
    else 
        return 0;
}

int exception_int_divide_by_zero_filter(LPEXCEPTION_POINTERS p_exinfo)
{
    if(p_exinfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
    {
        printf("被0除异常\n");
        return 1;
    }
    else 
        return 0;
}

void main()
{

    __try
    {
        __try
        {
            int* p;

            // 下面将导致一个异常
            p = 0;
            *p = 45;
        }
        // 注意,__except模块捕获一个存储保护异常
        __except(exception_access_violation_filter(GetExceptionInformation()))
        {
            puts("内层的except块中");
        }
  //可以在此写除0异常的语句
     int b = 0;
      int a = 1 / b;
    }
    // 注意,__except模块捕获一个被0除异常
    __except(exception_int_divide_by_zero_filter(GetExceptionInformation())) 
    {
        puts("外层的except块中");
    }
}

澳门金沙国际 11

地点的程序运行结果如下:

存储尊敬非凡
内层的except块中
Press any key to continue

 

  呵呵!感觉不错,大家可以在地点的主次基础之上改动一下,让它抛出一个被0除相当,看程序的周转结果是或不是如预期那样。
  最终还有一些亟待讲演,在C++的相当处理模型中,有一个throw关键字,也即在受监控的代码中抛出一个卓殊,那么在SEH非凡处理模型中,是否也应当有这么一个类似的要紧字或函数呢?是的,没错!SEH卓殊处理模型中,对更加划分为两大类,第一种就是上面一些例程中所见到的,这类非常是系统充足,也被称呼硬件相当;还有一类,就是程序中协调抛出相当,被誉为软件很是。怎么抛出呢?照旧Windows提供了的API函数,它的表明如下:
 

VOID RaiseException(
DWORD dwExceptionCode, // exception code
DWORD dwExceptionFlags, // continuable exception flag
DWORD nNumberOfArguments, // number of arguments in array
CONST DWORD *lpArguments // address of array of arguments
);

 

  很粗略吗!实际上,在C++的极度处理模型中的throw关键字,最后也是对RaiseException()函数的调用,也即是说,throw是RaiseException的上层封装的更尖端一类的函数,那将来再详细分析它的代码落成。那里依旧看一个不难易行例子吗!代码如下:

澳门金沙国际 12

int seh_filer(int code)
{
    switch(code)
    {
    case EXCEPTION_ACCESS_VIOLATION :
        printf("存储保护异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_DATATYPE_MISALIGNMENT :
        printf("数据类型未对齐异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_BREAKPOINT :
        printf("中断异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_SINGLE_STEP :
        printf("单步中断异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_ARRAY_BOUNDS_EXCEEDED :
        printf("数组越界异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_FLT_DENORMAL_OPERAND :
    case EXCEPTION_FLT_DIVIDE_BY_ZERO :
    case EXCEPTION_FLT_INEXACT_RESULT :
    case EXCEPTION_FLT_INVALID_OPERATION :
    case EXCEPTION_FLT_OVERFLOW :
    case EXCEPTION_FLT_STACK_CHECK :
    case EXCEPTION_FLT_UNDERFLOW :
        printf("浮点数计算异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_INT_DIVIDE_BY_ZERO :
        printf("被0除异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_INT_OVERFLOW :
        printf("数据溢出异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_IN_PAGE_ERROR :
        printf("页错误异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_ILLEGAL_INSTRUCTION :
        printf("非法指令异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_STACK_OVERFLOW :
        printf("堆栈溢出异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_INVALID_HANDLE :
        printf("无效句病异常,错误代码:%x\n", code);
        break;
    default :
        if(code & (1<<29))
            printf("用户自定义的软件异常,错误代码:%x\n", code);
        else
            printf("其它异常,错误代码:%x\n", code);
        break;
    }

    return 1;
}


void main()
{
    __try
    {
        puts("try块中");

        // 注意,主动抛出一个软异常
        RaiseException(0xE0000001, 0, 0, 0);
    }
    __except(seh_filer(GetExceptionCode()))
    {
        puts("except块中");
    }

}

澳门金沙国际 13

上边的程序运行结果如下:
hello
try块中
用户自定义的软件极度,错误代码:e0000001
except块中
world
Press any key to continue

 

地点的顺序很简短,那里不做越来越的剖析。大家需要重视谈论的是,在__except模块中哪些辨别分裂的老大,以便对更加进行很好的归类处理。毫无疑问,它自然是透过GetExceptionCode()或GetExceptionInformation
()函数来获取当前的不行错误代码,实际也即是DwExceptionCode字段。相当错误代码在winError.h文件中定义,它服从Windows系统下统一的错误代码的规则。每个DWORD被剪切多少个字段,如下表所示:
例如大家得以在winbase.h文件中找到EXCEPTION_ACCESS_VIOLATION的值为0
xC0000005,将那些越发代码值拆开,来分析看看它的次第bit位字段的涵义。
C 0 0 0 0 0 0 5 (十六进制)
澳门金沙国际,1100 0000 0000 0000 0000 0000 0000 0101 (二进制)
第3 0位和第3
1位都是1,表示该越发是一个严重的失实,线程可能不可见继续往下运行,必需要及时处理苏醒这几个尤其。第2
9位是0,表示系统中早已定义了卓殊代码。第2 8位是0,留待后用。第1 6 位至2
7位是0,表示是FACILITY_NULL设备档次,它象征存取很是可发生在系统中其余地点,不是接纳一定设备才发出的不行。第0位到第1
5位的值为5,表示尤其错误的代码。
  假设程序员在程序代码中,陈设抛出有些自定义类型的至极,必要求规划设计好自己的很是类型的分开,依照地点的平整来填充十分代码的逐一字段值,如下面示例程序中抛出一个格外代码为0xE0000001软件至极。

总结
  (1)
C++卓殊模型用try-catch语法定义,而SEH分外模型则用try-except语法;
  (2) 与C++非常模型相似,try-except也协助多层的try-except嵌套。
  (3)
与C++分外模型分歧的是,try-except模型中,一个try块只可以是有一个except块;而C++十分模型中,一个try块可以有七个catch块。
  (4)
与C++格外模型相似,try-except模型中,查找搜索格外模块的平整也是逐级提升拓展的。可是稍有分其余是,C++相当模型是根据相当对象的品种来开展匹配查找的;而try-except模型则分歧,它通过一个表明式的值来展开判断。即使表明式的值为1(EXCEPTION_EXECUTE_HANDLER),表示找到了非凡处理模块;假如值为0(EXCEPTION_CONTINUE_SEARCH),表示继续向上一层的try-except域中一而再搜寻其它可能卓殊的不行处理模块;若是值为-1(EXCEPTION_CONTINUE_EXECUTION),表示忽略那几个更加,注意那个值一般很少用,因为它很不难导致程序难以预测的结果,例如,死循环,甚至造成程序的夭折等。
   (5)
__except关键字背后跟的表明式,它可以是各种类型的表明式,例如,它可以是一个函数调用,或是一个尺度表明式,或是一个逗号表达式,或干脆就是一个整型常量等等。最常用的是一个函数表明式,并且经过行使GetExceptionCode()或GetExceptionInformation
()函数来收获当前的极度错误音信,便于程序员有效控制更加错误的归类处理。
   (6)
SEH十分处理模型中,相当被分开为两大类:系统万分和软件格外。其中软件十分通过RaiseException()函数抛出。RaiseException()函数的法力类似于C++很是模型中的throw语句。

C++不常用关键字(__leave)

**总结__finally块被实践的流水线时,无外乎三种状态。第一种就是逐一执行到__finally块区域内的代码,那种景况很简短,不难领会;第三种就是goto语句或return语句引发的主次控制流离开当前__try块效率域时,系统自动完毕对__finally块代码的调用;第二种就是出于在__try块中出现相当时,导致程序控制流离开当前__try块功效域,那种景观下也是由系统自动落成对__finally块的调用。无论是第
2种,如故第3种状态,毫无疑问,它们都会滋生很大的系统开发,编译器在编译此类程序代码时,它会为那二种境况准备很多的附加代码。一般第2种情景,被称作“局地进展(LocalUnwinding)”;第3种景况,被称呼“全局展开(GlobalUnwinding)”。在背后演讲SEH完结的时候会详细分析到那或多或少。
第3种状态,也即出于出现分外而造成的“全局展开”,对于程序员而言,那说不定是力不从心避免的,因为你在接纳很是处理机制升高程序可靠健壮性的同时,不可幸免的会引起性能上其它的一对费用。呵呵!那世界实质上也算瞒公平的,有得必有失。

  可是,对于第2种处境,程序员完全可以有效地幸免它,幸免“局地进展”引起的不须要的额外开支。实际那也是与结构化程序设计思想相平等的,也即一个程序模块应该唯有一个输入和一个开口,程序模块内尽量幸免使用goto语句等。然而,话虽如此,有时为了增强程序的可读性,程序员在编写代码时,有时可能只能够动用局地与结构化程序设计思想相悖的做法,例如,在一个函数中,可能有多处的return语句。针对那种气象,SEH提供了一种非常有效的折衷方案,那就是__leave关键字所起的效劳,它既具有像goto语句和return语句那样类似的作用(由于检测到某个程序运行中的错误,须要及时离开当前的
__try块成效域),不过又防止了“局地进展”
的额外开支。照旧看个例证吗!代码如下:** 

澳门金沙国际 14

#include <stdio.h>

void test()
{
puts("hello");
__try
{
int* p;
puts("__try块中");

// 直接跳出当前的__try作用域
__leave;
p = 0;
*p = 25;
}
__finally
{
// 这里会被执行吗?当然
puts("__finally块中");
}

puts("world");
}

void main()
{
__try
{
test();
}
__except(1)
{
puts("__except块中");
}
}

澳门金沙国际 15

地点的程序运行结果如下:
hello
__try块中
__finally块中
world
Press any key to continue

相关文章