Java JDK原始碼解析之:native方法
初次看見native關鍵字是自己在看Scanner類原始碼中傳遞System.in引數實現列印,之後轉到System觀看原始碼時看見native關鍵字,關於native關鍵字筆者表示,是Java與C語言的通訊介面,因為Java語言沒有操作底層的條件,所以Java語言只好用C語言來操作底層部件,定義了native關鍵字。 System出現的native方法:
public final class System {
private static native void registerNatives();
.......
}
筆者觀看程式碼時一臉懵,開始認為怎麼會有這種方法,沒有方法體。之後筆者也試了以下。順便搜尋一些前者資料。自己實現了一個。基於native關鍵字的加減法。
package com.java.jvm.demo;
public class JVMDemo {
public native int add(int i,int j);
public native int sub(int i,int j);
public native int mul(int i,int j);
public native int div(int i,int j);
}
因為native關鍵字時要用C去實現的方法,在編譯時尤其注意,需要將類編譯成C語言的標頭檔案。
1、首先將類編譯為.class的位元組碼檔案 2、第一種方法是在.class的目錄下使用javah命令編譯,還有一種方法是在eclipse中配置編譯器。具體就不詳細說明。 3、類與包名一旦定義好不能隨意更改,因為編譯好的標頭檔案會附帶包、類名
之後編譯的標頭檔案程式碼:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_java_jvm_demo_JVMDemo */ #ifndef _Included_com_java_jvm_demo_JVMDemo #define _Included_com_java_jvm_demo_JVMDemo #ifdef __cplusplus extern "C" { #endif /* * Class: com_java_jvm_demo_JVMDemo * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_java_jvm_demo_JVMDemo_add (JNIEnv *, jobject, jint, jint); /* * Class: com_java_jvm_demo_JVMDemo * Method: sub * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_java_jvm_demo_JVMDemo_sub (JNIEnv *, jobject, jint, jint); /* * Class: com_java_jvm_demo_JVMDemo * Method: mul * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_java_jvm_demo_JVMDemo_mul (JNIEnv *, jobject, jint, jint); /* * Class: com_java_jvm_demo_JVMDemo * Method: div * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_java_jvm_demo_JVMDemo_div (JNIEnv *, jobject, jint, jint); #ifdef __cplusplus } #endif #endif
直接使用#include 去引用標頭檔案編譯時肯定會報錯,因為C語言解釋不了這個標頭檔案,所以需要去Java JDK中尋找這個標頭檔案直譯器,標頭檔案直譯器在Java JDK安裝目錄:
D:\Program Files (x86)\Java\jdk1.7.0_72\include\jni.h D:\Program Files (x86)\Java\jdk1.7.0_72\include\win32\jawt_md.h D:\Program Files (x86)\Java\jdk1.7.0_72\include\win32\jni_md.h
將這三個檔案複製在VC標頭檔案庫中,VC的標頭檔案庫位於VC的安裝目錄下:
D:\softwere\Microsoft Visual Studio\VC98\Include
將三個標頭檔案直譯器放入這裡面之後,就可以進行元件.dll動態連結庫了。然後編寫的C語言原始碼:
#include "stdafx.h"
#include "jni.h"
#include "com_java_jvm_demo_JVMDemo.h"
JNIEXPORT jint JNICALL Java_com_java_jvm_demo_JVMDemo_add
(JNIEnv *en, jobject obj, jint x, jint y)
{
printf("hello welcome to add method ! \n");
return (x+y);
}
/*
* Class: com_java_jvm_demo_JVMDemo
* Method: sub
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_java_jvm_demo_JVMDemo_sub
(JNIEnv *en, jobject obj, jint x, jint y)
{
return (x-y);
}
/*
* Class: com_java_jvm_demo_JVMDemo
* Method: mul
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_java_jvm_demo_JVMDemo_mul
(JNIEnv *en, jobject obj, jint x, jint y)
{
return (x*y);
}
/*
* Class: com_java_jvm_demo_JVMDemo
* Method: div
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_java_jvm_demo_JVMDemo_div
(JNIEnv *en, jobject obj, jint x, jint y)
{
return (x/y);
}
注意:
1、C語言編譯不能是debug版本,需要轉換為Release版本 2、如果C語言編譯器32位,那麼Java必須是32位,不然丟擲UnsatisfiedLinkError異常:32 bit …amd 64等解釋異常。
編譯成功之後
拿到.dll檔案複製在java專案中:
之後執行是不可能的,這輩子都不能,還需要配置一個Java的 Native library location
測試:
package com.java.jvm.demo;
public class Demo {
static{
System.loadLibrary("jni");
}
public static void main(String[] args) {
//Scanner
//System
JVMDemo demo = new JVMDemo();
try {
int a = demo.add(2, 3);
int s = demo.sub(3, 2);
int m = demo.mul(2, 3);
int d = demo.div(4, 2);
System.out.println("method add() = " + a);
System.out.println("method sub() = " + s);
System.out.println("method mul() = " + m);
System.out.println("method div() = " + d);
} catch (UnsatisfiedLinkError e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
執行結果:
總結:每次檢視JDK原始碼時候,總是能重新整理自己對Java語言的世界觀,總是讓人吃驚。也有點小驚喜。
有不懂的可以直接回復筆者,筆者不定期回覆。歡迎各位俠義之士共同進步。