полнения программы, в языке Java используется конструкция try/
catch/finally. Блок try содержит инструкции, выполнение может
привести к возникновению исключительных ситуаций. Следующие за
ним один или несколько блоков catch предназначены для обработки
исключительных ситуаций. Наконец, блок finally содержит инст-
рукции, которые будут выполнены независимо от возникновения
исключительной ситуации в блоке try. При выходе из try-части
посредством инструкций передачи управления (break, return и
т.п.) блок finally также будет выполнен.

Для передачи информации об исключительной ситуации используются
объекты классов - наследников класса Throwable. Например, класс
ArrayIndexOutOfBoundsException отвечает за контроль выхода ин-
дексов за границы массивов, класс OutOfMemoryException - за ре-
акцию на исчерпание свободной памяти, класс ClassCastException
- за ошибки при преобразовании типов, класс
InterruptedException - за обработку прерывания текущего потока
и т.д. Компонентой всех этих классов является поле типа String,
в которое помещается текст сообщения об ошибке. Метод
getMessage возвращает этот текст.

В подобных объектах может содержаться и дополнительная информа-
ция. Например, объекты класса InterruptedIOException содержат
поле, в которое заносится число байт, переданных до возникнове-
ния исключительной ситуации.

Следующий фрагмент программы распечатывает сообщения из массива
messages. При этом мы не пытаемся выяснить размер этого масси-
ва, а просто полагаемся на механизм обработки исключительных
ситуаций. (Конечно, мы не советуем писать программы в таком
стиле).

try {
for (int i = 0; i < 100; i++) {
System.out.println (messages[i]);
}
}

catch (ArrayOutOfBoundException e) {
System.out.println ("No more messages");
}
catch (Exception e) {
System.out.println ("Unexpected exception");
System.out.println (e.getMessage());
}

finally {
System.out.println ("Work done");
}

Искобчительные ситуации могут возбуждаться программно при помо-
щи инструкций вида

throw new MyException ("Something's wrong");

Спецификации языка Java подразделяют исключительные ситуации на
две категории. К первой категории (класс Error) относятся ситу-
ации, на которые программа не обязана реагировать (это заведомо
сделает Java-машина). Ко второй категории (класс Exception) от-
носятся ситуации, которые программа должна обрабатывать обяза-
тельно. Если при выполнении метода может возникать исключитель-
ная ситуация второго типа, он должен либо обрабатывать ее сам с
помощью конструкции try/catch/finally, либо в его определении
должна фигурировать конструкция

throws Exception1, Exception2, ...

Пример.

class Foo extends Object {
. . .
public void readFromFile (String fn) throws InvalidFormatException {
FileInputStream fis;
try {
fis = new FileInputStream (fn);
// Читаем данные из файла.
. . .
// Если файл имеет неправильный формат,
// вожбуждаем исключительную ситуацию:
throw new InvalidFormatException ("Wrong format");
. . .
}
catch (FileNotFoundException e) {
// Предпринимаем соответствующие действия
}
finaly {
if (fis != null )
fis.close(); // всегда закрываем файл, если он был открыт
}
}
. . .
}

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

    4.1.6. Механизм потоков



Механизм потоков - обязательная черта современных операционных
сред. За счет потоков обеспечивается масштабируемость программ-
ных систем, оптимальное использование аппаратных ресурсов, вы-
сокая скорость отклика на запросы пользователей. Поэтому нет
ничего удивительного в том, что в языке Java механизм потоков
представлен с самого начала и в полном объеме.

В языке Java потоки представлены посредством класса Thread, ин-
терфейса Runnable, спецификатора метода synchronized и методов
класса Object wait и notify.

4.1.6.1. Класс Thread и интерфейс Runnable

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

Поток запускается с помощью вызова метода start() класса
Thread. Последовательность действий, выполняемых в рамках пото-
ка, задается в методе run(). Подчеркнем, что метод run() ис-
пользуется только для задания помледовательности действий; явно
вызывать его не только не нужно, но и просто вредно.

Поток заканчивается либо при завершении выполнения метода
run(), либо с помощью явных вызовов методов класса Thread
stop() или destroy(). Возобновить работу завершенного потока
невозможно.

