Java原生接口 java程序语言及其标准API十分丰富,可看用来撰写成熟的应用程序。但是,在某些特殊的情况中,你得调用non-java程序代码。例如,如果你想访问操作系统特有的功能、想要和特殊的硬件设备沟通、想要重复使用过去撰写的non-java程序代码、或是想要实现极重视时间效率的程序代码,你就会想调用non-java程序代码。

    Java原生接口(Java Native Interface,JNI),JNI是个颇为丰富的编程接口,它让你得以于Java应用程序中调用原生函数(native methods)。JNI与Java1.1被加入,和Java1.0的对等机制——也就是原生函数接口(Native Method Interface,NMI)——维持了某种程度的相容性。NMI的设计特制不适用于所有虚拟机制。基于这个原因,Java语言的未来版本中可能不会再支持NMI。

    JNI的现状:只能用来和C或C++所写成的原生函数相接,通过JNI,可以作如下事情:

    1、产生Java对象(包括数组和Strings),取得并更新Java对象值。
    2、调用Java函数
    3、捕捉、掷出异常(exceptions)
    4、装载classes,并取得class的相关信息
    5、执行动态时期型别检验 run-time type checking

    一、原生函数的调用
    以下是一个调用原生函数的Java程序,它会依序调用C标准程序的库的printf()函数。

    public class ShowMessage{
    private native void ShowMessage(String msg);
    static{
    //可以随处调用,但这种调用比较恰当,将DLL装载内存,并加以连接。DLL必须位于你得系统程序库路径中,JVM会自动补上文件副名,副名视平台不同而异。
    System.loadLibrary("MsgImpl");
    }
    public static void main(String []args){
    ShowMessage app=new ShowMessage();
    app.ShowMessage("Generated with JNI")//引数传递;
    }
    }

    二、头文件产生器:javah

    接下来编译Java原始文件,针对所得到的.class文件执行javah,并指定-jni选项:
    javah -jni ShowMessage
    javah会读取Java.class文件,针对其中每一个原生函数声明,以C或C++头文件的型式产生出函数原型声明。

    三、名称重整(Name mangling)与函数标记(function signatures)
    JNI对于原生函数的名称,规范了一种独一无二的名称重整手法。这很重要,因为这是“虚拟机将Java调用动作连接至原生函数”机制的一部分。基本上所有原生函数皆以“Java”起首,其后跟着“Java native声明”所栖身的class名称,再跟着Java原生函数名称,其间各以底线符'_'分隔。

    四、实现出你自己的DLL
    接下来撰写C或者C++源代码,并在其中含入javah所产生的头文件,并实现出原生函数.编译之后会产生一个动态链接程序库(dynamic link library,DLL)

    传入原生函数的那些引数,是原生函数用来和Java程序沟通的管道。第一引数的型别为JNIEnv,其中包含所有必要的hook(扣钩),让你得以回调(callback)JVM;第二引数的意义依函数的类型而有不同,对non-static函数,第二引数和C++的this指针功用相同,而且类似Java的this:是个reference,指向原生函数的调用者(某个对象)。对static函数来说,他是个reference,指向原生函数的实现者(以某个Class对象作为代表)。

    其余引数代表的是传入原生函数的各个Java对象。基本型别德传递也是通过这种方式,不过却是采用传值(by value)的方式

    五、取用JNI函数:通过JNIEnv引数
    所谓JNI函数,就是程序员可于原生函数中用来和JVM沟通的函数。
    每个JNI原生函数都收到一个特殊引数(JNIEnv引数)作为其第一参数。那是一个指针,指向型别为JNIEnv_的一个特殊的JNI数据结构。JNI数据结构中有个元素也是指针,指向JVM所产生的数组,其内每个元素都是指针,分别指向各个JNI函数。提取这些指针后,便可资原生函数中调用JNI函数。每个JVM都提供有自己的JNI函数实现版本,永远位于预先定义好的便宜位置上。

    六、传递和运用Java对象
    如果想传递对象,需要在声明原生函数时使用一般Java语法

    七、JNI和Java异常
    通过JNI可掷出或捕捉Java异常,并可打印其指或重掷

    八、JNI和多线程
    由于Java是个多线程程序语言,所以可能有多个线程同时调用同一个原生函数。当某个原生函数执行未完,第二个线程加以调用,该原生函数的执行便有可能被暂停。