Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
GoslingJava2.doc
Скачиваний:
139
Добавлен:
23.02.2016
Размер:
2.39 Mб
Скачать

А.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-контекст.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]