来源:http://blog.csdn.net/flxyzsby/article/details/597588
三. Java与C连动时的数据类型分析
刚才我们只是举了一个简单的例子来说明连动的基本方法
我们在编写真正实用的程序的时候
,
我们必然需要传递参数
.Java
与
C
数据类型的对应关系是这样的
-----------------------------------------------------------------------------------------------------------------------
Java
数据类型
C
数据类型
字节数
-----------------------------------------------------------------------------------------------------------------------
boolean jboolean 1
byte jbyte 1
char jchar 2
short jshort 2
int jint 4
long jlong 8
float jfloat 4
double jdouble 8
-----------------------------------------------------------------------------------------------------------------------
Java
所有的数组都拥有相应的
C
数组类型
-----------------------------------------------------------------------------------------------------------------------
Java
数据类型
C
数据类型
-----------------------------------------------------------------------------------------------------------------------
Boolean[] jbooleanArray
byte[] jbyteArray
char[] jcharArray
short[] jshortArray
int[] jintArray
long[] jlongArray
float[] jfloatArray
double[] jdoubleArray
object[] jobjectArra
y
-----------------------------------------------------------------------------------------------------------------------
还有一个类型
jarray,
它是一个通用数组
从
jini.h
可以看出
在
C
中
,
所以的数组数据类型都是
jobject
的同义类型
,
但是在
C++
中是有继承关系的
.
继承关系为
Jobject <
――
jarray <
――
jobjectArra
y,
jbooleanArray, jbyteArray, jcharArray, jshortArray, jintArray, jlongArray, jfloatArray, jdoubleArray
对这些数组类型的操作函数典型的有
jsize (JNICALL *GetArrayLength)
(JNIEnv *env, jarray array);
获得数组的长度
,
适用于所有类型
你可以这样使用
在
C
中
jsize length = (*env)->GetArrayLength(env,array);
在
C++
中
Jsize length = env->GetArrayLength(array);
在
C
和
C++
中使用略有区别
,
请大家使用注意以下
,
下面提到的函数也是如此
.
在标题文件
jni.h
中
,
这些数据类型用
typedef
语句声明为目标平台上的等价数据类型
.
还有这两个常量
JNI_FALSE = 0
和
JNI_TRUE = 1.
jboolean * (JNICALL *GetBooleanArrayElements)
(JNIEnv *env, jbooleanArray array, jboolean *isCopy);
jbyte * (JNICALL *GetByteArrayElements)
(JNIEnv *env, jbyteArray array, jboolean *isCopy);
jchar * (JNICALL *GetCharArrayElements)
(JNIEnv *env, jcharArray array, jboolean *isCopy);
jshort * (JNICALL *GetShortArrayElements)
(JNIEnv *env, jshortArray array, jboolean *isCopy);
jint * (JNICALL *GetIntArrayElements)
(JNIEnv *env, jintArray array, jboolean *isCopy);
jlong * (JNICALL *GetLongArrayElements)
(JNIEnv *env, jlongArray array, jboolean *isCopy);
jfloat * (JNICALL *GetFloatArrayElements)
(JNIEnv *env, jfloatArray array, jboolean *isCopy);
jdouble * (JNICALL *GetDoubleArrayElements)
(JNIEnv *env, jdoubleArray array, jboolean *isCopy);
这些函数获得数组的内容
,
返回数组指针
, i
sCopy
如果进行拷贝,指向以
JNI_TRUE
填充的
jboolean,
否则指向以
JNI_FALSE
填充的
jboolean
。
void (JNICALL *ReleaseBooleanArrayElements)
(JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode);
void (JNICALL *ReleaseByteArrayElements)
(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode);
void (JNICALL *ReleaseCharArrayElements)
(JNIEnv *env, jcharArray array, jchar *elems, jint mode);
void (JNICALL *ReleaseShortArrayElements)
(JNIEnv *env, jshortArray array, jshort *elems, jint mode);
void (JNICALL *ReleaseIntArrayElements)
(JNIEnv *env, jintArray array, jint *elems, jint mode);
void (JNICALL *ReleaseLongArrayElements)
(JNIEnv *env, jlongArray array, jlong *elems, jint mode);
void (JNICALL *ReleaseFloatArrayElements)
(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode);
void (JNICALL *ReleaseDoubleArrayElements)
(JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode);
这些函数用于通知虚拟机指针已经不再需要
.
其中
mode
的参数有
0
=在更新数组元素后释放
elems
缓冲器
JNI_COMMIT
=在更新数组元素后不释放
elems
缓冲器
JNI_ABORT
=不更新数组元素释放
elems
缓冲器
void (JNICALL *GetBooleanArrayRegion)
(JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf);
void (JNICALL *GetByteArrayRegion)
(JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf);
void (JNICALL *GetCharArrayRegion)
(JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf);
void (JNICALL *GetShortArrayRegion)
(JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf);
void (JNICALL *GetIntArrayRegion)
(JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf);
void (JNICALL *GetLongArrayRegion)
(JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf);
void (JNICALL *GetFloatArrayRegion)
(JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf);
void (JNICALL *GetDoubleArrayRegion)
(JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf);
void (JNICALL *SetBooleanArrayRegion)
(JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf);
void (JNICALL *SetByteArrayRegion)
(JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf);
void (JNICALL *SetCharArrayRegion)
(JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf);
void (JNICALL *SetShortArrayRegion)
(JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf);
void (JNICALL *SetIntArrayRegion)
(JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf);
void (JNICALL *SetLongArrayRegion)
(JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf);
void (JNICALL *SetFloatArrayRegion)
(JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf);
void (JNICALL *SetDoubleArrayRegion)
(JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf);
这些函数用于访问和设置局部元素
.
其他函数请参照
jni.h
四. Java与C连动参数的传递
还有我要和你讨论的是参数传递有值传递和地址
(
还有引用传递
)
传递的两种方式
.
在它们连动时我们也要进行这两种传递的实现
.
我们知道
Java
参数传递都是值传递
,
而没有地址传递的说法
.
但是为了带回参数的变化值
(
你不可能把所有的信息都在函数返回值里返回
),
我们不得不创造一个等价的地址传递方式
.
幸运的是
Java
里的对象是可以作为参数传递给函数并且可以带回其在函数中的变化
(
从内部实现机制看
,
其实就是一种引用传递
).
于是我们的出结论
:
Java
中传递用传递值静态对象时为值传递
,
用
new
传递对象引用传递
.
我们要编写这样的一个函数
:
将一个
byte[]
里的内容拷贝到另一个
byte[]
里
int javabytecpy( byte[] dest, byte [] src, int req_byte_len);
我们的
java
文件内容为
package java2c.testdll;
import java.io.UnsupportedEncodingException;
public class javatest {
public static void main(String args[]){
int rtn = 0;
String str="
你好我的祖国
--China";
byte[] src_byte_array = str.getBytes();
byte[] dest_byte_array =new byte[src_byte_array.length];
System.out.println("
拷贝前源
byte
数组内容为
: " +BytearrayToStgring(src_byte_array));
System.out.println("
拷贝后目标
byte
数组内容为
:"+BytearrayToStgring(dest_byte_array));
System.out.println("DLL
函数返回值为
"+rtn);
rtn = javabytecpy(dest_byte_array, src_byte_array, 6);
System.out.println("
拷贝前源
byte
数组内容为
: " +BytearrayToStgring(src_byte_array));
System.out.println("
拷贝后目标
byte
数组内容为
:"+BytearrayToStgring(dest_byte_array));
System.out.println("DLL
函数返回值为
"+rtn);
}
//
加载库
static {
//
显示
java.library.path
内容
System.out.println(System.getProperty("java.library.path"));
//
在这里加载的是
C/C++
生成的
DLL
文件名
,
这里是
java_c_dll.dll
System.loadLibrary("java_c_dll");
System.out.println("------------------DLL Started--------------------------");
}
/**
*
参数
:
*dest
目标数组
对象传递
*src
源数组
对象传递
*dest_byte_len
需要拷贝长度
值传递
*
返回值
:
*
成功
0 ,
失败
-1
**/
public native static int javabytecpy( byte[] dest, byte [] src, int req_byte_len);
public static String BytearrayToStgring(byte[] byte_in){
try{
String encoding="GB2312";
String ret_str=new String(byte_in,encoding);
return ret_str;
}
catch(UnsupportedEncodingException exp){
return "";
}
}
}
生成的头文件为
java2c_testdll_javatest.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class java2c_testdll_javatest */
#ifndef _Included_java2c_testdll_javatest
#define _Included_java2c_testdll_javatest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: java2c_testdll_javatest
* Method: javabytecpy
* Signature: ([B[BI)I
*/
JNIEXPORT jint JNICALL Java_java2c_testdll_javatest_javabytecpy
(JNIEnv *, jclass, jbyteArray, jbyteArray, jint);
#ifdef __cplusplus
}
#endif
#endif
我们的
C/C++
实现文件为
java2c_testdll_javatest.cpp
#include "stdio.h"
#include "jni_md.h"
#include "java2c_testdll_javatest.h"
/*
* Class: java2c_testdll_javatest
* Method: execute
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_java2c_testdll_javatest_javabytecpy
( JNIEnv * env,
jclass cls,
jbyteArray dest_byte_array,
jbyteArray src_byte_array,
jint req_len){
printf("DLL
调用开始
./n");
if ( env->GetArrayLength(src_byte_array) < req_len ){
printf("
你需要的长度超过源
byte
数组
./n");
return -1;
};
if ( env->GetArrayLength(dest_byte_array) < env->GetArrayLength(src_byte_array)){
printf("
你的目标数组小于源数组
./n");
return -1;
};
jbyte* byte_temp = env->GetByteArrayElements(src_byte_array, JNI_FALSE);
env->SetByteArrayRegion(dest_byte_array, 0, req_len, (const jbyte*)byte_temp);
printf("DLL
调用结束
./n");
return 0;
};
我们运行的结果是
C:/WINNT/system32;.;C:/WINNT/system32;C:/WINNT;C:/WINNT/system32;C:/WINNT;C:/WINNT/System32/Wbem;D:/Program Files/Microsoft Visual Studio/Common/Tools/WinNT;D:/Program Files/Microsoft Visual Studio/Common/MSDev98/Bin;D:/Program Files/Microsoft Visual Studio/Common/Tools;D:/Program Files/Microsoft Visual Studio/VC98/bin;D:/Java/jdk1.5.0_02/bin
------------------DLL Started--------------------------
拷贝前源
byte
数组内容为
:
你好我的祖国
--China
拷贝前目标
byte
数组内容为
:
DLL
函数返回值为
0
拷贝后源
byte
数组内容为
:
你好我的祖国
--China
拷贝后目标
byte
数组内容为
:
你好我
DLL
函数返回值为
0
DLL
调用开始
.
DLL
调用结束
.
这个例子中我们看到了函数传递参数的方法
.
限于篇幅,我们也不深入讨论了
.
C/C++
端的编程大家可以参照
jni.h
里的各种声明
,
我想熟悉
C/C++
的都可以看的明白
.
并很快熟练地掌握
.
你必须保证你的
C/C++
程序正确而且没有内存泄露,否则你的虚拟机将会捕捉到无法处理的异常而退出或者崩溃
.
你可能会花费很多时间才能找到错误
.
所以我们的程序必须经过严格的测试才能应用
.
还有一点提醒大家,与
C/C++
函数传递参数的时候请你对字符的转换一下,
因为
java
字符
unicode
编码的
,
在
C/C++
中有选择的
,
普通的是
ascii
码
,
你在实际开发过程中
,
请参照相关的
Java
下
unicode
解码编码的资料
.
附录一
javah
功能说明:
C
头文件和
Stub
文件生成器。
javah
从
Java
类生成
C
头文件和
C
源文件。这些文
件提供了连接胶合,使
Java
和
C
代码可进行交互。
语法:
javah [
命令选项
] fully-qualified-classname. . .
javah_g [
命令选项
] fully-qualified-classname. . .
补充说明:
javah
生成实现本地方法所需的
C
头文件和源文件。
C
程序用生成的头文件和源文件在
本地源代码中引用某一对象的实例变量。
.h
文件含有一个
struct
定义,该定义的布局
与相应类的布局平行。该
struct
中的域对应于类中的实例变量。
头文件名以及在头文件中所声明的结构名都来源于类名。如果传给
javah
的类是在某个
包中,则头文件名和结构名前都要冠以该包名。下划线
(_)
用作名称分隔符。
缺省情况下,
javah
为每个在命令行中列出的类都创建一个头文件,且将该文件放在当
前目录中。用
-stubs
选项创建源文件。用
-o
选项将所有列出类的结果串接成一个单
一文件。
缺省情况下,
javah
为每个在命令行中列出的类都创建一个头文件,且将该文件放在当
前目录中。用
-stubs
选项创建源文件。用
-o
选项将所有列出类的结果串接成一个单
一文件。
命令选项
-o[
输出文件
]
将命令行中列出的所有类的头文件或源文件串接到输出文件中。
-o
或
-
d
两个选项只能选择一个。
-d[
目录
]
设置
javah
保存头文件或
stub
文件的目录。
-d
或
-o
两个选项只能选择一
个。
-stubs
使
javah
从
Java
对象文件生成
C
声明。
-verbose
指明长格式输出,并使
javah
将所生成文件的有关状态的信息输出到标准输
出设备中。
-help
输出
javah
用法的帮助信息。
-version
输出
javah
的版本信息。
-jni
使
javah
创建一输出文件,该文件包含
JNI
风格的本地方法函数原型。这是缺省
输出,所以
-jni
的使用是可选的。
-classpath[
路径
]
指定
javah
用来查询类的路径。如果设置了该选项,它将覆盖缺省
值或
CLASSPATH
环境变量。目录用冒号分隔。
-bootclasspath[
路径
]
指定加载自举类所用的路径。缺省情况下,自举类是实现核心
Java
平台的类,位于
jrelib
t.jar
和
jrelibi18n.jar
中。
-old
指定应当生成旧
JDK1.0
风格的头文件。
-force
指定始终写输出文件
.