Руководство по actionscript. часть 2, стр. 087

TraceC’An error occurred: » + e. message);

traceCPlease specify a smaller value.»): } catch (e:VirtualPetlnsufficientDataException) {

// Обработка нулевой длины.

traceC’An error occurred: » + e. message);

traceCPlease specify a larger value.»); } catch (e:VirtualPetNameException) {

// Обработка общих ошибок, связанных с размерностью.

traceC’An error occurred: » + е. message);

traceCPlease specify a valid dimension.»); } catch (e:Error) {

// Обработка любых ошибок, которые не относятся

// к ошибкам VirtualPetNameException.

}

Не забывайте, что выбор степени детализации ошибок зависит от ситуации. Реализуя вариант 4, мы создали пользовательские подклассы класса Error для каждого исключения, генерируемого классом VirtualPet. Этот подход дает нашей программе максимальную возможность независимо обрабатывать различные типы ошибок. Однако во многих ситуациях подобная гибкость является излишней. Будет лучше, если степень детализации ошибок будет определяться требованиями логики вашей программы.

Передача исключений вверх по иерархии объектов

В ActionScript исключение может быть сгенерировано в любом месте программы, даже в сценарии кадра на временной шкале! В этом случае возникает вопрос: каким образом среда выполнения Flash находит соответствующий блок catch для обработки этого исключения? И что произойдет при отсутствии блоков catch? Эти загадки решаются с. помощью магии передачи исключений вверх по иерархии объектов. Проследуем по пути передачи исключений вместе со средой Flash с того момента, как она выполнит в программе оператор throw. В ходе последующей инсценировки «размышления» среды выполнения Flash оформлены в виде комментариев.

Руководство по actionscript. часть 2, стр. 088

После исполнения оператора throw нормальная работа программы немедленно прекращается и среда Flash пытается найти блок try, который содержит этот оператор. Например, рассмотрим оператор throw:

// Среда выполнения Flash: Хм. Оператор throw.

// Существует ли блок try, который содержит этот оператор?

throw new Error(«Something went wrong»);

Если оператор throw включен в блок try, среда выполнения Flash пытается найти блок catch, тип данных параметра которого совпадает с типом данных значения сгенерированного исключения (в данном случае с типом Error):

// Среда выполнения Flash: Отлично, я нашла блок try. // Существует ли соответствующий блок catch? try {

throw new Error(«Something went wrong»);

}

Если соответствующий блок catch найден, среда выполнения Flash передает управление программой этому блоку:

try {

throw new Error(«Something went wrong»); // Среда выполнения Flash: Найден блок catch, типом данных параметра // которого является Error! Поиск завершен. Сейчас я выполню этот // блок catch… } catch (e:Error) {

// Обработка ошибок…

}

Однако если соответствующий блок catch не может быть найден или если оператор throw изначально не был включен в блок try, среда Flash проверяет, размещен ли этот оператор внутри метода или функции. Если да, то среда выполнения ищет блок try вокруг кода, вызвавшего этот метод или функцию. Следующий код демонстрирует, как среда Flash реагирует на оператор throw, который размещен внутри метода и не включен в блок try:

public function doSomething ( ):void { // Среда выполнения Flash: Хм. Блок try отсутствует. // Проверю, кто вызвал этот метод, throw new Error(«Something went wrong»);

}

Если код, вызвавший этот метод или функцию, включен в блок try, среда Flash попытается найти соответствующий блок catch и, если такой блок будет найден, выполнит его. Следующий код демонстрирует исключение, генерируемое в методе и обрабатываемое там, где вызывается этот метод (то есть уровнем выше по стеку вызовов):

public class ProblemClass { public function doSomething ( ):void { // Среда выполнения Flash: Хм. Блок try отсутствует. // Проверю-ка я, кто вызвал этот метод, throw new Error(«Something went wrong»);

}

}

