java native本地方法详解(转)

文章链接出处:

  详解native方法的使用

  自己实现一个Native方法的调用

 

JNI

  开始本篇的内容之前,首先要讲一下JNI。Java很好,使用的人很多、应用极 广,但是Java不是完美的。Java的不足体现在运行速度要比传统的C++慢上许多之外,还有Java无法直接访问到操作系统底层如硬件系统,为此 Java提供了JNI来实现对于底层的访问。JNI,Java Native Interface,它是Java的SDK一部分,JNI允许Java代码使用以其他语言编写的代码和代码库,本地程序中的函数也可以调用Java层的函 数,即JNI实现了Java和本地代码间的双向交互。

 

Native

  JDK开放给用户的源码中随处可见Native方法,被Native关键字声明的方法说明该方法不是以Java语言实现的,而是以本地语言实现的,Java可以直接拿来用。这里有一个概念,就是本地语言,本地语言这四个字,个人理解应该就是可以和操作系统直接交互的语言。

 

通过JNI调用C++写的代码

  下面演示一下如何写一段简单的C++代码,在Java中用一个Native方法去调用的步骤。C++代码使用到的IDE是Microsoft Visual Studio 2010,这是一款市面上开发C++代码最常用的IDE,功能强大。OK,接下来一步一步演示一下:

 1、写一段Java代码。由于我们在Windows环境下,所以用的是一 个.dll文件,如果在Linux环境下的话,用的是一个.so文件。最后C++代码写完之后要生成一个.dll/.so文件,生成的文件可以使用 static静态加载的方法加载进来,也可以通过配置环境变量的方式,这里选择前者。

public class TestMain
{
    static
    {
        System.load("D:" + File.separator + "Hello.dll");
    }
    
    public native static void Hello();
    
    public static void main(String[] args)
    {
        Hello();
    }
}

 2、cmd进入命令行程序中,进入刚才写的那个类的CLASSPATH下,CLASSPATH就是.class文件所在的根路径。运行“JNI -jni com.xrq.test1.TestMain” ,表示为指定的类下的Native方法生成.h文件。.h文件是C/C++使用的头文件,用于将声明和实现分离,不熟悉C/C++的同学也不用深究

                 

    3、进入自己的CLASSPATH底下查看一下有没有多一个.h文件,我自己这边的是

               

    这个命名是javah这个命令的实现帮我们命名的,只要多了这个文件就可以了

 4、打开VS2010,文件-->新建-->项目,命名为“Hello”,和我们静态块中load进去的名字要一致

              

 注意选择DLL

 5、刚才通过javah生成的.h文件复制到Hello目录下

            

   6、把这个.h添加到项目中,右键头文件-->添加-->现有项,选择Hello目录下的“com_xrq_test1_TestMain.h”添加进去就好了

 7、右键 源文件-->添加-->新建项,选择.cpp文件,随便命名,我叫做source.cpp。实现很简单,就打印一下“Hello”这个字符 串。cout是C++的控制台输出语句,相当于Java的System.out.print(),endl是换行的意思,所以这整句相当于Java的 System.out.println("Hello");

         

 8、修改“com_xrq_test1_TestMain.h”头文件中的#include <jni.h>为#include "jni.h"

 9、把%JAVA_HOME%/include目录下的“jni.h”%JAVA_HOME%/include/win32目录下的“jni_md.h”两 个文件复制到Hello目录下,用刚才添加“com_xrq_test1_TestMain.h”一样的方式添加这两个.h文件到头文件目录中,这样目录 下Hello目录下应该多出了两个文件,VS2010工程目录下应该有3个.h文件。JAVA_HOME就是JDK安装目录,不知道的cmd进入命令行,echo %JAVA_HOME%就可以查看了。这一步很重要,没有这一步,C++程序是无法运行的

            

          

  10、右键项目-->生成,就可以生成.dll文件了

          

  11、刚才生成的.dll文件是32系统下的,32系统的用户可以直接使用这 个.dll文件了,只要复制到静态代码块指定的目录下就好了。换句话说,64位系统的用户是不能使用这个.dll文件的。如果要生成64位计算机可以使用 的.dll文件,还要额外再为64位计算机成生一个.dll文件 

         

  下拉菜单里面没有x64的点击下拉菜单-->配置管理器配置一下,自己摸索一下就好了,VS2010有自带x64的

 12、生成64位的.dll,复制到D盘下即可,也就是静态代码块里面指定的Hello.dll的路径

         

 13、运行一下第一步里面写的Java程序,C++打印的“Hello”就出来了,至此,Java通过JNI调用C++编写的代码的功能完成。

 

有什么心得?

 自己完成了这么一个过程,肯定是颇有成就感的,成就感过后,我们可以从这13个步骤中感悟到什么?至少个人有以下心得:

  1、1个类中有很多Native方法-->这个类中的所有Native方法生成到1个.h文件中-->本地代码生成一个.dll/.so文件和一个类的Native方法实现相对应

  2、为什么有Native方法的类中必有这么一段代码

