Модификатор native для объявления методов - Вопросы
Всего: 6 вопросов
1. Что означает модификатор native в Java?
Что означает модификатор native в Java?
native — модификатор метода, который сообщает, что тело метода реализовано вне JVM: в скомпилированной библиотеке (.dll, .so, .dylib), написанной на языке с C-совместимым ABI — обычно C или C++, но подойдут и Rust, Fortran, ассемблер. В Java остаётся только сигнатура, тела нет, объявление заканчивается точкой с запятой: public native int hashCode();
Само ключевое слово ничего не компилирует и не подключает. Оно решает одну задачу: разрешает объявить метод без тела и помечает его как «реализация придёт извне». Связыванием объявления с настоящей функцией во время выполнения занимается JNI (Java Native Interface). Применяют native ради доступа к возможностям ОС, переиспользования готовых C/C++-библиотек (кодеки, криптография, драйверы) и внутри самой JDK.
2. Как JNI находит реализацию native-метода и что для этого должно совпасть?
Как JNI находит реализацию native-метода и что для этого должно совпасть?
Должны совпасть три вещи.
1. Библиотека загружена. Обычно в статическом инициализаторе класса: System.loadLibrary("demo") ищет библиотеку в java.library.path, а System.load("/абсолютный/путь/libdemo.so") берёт полный путь. Расширение и префикс lib в loadLibrary() писать не нужно — JVM подставляет их сама по правилам платформы.
2. Имя функции совпадает. Символ строится по строгому правилу: Java_ + пакет с точками, заменёнными на подчёркивания + _ + имя класса + _ + имя метода. Для com.example.Demo.sum() это Java_com_example_Demo_sum.
3. Сигнатура совпадает. Первый параметр C-функции — всегда JNIEnv*, второй — jobject для обычного метода или jclass для static, дальше идут параметры в JNI-типах (jint, jlong, jstring).
Вручную имена писать не нужно: заголовок генерирует компилятор командой javac -h . Demo.java (флаг доступен с JDK 8). Отдельная утилита javah объявлена устаревшей в JDK 9 и удалена в JDK 10.
3. С какими модификаторами можно сочетать native и где его применять нельзя?
С какими модификаторами можно сочетать native и где его применять нельзя?
Разрешено: static (в C-функцию вторым аргументом придёт jclass вместо jobject), synchronized (монитор захватывается до входа в нативный код), final и любой модификатор доступа — private, public, protected. Native-метод может объявлять throws и бросать обычные Java-исключения (нативный код возбуждает их через ThrowNew), а также участвует в рефлексии: Modifier.isNative(m.getModifiers()).
Запрещено:
native+ тело метода — ошибка native methods cannot have a body, объявление заканчивается;;native+abstract— illegal combination of modifiers: оба означают «тела здесь нет», но abstract обещает реализацию в подклассе, а native — в библиотеке;nativeу поля или класса — modifier native not allowed here, модификатор применим только к методам;nativeу конструктора — нативную инициализацию выносят в отдельный native-метод и вызывают из конструктора;nativeв интерфейсе — методы интерфейса либо абстрактные, либоdefault/staticс телом на Java.
4. Можно ли переопределить native-метод обычным Java-методом в подклассе?
Можно ли переопределить native-метод обычным Java-методом в подклассе?
Да. Для наследования не имеет значения, где лежит реализация метода: native не входит в сигнатуру метода, поэтому в переопределении не участвует. Нативный метод можно переопределить обычным Java-методом, а обычный метод суперкласса — нативным.
Самый наглядный пример есть в самой JDK: Object.hashCode() объявлен нативным, а String переопределяет его чистым Java-кодом. Проверяется рефлексией:
System.out.println(Modifier.isNative(
Object.class.getDeclaredMethod("hashCode").getModifiers())); // true
System.out.println(Modifier.isNative(
String.class.getDeclaredMethod("hashCode").getModifiers())); // false
5. Правда ли, что native-метод работает быстрее обычного, и где native встречается в стандартной библиотеке?
Правда ли, что native-метод работает быстрее обычного, и где native встречается в стандартной библиотеке?
Нет, слово native не означает «быстрее». Каждый переход из Java в нативный код и обратно стоит дорого: JVM фиксирует состояние потока, JIT теряет часть оптимизаций (например, встраивание через границу вызова), а до объектов приходится добираться через JNI-функции. Мелкий метод, вызываемый в цикле, после переноса в C нередко работает медленнее Java-версии.
При этом нативные методы вы вызываете каждый день: Object.hashCode(), Object.getClass(), Object.clone(), System.arraycopy(), System.currentTimeMillis(), System.nanoTime(), Thread.currentThread(), Runtime.availableProcessors().
Многие из них помечены в исходниках JDK аннотацией @IntrinsicCandidate: JIT-компилятор HotSpot вправе подставить вместо вызова готовый машинный код (интринсик) прямо в точке вызова. Поэтому System.arraycopy() или Math.sqrt() в горячем коде вообще не проходят через JNI — здесь native лишь фиксирует, что тела на Java нет.
6. Чем в современной Java заменяют JNI и native-методы?
Чем в современной Java заменяют JNI и native-методы?
Foreign Function & Memory API — пакет java.lang.foreign (проект Panama). Прошёл preview-итерации в JDK 19—21 и финализирован в JDK 22 (JEP 454). Он позволяет вызвать функцию из нативной библиотеки без единой строки на C:
Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle(
linker.defaultLookup().find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
try (Arena arena = Arena.ofConfined()) {
MemorySegment text = arena.allocateFrom("Hello, native!");
System.out.println((long) strlen.invoke(text)); // 14
}Отличия от JNI: не нужна C-обвязка и отдельная сборка .so/.dll под каждую ОС и архитектуру; часть ошибок превращается в Java-исключения вместо падения всей JVM; память освобождается детерминированно через Arena.
Переписывать существующий JNI-код срочно не нужно: он поддерживается, а модификатор native остаётся частью языка. Но с JDK 24 (JEP 472) и JNI, и FFM считаются ограниченными операциями: JVM печатает предупреждение при загрузке нативной библиотеки и при связывании native-методов, убирается флагом --enable-native-access=ALL-UNNAMED.