Для временной приостановки работы потока с последующим возоб-
новлением служат методы suspend(), sleep() и yeild().

Обычно поток, приостановленный с помощью метода suspend, возоб-
новляет работу посредством метода resume().

Вызов метода sleep() приводит к приостановке потока на заданное
число миллисекунд.

Вызов метода yeild() означает добровольную уступку процессора
другому потоку; первоначальный поток остается готовым к выпол-
нению.

Java-потоки обладают приоритетами. В спецификациях оговаривает-
ся, что Java-машина реализует вытесняющую многопотоковость. Это
означает, что поток с большим приоритетом может прервать выпол-
нение менее приоритетного потока. Однако, спецификации не тре-
буют наличия разделения времени. Это значит, что для передачи
управления потоку с тем же приоритетом, вообще говоря, требуют-
ся явные действия со стороны первоначального потока - вызов ме-
тодов suspend(), sleep() или yeild().

На рис. 10 представлена диаграмма состояний потоков.

Рис. 10. Диаграмма состояний потоков.

Следующий пример содержит фрагмент одного из многочисленных ва-
риантов решения задачи "производитель/потребитель". Он заимст-
вован из письма, которое написал Mark Tillotson в группу сете-
вых новостей comp.lang.java.

class my_producer extends Thread
{
int items_to_do ;
my_buffer the_buffer ;

my_producer (my_buffer buf, int count)
{ super() ;
the_buffer = buf ;
items_to_do = count ;
}

public void run ()
{
while (items_to_do > 0)
{ System.out.println ("producer to_do = " + items_to_do) ;
Integer item = new Integer (items_to_do*items_to_do) ;
the_buffer.insert (item) ;
items_to_do-- ;
}
System.out.println ("producer exiting") ;
}
}

Данный производитель помещает в буфер квадраты целых чисел.

В приведенном простом примере класс my_producer является нас-
ледником класса Thread, что делает его потоком с последователь-
ностью действий, заданной методом run(). В реальных программах,
как правило, объект должен наследовать у какого-либо предшест-
венника содержательные свойства, а возможность паралелльного
выполнения ему предоставляется интерфейсом Runnable. Этот ин-
терфейс содержит единственный метод - run(). Пример.

1 class SomethingToRun extends BaseRunner implements Runnable {
2 private Thread aThread;
3 public void run () {
// выполняемые действия
. . .
4 }

5 SomethingToRun () {
6 aThread = new Thread (this);
7 aTread.start ();
8 }
9 }

В строке 6 создается новый поток. Аргументом конструктора явля-
ется обект класса SomethingToRun, а, значит, последовательность
выполняемых действий потока будет определяться методом run()
этого класса. Вызов метода start() в строке 7 ставит поток в
очередь готовых для выполнения.

    4.1.6.2. Средства синхронизации потоков



Как и во всякой многопроцессной или многопотоковой среде, в
Java существует проблема синхронизации доступа к разделяемым
ресурсам. Примером такого ресурса является буфер в задаче "про-
изводитель/потребитель".

Для опытных программистов отметим, что модель синхронизации,
принятая в языке Java, опирается на концепцию монитора, предло-
женную в 70-е годы Бринк-Хансеном.

В Java-программах можно выделять критические интервалы, которые
обозначаются ключевым словом synchronized. Если критическим ин-
тервалом является метод, спецификатор synchronized помещается в
его (метода) заголовок. Для превращения произвольной инструкции
(обычно это блок) в критический интервал служит конструкция

synchronized (выражение) инструкция;

где результатом выражения должен быть объект или массив.

Выполнение критического интервала начинается только после полу-
чения потоком монопольного доступа к соответствующему объекту.
До наступления этого момента поток блокируется.

Вызов wait() внутри критического интервала приводит к тому, что
текущий поток уступает монопольное право на критический интер-
вал и приостанавливается до тех пор, пока из какого-либо друго-
го потока не будет сделан вызов notify() или notifyAll(). Хоро-
шей иллюстрацией использования средств синхронизации потоков
является упоминавшаяся выше программа Марка Тиллотсона.

