java Native 关键字详解及自己实现一个Native方法的调用

您所在的位置:网站首页 java是一个什么语言编写的 java Native 关键字详解及自己实现一个Native方法的调用

java Native 关键字详解及自己实现一个Native方法的调用

2024-07-15 02:14| 来源: 网络整理| 查看: 265

Java中Native关键字的作用

初次遇见 native是在 java.lang.Object 源码中的一个hashCode方法:

public native int hashCode();

为什么有个native呢?这是我所要学习的地方。所以下面想要总结下native。

一、认识 native 即 JNI,Java Native Interface

凡是一种语言,都希望是纯。比如解决某一个方案都喜欢就单单这个语言来写即可。Java平台有个用户和本地C代码进行互操作的API,称为Java Native Interface (Java本地接口)。

二、用 Java 调用 C 的“Hello,JNI”

我们需要按照下班方便的步骤进行:

1、创建一个Java类,里面包含着一个 native 的方法和加载库的方法 loadLibrary。HelloNative.java 代码如下:

public class HelloNative {      static      {          System.loadLibrary("HelloNative");      }            public static native void sayHello();            @SuppressWarnings("static-access")      public static void main(String[] args)      {          new HelloNative().sayHello();      } }

首先让大家注意的是native方法,那个加载库的到后面也起作用。native 关键字告诉编译器(其实是JVM)调用的是该方法在外部定义,这里指的是C。如果大家直接运行这个代码,  JVM会告之:“A Java Exception has occurred.”控制台输出如下:

1 2 3 4 5 Exception in thread "main" java.lang.UnsatisfiedLinkError: no HelloNative in java.library.path      at java.lang.ClassLoader.loadLibrary(Unknown Source)      at java.lang.Runtime.loadLibrary0(Unknown Source)      at java.lang.System.loadLibrary(Unknown Source)      at HelloNative.(HelloNative.java:5)

这是程序使用它的时候,虚拟机说不知道如何找到sayHello。下面既可以手动写,自然泥瓦匠是用

    2、运行javah,得到包含该方法的C声明头文件.h

将HelloNative.java ,简单地 javac javah,如图

就得到了下面的 HelloNative.h文件 :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class HelloNative */   #ifndef _Included_HelloNative #define _Included_HelloNative #ifdef __cplusplus extern "C" { #endif /*   * Class:     HelloNative   * Method:    sayHello   * Signature: ()V   */ JNIEXPORT void JNICALL Java_HelloNative_sayHello    (JNIEnv *, jclass);   #ifdef __cplusplus } #endif #endif

jni.h 这个文件,在/%JAVA_HOME%include

3、根据头文件,写C实现本地方法。

这里我们简单地实现这个sayHello方法如下:

1 2 3 4 5 6 7 #include "HelloNative.h" #include   JNIEXPORT void JNICALL Java_HelloNative_sayHello {      printf("Hello,JNI");    }

4、生成dll共享库,然后Java程序load库,调用即可。

在Windows上,MinGW GCC 运行如下

1 gcc -m64  -Wl,--add-stdcall-alias -I"C:\Program Files\Java\jdk1.7.0_71\include" -I"C:\Program Files\Java\jdk1.7.0_71\include\include\win32" -shared -o HelloNative.dll HelloNative.c

-m64表示生成dll库是64位的。然后运行 HelloNative:

1 java HelloNative

 

终于成功地可以看到控制台打印如下:

1 Hello,JNI 回到顶部 三、JNI 调用 C 流程图

回到顶部 四、其他介绍

native是与C++联合开发的时候用的!java自己开发不用的!

使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。这些函数的实现体在DLL中,JDK的源代码中并不包含,你应该是看不到的。对于不同的平台它们也是不同的。这也是java的底层机制,实际上java就是在不同的平台上调用不同的native方法实现对操作系统的访问的。

1。native 是用做java 和其他语言(如c++)进行协作时用的也就是native 后的函数的实现不是用java写的2。既然都不是java,那就别管它的源代码了,呵呵

native的意思就是通知操作系统,这个函数你必须给我实现,因为我要使用。所以native关键字的函数都是操作系统实现的,java只能调用。

java是跨平台的语言,既然是跨了平台,所付出的代价就是牺牲一些对底层的控制,而java要实现对底层的控制,就需要一些其他语言的帮助,这个就是native的作用了

Java不是完美的,Java的不足除了体现在运行速度上要比传统的C++慢许多之外,Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。  可以将native方法比作Java程序同C程序的接口,其实现步骤:  1、在Java中声明native()方法,然后编译;  2、用javah产生一个.h文件;  3、写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK带的jni.h文件);  4、将第三步的.cpp文件编译成动态链接库文件;  5、在Java中用System.loadLibrary()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。

