Java Native Interface 二 JNI中对Java基本类型和引用类型的处理
本文是《The Java Native Interface Programmer’s Guide and Specification》读书笔记
Java编程里会使用到两种类型:基本类型(如int,float等)和引用类型(如class,instance,arrays),JNI编程里对这两种的类型的处理方法也是不一样的。
JNI定义了与Java的基本类型和引用类型相对应的C/C++类型:
| Java类型 | 本地类型(JNI中使用) |
|---|---|
| boolean | jboolean |
| int | jint |
| float | jfloat |
| byte | jbyte |
| char | jchar |
| short | jshort |
| long | jlong |
| double | jdouble |
Java中所有对象都是Object类的子类,在JNI中与之对应的为jobject类,所有的类型都是jobject类的子类,直接继承自jobject 的引用类型:
| Java类型 | 本地类型 |
|---|---|
| java.lang.Class | jclass |
| java.lang.String | jstring |
| arrays | jarray |
| java.lang.Throwable | jthrowable |
其中数组又会有其他的子类型,如Java中的int[] 对应到JNI中为jintArray,Object[]对应为jobjectArray[]等;
在使用C++时,类的继承的语法为
class _jobjct{};
class _jclass:public_jobjct{};//公有继承
同时在JNI中会使用类标识符来表示Java中的类或接口。数组用'['来标识,如int[]在JNI中用'[I'来标识,三维数组double[][][],用['[[[D'来标识;下面是JNI中常用基本类型的标识符与对应的Java类:
| 成员域标识符 | Java 类型 |
|---|---|
| Z | boolean |
| B | byte |
| C | char |
| S | short |
| I | int |
| J | long |
| F | float |
| D | double |
JNI中引用类型的标识符以字符'L'开始,并以';'结束,但数组的标识符与前面的基本类型的标识符相同;
| 标识符 | Java类型 |
|---|---|
| "Ljava/lang/String;" | String |
| "[I" | int[] |
| "[Ljava/lang/Object;" | Object[] |
在JNI中方法的标识符里,方法所含的参数之间是没有空格隔开的,并用字符"V"作为void方法的返回标识;构造函数一般使用"V"作为返回值,"init"作为名字;
| 方法标识符 | Java方法 |
|---|---|
| "()Ljava/lang/String; " | String f() |
| "(ILjava/lang/Class;)J" | long f(int i,Class c) |
| "([B)V" | String(byte[]bytes) |
在了解完JNI中对应的类型与标识符后,接下来就是怎样使用它们了。
String类型的使用
在实现本地方法时,必须使用JNI的方法将对应的jstring对象类型转型为c/c++可以识别的string 对象,否则在调用这个本地方法时会导致Java虚拟机崩溃;
假设有一个本地方法,在Java里声明为:
public native String getLine(String promt);
则你在实现这个方法时,不能这样写:
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
/* ERROR: incorrect use of jstring as a char* pointer */
printf("%s", prompt);
...
}
你可以这样写:
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const jbyte *str;
str = (*env)->GetStringUTFChars(env, prompt, NULL);
//必须要记得检查JNI方法的返回值,因为JVM在执行时需要分配内存,可能存在内存分配失败的情况
if (str == NULL) {
return NULL; /* OutOfMemoryError already thrown */
}
printf("%s", str);
//当使用完JNI方法返回的对象后,调用相应的Release方法释放掉对应的内 存,忘记使用Release方法可能会导致内存泄漏
(*env)->ReleaseStringUTFChars(env, prompt, str);
/* We assume here that the user does not type more than
* 127 characters */
scanf("%s", buf);
return (*env)->NewStringUTF(env, buf);
}
jni中的可以对String(jstring)对象使用的方法总结:
| 方法名 | 方法描述 |
|---|---|
| GetStringChars,ReleaseStringChars | 获取或释放一个指向Unicode编码的字符串内容的指针,可能会返回一个字符串的拷贝 |
| GetStringUTFChars,ReleaseStringUTFChars | 获取或释放一个指向UTF-8编码的字符串内容的指针,可能会返回一个字符串的拷贝 |
| GetStrinLength | 返回字符串里Unicode编码的字符的个数 |
| GetStringUTFLength | 返回字符串里UTF-8编码的字符的个数 |
| NewString | 创建一个包含给定的Unicode编码的C字符串的所有字符的java.lang.String实例 |
| NewStringUTF | 创建一个包含给定的UTF-8编码的C字符串的所有字符的java.lang.String实例 |
| GetStringCritical,ReleaseStringCritical | 获取一个指向Unicode编码的字符串内容的指针,可能返回一个字符串的拷贝。并且本地代码在调用Get/ReleaseStrinCritical这对方法时,不能阻塞。 |
| GetStringUTFRegion,SetStringUTFRegion | 从一个预先分配的UTF-8格式的C的缓冲区拷贝字符串内容(或将字符串内容写入缓冲区中) |
Arrays类型的使用
JNI对普通数组(基本类型的数组,如int[],float[])和对象数组的处理方法是不一样的。下面以简单的例子来说明:
1 普通的数组的使用
求一个数组的和的例子:Java Class为:
class IntArray {
private native int sumArray(int[] arr);//jni方法,求数组的和
public static void main(String[] args) {
IntArray p = new IntArray();
int arr[] = new int[10];
for (int i = 0; i < 10; i++) {
arr[i] = i;
}
int sum = p.sumArray(arr);
System.out.println("sum = " + sum);
}
static {
System.loadLibrary("IntArray");
}
}
Java的Arrays在JNI中用jarrays和它的子类,如jintArray来表示,就像jstring不是C字符串的类型,jarrays也不是C的数组类型,因此,你不能在实现JNI方法时,直接使用jarrays的引用,下面是错误的JNI方法的编写:
JNIEXPORT jint JNICALL
Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
int i, sum = 0;
for (i = 0; i < 10; i++) {
sum += arr[i];
}
}
正确的JNI的方法应该为:
JNIEXPORT jint JNICALL
Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
jint buf[10];
jint i, sum = 0;
(*env)->GetIntArrayRegion(env, arr, 0, 10, buf);
for (i = 0; i < 10; i++) {
sum += buf[i];
}
return sum;
}
也可以这样写:
JNIEXPORT jint JNICALL
Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
jint *carr;
jint i, sum = 0;
carr = (*env)->GetIntArrayElements(env, arr, NULL);
if (carr == NULL) {
return 0; /* exception occurred */
}
for (i=0; i<10; i++) {
sum += carr[i];
}
(*env)->ReleaseIntArrayElements(env, arr, carr, 0);
return sum;
}
JNI中可以对基本类型数组使用的方法有:
<类型>:表示需要用基本类型来替代,如Get<类型>ArrayRegion,就可以有GetIntArrayRegion,GetFloatArrayRegion等;
| 方法名 | 方法描述 |
|---|---|
| Get<类型>ArrayRegion,Set<类型>ArrayRegion | 得到基本类型的数组的内容,或将内容写入预先分配的C的缓冲区里 |
| Get<类型>ArrayElements,Release<类型>ArrayElements | 获得一个指向基本类型数组的内容的指针,可能会返回一个数组的拷贝 |
| GetArrayLength | 返回数组的大小 |
| New<类型>Array | 创建一个给定长度的数组 |
| GetPrimitiveArrayCritical,ReleasePrimitiveArrayCritical | 获得或释放指向基本类型数组的内容的指针 |
2 对象数组的使用:
JNI提供了许多对方法来得到对象数组。GetObjectElement方法会返回一个给定位置的元素,SetObjectElement方法更新给定位置的元素。但我们不能一次性就得到(复制)所有的对象数组里的元素。下面还是以简单的例子来说明:
Java Class为:
class ObjectArrayTest {
//本地方法,用来生成一个给定大小的二维矩阵
private static native int[][] initInt2DArray(int size);
public static void main(String[] args) {
int[][] i2arr = initInt2DArray(3);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
System.out.print(" " + i2arr[i][j]);
}
System.out.println();
}
}
static {
System.loadLibrary("ObjectArrayTest");
}
}
本地方法的实现为:
JNIEXPORT jobjectArray JNICALL
Java_ObjectArrayTest_initInt2DArray(JNIEnv *env,
jclass cls,
int size)
{
jobjectArray result;
int i;
//获得一个元素类型(Int)的Class引用,类似于Java中得到某一类型的类加载器
jclass intArrCls = (*env)->FindClass(env, "[I");
if (intArrCls == NULL) {
return NULL; /* exception thrown */
}
//生成一个元素类型为intArrCls的数组,这个方法只能生成一维数组
result = (*env)->NewObjectArray(env, size, intArrCls,
NULL);
if (result == NULL) {
return NULL; /* out of memory error thrown */
}
for (i = 0; i < size; i++) {
jint tmp[256]; /* make sure it is large enough! */
int j;
//生成数组里存放的元素(仍然为一个数组)
jintArray iarr = (*env)->NewIntArray(env, size);
if (iarr == NULL) {
return NULL; /* out of memory error thrown */
}
for (j = 0; j < size; j++) {
tmp[j] = i + j;
}
//用tmp里的元素填充iarr数组,使用的是基本类型数组的方法
(*env)->SetIntArrayRegion(env, iarr, 0, size, tmp);
//将iarr作为数组元素填充到result数组相应位置中,使用的是对象数 组的方法
(*env)->SetObjectArrayElement(env, result, i, iarr);
//释放到对iarr对象的引用
(*env)->DeleteLocalRef(env, iarr);
}
//二维数组就是数组里存放的元素仍然为数组,返回一个对象数组
return result;
}
Java Native Interface 二 JNI中对Java基本类型和引用类型的处理的更多相关文章
- Java Native Interface 四--JNI中引用类型
本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 JNI支持将类实例和数组类型(如jobjec ...
- Java Native Interface 六JNI中的异常
本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 在这里只讨论调用JNI方法可能会出现的异常, ...
- Java Native Interface Specification(JNI)
Java Native Interface Specification(JNI) 使用场景: 需要的功能,标准的java不能提供 有了一个用其他的语言写好的工具包,希望用java去访问它 当需要高性能 ...
- Java Native Interface 五 JNI里的多线程与JNI方法的注册
本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 JNI里的多线程 在本地方法里写有关多线程的 ...
- Java Native Interface 基于JNI的嵌入式手机软件开发实例
1.通过JNI和c/c++的库组件.其他代码交互 2.java和c不能互通的原因时数据类型问题 Introduction https://docs.oracle.com/javase/8/docs/t ...
- Java Native Interface(JNI)
JNI能让Java代码在Java虚拟机里调用其他编程语言(例如C.C++)写的应用或库,且不会影响任何Java虚拟机的实现. 什么时候用JNI? 1.应用程序所需的平台相关功能,标准的Java类库不支 ...
- js中函数参数基本类型和引用类型的区别
高级程序设计中说明,所有函数的参数都是按值传递的. 基本类型 向参数传递基本类型的值时,被传递的值会被复制给对应的命名参数 function addTen(num){ num=+10; return ...
- 【详解】JNI (Java Native Interface) (二)
案例二:传递参数给C代码,并从其获取结果 注:这里传递的参数是基本类型的参数,在C代码中有直接的映射类型. 此案例所有生成的所有文件如下: (1)编写案例二的Java代码,如下: 这里我们定义了一个n ...
- +Java中的native关键字浅析(Java+Native+Interface)++
JNI是Java Native Interface的 缩写.从Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的 ...
随机推荐
- vi快捷键
/** * eclipse内置快捷: * * * 导入包:Ctrl+Shift+O * Ctrl+T 查看一个类的继承关系树,是自顶向下的,再多按一次Ctrl+T, 会换成自底向上的显示结构. 提示: ...
- Linux进程管理子系统分析【转】
本文转载自:http://blog.csdn.net/coding__madman/article/details/51298732 Linux进程管理: 进程与程序: 程序:存放在磁盘上的一系列代码 ...
- C#汉字转拼音(npinyin)将中文转换成拼音全文或首字母
汉字转拼音貌似一直是C#开发的一个难题,无论什么方案都有一定的bug,之前使用了两种方案. 1.Chinese2Spell.cs 一些不能识别的汉字全部转为Z 2.Microsoft Visual S ...
- Jumony Core 3,真正的HTML引擎,正式版发布
Jumony是一个开源项目,已经有三年的历史了,在这三年中,秉承提供给.NET程序员完整的HTML掌控能力,Jumony历经无数次的改进,终于进入了一个新的阶段.Jumony Core 3是一个真正意 ...
- C++链表
之前用C写链表的时候,结点使用结构体封装的,操作起来总是感觉很麻烦.C++中使用类来封装结点,感觉操作起来方便多了,内存回收也感觉很清楚. 最近,做Gps数据分析时,别人要求加一个树形控件. Gps数 ...
- js ie中实现拖拽
获取鼠标移动信息 开始我们需要获取鼠标的坐标.我们添加一个document.onmousemove 就可以达到此目的: Javascript: document.onmousemove = mou ...
- Cocos2d-X3.0 刨根问底(四)----- 内存管理源码分析
本系列文章发表以来得到了很多朋友的关注,小鱼在这里谢谢大家对我的支持,我会继续努力的,最近更新慢了一点,因为我老婆流产了抽了很多时间来照顾她希望大家谅解,并在此预祝我老婆早日康复. 上一篇,我们完整的 ...
- cxf设置代理访问webservice接口
由于业务上的需要,需要访问第三方提供的webservice接口,但由于公司做了对外访问的限制,不设置代理是不能外网的,如果使用http设置代理访问外网还是比较容易的,但使用cxf有点不知道从哪里入手. ...
- transform
{ transform: scale3d(x,y,z) /*放大*/ translate3d(x,y,z) /*位置*/ rotate3d(x,y,z,angle) /*旋转*/ skew(x-ang ...
- JavaScript学习1
http://blog.csdn.net/lilongsheng1125/article/details/8479391 数据类型 1.基础数据类型 数值型.字符串型.逻辑型.undefined.nu ...