// Среда выполнения Flash: Ага, вот кто вызвал метод doSomething( ). // И вот блок try, включающий этот код, вместе с блоком catch, типом // данных параметра которого является Error! Моя работа сделана. // Блок catch, пожалуйста, выполняйтесь… try {

var problemObject:ProblemClass = new ProblemClass( ); problemObject. doSomething( ); } catch (e:Error) { // Обработка ошибок…

Руководство по actionscript. часть 2, стр. 089

Trace(«Exception caught in ErrorDemo, thrown by doSomething( ).»);

}

}

Стек вызовов — это список функций или методов программы, исполнением которых среда А щ Flash занимается в любой момент времени. Функции и методы размещаются в списке 3-У в порядке, обратном порядку их вызова, по направлению сверху вниз. Если функция

находится непосредственно под другой функцией в стеке вызовов, значит, нижняя

функция была вызвана верхней функцией. Самая нижняя функция в стеке вызовов — это

функция, выполняемая в настоящий момент.

В приложении Flex Builder и среде разработки Flash вы можете использовать отладчик для просмотра стека вызовов текущей программы, как описано в документации корпорации Adobe.

В предыдущем коде исключение, сгенерированное методом, было поймано блоком try/catch, в который включена инструкция вызова метода. Тем не менее, если вокруг кода, вызывающего функцию или метод, не найден блок try, среда выполнения Flash просматривает весь стек вызовов в поисках блока try с соответствующим блоком catch. Следующий код демонстрирует метод, генерирующий ошибку, которая обрабатывается двумя уровнями выше в стеке вызовов:

public class ProblemClass { public function doSomething ( ):void { // Среда выполнения Flash: Хм. Блок try отсутствует.^ // Проверю-ка я, кто вызвал этот метод, throw new Error(«Something went wrong»):

}

public class NormalClass { public function NormalClass ( ) { // Среда выполнения Flash: Ага, вот кто вызвал метод doSomething( ). // Но здесь все равно нет блока try. Проверю,

// кто вызвал этот метод,

var problemObject:ProblemClass = new ProblemClass( ); problemObject. doSomething( );

}

}

// Среда выполнения Flash: Ага! Нашла блок try, который имеет блок // catch, типом данных параметра которого

// является Error! Моя работа сделана.

Руководство по actionscript. часть 2, стр. 090

// Блок catch, пожалуйста, выполняйтесь…

try {

var normal Object:Normal CI ass = new NormalClass( ); } catch (e:Error) { // Обработка ошибок…

trace(«Exception caught in ErrorDemo, thrown by doSomething( ).»);

}

}

}

Обратите внимание, что среда выполнения Flash находит блок try/catch, даже несмотря на то, что этот блок не включает ни код, генерирующий ошибку, ни код, который вызывает метод, генерирующий ошибку, а только код, вызывающий метод, который, в свою очередь, вызывает метод, генерирующий ошибку!

Следующий код демонстрирует предыдущий пример с передачей исключения вверх по иерархии объектов в контексте нашей программы по созданию виртуального зоопарка. Для краткости в следующем листинге показан только код, присваивающий объекту имя животного. Комментарии в коде описывают, как происходит передача исключения.

package { import flash. display. Sprite; import zoo.*;

public class VirtualZoo extends Sprite { private var pet:Virtual Pet;

public function VirtualZoo ( ) { try {

// Этот код пытается присвоить животному слишком длинное имя. // В результате метод setName( ) генерирует ошибку. // Однако возникшее исключение не обрабатывается в конструкторе // класса VirtualPet (откуда вызывается метод setName( )). Вместо // этого исключение обрабатывается там, где вызывается конструктор // класса VirtualPet (то есть двумя уровнями выше в стеке вызовов) pet = new Virtual Pet(«Bartholomew McGillicuddy»); } catch (e:Error) { traceC’An error occurred: » + e. message);

// Если в процессе создания объекта VirtualPet возникает исключение, // объект не будет создан. Таким образом, здесь мы создадим новый // объект VirtualPet с предопределенным допустимым именем.

Руководство по actionscript. часть 2, стр. 091

Pet = new Virtual Pet(«Unnamed Pet»);

}

}

}

}

