zoukankan      html  css  js  c++  java
  • Java Native Interface 编程系列一

    本文是《Java Native Interface Programmer's Guide and Specification》的读书笔记

    Java Native Interface可以让编程人员在Java里调用其他语言编写的方法,来弥补Java运行效率低下的缺点;

    jni可以用来做什么?:

    • 在Java应用中使用本地编程语言如c/c++编写的代码;
    • 将Java虚拟机融入到由C/c++编写的的应用中;
    • 实现一个Java虚拟机;
    • 从技术层面理解语言的互操作性,特别是垃圾收集机制和多线程实现机制;

    jni支持两种的本地代码:本地链接库和本地应用;

    • 使用jni在本地链接库中编写可以让Java应用调用的本地方法(native methods),Java应用可以像调用Java编写的方法一样调用本地方法(本地方法是由其他语言编写的,如C/C++);
    • jni允许调用接口(invocation interface),在本地应用中嵌入Java虚拟机(java virtual mathine),本地应用可以调用实现了Java虚拟机的本地链接库,然后就可以调用接口来执行Java编写的组件;

    使用jni的步骤为:

    1. 创建一个java Class(如HelloWorld.java),在里面声明本地方法;
    2. 使用javac 命令编译源文件(HelloWorld.java),这个命令会在当前目录下生成一个HelloWorld.class文件;
    3. 使用命令:javah -jni 来生成一个C的头文件(HelloWord.h),头文件里包含本地方法实现的原型;
    4. 用C语言编写本地方法的实现(HelloWorld.c);
    5. 使用宿主环境的C编译器和链接器将本地方法的实现的文件(HelloWorld.c)编译为本地链接库(HelloWorld.dll 或者libHelloWorld.so);
    6. 运行HelloWorld的程序,类文件(HelloWorld.class)和链接库(HelloWorld.dll或者libHelloWorld.so)都会在运行时加载;

    下面以一个HelloWorld的例子来掌握使用jni编程的步骤:

    本地方法的声明:

    class HelloWorld {
    /**本地方法的声明与普通的Java方法的声明区别就是多一个native的修饰
    **/
        private native void print();//本地方法
        private void doSomething(){};//普通Java方法
        public static void main(String[] args) {
            new HelloWorld().print();
        }
        static {
        /**HelloWorld是链接库的名字,为了保证这个方法可以成功调用,必须创建好这个链接库(HelloWorld.dll在Win32里,libHelloWorld.so在Solaris中)***/
            System.loadLibrary("HelloWorld");
        }
    }
    

    native关键字表明这个方法是由其他语言实现的,但这个native方法被调用前,实现本地方法的本地链接库必须先被加载;如上面代码所示,在static区域加载这个本地链接库。Java虚拟机会在调用任何方法(本地方法或普通方法)前,自动初始化静态区域,这样就可以保证在调用本地方法时,链接库文件已经加载进来了。

    编译原文件:使用命令

    javac HelloWorld.java
    

    这个命令执行成功后,会在当前目录下生成一个HelloWorld.class 文件;

    生成头文件:使用命令:

     javah -jni HelloWorld
    

    这个命令执行成功后,会生成一个HelloWorld.h的文件,头文件里最重要的就是本地方法的原型,也就是在用其他语言实现的的文件里,这个本地方法是以怎样一个形式出现的。上面的print()本地方法在头文件里的形式为:

    JNIEXPORT void JNICALL 
    Java_HelloWorld_print (JNIEnv *, jobject);
    

    JNIEXPORT和JNICALL是两个宏变量,用来保证可以从本地链接库导出这个本地方法并且C编译器会为这个本地方法生成正确的调用代码(怎样调用这个本地方法),函数名Java_HelloWorld_print,Java表明这是一个Java调用的方法,HelloWorld是原来的对象的名字,print是在Java里声明的方法的名字.我们发现本地方法的实现会包含两个参数,而我们声明本地方法是没有参数的,每一个本地方法的实现的第一个参数就是JNIEnv接口指针(包含所有的本地方法的入口指针),第二个参数是原来对象的引用(HelloWorld对象)类似于C++中的this指针;

    实现本地方法:在HelloWorld.c中实现本地方法:

    #include <jni.h> //这个头文件里,包含本地代码调用JNI方法的信息,在实现的文件里必须包含这个头文件
    #include <stdio.h>//C语言头文件,需要使用里面提供的方法时才需要包含进来
    #include "HelloWorld.h"//包含有本地方法原型,实现时,也必须包含进来
    JNIEXPORT void JNICALL
    Java_HelloWorld_print(JNIEnv *env, jobject obj)
    {
        printf("Hello World!
    ");
        return;
    }
    

    在编写完本地方法的实现后,就要将HelloWorld.c编译成本地链接库了,不同的操作系统下生成本地链接库的命令也不一样,在Solaris中,可以使用下面的命令生成本地链接库

    cc -G -I/java/include -I/java/include/solaris  HelloWorld.c -o libHelloWorld.so
    

    参数G表明让C编译器生成链接库,而不是一般的Solaris文件;

    在Win32中,可以使用下面的命令让C++编译器生成动态链接库(命令需要写在同一行里面):

    cl -Ic:javainclude -Ic:javaincludewin32
        -MD -LD HelloWorld.c -FeHelloWorld.dll
    

    最后就可以运行HelloWorld应用了,需要注意的是,你必须将链接库的路径让Java虚拟机可以搜索到,不然会找不到链接库,导致程序出错

  • 相关阅读:
    poj2661
    poj2624
    无法使用 mask和unmask,报错“对象不支持此属性或方法”
    document.getElementsByName("IPInput3").disabled=(id!=1); 操作无效的原因是应该为
    jquery获取input的值
    POST过来的数据,php中提示Undefined index
    jQuery插件之zTree
    jQuery插件之Smart spin
    jQuery插件开发全解析
    使用IE调试检查JavaScript的错误
  • 原文地址:https://www.cnblogs.com/WoodJim/p/4789313.html
Copyright © 2011-2022 走看看