Кен Арнольд Джеймс Гослинг
Вид материала | Документы |
- Джеймс трефил, 41001.36kb.
- Джеймс А. Дискретная математика и комбинаторика [Текст] / Джеймс А. Андерсон, 42.79kb.
- Человеческая способность эти ценности производить и использовать; является важнейшей, 110.76kb.
- Джеймс блиш города в полете 1-4 триумф времени вернись домой, землянин жизнь ради звезд, 10495.38kb.
- Джеймс Н. Фрей. Как написать гениальный роман, 2872.12kb.
- Дп «авто интернешнл» Київ, вул. Урицького, 1а Тел. (044) 20-60-333 Факс. (044) 20-60-343, 82.44kb.
- Тема Кол-во страниц, 26.85kb.
- Тема Кол-во страниц, 56.3kb.
- Тема Кол-во страниц, 20.7kb.
- Арнольд И. В. Стилистика современного английского языка, 20.42kb.
А.1 Обзор
При стыковке программ на Java с языком C возникают следующие основные проблемы:
- Как происходит согласование имен? Полное имя метода в Java имеет вид пакет.класс.метод, однако в C нет ни пакетов, ни классов. Кроме того, согласование усложняется тем, что в идентификаторах Java используется кодировка Unicode, а в идентификаторах С — кодировка ASCII, поэтому необходим дополнительный перевод символов Unicode в символы, разрешенные в C.
- Как разрешается проблема различных парадигм вызова? Например, каждый нестатический метод в Java располагает ссылкой this, которая, в сущности, является неявным параметром метода. В C нет ни методов, ни неявных параметров.
- Как происходит согласование типов? Родная реализация метода должна обращаться к полям объекта this, и, возможно, методам или полям объектов других типов. Как представить классы Java в языке С?
- Как происходит согласование ошибок? Java сообщает о них при помощи исключений, но в C исключения отсутствуют.
- Как происходит согласование средств безопасности? Java следит за выходом за границы массивов и преобразованиями типов, а также осуществляет сборку мусора для борьбы с утечкой памяти и “зависшими” указателями. C и C++ не обладают этими возможностями, так как же производить такие проверки? А что должно происходить в языках типа Pascal, где такая проверка присутствует?
- Как происходит согласование работы с памятью? Как программа на языке C создает объекты Java?
Решая эти и другие проблемы, приходится идти на компромиссы. Например, C и C++ не обладают средствами безопасности Java в работе с массивами, поэтому при согласовании предполагается, что родные методы C и C++ небезопасны в этом отношении. Хотя такой выход и не идеален, он все же выглядит вполне естественно по отношению к C и C++, для которых скорость считается более важной, чем страховка. Например, чтобы реализовать подобную проверку в C или C++, пришлось бы обращаться ко всем элементам массива посредством проверочных функций Java. Такой вариант выглядит неестественно и медленно работает, а поскольку основным доводом в пользу родных методов является скорость, подобный компромисс окажется неверным. К тому же он не будет нормально работать с существующим кодом, в котором используется стандартная для C и C++ парадигма работы с массивами.
А.2 Согласование с C и C++
Согласование Java c языком С происходит довольно прямолинейно. Для стыковки родных методов с вызовами C используется сгенерированный заголовочный файл, содержащий все необходимые объявления типов и сигнатуры функций, а также программные “заглушки” на C, которые помогают runtime-системе Java вызывать эти методы.
Мы рассмотрим только основные моменты согласования. Программа, которая используется здесь в качестве примера, представляет собой текст класса LockableClass из пакета local: /К сожалению, мы не смогли воспользоваться соглашением об именах пакетов, поскольку это привело бы к удлинению идентификаторов и затруднило бы работу с книгой./
package local;
import java.io.*;
class LockableFile extends File {
LockableFile(String path) {
super(path);
}
// допустимые параметры lock()
public final static int READ = 0,
WRITE = 1;
public native void lock(int type) throws IOException;
public native void unlock() throws IOException;
private int fd = -1;
static {
System.load("LockableFile");
}
}
После того, как эта программа будет обработана компилятором Java, следует сгенерировать заголовочный файл с помощью утилиты javah, передав ей имя класса, для которого создается данный файл. Утилита сгенерирует файл, содержащий все объявления и определения на языке C, необходимые для стыковки. В нашем примере команда будет выглядеть следующим образом:
javah local.LockableFile
Имя сгенерированного файла определяется по имени класса, в соответствии с описанной ниже схемой согласования имен. В нашем случае заголовочный файл будет называться local_LockableFile.h. Содержимое этого заголовочного файла и реализация его родных методов будут приведены ниже в данной главе.
Процесс согласования с C++ выглядит так же. Фактически, согласование с C++ сводится к включению сгенерированного заголовочного файла в объявление extern " C":
"C":
extern "C" {
# include local_LockableFile.h
}
Символы, которые используются во время выполнения программы для вызова оболочек родных методов, создаются в пространстве имен С, а не так называемых “преобразованных” (mangled) имен C++, поэтому родные методы должны быть написаны на C. /На самом деле это не совсем так —приложив некоторые усилия, опытный программист сможет обойти это ограничение, но для простоты описания и реализации будет лучше согласиться с ним./ Основное следствие заключается в том, что вы не сможете использовать перегрузку методов C++ для реализации родных методов. В сущности, правильнее было бы сказать, что для родных методов прямая стыковка с C++ вообще не используется, однако возможна косвенная стыковка за счет вызовов функций C в C++. Разумеется, согласование программ на Java с C++ может быть улучшено, и несомненно это будет сделано в будущих версиях.
Реализуя родные методы на C или C++, вы должны связать откомпилированный код с приложением на Java; для этого необходимо создать библиотеку динамического связывания и соединить ее со своей программой. Чаще всего программист выделяет статический блок, наподобие приведенного выше в классе LockableFile, а затем вызывает один из двух статических методов класса System, предназначенных для загрузки библиотек:
public synchronized void load(String pathname)
Загружает динамическую библиотеку, расположенную по заданному полному имени pathname. В некоторых случаях полное имя модифицируется в соответствии с требованиями локальной системы. Если файл не обнаружен или не найдены символы, необходимые для работы библиотеки, возбуждается исключение UnsatisfiedLinkError.
public synchronized void loadLibrary(String libname)
Загружает динамическую библиотеку с указанным именем libname. Вызов LoadLibrary должен осуществляться в статическом инициализаторе первого загружаемого класса (то есть класса, содержащего вызываемый метод main). Попытки многократной загрузки одной и той же библиотеки игнорируются. Если библиотека не найдена, возбуждается исключение UnsatisfiedLinkError.
Метод load требует указания полного имени файла, однако во время разработки лучше пользоваться именно этой версией, поскольку она нормально сообщает о неопределенных символах. Метод loadLibrary переводит любую ошибку, включая неопределенные символы, в ошибку “библиотека не найдена”. Если в библиотеке присутствуют неопределенные символы, то подобный перевод скроет от вас важную информацию об истинной причине происходящего. Если вы будете пользоваться методом load до того момента, когда родные методы заработают, после чего замените его методом loadLibrary, то сможете добиться более подробной информации в ходе разработки и переносимости кода после ее завершения.
Методы загрузки библиотек класса System представляют собой сокращения для вызова тех же самых методов класса Runtime, представляющего текущий runtime-контекст.
А.2.1 Имена
Для перевода имен методов и полей на язык C используются полные имена, включающие названия пакетов, в которых все точки (.) заменяются подчеркиваниями (_). В тех языках, где не поддерживается возможность перегрузки методов (к ним относится C) вы не сможете реализовать в классе несколько методов с одинаковыми именами, поскольку им будет соответствовать одно и то же имя функции.
Аналогично переводятся и имена типов — за тем исключением, что перед именем типа ставится префикс Class. В программе на C тип для LockableFile будет называться Classlocal_LockableFile. Для каждого класса также необходим дескриптор (handle), поскольку он используется для внутреннего представления ссылок. Имя дескриптора совпадает с именем класса, но вместо префикса Class используется префикс H. Таким образом, тип дескриптора для LockableFile будет называться Hlocal_LockableFile.
Символы имен, относящиеся к набору ASCII (символы, меньшие или равные \u007f) переходят в C и в имена пакетов без изменений. Все остальные символы переводятся в вид _0dddd, где d — цифры, используемые в символьном представлении Java. Например, символ г (код \u00e3) будет представлен идентификатором _000e3. Косая черта (/) в имени пакета переходит в символ подчеркивания (_).
А.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 | Тип C |
boolean | long |
byte | long |
short | long |
int | long |
long | int64_t |
float | float |
double | double |
char | long |
Классы 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 1L
long fd;
} Classlocal_LockableFile;
Ссылки на объекты представляются типом “дескриптор”, в составном имени которого Class заменяется на H. Ссылка на класс LockableFile будет называться Hlocal_LockableFile. Макрос unhand получает дескриптор и возвращает указатель на структуру, которая представляется этим дескриптором.
Ниже приведены сигнатуры функций языка C, в которых ниже мы определим родные методы класса LockableFile:
extern void local_Lockable_File_lock(
struct Hlocal_LockableFile *, long);
extern void local_Lockable_File_unlock(
struct Hlocal_LockableFile *);
В Java об ошибках сигнализируют исключения. В языке C исключений нет. Чтобы возбудить исключение из C, следует вызвать функцию SignalError и затем выйти. Runtime-система Java обнаруживает и возбуждает исключение, о котором сигнализировала функция SignalError. Ниже вы увидите несколько примеров того, как это делается.
А.2.5 Средства безопасности
Средства безопасности языка Java не имеют аналогов в C. Вы полностью отвечаете за работу программы на C (как это обычно бывает) без какой-либо автоматической помощи со стороны Java.
А.2.6 Работа с памятью
Родные методы могут создавать новые объекты Java с помощью функций, описанных ниже.