class my_buffer
{
Object [] vec = new Object [8] ;
int ip = 0 ;
int ep = 0 ;

synchronized void insert (Object item)
{
do
{
if (ip-ep < 8)
{ vec [(ip++) & 7] = item ;
if (ip-ep == 1) notify () ; // Уведомить, если буфер был пуст
return ;
}
try wait () ; catch (InterruptedException e) ;
} while (true) ;
}

synchronized Object extract ()
{
do
{
if (ip > ep)
{ Object result = vec [(ep++) & 7] ;
if (ip-ep == 7) notify(); // Уведомить, если буфер был полон
return result ;
}
try wait () ; catch (InterruptedException e) ;
} while (true) ;
}
}

class my_producer extends Thread
{
int items_to_do ;
my_buffer the_buffer ;

my_producer (my_buffer buf, int count)
{ super() ;
the_buffer = buf ;
items_to_do = count ;
}

public void run ()
{
while (items_to_do > 0)
{ System.out.println ("producer to_do = " + items_to_do) ;
Integer item = new Integer (items_to_do*items_to_do) ;
the_buffer.insert (item) ;
items_to_do-- ;
}
System.out.println ("Производитель заканчивает работу") ;
}
}

class my_consumer extends Thread
{
int items_to_do ;
my_buffer the_buffer ;

my_consumer (my_buffer buf, int count)
{ super() ;
the_buffer = buf ;
items_to_do = count ;
}

public void run ()
{
while (items_to_do > 0)
{ System.out.println ("consumer to_do = " + items_to_do) ;
Object item = the_buffer.extract () ;
System.out.println ("consumer got " + item) ;
items_to_do-- ;
}
System.out.println ("Потребитель заканчивает работу") ;
synchronized (this){
notify () ; // Посылаем уведомление о завершении работы
// (см. con.wait() в main())
}
}
}

public class threaded3
{
public static void main (String [] args) throws InterruptedException
{
my_buffer the_buffer = new my_buffer () ;
my_producer prod = new my_producer (the_buffer, 40) ;
my_consumer con = new my_consumer (the_buffer, 40) ;

Thread.currentThread().setPriority (5) ;
prod.setPriority (4) ; // Производитель получает более высокий приоритет
con.setPriority (3) ; // по сравнению с потребителем

prod.start() ;
con.start() ;
synchronized (con)
{
con.wait() ; // Ждем уведомления от производителя об окончании
// его работы
}
System.out.println ("Производитель и потребитель закончили работу") ;
}
}

Приведенная программа написана в очень хорошем, понятном стиле.
Мы прокомментируем лишь один момент. В методах insert() и
extract() класса my_buffer вызов wait() содержится внутри бес-
конечного цикла. Дело в том, что вызов notify() относится к об-
ъекту в целом. "Разбуженный" объект должен проанализировать
свое состояние и решить, что делать дальше. Так, если "заснул"
метод insert(), то после возобновления работы необходимо прове-
рить, что буфер уже не полон и добавление нового элемента стало
возможным. Если это не так, метод insert() заснет вновь.

    * 4.2. Технология Java



    4.2.1. Технологический цикл обработки Java-программ



В принципе, технологический цикл подготовки, трансляции, редак-
тирования внешних связей, тестирования, отладки и выполнения
Java-программ тот же, что и для других интерпретируемых языков
программирования, но с одним существенным отличием - при редак-
тировании внешних связей требуемые компоненты могут доставлять-
ся по сети (рис. xxx).

Рис. xxx. Технологический цикл Java-программы

Важно отметить, однако, что Java-программы могут представать
как бы в двух ипостасях - как самостоятельное приложение и как
аплет, то есть совокупность объектов, выполняющихся в среде
WWW-навигатора.

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

Приложение в качестве точки входа имеет метод

public static void main (String args[]);

этот метод должен быть определен в том public-классе, который содержится в
файле, выполняемом виртуальной Java-машиной.
В параметр args передается массив строк - параметров командной строки.

Пример: программа, печатающая свои аргументы

public class myTop {
public static void main (String args[]){
int argc = args.length;

for (int i = 0; i < argc; i++)
System.out.println (argc[i]);
}
}

Аплет выполняется в контексте навигатора и его жизненный цикл
определяется следующими методами класса Applet:

public void init () вызывается навигатором при загрузка аплета;
public void start (); вызывается навигатором при показе страницы;
public void stop (); вызывается навигатором, когда тот уходит с Web-страницы;
public void destroy (); этот метод предназначен для освобожденя
ресурсов; аналог деструктора, но не вызывается автоматически; всегда
вызывает stop(); всегда вызывается при выходе из навигатора и при
перезагрузке аплета.

Простейший аплет выглядит так:

1 import java.awt.Graphics;
2 import java.applet.Applet;

3 class SimpleApplet extends Applet {
4 public void paint (Graphics g) {
5 g.drawString ("Hello world!", 10, 10);
6 }
7 }

Метод public void paint (Graphics g) (строки 4-6) определяет,
как аплет перерисовывает себя в тот момент, когда оконный ме-
неджер посылает WWW-навигатору запрос на перерисовку.

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

Ниже приведен простой пример вкллючения аплета в WWW-страницу.




Если вы видите этот текст, то ваш навигатор не поддерживает Java




Поскольку WWW-навигаторы игнорируют неизвестные конструкции, в
навигаторе, не поддерживающем Java, будет виден текст

--------------------------------------------------------------------------
Если вы видите этот текст, то ваш навигатор не поддерживает Java
--------------------------------------------------------------------------

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

public void init () {
String fontname = getParameter ("name");
String fontSizestring = getParameter ("size");
int theSize = Int.parseInt (fontSizeString);
. . .
}

    4.2.2. Java-машина



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

Идея языковых процессоров, разумеется, не нова. Известны попыт-
ки внедрить так называемый P-код в качестве стандарта на ре-
зультат работы Паскаль-компиляторов; в свое время много писали
о языке и машине Форт; была выполнена аппаратная реализация ре-
фал-машины, и список этот можно продолжать и продолжать.

В контексте проекта Java спецификация виртуальной машины явля-
ется частью комплекса мер, направленных на стандартизацию Java-
среды и на обеспечение ее независимости от аппаратно-программ-
ной платформы. Кроме того, следует учитывать ту специфическую
среду, в которой должны готовиться и работать Java-программы.
Если Web-страница содержит Java-аплеты, эти аплеты будут пере-
даваться по сети. Значит, весьма желательно, чтобы Java-код был
как можно более компактным; в противном случае время загрузки
страницы рискует стать раздражающе большим. Соответственно, ар-
хитектура и система команд Java-машины проектировались таким
образом, чтобы всячески способствовать компактификации кода. С
другой стороны, формат команд Java-машины довольно прост (обыч-
но команды не имеют операндов и занимают один байт), поэтому
возможна ее (машины) эффективная эмуляция. По этой причине
программы, подготовленные для выполнения на Java-машине, часто
называют байт-кодами.

Мы опишем архитектуру Java-машины довольно кратко. Последующее
изложение опирается на версию спецификаций 1.0.

    4.2.2.1. Типы данных, поддерживаемые Java-машиной



Java-машина поддерживает следующие стандартные типы данных:

- byte - однобайтные целые цисла в двоичном дополнительном коде;

- short - двухбайтные целые числа;

- int - четырехбайтные целые числа;

- long - восьмибайтные целые числа;

- float - четырехбайтные вещественные числа в формате IEEE-754;

- double - восьмибайтные вещественные числа;

- char - двухбайтные беззнаковые символы в кодировке Unicode.

Поскольку Java-компилятор в состоянии проверить типы данных во
время трансляции, при выполнении нет нужды ассоциировать допол-
нительную информацию со значениями стандартных типов. Вместо
этого генерируются команды, рассчитанные на обработку данных
определенных типов. Например, для сложения целых чисел будет
сгенерирована команда iadd, а для сложения вещественных чисел
двойной точности - команда dadd.

Значения типа boolean представляются однобайтными целыми числа-
ми и обрабатываются посредством соответствующих команд.

Имеется еще два стандартных типа данных:

- object - четырехбайтная ссылка на объект (массивы трактуются
как объекты);

- returnAddress - четырехбайтный адрес возврата из метода.

Спецификации Java-машины не описывают внутренней структуры об-
ъектов. В реализации Sun Microsystems значение типа object ука-
зывает на описатель, хранящий две ссылки - на таблицу методов и
на данные объекта. Возможны и другие представления.

Java-машина является 32-разрядной. Более длинные значения
(long, double) представляются как пара четырехбайтных величин.
Не оговоривается, в каком порядке располагаются элементы пары;
более того, верификатор байт-кодов обязан выявлять и отвергать
программы, пытающиеся "вручную" составлять длинные значения.

    4.2.2.2. Регистры



В Java-машине должны поддерживаться следующие регистры:

- pc - счетчик команд; указывает на код операции для команды,
которая будет выполняться следующей.

- vars - базовый регистр для доступа к локальным переменным те-
кущего метода.

- optop - указатель на вершину стека операндов. Java-машина яв-
ляется стековой, поэтому основная часть команд берет операнды
из стека и туда же помещает результат.

- frame - указатель на структуру, содержащую окружение времени
выполнения.

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

Для обеспечения динамической загрузки, окружение времени выпол-
нения содержит ссылки на таблицу сивмолов текущего метода и те-
кущего класса. Перед началом выполнения метода выполняется ре-
дактирование его внешних связей (настройка ссылок на внешние
методы и внешние данные). Подобная поздняя настройка ссылок де-
лает сгенерированный код устойчивым по отношению к изменениям
во внешних классах.

Для обеспечения нормального возврата из методов выполняется
восстановление регистрового окружения вызывающего метода.

Для обработки исключительных ситуаций Java-машина выполняет
проход по стеку вызова методов и отыскивает самую внутреннюю
конструкцию catch, обрабатывающую случившееся событие.

В принципе окружение времени выполнения может содержать допол-
нительную информацию, необходимую, например, для отладки, но в
спецификациях Java-машины это оставлено на усмотрение авторов
реализации.

    4.2.2.3. Сбор мусора



Для создания объектов во время выполнения выделяется область
динамической памяти. Язык Java рассчитан на то, что эту область
обслуживает сборщик мусора, поскольку в языке нет средств для
освобождения памяти. Как именно работает сборщик мусора, опре-
деляется реализацией Java-машины.

    4.2.2.4. Система команд Java-машины



Команда Java-машины состоит из однобайтного кода операции, за
которым следуют операнды (если таковые имеются). Можно выделить
следующие группы команд:

- команды загрузки констант и переменных в стек операндов. Для
каждого типа данных имеются свои команды загрузки. Например,
команда с кодом операции dload и операндом, задающим смещение,
загружает в стек из локальной переменной вещественное число
двойной точности, а команда aload делает то же для ссылки на
объект.

- команды запоминания данных из стека в локальных переменных.

- команды управления массивами. Например, команда newarray с
операндом, задающим тип элементов, извлекает из стека требуемый
размер массива, создает его и помещает в стек ссылку на массив.
Отметим, что для создания массивов с элементами-объектами слу-
жит другая команда, anewarray. За счет подобной специализации
достигается эффективность интерпретации Java-программ.

- команды работы со стеком. К этой группе относятся команды,
которые удаляют, дублируют, меняют местами верхние элементы
стека операндов, а также выполняют другие, более сложные мани-
пуляции со стеком.

- арифметические команды. Операнды извлекаются из стека; туда
же помещается результат.

- логические команды (сдвиг, и, или, исключающее или).

- команды преобразования к другому типу.

- команды передачи управления. Например, в команде jsr (переход
на подпрограмму) операндом служит относительный адрес перехода;
адрес команды, следующей за jsr, помещается на вершину стека
операндов. Имеются команды для реализации переключателей.

- команды возврата из функции. Для возврата результатов разных
типов используются команды с разными кодами операции. Кроме то-
го, имеется команда breakpoint, которая останавливает нормаль-
ный ход выполнения и передает управление обработчику этого со-
бытия.

