К. Арнольд, Д. Гослинг - Язык программирования Java (1160779), страница 24
Текст из файла (страница 24)
Косая черта (/) в имени пакета переходит в символ подчеркивания (_).А.2.2 МетодыКаждый родной метод представляется в виде функции. Например, метод lock будет называться local_LockableFile_lock и иметь соответствующиепараметры. Первый параметр функции — это дескриптор объекта, для которого вызывается метод (ссылка this). Для статических методов такойдескриптор всегда равен null.
Ниже дескрипторы рассматриваются более подробно.Для вызова методов нужен дополнительный слой в виде файлов-заглушек (stub files). Последние также генерируются утилитой javah, но спараметром -stubs:javah -stubs local.LockableFileТакая команда генерирует исходный файл на языке C, который должен быть скомпилирован и загружен в динамическую библиотеку вместе вреализациями родных методов.
Имя этого файла совпадает с именем заголовочного файла, однако расширение h изменяется на c — в нашемслучае это будет файл local_LockableFile.c.А.2.3 ТипыВ приведенной ниже таблице показано соответствие между примитивными типами Java и типами языка C, когда они используются в качествепараметров методов или полей (согласование типов для массивов рассматривается ниже).ТипJavaТип Cbooleanlongbytelongshortlongintlonglongint64_tfloatfloatdoubledoublecharlongКлассы Java представляются в языке C структурами (struct). Все нестатические поля класса являются членами структуры, а их имена совпадают сименами в Java (за исключением символов Unicode, отображаемых в эквиваленты _0dddd).
Это означает, что класс, содержащий родные методы,не может иметь два нестатических поля с одинаковыми именами; в противном случае структура на языке C содержала бы члены с одинаковымиименами, что запрещено.Проблема возникает с теми классами, которые скрывают поля суперклассов — кстати, еще одна причина, по которой не следует скрывать именаполей. Тем не менее, проблема относится даже к тем классам, которые содержат закрытые поля с тем же именем, поскольку такие поляприсутствуют в структуре наравне со всеми остальными. Вы не сможете выяснить, актуальна эта проблема или нет, пока она не возникнет принаписании родного метода. Дело обстоит еще хуже, если скрываемые поля находятся в суперклассе, который невозможно изменить. В такихслучаях дело заходит в тупик — вы не сможете написать родной метод без модификации имен полей класса.Каждая статическая константа с атрибутом final представлена константой #define с префиксами (именем пакета и класса).
Статические поля, неявляющиеся final, не переводятся ни во что. Например, тип и константы для класса LockableFile определяются в заголовочном файле следующимобразом:typedef struct Classlocal_LockableFile {struct Hjava_lang_String *path;/* недоступное статическое поле: separator *//* недоступное статическое поле: separatorChar *//* недоступное статическое поле: pathSeparator *//* недоступное статическое поле: pathSeparatorChar */#define local_LockableFile_READ 0L#define local_LockableFile_WRITE 1Llong fd;} Classlocal_LockableFile;Ссылки на объекты представляются типом “дескриптор”, в составном имени которого Class заменяется на H.
Ссылка на класс LockableFile будетназываться Hlocal_LockableFile. Макрос unhand получает дескриптор и возвращает указатель на структуру, которая представляется этимдескриптором.Ниже приведены сигнатуры функций языка C, в которых ниже мы определим родные методы класса LockableFile:extern voidstructextern voidstructlocal_Lockable_File_lock(Hlocal_LockableFile *, long);local_Lockable_File_unlock(Hlocal_LockableFile *);В Java об ошибках сигнализируют исключения. В языке C исключений нет. Чтобы возбудить исключение из C, следует вызвать функцию SignalErrorи затем выйти.
Runtime-система Java обнаруживает и возбуждает исключение, о котором сигнализировала функция SignalError. Ниже вы увидитенесколько примеров того, как это делается.А.2.5 Средства безопасностиСредства безопасности языка Java не имеют аналогов в C. Вы полностью отвечаете за работу программы на C (как это обычно бывает) без какойлибо автоматической помощи со стороны Java.А.2.6 Работа с памятьюРодные методы могут создавать новые объекты Java с помощью функций, описанных ниже.А.3 ПримерДавайте рассмотрим возможную реализацию родных методов для класса LockableFile.
Прежде всего, мы должны сгенерировать заголовочныйфайл и файл-заглушку и скомпилировать последний. Затем нужно написать сами реализации методов. Например, метод lock может бытьреализован следующим образом:#include#include#include#include"local_LockableFile.h"<<javaString.h>><<fcntl.h>><<errno.h>>voidlocal_LockableFile_lock(struct Hlocal_LockableFile *this_h,long mode){Classlocal_LockableFile *this = unhand(this_h);struct flock lock;if (this->>fd == -1 && !open_fd(this))return;/* произошла ошибка */if (!setup_lock(&lock, mode))return;if(fcntl(this->>fd, F_SETLKW, &lock) == -1)SignalError(EE(),"java/io/IOException", strerror(errno));}Сначала мы включаем нужные заголовочные файлы — сгенерированный файл для LockableFile, вспомогательный заголовочный файл для работысо строками <<javastring.h>>, системный заголовочный файл <<fcntl.h>>, определяющий вызовы для осуществления блокировки в POSIX, исистемный заголовочный файл <<errno.h>> для обработки ошибок, полученных в ходе вызовов системных функций.Первая строка в реализации метода lock переводит дескриптор this_h в указатель на структуру Classlocal_LockableFile, для чего используетсямакрос unhand, который возвращает объект, соответствующий данному дескриптору.
Ссылкам null в Java ставится в соответствие указатели надескрипторы, которые также равны NULL. Чтобы убедиться в том, что передача ссылки null не приведет к ошибкам, необходимо проверитьдескриптор перед тем, как вызывать для него макрос unhand — это демонстрируется в последующих примерах.Во второй строке local_LockableFile_lock объявляется структура flock. Структура flock используется для работы с блокировкой в POSIX. Реализацииopen_fd и setup_lock приводятся в разделе “Внутреннее строение LockableFile”.Далее мы проверяем, имеется ли файловый дескриптор. Если он отсутствует и функция open_fd не может открыть файл, видимо, быловозбуждено исключение, сигнализирующее об ошибке, поэтому мы просто выходим из функции. Если же дескриптор имеется, то структуру flockнеобходимо подготовить вызовом внутренней функции setup_lock.
Вызов функции также может закончиться неудачей (например, если modeимеет недопустимое значение) и возбуждением исключения — и в этом случае мы выходим из функции. Функции open_fd и setup_lock являютсячастью кода, специфичного для POSIX.Затем мы пытаемся заблокировать файл с помощью режима F_SETLKW функции POSIX с именем fcntl, который при необходимости ожидаетвозможности блокировки.
Если fcntl возвращает -1, то попытка блокировки оказалась неудачной, и мы возбуждаем исключение, вызывая runtimeфункцию Java с именем SignalError:void SignalError(ExecEnv *exenu, char *type, char *constructor)Сигнализирует о том, что после выхода из родного метода должно быть возбуждено исключение.
Структура exenu обычновозвращается функцией EE и представляет текущее состояние среды. Параметр type является полным именем классавозбуждаемого объекта-исключения, в котором каждая точка (.) заменяется чертой (/). Последний параметр содержит строковоеописание исключения или NULL при его отсутствии.В нашем случае используется значение функции POSIX с именем strerror, которая возвращает строку с описанием номера ошибки. Практическиэто самое лучшее, что мы можем сделать для описания ошибок в родных методах.Функция SignalError лишь подготавливает исключение; она не возбуждает его.
В языке C исключения не предусмотрены, поэтому возбудить их изпрограммы невозможно. После выхода из функции, содержащей реализацию родного метода, runtime-система проверяет флаги и по нимопределяет, был ли получен сигнал о возбуждении исключения. Если такой сигнал получен, то runtime-система возбуждает исключение.Подобная схема позволяет в программе на C выполнить необходимые завершающие действия после “возбуждения” исключения.
В функцииlocal_LockableFile_lock такие действия не требуются, поэтому после “возбуждения” исключения мы просто выходим из нее.Реализация unlock выглядит проще. Мы подготавливаем структуру flock и вызываем fcntl для режима F_UNLCK (снятие блокировки). И снова принеудаче возбуждается исключение, содержащее строку с описанием ошибки:voidlocal_LockableFile_unlock(struct Hlocal_LockableFile *this_h){Classlocal_LockableFile *this = unhand(this_h);struct flock lock;lock.l_whence = lock.l_start = lock.l_len = 0;lock.l_type = F_UNLCK;if (fcntl(this->>fd, F_SETLKW, &lock) == -1)SignalError(EE(),"java/io/IOException", strerror(errno));}В класс LockableFile можно внести ряд усовершенствований.
Например, создать функцию для проверки того, заблокирован ли файл и если да, токаким программным потоком. Можно сконструировать специальный вариант lock без ожидания, который лишь осуществляет блокировку, еслиона возможна, а в противном случае завершает свою работу. Или создать отдельные исключения для каждой ошибки, чтобы ошибка “файл несуществует” отличалась от “доступ запрещен”.Упражнение А.1Если у вас имеется доступ к системе, отличной от POSIX и поддерживающей блокировку файлов, реализуйте класс LockableFile с использованиемее механизмов.Упражнение А.2Если вы работаете только с POSIX-совместимыми системами или выполнили упражнение А.1, включите в класс описанные выше возможности.А.3.1 Внутреннее строение LockableFileДля полноты картины приведем текст внутренних функций, используемых классом LockableFile.
Статические функции open_fd и setup_lockиспользуются при реализации родных методов lock и unlock:static intopen_fd(Classlocal_LockableFile *this){char *path = allocCString(this->>path);if ((this->>fd = open(path, O_RDWR)) == -1)SignalError(EE(),"java/io/IOException", strerror(errno));free(path);/* больше не требуется */return (this->>fd != -1);}static intsetup_lock(struct flock *lock,long mode){lock->>l_whence = lock->>l_start = lock->>l_len = 0;switch (mode) {case local_LockableFile_READ:lock->>l_type = F_RDLCK;break;case local_LockableFile_WRITE:lock->>l_type = F_WRLCK;break;default:SignalError(EE(),"java/lang/IllegalArgumentException", NULL);return 0;}return 1;}А.4 СтрокиРодные методы часто должны использовать строковые объекты String.