ActionScript3教程-异常和错误处理


黑羽 ActionScript 3 系列教程 之 12 http://www.kingda.org/ 黑羽 ActionScript 3 系列教程之 12 (欢迎转载!但请注明来自www.kingda.org,thx ^_^) 本章教程首发于www.j2eemx.com和www.kingda.org。 12. 异常和错误的捕捉和处理 .................................................................................................. 2 12.1. 什么是异常和错误? .................................................................................................. 2 12.1.1. 一个简单的异常例子.......................................................................................... 2 12.1.2. 异常的概念和在 ActionScript 3 中的实现.................................................... 3 12.1.3. 使用异常处理机制的好处.................................................................................. 4 12.2. 使用 try-catch-finally 处理异常 ............................................................................. 5 12.2.1. try.......................................................................................................................... 6 12.2.2. catch..................................................................................................................... 6 12.2.3. finally.................................................................................................................... 7 12.2.4. try-catch-finally 的语法规则.......................................................................... 7 12.3. 使用 throw 抛出异常 ................................................................................................. 7 12.3.1. 抛出 Error 类或其子类的实例 .......................................................................... 7 12.3.2. 自定义异常........................................................................................................... 9 12.4. ActionScript 3 中异常的层次和结构 12.4.1. ActionScript 3 中对异常的良好支援 12.4.2. Error 相关类别的层次和架构 12.4.3. 最常碰到的几种异常 12.4.4. 自定义异常的资讯 12.5. 处理异常的原则和方式 12.5.1. 处理异常的几条原则 12.5.2. 处理异常的常用的几种方式 12.5.3. 4 大提倡与 4 大忌讳 1 黑羽 ActionScript 3 系列教程 之 12 http://www.kingda.org/ 12. 异常和错误的捕捉和处理 (上) 在 ECMAScript Edition 4 draft 中规定异常(Error)类要作为内置类。ActionScript 3 中的顶级包(Top Level)中,Error 及其子类共占据了 11 个位置,超过了核心类总数三分之 一。ActionScript 3 对异常处理的改进是非常显著的。在 AVM2 宣传中,对异常处理机制 的良好支持一直是主要宣传点。 掌握异常处理机制,不论是对于程序易维护性、代码健壮性、用户友好性都至关重要。 这也是为什么 ActionScript 3 在异常处理机制上下如此大的功夫之原因。 而且异常处理机制并不像初学者想象的那么抽象和复杂,实际上只要理解了它的设计用 意和思想,那么我们会发现它是非常实用而且简洁的。 下面我们就慢慢的来了解这个日后经常要打交道的好帮手。 12.1. 什么是异常和错误? 异常和错误是指程序执行时遇到的任何错误情况或意外行为。 似乎有些抽象。没有关系,最快了解异常的方法就是先看一个出现异常的代码例子。然 后,我们再来看一下异常的详细概念、ActionScript 3 中的实现以及与其他语言的不同之 处。 12.1.1. 一个简单的异常例子 示例 12-1 简单的异常例子 package org.kingda.codesample.error { import flash.display.Sprite; public class ErrorSample extends Sprite { public function ErrorSample() { var dataSource:OtherData = new OtherData(); try { dataSource.getArray().push("kingda"); } catch (error:Error) { trace (error); //输出: 2 黑羽 ActionScript 3 系列教程 之 12 http://www.kingda.org/ //TypeError: Error #1009: 无法访问空对象引用的属性或方法。 } } } } internal class OtherData { private var foo:Array; public function OtherData() { //... } public function getArray():Array { return foo; //注意:foo 并没有被初始化 } } 在上面的 ErrorSample 中,使用了某个第三方的类的实例(dataSource)所返回的 Array 对象。在本例中,这个第三方的类就是同一个包下的 OtherData 实例。现实开发中, 这个第三方的类可能处在不同的包中,甚至是别人已经编译好的组件,甚至不能被我们查看 或者修改代码的。 在 try{}中的代码试图操作 dataSource 返回的 Array 对象,本例中的操作是希望在中 多加入一个字符串“kingda”。结果怎料到,出于某种原因,dataSource 其实返回的是一 个 null。(本例中的原因是未经初始化。)如果没有 try-catch,那么会导致程序没有办法 正常执行下去,一旦执行,Flash Debug Player 就会抛出一个 Error(异常):TypeError: Error #1009: 无法访问空对象引用的属性或方法。 这是一个好事儿,至少通知我们出了错。而不是像在 ActionScript 2 中那样“蒙混过 关”,静悄悄的失败让我们一头雾水,调试时也不知从何查起。 读者可能已经从 try-catch 的字面意思已经猜出,try{}就是试试 try 下面花括号括住的 代码块,执行一下,如果发生异常就会有 Error 对象抛出,那么就被 catch 语句抓住,使用 catch 语句中的代码来处理这个错误。本例中的处理方法是将 Error 对象的内容 trace 出来。 这就是一个很简单的代码发生异常的例子。 异常的具体定义是什么?还有哪些时候会引发异常?这就是我们下一小节要讲述的内 容。 12.1.2. 异常的概念和在 ActionScript 3 中的实现 可将异常理解成是一类事件(Event),表示程序出错了的消息。 它传送一些 AVM 问题、故障及未按预想的逻辑执行的相关信息。在 ActionScript 3 中,使用 Error 类及其子类的实例来统一表示异常和错误。这些实例对象中,一般包括异 3 黑羽 ActionScript 3 系列教程 之 12 http://www.kingda.org/ 常信息、异常 ID、堆栈跟踪数据等。通过这些 Error 实例将信息从应用程序的一部分发送 到另一部分。 由于 ActionScript 3 没有对线程的支持,所以异常分为两种:同步异常和异步异常。 Error 和其子类一般用来处理同步时的程序异常。在异步处理时,则交给所产生的异常用相 关的异常事件(ErrorEvent)。 异常事件并不是 Error 的子类,而是 Event 的子类,这一部分我们将放在以后教程的 事件处理机制中介绍。 本章教程关注于Error,开始,后文中如无特别说明,将以Error(异常)1来指代异常和 错误。 在 Java 中,Throwable 类是 Java 语言中所有错误或异常的超类。两个子类 Error 和 Exception,通 常用于指示发生了异常情况。Error 用于指示合理的应用程序不应该试图捕获的严重问题。Exception 指示合理的应用程序想要捕获的条件。 但 ActionScript 3 并没有这样的划分,所有错误和异常都是 Error 类或其子类的实例。处于顶级包(Top Level)中的 Error 子类们近似于 Java 中的 Exception。flash.errors 包中的 Error 子类代表发生的严重问题, 类似于 Java 中的 Error。而且 ActionScript 3 中的 Error 不支持异常链,不包含 cause(原因)。 在 .NET Framework 中,异常是从 Exception 类类继承的对象。从这个方面说,ActionScript 3 中的 Error 与.NET Framework 中的 Exception 地位对等。但是.NET 中 Exception 分了两个子类:一个是 SystemException,从其 派生的预定义公共语言运行库异常类;一个是 ApplicationException,从其派 生的用户定义的应用程序异常类。在 ActionScript 3 中不是这样.,所有 AVM 系统内置异常类和用户 的自定义类都是直接从 Error 派生的。 ActionScript 3 不支持线程的概念,但有同步和异步之分。异步应用产生的异常将发出异常事件 (ErrorEvent),而不是异常(Error)。要加以注意。 以下这些情况都可以引发异常: 您的代码或调用的代码中有错误,操作系统资源不可用,AVM 遇到意外情况,等等。 对于这些情况,ActionScript 3 应用程序可以从其中一些恢复,而对于另一些,则不能恢 复。尽管可以从大多数应用程序异常中恢复,但一般不能从大多数 AVM 异常中恢复。 12.1.3. 使用异常处理机制的好处 使用异常好处一个最主要的原因是让程序更加健壮,更加易于维护。比如,从一些异常 状态中修复程序的运行。比如当异常发生时(比如所读取资源出错,运行时出现空对象时), 提示用户或者改变程序的运行路线,确保程序正常运行。 异常处理机制,对设计一个“用户友好”的 RIA 程序,至关重要。 但是初初一想,这些情况下“似乎可能好像”可以不用异常也可以处理。直接在异常发 生处解决不好吗? 1 Error 按字面翻译,应当译为错误。但由于 ActionScript 3 中 Error 类实际代表了通常所说的异常和 错误。本教程大部分程序语言的习惯,将 Error 称为异常。 4 黑羽 ActionScript 3 系列教程 之 12 http://www.kingda.org/ 那么,为什么要引入异常?为什么不在异常出现位置直接写入相关逻辑来处理呢? 作为大部分语言所采用的异常处理机制,当然有很多原因和优点。 比如,有时,根本不能修改代码来处理异常。原因可能有异常来自于第三方组件或者源 码的缺陷。如果是编译好的组件,则不能访问其代码。如果是源码,可能由于其架构庞大, 又没有详细文档,查错困难,不如在自己的代码中集中处理这些异常。 有时需要在程序整个逻辑系统中交流错误信息,以便按统一方式来处理问题。在大型的 RIA 软件设计中,经常要使用该策略。 有时候,出现了异常但可能有若干种处理它的方式,但您不知道使用哪一种。这时,可 以通过 try-catch-finally 将异常处理托付给异常所在方法的调用者。方法调用者,一般更加 了解异常的原因,以及异常发生的环境,可以更加准确的选择处理方法。 这样的总结有点抽象,如果您不太明白没有关系,后文中有详细的异常处理实例和处理 原则。相信您可以在具体的实例应用中体会到异常的好处。 不过在这之前,要先简单介绍一下 try-catch-finally 的使用方法。 12.2. 使用 try-catch-finally 处理异常 在本章教程开头的例子中已经讲述了一个具体的异常处理的代码例子。一旦我们认为某 段代码可能会有潜在的异常发生时,那么就应该在这段代码包括在 try 花括号的代码块中。 常见的潜在的异常发生情景有:某对象为 undefined,却对它进行操作;运行时访问了 本不属于类定义的方法;运行时调用方法,类型不匹配;使用网络 Socket 连接时,跨越安 全沙箱限制或者传入的端口参数不正确等等。这些在下一节:“ActionScript 3 中异常的 层次和结构”有更加详细介绍。 先看一个简单的代码例子,处理 Socket 连接异常。 在 ActionScript 3 中 Socket 的连接只允许连接 65535 以下的端口号。本例中,端口 号居然是 66666,虽然顺口却是非法的,因此会抛出一个 SecurityError 异常。 示例 12-2 Socket 连接异常处理的简单示例 package org.kingda.codesample.error { import flash.display.Sprite; import flash.net.Socket; public class SecurityErrorExample extends Sprite { public function SecurityErrorExample() { var targetServer:String = "www.kingda.org"; var port:uint = 66666; try { var socket:Socket = new Socket(); 5 黑羽 ActionScript 3 系列教程 之 12 http://www.kingda.org/ socket.connect(targetServer, port); //注意一旦异常发生后,下面这句 trace 是不会执行到的。 trace("try end"); } catch(e:SecurityError) { trace(e); //输出:SecurityError: Error #2003: 指定的 socket 端口号无效。 } finally { trace ("finally ended!"); } } } } 下面,依次讲述 try,catch, finally 的含义和用法。 12.2.1. try 将可能会抛出异常的语句,放置在 try 里面。这是告诉编译器,我的这些语句可能会出 现异常哦,你要小心点,使用特殊的机制来评估。一旦发生异常,将异常提交给catch 语句, 而不要“蒙混过关”,沉默失败(Silent Failure)。 要注意的是,不要一股脑儿将所有语句都放入 try 块。笔者认为尽可能只放可能出错的 语句,这样 try 中的内容简洁,一眼就知道调试重点在哪里。 要特别注意的是,异常一旦发生,那么try块中的语句就会立即停止执行。换句话说, 产生异常的语句后面所有其他语句都会停止执行。在示例 12-2中,由于异常发生了,try 块中最后一句trace不会被执行。如果将端口号改到 65535 以下,异常不会发生,trace语 句也就会执行了。但try块外面的语句不受影响,会继续执行。 那么,犯罪嫌疑语句已经放入 try 后,该找相关负责人来处理它们了。这些相关负责人 就是 catch 块中的内容。 12.2.2. catch catch 就是抓住的意思。 一个 try 可以跟随几个 catch,以便应付可能抛出的不同异常。 异常一旦发生,那么如果 try{}后面跟着 catch,那么实质上有以下几件事发生: 1.生成异常对象(Error 类实例或其子类实例),try 块中语句执行立刻中止。 2.AVM 会按 catch 语句块出现的先后顺序,查找和异常对象对应的 catch 块。 3.一旦发现异常和 catch()中定义的异常类型相符。就会被 catch“抓住” 。抓住之后, 就会交给这个 catch 块中的语句处理。 本例中,只是简单的将异常对象 trace 出来,在实际运用中,可能是在其中更换端口号, 6 黑羽 ActionScript 3 系列教程 之 12 http://www.kingda.org/ 再重新连接一遍;或者提示用户程序异常,需要退出。如果当前没法解决,比如无法决定有 效的端口号,那么继续抛出一个异常,希望更高层的代码能捕捉到这个错误。 12.2.3. finally 不论抛出的异常是否被 try{}后面跟随的 catch 语句捕捉到,finally 中的语句一定都会 执行。这就是 finally 的好处,无论 try 块中的语句是否碰到异常而中止,finally 块中的语 句执行不会受到影响。 一般情况下,是在 finally 中做一些释放资源,清除打扫的工作,比如关闭 Socket 的连 接等。 finally 是可选的,不是必须的。finally 只能有一个。 12.2.4. try-catch-finally 的语法规则 try-catch-finally 异常处理机制必须符合本小节介绍语法规则,不然编译器会报错。 try 后面至少要跟有一个 catch 语句块或者 finally 语句块。否则会报错“1073: 语法错 误: 需要 catch 或 finally 子句。” try 后面可以跟有多个 catch 语句块。一个 catch 块处理一种异常类型。catch 块的顺 序应根据所处理的具体异常类型来排列。一般来说,从最具体的 Error 子类到最不具体的 Error 类顺序来先后排列。更加详细的原则请参见“处理异常的原则和方式”。 catch 语句块里面可以再嵌套 try-catch-finally 的结构。 try,catch,finally 这三个块中可以再次抛出异常。如何抛出异常?请看下一节。 12.3. 使用 throw 抛出异常 除了系统 API 可以抛出异常外,我们还可以在自己的代码中指定抛出异常。我们不仅 可以抛出系统预定义好的异常类实例,还可以扩展 Error 或其子类生成自定义的异常类。 在 ActionScript 3 中使用 throw 关键字来抛出异常。 Java 用户注意,在 ActionScript 3 中,类方法抛出异常无需声明。throw 关键字只用于在语句中抛出异 常。 在继续讲述用 throw 抛出异常之前,先必须唠叨两句:抛出异常要谨慎使用。何时该 处理异常,何时该抛出异常,本文在“处理异常的原则和方式”中有详细讲述。先给大家提 个醒。 12.3.1. 抛出 Error 类或其子类的实例 抛出 Error 类或其子类的异常很简单。Error 类的构造函数接受两个参数:第一个是字 符串变量,作为异常的文字信息,默认为空;第二个是异常的数字 ID,int 型的,默认为 0。 两个参数都是可选的。从使用上来说一般都会定义一下异常的信息,即第一个变量。 第二个参数较少用到,如果需要自定义,那么建议避开 1000 到 2152 这段数字。因为这段数字已经被 7 黑羽 ActionScript 3 系列教程 之 12 http://www.kingda.org/ ActionScript 3 内置的 Runtime Error 占用了。为了避免冲突,和考虑到 ActionScript 3 日后可能添加 更多的内置 Runtime Error,笔者建议保险起见,避开 1000-10000 这一段。 具体有如下几种常用形式: 生成一个 Error 类或其子类的实例,用 throw 抛出。因为 Error 是动态类,所以某些情 况下,我们可能希望再让它携带一些信息,再将它抛出。 生成一个匿名的 Error 或其子类的实例,用 throw 抛出。这种情况往往是只需要抛出 异常即可。最多定义一下异常的信息。 将一些相关信息传给一个特定的方法(或者函数),由该方法(或者函数)根据这些信 息来判断返回什么样的异常。这样的应用,比较灵活,代码集中,常见于比较复杂的环境中。 值得推荐。 下面这个代码例子按顺序给出了这几种常用形式的代码。读者可以更换这几段代码顺 序,来查看异常处理的结果。 示例 12-3 抛出异常的几种常用形式 package org.kingda.codesample.error { import flash.display.Sprite; import flash.utils.*; public class ThrowErrorSample extends Sprite { public function ThrowErrorSample() { try { //1.生成一个 Error 对象,并抛出 var CustomError_1:TypeError = new TypeError("Try the first error:", 1200); throw CustomError_1; //2.抛出一个匿名 Error 对象 throw new Error("Try the second error") //3.抛出一个函数返回的 Error 对象 var errorCause:Number = 5000; throw reportErrorFunc(errorCause); } catch(e:Error) { trace (e); } } //特定的根据相关信息判断,并返回不同类型的 Error 对象 private function reportErrorFunc(eC:Number):Error { if (!(eC is Number)) 8 黑羽 ActionScript 3 系列教程 之 12 http://www.kingda.org/ return new TypeError("CustemFuncError:Not a Number"); if (eC > 1000) { return new Error("CustemFuncError:A big number error."); } else { return new Error("CustemFuncError:A small number error."); } } } } 12.3.2. 自定义异常 所谓自定义异常,就是通过扩展 Error 类,或者 Error 的子类来创建一个新的异常类。 为什么要自定义异常? 我们开发具体应用程序时,会碰到如下几种类似情况: 当某种不符合我们程序规定的对象(或者行为)出现时,我们希望抛出一种自定义异常, 从而利用异常处理机制来管它。 这种对象(或者行为),本身符合语法,而且也不会引起任何系统内置异常。我们抛出 异常,只是因为它不符合我们应用程序的要求。 比如说,我们从第三方数据源接收到一个格式良好的 XML。我们 RIA 程序要求它包含 三个名为 action 的子元素,但是这个 XML 并不符合这个要求。而且这很可能会引起我们 后续程序出错。于是我们新建了一个 CustomXMLError 这个类,扩展自 Error 类。一旦发 现验证失败,我们就抛出这个 Error 子类的实例,由专门 catch 这个 CustomXMLError 类 实例的代码块捕捉到,并执行相关的处理。 某种系统内置异常出现了,但是在我们的程序中这种异常可能有多种含义。而且不同的 含义中,需要在这个异常对象中加入不同的信息。而对不同信息又有不同的异常处理方式。 那么这时候,一个好的做法就是扩展这个系统内置异常类,生成几个子类,各自代表不同含 义下的异常。 比如,在示例 12-2中,我们得到了一个无效的端口号导致了连接失败。如果这个类中 有三个方法调用了socket.connect(),虽然都会抛出SecurityError异常。但如果是调用第 一个方法引起的,那么可能是由于A原因;调用了B方法引起的,可能是由于B原因引起的; 如果是C方法,那么可能是C原因。而且这三种原因各不相同,我们需要有不同的信息加入 到Error对象中。那么虽然抛出的都是SecurityError异常实例,但已经不能满足我们的需 要了。这样我们就可以扩展SecurityError类,生成三个子类各自表示原因。 示例 12-4 使用自定义异常的例子 package org.kingda.codesample.error { 9 黑羽 ActionScript 3 系列教程 之 12 http://www.kingda.org/ import flash.display.Sprite; import flash.net.Socket; import mx.validators.ValidationResult; public class SecurityErrorExample extends Sprite { var socket:Socket; public function SecurityErrorExample() { socket = new Socket(); var targetServer:String = "www.kingda.org"; var port:uint = 66666; try { connectMethodA(targetServer, port); } catch(e:CustomBError) { trace (e); } catch(e:CustomAError) { trace ("抓到 A 了:"+ e +"\t 出错端口号:" + e.getPort()); //输出:抓到 A 了:SecurityError 出错端口号:66666 } catch(e:SecurityError) { trace(e); } } private function connectMethodA(host:String, portNum:int):void { //...A 方法的其他代码.... try { socket.connect(host, portNum); } catch (e:SecurityError) { var customAErr:CustomAError = new CustomAError(); customAErr.record(host, portNum); throw customAErr; } } private function connectMethodB(host:String, portNum:int):void { //...B 方法的其他代码.... try { socket.connect(host, portNum); 10 黑羽 ActionScript 3 系列教程 之 12 http://www.kingda.org/ } catch (e:SecurityError) { throw new CustomBError(); } } } } //下面就是自定义的异常类 class CustomAError extends SecurityError { //...CustomAError 的一些代码.... private var infoForA:Object = new Object(); public function record (host:String, portNum:int):void { infoForA.host = host; infoForA.port = portNum; //...其他代码.... } public function getPort():int { return infoForA.port; } } class CustomBError extends SecurityError { //...CustomBError 的代码... } 我们在 connectMethodA 方法中抛出了特有的 CustomErrorA 类的异常实例。可以看 到 CustomErrorA 有自己的属性和方法。由于实际执行的是 connectMethodA,所以抛出 的异常不会当成 CustomErrorB 接受,而被专门管 CustomErrorA 的 catch 块捕捉到了, 并使用了 A 异常的特有的方法 getPort()所返回的信息。 在了解了 try-catch-finally 和 throw 异常的应用之后,我们需要深入的了解一下 ActionScript 3 中的异常架构,以及常用的异常类。这样我们才可以最好的利用 ActionScript 3 强大的异常处理系统。 (未完待续,下节正在整理中,一周内发布) 11 黑羽 ActionScript 3 系列教程 之 12 http://www.kingda.org/ 12.4. ActionScript 3 中异常的层次和结构 12.4.1. ActionScript 3 中对异常的良好支援 12.4.2. Error 相关类别的层次和架构 12.4.3. 最常碰到的几种异常 12.4.4. 自定义异常的资讯 12.5. 处理异常的原则和方式 12.5.1. 处理异常的几条原则 12.5.2. 处理异常的常用的几种方式 12.5.3. 4 大提倡与 4 大忌讳 12
还剩11页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

sweetbaybe

贡献于2011-09-15

下载需要 20 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf