Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык программирования JAVA.pdf
Скачиваний:
374
Добавлен:
02.05.2014
Размер:
2.57 Mб
Скачать

converted to PDF by BoJIoc

static int

cmp(const void *str1, const void *str2)

{

return strcoll(*(char **)str1, *(char **)str2);

}

Функция сравнения cmp преобразует свои параметры-указатели к типу char ** (qsort требует, чтобы параметры имели тип void *; предполагается, что программист сам произведет все необходимые приведения типов). Затем вызывается стандартная библиотечная функция C с именем strcoll, которая сравнивает две строки с учетом локального контекста. Эта функция возвращает отрицательное, равное нулю или положительное число, если первая строка соответственно меньше, равна или больше второй в локальном языковом контексте упорядочения строк. То же самое функция qsort ожидает от своей функции сравнения, так что значение, возвращаемое strcoll, может возвращаться и самой функцией cmp.

После выполнения qsort, строки оказываются отсортированными в соответствии с функцией strcoll. Все, что остается построить новый массив объектов String и заполнить его результатами. Далее, строится массив с помощью рассмотренной выше функции alloc_class_array и затем в цикле вызывается makeJavaString для задания каждого объекта String. Наконец, мы освобождаем массив strings, созданный функцией malloc, и возвращаем результат.

Упражнение А.6

Модифицируйте класс LocalString, чтобы он мог работать с объектами, каждый из которых обладает собственным локальным контекстом, вместо того, чтобы полагаться на один общий контекст. Методы перестанут быть статическими, и понадобится новое строковое поле, определяющее контекст. Помните, что функция POSIX с именем setlocale устанавливает локальный контекст лишь до следующего вызова setlocale.

А.6 Создание объектов

Вы можете создавать объекты Java внутри реализаций родных методов с помощью функции execute_java_constructor:

HObject *execute_java_constructor(ExecEnv *ee, char *className, ClassClass *classObj, char *signature, ...)

Создает новый объект указанного типа, задаваемого одним из двух параметров className или ClassObj (не используемый параметр должен быть равен NULL). Для создания объекта вызывается конструктор, описываемый строкой signature. За параметром signature следуют параметры конструктора.

Например, создание нового объекта типа Simple с помощью безаргументного конструктора класса происходит следующим образом:

execute_java_contructor(NULL, "Simple", NULL, "()")

В данном случае сигнатура конструктора выглядит тривиально. Чтобы воспользоваться конструктором, который получает один или несколько параметров, необходимо включить их типы в сигнатуру, и поместить их значения в нужном порядке после строки сигнатуры. Типы в строке сигнатуры аналогичны тем, которые возвращаются Class.getName, и используют односимвольные сокращения для примитивных типов. Применяются следующие сокращения:

converted to PDF by BoJIoc

Z boolean

Iint

Cchar

Jlong

Bbyte

Ffloat

Sshort

Ddouble

Чтобы избежать конфликтов между именами классов/интерфейсов и этими буквами, типы объектов получают имена вида " Ltype" , где type полное имя класса или интерфейса, в котором разделители-точки заменяются косой чертой, а в конце ставится точка с запятой. Например, параметр, представляющий собой объект String, будет выглядеть как "