package zoo { public class VirtualPet {

public function VirtualPet (name:String):void { // Даже несмотря на то, что метод setName( ) вызывается здесь, // исключения, генерируемые методом setName( ), не обрабатываются // в данном конструкторе. Они обрабатываются выше в стеке вызовов // кодом, который создал данный объект VirtualPet. setName(name);

}

public function setName (newName:String):void { // Исключения, генерируемые в этом методе, не обрабатываются здесь. // Они обрабатываются на два уровня выше в стеке вызовов кодом, // который создал данный объект VirtualPet.

if (newName. indexOfC» «) == 0) {

throw new VirtualPetNameException( ); } else if (newName == «») {

throw new VirtualPetInsufficientDataException( ); } else if (newName. length > VirtualPet. maxNameLength) {

throw new VirtualPetExcessDataException( );

}

petName = newName:

}

}

}

Необработанные исключения. Мы рассмотрели ряд сценариев, в которых обрабатывались различные ошибки. Что же произойдет в том случае, когда среда выполнения Flash не найдет блок catch, способный обработать сгенерированное исключение? Если подходящий блок catch не найден во всем стеке вызовов, то Flash завершает выполнение кода, который на текущий момент остается в стеке вызовов. Кроме того, если программа выполняется в отладочной версии среды Flash, информация о возникшей ошибке появится в отдельном окне: в окне Output (Вывод) (среда разработки Flash) или в окне Console (Консоль) (приложение Flex Builder). После этого выполнение программы продолжится в обычном режиме.

Руководство по actionscript. часть 2, стр. 092

Следующий код демонстрирует метод, генерирующий ошибку, которая никогда не будет обработана:

public class ProblemClass { public function doSomething ( ):void { // Среда выполнения Flash: Хм. Блок try отсутствует. // Проверю-ка я, кто вызвал этот метод, throw new Error(«Something went wrong»);

}

}

public class ErrorDemo extends Sprite { public function ErrorDemo ( ) { // Среда выполнения Flash: Ага, вот кто вызвал метод doSomething( ). // Но здесь все равно нет блока try. Хм. Я просмотрела весь стек вызовов // до самого верха и не нашла блока try. Если это отладочная версия // среды выполнения Flash, я сообщу о проблеме. Возможно, программист

// знает, что нужно делать.

var problemObject-.ProblemClass = new ProblemClass( ); problemObject. doSomething( );

}

}

Как мы только что увидели, метод не обязан обрабатывать свои собственные исключения, поскольку исключения обладают способностью подниматься вверх по стеку вызовов. Исключения метода не обязан обрабатывать даже код, вызывающий этот метод. Обработка исключения допускается на любом уровне в стеке вызовов. Любой метод может делегировать обработку исключений коду, вызывающему данный метод. С другой стороны, генерация исключений, которые никогда не будут обработаны, является дурным тоном и оказывает опасное воздействие на программу. Вы должны всегда обрабатывать исключения или, если столкнетесь с необработанным исключением, в первую очередь изменить свой код, чтобы избежать повторной генерации данного исключения.

Блок finally

Пока мы рассмотрели лишь блоки try и catch инструкции try/catch/finally. Как уже известно, блок try содержит код, который может генерировать исключение, а блок catch включает в себя код, который выполняется в ответ на сгенерированное исключение. Для сравнения, блок finally содержит код, который выполняется всегда, независимо от того, возникло в блоке try исключение или нет.

Инструкция try/catch/finally содержит один (и только один) блок finally, который является ее последним блоком. Например:

try {

// Вложенные инструкции } catch (е:Тип0шибки1) {

II Обработка исключений типа Тип0шибки1. } catch (е:ТипОшибкип) {

II Обработка исключений типа ТипОшибкип. } finally {

// Этот код выполняется всегда, независимо от того, как завершается // выполнение блока try.

Руководство по actionscript. часть 2, стр. 093

}

Неправильное размещение блока finally вызовет ошибку на этапе компиляции. В предыдущем коде блок finally будет выполнен сразу после того, как:

? выполнение блока try завершится без ошибок;

? блок catch обработает исключение, сгенерированное блоком try;

? необработанное исключение поднимется вверх по иерархии объектов;

? оператор return, continue или break передаст управление программой за пределы блоков try или catch.

Блок final 1у инструкции try/ cat ch/final 1у обычно содержит очищающий код, который должен выполняться независимо от того, возникло исключение в соответ-

ствующем блоке try или нет. Предположим, что мы создаем игру в жанре «космический шутер» и определяем класс Spaceship, представляющий космические корабли. У класса Spaceship есть метод attackEnemy ( ), который выполняет следующее.

? Устанавливает текущую цель для космического корабля.

? Стреляет по выбранной цели.

? Удаляет выбранную цель (присваивая переменной currentTarget объекта Spaceship значение null).

Предположим, что в нашем гипотетическом приложении при выполнении первых двух из описанных задач может возникнуть исключение. Более того, предположим, что метод attackEnemy ( ) не обрабатывает эти исключения самостоятельно; он передает исключения вызывающему методу. Независимо от того, было сгенерировано исключение или нет, метод attackEnemy ( ) должен присвоить переменной currentTarget значение null.

Вот так выглядел бы метод attackEnemy ( ), если бы мы запрограммировали его с помощью оператора catch (то есть без использования блока finally):

public function attackEnemy (enemy:SpaceShip):void { try {

setCurrentTarget(enemy): fireOnCurrentTargetC ): } catch (e:Error) { // Удаляем текущую цель, если возникло исключение. setCurrentTarget(nul1): // Передаем исключение вызывающему методу, throw е:

}

// Удаляем текущую цель, если никаких исключений не возникло. setCurrentTarget(nul1);

}

Здесь мы вынуждены дублировать инструкцию setCurrentTarget (null). Мы поместили ее и внутрь блока catch, и после инструкции try/catch, гарантируя тем самым, что она будет выполнена независимо от того, возникло исключение в блоке try или нет. Тем не менее дублирование инструкции может привести к ошибке. В предыдущем методе программист мог бы легко забыть удалить текущую цель после блока try/catch.

Руководство по actionscript. часть 2, стр. 094

Если изменить нашу стратегию так, чтобы текущая цель удалялась в блоке fina 11 у, мы устраним ненужные команды в предыдущем коде:

public function attackEnemy (enemy:SpaceShiр):void { try {

setCurrentTarget(enemy): fireOnCurrentTarget( ): } finally { setCurrentTarget(nul1);

В модифицированной версии блок finally удаляет текущую цель независимо от того, возникло исключение или нет. Поскольку блок finally обрабатывает обе ситуации, у нас нет необходимости в блоке catch; мы можем просто позволить исключению автоматически подняться вверх к вызывающему методу.

Вы можете поинтересоваться, а зачем нам вообще нужен блок finally? Иными словами, почему нельзя просто использовать следующий код?

// Этот код выглядит подходящим, однако в нем существует проблема. // Сможете определить ее?

public function attackEnemy (enemy:SpaceShiр):void { setCurrentTarget(enemy); fireOnCurrentTarget( ); setCurrentTarget(nul1);

}

Запомните, что, когда генерируется исключение, управление программой передается в ближайший подходящий блок catch в стеке вызовов. Следовательно, если метод fir eOnCur rent Target ( ) генерирует исключение, управление передается в метод attackEnemy ( ), при этом обратно в метод fireOnCurrentTarget ( ) управление возвращено не будет и инструкция setCurrentTarget (null) останется невыполненной. Однако с помощью блока finally мы гарантируем, что инструкция setCurrentTarget (null) будет выполнена до того, как исключение поднимется вверх по иерархии объектов.

Пример метода attackEnemy ( ) отражает наиболее распространенное использование блока finally в многопоточных приложениях, в которых одновременно может выполняться сразу несколько фрагментов кода и которые разрабатываются с помощью таких языков программирования, как, например, Java. В языке Java следующая общая структура — обычное явление; она исключает возможность удаления объекта, выполняющего некую задачу, другим объектом в процессе выполнения текущей задачи:

// Установить состояние, обозначающее выполнение данным объектом текущей // задачи. Внешние объекты должны проверять состояние данного объекта перед // тем, как обратиться к нему или выполнить над ним какие-либо действия. doingSomething = true: try {

// Выполняем задачу. doSomething( ); } finally {

// Сбросить состояние, обозначающее выполнение текущей задачи (независимо // от того, возникло в процессе выполнения задачи исключение или нет). doingSomething = false:

}

В языке ActionScript приведенный код, управляющий состоянием объекта, на самом деле необязателен, поскольку разрабатываемые с помощью этого языка приложения являются однопоточными, следовательно, никакой внешний объект не сможет изменить состояние другого объекта, выполняющего некий метод. Таким образом, в языке ActionScript блок finally используется гораздо реже, чем в языках, применяемых для разработки многопоточных приложений. Тем не менее этот блок

можно использовать для организационных целей — помещать в него код, который выполняет очистку после выполнения другого кода.

Руководство по actionscript. часть 2, стр. 095

Вложенные исключения

До сих пор мы использовали только одноуровневые инструкции try/catch/ finally, однако логика обработки исключений может быть и вложенной. Инструкция try/catch/finally может размещаться внутри блока try, catch или finally другой инструкции try/catch/finally. Такое иерархическое вложение позволяет любому блоку инструкции try/catch/finally выполнять код, который, в свою очередь, может генерировать исключения.

Предположим, что мы создаем многопользовательское веб-приложение, представляющее доску объявлений. Мы определяем следующие классы: BulletinBoard — основной класс приложения, GUIManager — класс, управляющий пользовательским интерфейсом, и User — класс, который представляет пользователя на доске. В классе BulletinBoard мы описываем метод populateUserList ( ), который отображает список активных пользователей на текущий момент. Выполнение мето-дapopulateUserList ( ) состоит из двух этапов: на первом этапе метод получает экземпляр класса List из экземпляра класса GUIManager нашего приложения. Класс List представляет отображаемый на экране список пользователей. Затем метод populateUserList ( ) заполняет экземпляр класса List пользователями из переданного массива экземпляров класса User. На обоих этапах существует потенциальная возможность возникновения исключения, поэтому в методе populateUserList ( ) используется вложенная структура try/catch/finally. Рассмотрим эту вложенную структуру поближе.

EcлинaпepвoмэтaпeвыпoлнeниямeтoдapopulateUserList ( ) экземпляр класса List окажется недоступным, будет сгенерировано исключение UserLis tNotFound экземпляром класса GUIManager. Исключение UserLi s tNotFound обрабатывается внешней инструкцией try/catch/finally.

Если, с другой стороны, экземпляр класса List окажется доступным, метод populateUserList ( ) перейдет к выполнению второго этапа, где с помощью цикла заполнит экземпляр класса List пользователями из переданного массива. На каждой итерации цикла, если ID текущего пользователя не может быть найдено, метод User. gelD ( ) генерирует исключение UserldNotSet. Оно обрабатывается вложенной инструкцией try/catch/finally.

Рассмотрим этот код:

public function populateUserList (users:Array):void { try {

// Приступаем к выполнению этапа 1… получаем экземпляр класса List. // Если метод getllserListC ) сгенерирует исключение, будет выполнен // внешний блок catch.

var ulist:List = getGUIManager( ).getUserList( ); // Приступаем к выполнению этапа 2… заполняем экземпляр класса List, for (var i-.Number = 0; i < users.length: i++) { try {

var thisUser:User = User(users[i]);

// Если метод getID( ) сгенерирует исключение, будет выполнен // вложенный блок catch. В противном случае пользователь будет // добавлен в экземпляр класса List вызовом метода addltem( ). ulist. addItem(thisUser. getName( ). thisUser. getID( )); } catch (e:UserIdNotSet) { trace(e. message);

continue; // Пропускаем этого пользователя.

}

}

} catch (e:UserListNotFound) { trace(e. message);

}

}

Теперь, когда мы рассмотрели конкретный пример вложенного исключения, познакомимся с общим процессом обработки вложенных исключений.

Руководство по actionscript. часть 2, стр. 096

Если исключение генерируется в блоке try, вложенном в другой блок try, и внутренний блок try содержит блок catch, способный обработать сгенерированное исключение, выполняется внутренний блок catch и программа продолжает свое выполнение сразу после внутренней инструкции try/catch/finally.

try { try {

// Здесь генерируется исключение, throw new ErrorCTest error»); } catch (e;Error) { // Здесь обрабатывается исключение. trace(e. message); // Выводит; Test error

}

// Здесь продолжается выполнение программы. } catch (e:Error) { // Обработка исключений, генерируемых внешним блоком try.

}

Если, с другой стороны, исключение возникло в блоке try, вложенном в другой блок try, однако внутренний блок try не содержит блока catch, способного обработать данное исключение, сгенерированное исключение будет передаваться вверх к внешней инструкции try/catch/finally (и при необходимости дальше по стеку вызовов) до тех пор, пока не будет найден подходящий блок catch или пока не будет достигнута верхняя точка стека вызовов. Если исключение будет обработано в некоторой точке стека вызовов, то выполнение программы продолжится сразу после инструкции try/catch/finally, обработавшей это исключение. Обратите внимание, что в следующем примере кода (и последующих примерах) гипотетический тип данных ошибки SomeSpecificEr ror является заполнителем, используемым для того, чтобы сгенерированное исключение не было поймано. Чтобы протестировать пример кода в вашем собственном коде, вы должны создать подкласс SomeSpecificError класса Error.

try { try {

// Здесь генерируется исключение, throw new Error(«Test error»); } catch (e;SomeSpecificError) { // Здесь исключение не обрабатывается. trace(e. message); // Инструкция никогда не будет выполнена, // поскольку типы не совпадают.

}

} catch (е:Error) { // Исключение обрабатывается здесь. trace(e. message); // Выводит; Test error

}

// Выполнение программы продолжается здесь, сразу после обработки исключения // внешним блоком catch.



Полезные ссылки
Случайные записи
  • 28.02.2011">Руководство по actionscript. часть 6, стр. 021
  • 18.03.2011">Руководство по actionscript. часть 2, стр. 111
  • 15.03.2011">Руководство по actionscript. часть 3, стр. 053
  • 08.03.2011">Руководство по actionscript. часть 4, стр. 080
  • 02.03.2011">Руководство по actionscript. часть 5, стр. 089
  • 28.02.2011">Руководство по actionscript. часть 5, стр. 137
  • 11.05.2010">Самоучитель по креативному веб-дизайну. Книга 1, стр.16
  • 10.08.2011">Сделай из смартфона игровую приставку
  • 04.06.2010">Самоучитель по креативному веб-дизайну. Книга 3, стр.6
  • 23.01.2011">Руководство по actionscript. часть 1, стр. 061
  • 20.03.2011">Руководство по actionscript. часть 2, стр. 066
  • 28.02.2011">Руководство по actionscript. часть 6, стр. 014
  • 06.03.2011">Руководство по actionscript. часть 4, стр. 144
  • 03.06.2010">Самоучитель по креативному веб-дизайну. Книга 3, стр.81
  • 23.08.2011">Покупайте только качественный трикотаж
Опрос

Какие цвета вы предпочитаете?

View Results

Loading ... Loading ...