JAVA本地方法适用的情况 1.为了使用底层的主机平台的某个特性,而这个特性不能通过JAVA API访问

2.为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用JAVA编写的

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

-------------我是低调的分割线--------------------------                          

今天研究Java基础类库,Object类的时候,发现了一个关键字:native

咦?这是个什么东东?它认识我,我可不认识它!

嘿嘿,没关系,baidu一下。

 

Java native关键字

 一. 什么是Native Method   简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数。   "A native method is a java method whose implementation is provided by non-java code."   在定义一个native method时,并不提供实现体(有些像定义一个java interface),因为其实现体是由非java语言在外面实现的。,下面给了一个示例:   

package java.lang; public class Object {     ......    public final native Class getClass();     public native int hashCode();     protected native Object clone() throws CloneNotSupportedException;     public final native void notify();     public final native void notifyAll();     public final native void wait(long timeout) throws InterruptedException;     ......} 

        标识符native可以与所有其它的java标识符连用,但是abstract除外。这是合理的,因为native暗示这些方法是有实现体的,只不过这些实现体是非java的,但是abstract却显然的指明这些方法无实现体。native与其它java标识符连用时,其意义同非Native Method并无差别。

    一个native method方法可以返回任何java类型,包括非基本类型,而且同样可以进行异常控制。这些方法的实现体可以自制一个异常并且将其抛出,这一点与java的方法非常相似。    native method的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法。JVM将控制调用本地方法的所有细节。

    如果一个含有本地方法的类被继承,子类会继承这个本地方法并且可以用java语言重写这个方法(这个似乎看起来有些奇怪),同样的如果一个本地方法被fianl标识,它被继承后不能被重写。   本地方法非常有用,因为它有效地扩充了jvm.事实上,我们所写的java代码已经用到了本地方法,在sun的java的并发(多线程)的机制实现中,许多与操作系统的接触点都用到了本地方法,这使得java程序能够超越java运行时的界限。有了本地方法,java程序可以做任何应用层次的任务。

二.为什么要使用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调用。

三.JVM怎样使Native Method跑起来:    我们知道,当一个类第一次被使用到时,这个类的字节码会被加载到内存,并且只会回载一次。在这个被加载的字节码的入口维持着一个该类所有方法描述符的list,这些方法描述符包含这样一些信息:方法代码存于何处,它有哪些参数,方法的描述符(public之类)等等。    如果一个方法描述符内有native,这个描述符块将有一个指向该方法的实现的指针。这些实现在一些DLL文件内,但是它们会被操作系统加载到java程序的地址空间。当一个带有本地方法的类被加载时,其相关的DLL并未被加载,因此指向方法实现的指针并不会被设置。当本地方法被调用之前,这些DLL才会被加载,这是通过调用java.system.loadLibrary()实现的。     最后需要提示的是,使用本地方法是有开销的,它丧失了java的很多好处。如果别无选择,我们可以选择使用本地方法。

 

可以将native方法比作Java程序同C程序的接口,其实现步骤:1、在Java中声明native()方法,然后编译;2、用javah产生一个.h文件;3、写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK带的jni.h文件);4、将第三步的.cpp文件编译成动态链接库文件;5、在Java中用System.loadLibrary()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。

 

具体实现方法可以从网上查找,这里就不写了

不过,又引出两个东西:javah.exe命令和JNI

 

自己写了一个调用VB的DLL的例子,不过还没写完,调用函数部分不会写,只写了加载DLL的过程

JAVA部分:

public class TestNativeMothod {    public static void main(String[] args) {                //PROCESS LOGIC            }        public native String callNativeMothod();        static {         System.loadLibrary("TestNativeMethodProj");    }}

VB部分:

Option ExplicitPublic Function callNativeMothod() As String    callNativeMothod = "This is a method implemented by VB"End Function 自己实现一个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静态加载的方法加载进来,也可以通过配置环境变量的方式,这里选择前者。

复制代码 复制代码 1 public class TestMain 2 { 3 static 4 { 5 System.load("D:" + File.separator + "Hello.dll"); 6 } 7 8 public native static void Hello(); 9 10 public static void main(String[] args) 11 { 12 Hello(); 13 } 14 } 复制代码 复制代码

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 为#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方法的类中必有这么一段代码

1 private static native void (); 2 static { 3 registerNatives(); 4 }

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

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

转载: https://www.cnblogs.com/KingIceMou/p/7239668.html

https://www.cnblogs.com/szlbm/p/5504603.html



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3