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

converted to PDF by BoJIoc

согласования языка C с системой Java компании Sun, версия 1.0.2. Возможно, в вашей

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

А.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 вызывать эти методы.

converted to PDF by BoJIoc

Мы рассмотрим только основные моменты согласования. Программа, которая используется здесь в качестве примера, представляет собой текст класса 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++ может быть улучшено, и несомненно это будет сделано в будущих версиях.

converted to PDF by BoJIoc

Реализуя родные методы на 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. Косая черта (/) в имени пакета переходит в символ подчеркивания (_).

converted to PDF by BoJIoc

А.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 содержала бы члены с одинаковыми именами, что запрещено.

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