private static native void ();
static {
    registerNatives();
}

  现在想来,估计和我们的静态代码块起的作用一样,都是为这个类导入特定的.dll/.so文件用的。至于为什么不能像我们这么写,个人猜测,是因为不同的用户磁盘上的.dll/.so文件位置不固定,和JDK安装目录相关?

  3、Java不在乎Native方法是用什么语言实现的,只要一来语言能和底层打 交道就好了,二来语言实现完可以提供出来.dll/.so文件。因此同一个Native方法,如果不同的Java虚拟机去调用它,那么结果可能都不同,比 如Object的hashCode(),当然,运行效率也不尽然相同,因为不同的虚拟机对于不同的Native方法有自己的实现。

 

使用native关键字目的

 为什么要使用Native Method?

  java使用起来非常方便,然而有些层次的任务用java实现起来不容易,或者我们对程序的效率很在意时,问题就来了。
  与java环境外交互:
    有时java应用需要与java外面的环境交互。这是本地方法存在的主要原因,你可以想想java需要与一些底层系统如操作系统或某些硬件交换信息时的情况。本地方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解java应用之外的繁琐的细节。
  与操作系统交互:
    JVM支持着java语言本身和运行时库,它是java程序赖以生存的平台,它由一个解释器(解释字节码)和一些连接到本地代码的库组成。然而不管怎 样,它毕竟不是一个完整的系统,它经常依赖于一些底层(underneath在下面的)系统的支持。这些底层系统常常是强大的操作系统。通过使用本地方法,我们得以用java实现了jre的与底层系统的交互,甚至JVM的一些部分就是用C写的,还有,如果我们要使用一些java语言本身没有提供封装的操作系统的特性时,我们也需要使用本地方法。
  Sun's Java
    Sun的解释器是用C实现的,这使得它能像一些普通的C一样与外部交互。jre大部分是用java实现的,它也通过一些本地方法与外界交互。例如:类java.lang.Thread 的 setPriority()方法是用java实现的,但是它实现调用的是该类里的本地方法setPriority0()。这个本地方法是用C实现的,并被植入JVM内部,在Windows 95的平台上,这个本地方法最终将调用Win32 SetPriority() API。这是一个本地方法的具体实现由JVM直接提供,更多的情况是本地方法由外部的动态链接库(external dynamic link library)提供,然后被JVM调用。

总的来说,JAVA的native方法适用的情况:
  1、为了使用底层的主机平台的某个特性,而这个特性不能通过JAVA API访问。
  2、为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用JAVA编写的。

  3、为了加快程序的性能,而将一段时间敏感的代码作为本地方法实现。

 

native关键字特点

  标识符native可以与所有其它的java标识符连用,但是abstract除外。这是合理的,因为native暗示这些方法是有实现体的,只不过这些实现体是非java的,但是abstract却显然的指明这些方法无实现体。native与其它java标识符连用时,其意义同非Native Method并无差别,比如native static表明这个方法可以在不产生类的实例时直接调用,这非常方便,比如当你想用一个native method去调用一个C的类库时。上面的第三个方法用到了native synchronized,JVM在进入这个方法的实现体之前会执行同步锁机制(就像java的多线程。)


  一个native method方法可以返回任何java类型,包括非基本类型,而且同样可以进行异常控制。这些方法的实现体可以制一个异常并且将其抛出,这一点与java的方法非常相似。当一个native method接收到一些非基本类型时如Object或一个整型数组时,这个方法可以访问这非些基本型的内部,但是这将使这个native方法依赖于你所访问的java类的实现。有一点要牢牢记住:我们可以在一个native method的本地实现中访问所有的java特性,但是这要依赖于你所访问的java特性的实现,而且这样做远远不如在java语言中使用那些特性方便和容易。


  native method的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法。JVM将控制调用本地方法的所有细节。需要注意当我们将一个本地方法声明为final的情况。用java实现的方法体在被编译时可能会因为内联而产生效率上的提升。但是一个native final方法是否也能获得这样的好处却是值得怀疑的,但是这只是一个代码优化方面的问题,对功能实现没有影响。


  如果一个含有本地方法的类被继承,子类会继承这个本地方法并且可以用java语言重写这个方法(这个似乎看起来有些奇怪),同样的如果一个本地方法被fianl标识,它被继承后不能被重写。
  

  本地方法非常有用,因为它有效地扩充了jvm.事实上,我们所写的java代码已经用到了本地方法,在sun的java的并发(多线程)的机制实现中,许多与操作系统的接触点都用到了本地方法,这使得java程序能够超越java运行时的界限。有了本地方法,java程序可以做任何应用层次的任务。

 

posted @ 2019-10-19 15:02  myseries  阅读(5325)  评论(1编辑  收藏  举报