Java JDK源码解析之:native方法

时间:2024-03-19 10:51:07

初次看见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等解释异常。

编译成功之后
Java JDK源码解析之:native方法

拿到.dll文件复制在java项目中:

Java JDK源码解析之:native方法
之后运行是不可能的,这辈子都不能,还需要配置一个Java的 Native library location
Java JDK源码解析之:native方法

测试:

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();
			}
		}

}

运行结果:
Java JDK源码解析之:native方法

总结:每次查看JDK源码时候,总是能刷新自己对Java语言的世界观,总是让人吃惊。也有点小惊喜。

有不懂的可以直接回复笔者,笔者不定期回复。欢迎各位侠义之士共同进步。