Ljava/lang/String;" . Для массивов указывается тип массива с префиксом [; так, массив значений типа long будет иметь тип " [J" . В многомерных типах используются несколько квадратных скобок. Массив String[][] будет выглядеть как " [[Ljava/labg/String;" .

Если вам все же непонятно, как указать конкретный тип в такой строке, вы можете написать класс Java, создающий объект нужного типа, и вызвать для него метод getClass().getName(). Затем, если полученное имя является именем типа, замените все точки на /, поставьте L спереди и ; сзади, если эти символы отсутствуют.

Аргументы, которые представляют собой объекты, передаются в виде указателей на дескрипторы.

Ниже показано, как происходит создание двух объектов Attr; первый из них использует конструктор с одним аргументом, которому передается только имя, а второй конструктор с двумя аргументами, которому сообщается исходное значение.

oneArgAttr = (struct HAttr *) execute_java_constructor(EE(), "Attr", NULL,

"(Ljava/lang/String;)", attrStr);

twoArgAttr = (struct HAttr *) execute_java_constructor(EE(), "Attr", NULL,

"(Ljava/lang/String;Ljava/lang/Object;)", attrStr, attrStr);

Точка с запятой выполняет функцию терминатора (завершающего символа) типа, а не разделителя параметров. Конструктор, получающий два параметра типа long и два параметра типа double, описывается строкой " (JJDD)" .

А.7 Вызов методов Java

Вызов методов Java из программ на C напоминает вызов конструкторов Java. Для этого используются следующие основные функции:

long *execute_java_static_method(ExecEnv *ee, ClassClass *cb, char *method_name, char *signature, ...)

Выполняет статический метод класса, описываемого параметром cb.

long *execute_java_dynamic_method(ExecEnv *ee, HObject *obj, char *method_name, char *signature, ...)

Выполняет нестатический (динамический) метод для заданного объекта.

converted to PDF by BoJIoc

В обоих функциях, параметр method_name является именем вызываемого метода, а signature описывает передаваемые аргументы. В отличие от конструкторов, вы также должны объявить возвращаемый методом тип после закрывающей скобки в сигнатуре. Возвращаемый тип указывается с использованием тех же сокращений, что и для типа параметров, с дополнительной буквой V для void. Примеры приведены ниже. Вы должны сами привести тип long к возвращаемому типу метода или проигнорировать его для методов типа void.

Для получения структуры класса, используемой при вызове статических методов, применяется одна из двух функций FindClass:

ClassClass *FindClass(ExecEnv *ee, char *class_name, bool_t resolve)

Возвращает указатель на структуру ClassClass для заданного класса. Как и прежде, параметр типа ExecEnv следует получить от функции EE. Логическая величина resolve аналогична одноименному параметру, используемому методом ClassLoader.loadClass в разделе 13.2.

ClassClass *FindClassFromClass(ExecEnv *ee, char *class_name, bool_t resolve, ClassClass *from)

Возвращает указатель на объект-класс для заданного класса с использованием объекта ClassLoader класса from.

Приведем пример, в котором метод System.out.println() вызывается для вывода сведений о работе родного метода. Статический родной метод grindAway класса Crunch, приведенный ниже, осуществляет некоторые трудоемкие вычисления и возвращает результат типа double:

double

Crunch_grindAway(struct HCrunch *this_h)

{

ClassClass *myClass; HObject *out;

long i; double result;

ExecEnv *ee = EE(); /* используется в нескольких местах */

myClass = FindClass(ee, "Crunch", TRUE);

out = (HObject *)execute_java_static_method(ee, myClass, "outStream", "()Ljava/io/PrintStream;");

if (exceptionOccurred(ee)) return 0.0;

for (i = 0; i << NUM_PASSES; i++) { execute_java_dynamic_method(ee, out,

"println", "(I)V", i); if (exceptionOccurred(ee))

return 0.0; // необходимо что-то вернуть /* .. вычисления ... */

}

return result;

}

Во фрагменте программы, предшествующем циклу, мы получаем дескриптор объекта java.io.PrintStream для System.out. Мы не можем непосредственно использовать значение статического поля System.out, потому что статические поля не имеют своего представления в родных методах; из-за этого приходится прибегать к обходным маневрам. В данном случае мы создаем статический метод, возвращающий нужное значение, и вызываем его из родного метода:

converted to PDF by BoJIoc

public static java.io.PrintStream outStream() { return System.out;

}

Родной код должен получить указатель на структуру ClassClass для класса Crunch, поэтому мы вызываем FindClass. После этого можно вызвать функцию execute_java_static_method с указанием имени вызываемого метода и его сигнатуры, включающей тип возвращаемого значения. Мы преобразуем возвращаемое значение long к типу, необходимому для конкретного метода. В данном случае нам требуется общий дескриптор HObject, а не дескриптор для конкретного типа java.io.PrintStream.

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

После того, как получен доступ к выходному потоку, можно начинать циклические вычисления. При проходе каждого цикла его номер выводится методом System.out.println. Для вызова этого метода мы используем функция execute_java_dynamic_method, передавая ей в качестве параметров объект, для которого вызывается метод, имя метода, его сигнатуру и аргументы. Нам нужна версия println, получающая аргумент типа int; этот метод имеет тип void, поэтому мы используем сигнатуру " (I)V" и передаем целое число, которое нужно вывести (i) после аргумента-сигнатуры. И снова при возврате из метода необходимо проверить, не возбуждено ли исключение.

Для многих типов, возвращаемых методами Java, приведение long к нужному типу происходит элементарно. Однако типы double и long в Java являются 64-разрядными, тогда как в большинстве существующих компиляторов C тип long 32-разрядный, и поэтому возвращаемое значение будет иметь только половинную длину. Хотя существуют различные способы получения всех 64 бит возвращаемого значения, о них не говорится в этой книге из-за их машинной зависимости.

А.8 Последнее предупреждение

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

converted to PDF by BoJIoc

Приложение Б

Runtime-исключения в Java

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

Фрэнк Заппа

Runtime-система Java возбуждает исключения двух основных видов: runtime-исключения, расширяющие класс RuntimeException, и ошибки, которые расширяют класс Error. Исключения обоих видов являются непроверяемыми (см. раздел 7.3). Верхняя часть иерархии исключений выглядит следующим образом:

Исключения Error сигнализируют об очень серьезных проблемах, после которых программа обычно завершается, и которые никогда (или почти никогда) не должны перехватываться. Исключения Error не являются расширениями RuntimeException, так что программист, пытающийся написать универсальное условие catch для перехвата всех исключений Exception и RuntimeException (обычно делать этого не следует) не сможет перехватить исключения Error. Разумеется, после возникновения любого исключения будут выполнены условия finally операторов try, так как все исключения, в том числе и Error, просматривают стек вызовов. Следовательно, вы всегда сможете выполнить необходимые завершающие действия.

Программист может самостоятельно расширить классы RuntimeException и Error, чтобы создать свои собственные варианты непроверяемых исключений то есть таких исключений, которые можно возбуждать без указания их в условии throws. Мы сообщаем об этом по единственной причине чтобы вы знали, что этого делать не следует. Условие throws предусмотрено именно для того, чтобы при вызове метода были видны все возможные аспекты его поведения. Порождая свое исключение от RuntimeException или Error, вы сообщаете о нем ложные сведения (будто оно запускается runtime-системой). Кроме того, другие разработчики, читающие вашу программу, полагают, что условие throws дает им информацию о возможном поведении вашего метода; вы нарушаете это предположение.

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

Все классы Error и RuntimeException содержат по меньшей мере два конструктора: один вызывается без аргументов, а второй получает объект String с описанием. Исключения, которые прямо или косвенно расширяют RuntimeException или Error, не объявляются в условии throws, поскольку они могут произойти в любой момент, что делает их объявление излишним.

converted to PDF by BoJIoc

Исключение CloneNotSupportedException непосредственно порождается от класса Exception, поскольку каждая программа, которая вызывает метод clone, возбуждающий данное исключение, должна явным образом его обработать. Оно рассмотрено в разделе Дублирование объектов.

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

Б.1 Классы RuntimeException

ArithmeticException extends RuntimeException

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

ArrayStoreException extends RuntimeException

Попытка сохранения в массиве объекта неверного типа.

ClassCastException extends RuntimeException

Попытка недопустимого приведения типа.

IllegalArgumentException extends RuntimeException

Метод получил неверный аргумент (например, метод String.equals вызван для объекта, который не относится к типу String).

IllegalMonitorStateException extends RuntimeException

Механизм wait/notify использован за пределами синхронного кода.

IllegalThreadStateException extends IllegalArgumentException

Состояние потока не допускает выполнения требуемой операции.

IndexOutOfBoundsException extends RuntimeException

Runtime-система генерирует это исключение при выходе индекса массива или объекта String за пределы диапазона допустимых значений.

NegativeArraySizeException extends RuntimeException

Попытка создания массива отрицательного размера.

NullPointerException extends RuntimeException

Для доступа к полю или методу использована null-ссылка. Это же исключение сигнализирует о передаче методу параметра null, если для данного параметра это значение является недопустимым. Используется аналогично IllegalArgumentException.

NumberFormatException extends IllegalArgumentException

Неверное содержимое строки, в которой должно было находиться число. Исключение возбуждается такими методами, как Integer.parseInt.

SecurityException extends RuntimeException

converted to PDF by BoJIoc

Попытка выполнения действия, запрещенного системой безопасности обычно объектом SecurityManager для текущего runtime-контекста.

Б.2 Классы Error

AbstractMethodError extends IncompatibleClassChangeError

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

ClassFormatError extends LinkageError

Загружаемый класс или интерфейс имеет неверный формат (обычно это связано с использованием преобразованных” (mangled) имен).

IllegalAccessError extends IncompatibleClassChangeError

Исключение неразрешенного доступа.

IncompatibleClassChangeError extends linkageError

При загрузке класса или интерфейса было обнаружено изменение, несовместимое с информацией об этом классе или интерфейсе. Например,

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

InstantiationError extends IncompatibleClassChangeError

Интерпретатор попытался создать объект абстрактного класса или интерфейса.

InternalError extends VirtualMachineError

Произошел внутренний сбой runtime-системы. В нормальных условиях такая ошибка не должна возникнуть.

LinkageError extends Error

Исключения класса LinkageError и его подклассов означают, что класс тем

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

NoClassDefFoundError extends LinkageError

Нужный класс не найден.

NoSuchFieldError extends IncompatibleClassChangeError

Поле отсутствует в классе или интерфейсе.

NoSuchMethodError extends IncompatibleClassChangeError

Метод отсутствует в классе или интерфейсе.

OutOfMemoryError extends VirtualMachineError

Нехватка памяти.

StackOverflowError extends VirtualMachineError

converted to PDF by BoJIoc

Переполнение стека. Может свидетельствовать о бесконечной рекурсии.

ThreadDeath extends Error

Исключение ThreadDeath возбуждается потоком-“жертвой при его уничтожении методом thread.stop. Если исключение ThreadDeath перехватывается, его необходимо возбудить повторно, чтобы поток был уничтожен. Если ThreadDeath не перехватывается, то обработчик ошибок верхнего уровня не выводит никаких сообщений.

UnknownError extends VirtualMachineError

Произошла неизвестная, но серьезная ошибка.

UnsatisfiedLinkError extends LinkageError

Ошибка связывания внутри родного метода. Обычно это означает, что библиотека, реализующая родной метод, содержит неопределенные символы, которые не были найдены ни в одной библиотеке.

VerifyError extends LinkageError

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

VirtualMachineError extends Error

Нарушена работа виртуальной машины, или наблюдается нехватка ресурсов.