- команды манипулирования с полями объектов (установить/прочи-
тать обычное/статическое поле).

- команды вызова методов. Их четыре. Команда invokevirtual вы-
зывает (виртуальный) метод на основе анализа информации времени
выполнения. Команда invokenonvirtual осуществляет вызов на ос-
нове информации времени компиляции - например, вызов метода ро-
дительского класса. Команда invokestatic вызывает статический
метод класса. Наконец, команда invokeinterface вызывает метод,
представленный интерфейсом. Выполнение всех перечисленных ко-
манд связано не только с передачей управления, но и с анализом
разного рода таблиц.

- команда возбуждения исключительной ситуации - athrow.

- прочие объектные операции (создать объект, проверить тип об-
ъекта).

- команды сихронизации (войти в критический интервал, выйти из
него).

Мы видим, что не существует семантического разрыва между языком
Java и Java-машиной. Как уже отмечалось, это важно для компакт-
ности скомпилированных Java-программ и для обеспечения высокой
скорости трансляции.

    4.2.3. Java и безопасность



Концепция загрузки объектов по сети прозрачным для пользователя
образом столь же привлекательна, сколь и опасна. Если не предп-
ринимать никаких мер и не накладывать никаких ограничений на
возможности Java-аплетов, вход на любую Web-страницу может при-
вести к непредсказуемым последствиям. К счастью, разработчики
языка Java с самого начала уделяли самое пристальное внимание
вопросам информациионной безопасности.

Из языка удалены многие потенциально опасные возможности, такие
как оператор goto или тип данных "указатель". Интерпретируемый
характер выполнения позволяет не допустить выхода за границы
массива, обращения по пустой ссылке и т.п. В свое время за по-
добную осторожность выступал автор языка Паскаль Никлаус Вирт,
отмечавший, что при традиционном подходе программист напоминает
моряка, который носит спасательный круг только на суше.

Мы, однако, не будем подробно останавливаться на "обычной" бе-
зопасности и уделим основное внимание выполнению потенциально
враждебных аплетов. Смежный вопрос - проверка подлинности апле-
тов, снабженных электронной подписью, видимо, будет решен в
последующих версиях Java-систем.

Прежде всего, аплетам, загруженным по сети, запрещены чтения и
запись файлов из локальной файловой системы, а также выполнение
сетевых соединений со всеми хостами, кроме того, с которого был
получен аплет. Кроме того, таким аплетам не разрешается запус-
кать программы на клиентской системе (говоря языком ОС UNIX,
для них недоступны системные вызовы fork и exec), им запрещено
загружать новые библиотеки и вызывать программы, внешние по от-
ношению к Java-машине.

На самом деле, перечисленные ограничения не являются частью
спецификации Java-системы и могут выполняться с большей или
меньшей аккуратностью. Так, в Netscape Navigator 2.0 чтение и
запись локальных файлов действительно полностью запрещены. В то
же время, среда разработки JDK 1.0 компании Sun Microsystems
допускает задание списка каталогов, с которыми аплеты могут ра-
ботать.

Более точно, вне разрешенного списка каталогов аплет не может:

- проверять существование файлов;

- читать/писать/переименовывать файлы;

- создавать каталоги;

- проверять атрибуты файла - тип, время последней модификации,
размер.

Чтобы в JDK сделать каталог доступным для аплета, следует по-
местить в файл ~/.hotjava/properties строки вида

acl.read=/home/welcome
acl.write=/tmp

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

Следует отметить, что верный выбор баланса между возможностями
загружаемых аплетов и безопасностью клиентской системы является
очень тонким вопросом. Ряд компаний, например, Argus System
Group, предлагают реализовать на клиентской системе усиленные
меры безопасности, чтобы успешно отражать угрозы со стороны
враждебных аплетов без ограничения свободы действий для "благо-
надежных" программ. К сожалению, предлагаемые решения зависят
от операционной платформы, что противоречит требованию абсолют-
ной переносимости Java-программ. Можно предположить, что инфор-
мационная безопасность еще долгое время будет оставаться одним
из самых сложным и спорных вопросов, касающихся проекта Java.

    4.2.4. Java WorkShop