300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 本地方法(JNI)——访问数组元素+错误处理

本地方法(JNI)——访问数组元素+错误处理

时间:2023-12-13 07:15:59

相关推荐

本地方法(JNI)——访问数组元素+错误处理

【0】README

1)本文文字描述 均转自 core java volume 2 , 旨在理解本地方法(JNI)——访问数组元素+错误处理的基础知识 ;

2)for source code, please visit :/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter12/chapter12_8

【1】本地方法(JNI)——访问数组元素

1)元素类型:

1.1)Object:Get/SetObjectArrayElement1.2)基本类型:Get/SetXxxArrayElement + ReleaseXxxArrayElements

2) java 编程语言的所有数组类型都有相应的 C 语言类型, 如表12-2所示:

2.1)GetArrayLength 函数:返回数组的长度;

jarray array = ......;jsize length = (*env)->GetArrayLength(env, array);

2.2)怎样访问数组元素:这取决于数组中存储的是对象还是基本类型的数据

2.3)可以通过 GetObjectArrayElement 和 SetObjectArrayElement 方法:访问对象数组的元素;

jobjectArray array = ....;jobject x = (*env)->GetObjectArrayElement(env, array, i);(*env)->SetObjectArrayElement(env, array, j, x);以上方法效率非常低下;

2.4)GetXxxArrayElement函数:返回一个指向数组起始元素的C 指针;

2.5)ReleaseXxxArrayElements 函数:当你不再需要改指针时, 必须记得要调用 ReleaseXxxArrayElements 函数 通知虚拟机;

Attention)

A1)这里的 Xxx 必须是基本类型,不能是Object;A2)由于指针可能会指向一个 副本, 只有调用相应的 ReleaseXxxArrayElements 函数时, 你所做的改变才能保证在源数组里得到反映;

3)看个荔枝:下面是对double 类型数组中的所有元素乘以一个常量的示例代码。 我们获取一个 java 数组的C 指针a, 并用 a[i] 访问各个元素;

jdoubleArray array = ...;double scale = ...;double *a = (*env)->GetDoubleArrayElements(env, array_a, NULL);for(i=0; i<(*env)->GetArrayLength(env, array_a); i++)a[i] = a[i] * scale;(*env)->ReleaseDoubleArrayElements(env, array_a, a, 0);

4)虚拟机是否确实需要对数组进行拷贝:这取决于他是如何分配数组和如何进行垃圾回收的。 有些拷贝型的垃圾回收器例行进行移动对象,并更新对象引用;

5)该策略与 将数组锁定在 特定位置是不兼容的, 因为回收器不能更新本地代码中的指针值;

6) GetXxxArrayRegion和 SetXxxArrayRegion 函数:能把一定范围内的元素从 java 数组复制到 C 数组中或从 C 数组复制到 java 数组中;

7)NewXxxArray 函数:该函数在本地方法中创建新的 java 数组;

【2】错误处理

1)problem+solution:

1.1)problem:C的运行期系统对数组越界错误, 不良指针造成的间接错误等不提供任何防护;而C语言没有异常;

1.2)solution:必须调用Throw 或 ThrowNew 函数来创建一个新的异常对象。 当本地方法退出时, java 虚拟机就会抛出该异常;

2)NewObject 方法:要使用 Throw函数,就需要使用 NewObject 来创建一个 Throwable 子类的对象。

3)看个荔枝:我们分配了一个EOFException 对象,然后将其抛出:

jclass class_EOFException = (*env)->FindClass(env, "java/io/EOFException"); //获取类;jmethodID id_EOFException = (*env)->GetMethodID(env, class_EOFException , "<init>", "()V"); //获取方法标识符;jthrowable obj_exc = (*env)->NewObject(env, class_EOFException ,id_EOFException); // 创建一个 Throwable 子类对象;(*env)->Throw(env, obj_exc); //抛出异常;

4)通常调用ThrowNew 会更加方便:因为只需要提供一个类和一个 “改良UTF-8”字节序列, 该函数就会构建一个异常对象;

> (*env)->ThrowNew(env, (*env)->FindClass(env, "java/io/EOFException"), "Unexpected end of file");

5) Throw 和 ThrowNew 都仅仅只是发布异常, 他们不会中断本地方法的控制流。只有当该方法返回时, java 虚拟机才会抛出异常。所以,每一个对 Throw 和 ThrowNew 的调用语句之后总是紧跟着 return 语句;(干货——Throw 和 ThrowNew 都仅仅只是发布异常, 他们不会中断本地方法的控制流。)

6)problem+solution:

6.1)problem:通常, 本地代码不需要考虑捕获java 异常。 但是,当本地方法调用java 方法时, 该方法可能会抛出异常;6.2)solution:在这类情况下, 本地方法应该调用 ExceptionOccured 方法来确认是否有异常抛出。 如果没有任何异常被挂起, 则下面的调用返回 NULL, 否则返回一个当前异常对象 的引用;

jthrowable obj_exc = *(env)->ExceptionOccurred(env);

6.3)如果只要检查是否有异常抛出:调用, jboolean occured = (*env)->ExceptionCheck(env);

7)本地方法处理异常

7.1)确定异常是否能够处理,如果能够处理, 必须调用下面的函数来关闭该异常:

(*env)->ExceptionClear(env);

7.2) 在我们的荔枝中, 我们实现了 fprint 本地方法, 这是基于该方法适合编写为本地方法的假设而实现的。下面是我们抛出的异常(exceptions):

e1)如果格式字符串是NULL, 则抛出 空指针异常;e2)如果格式字符串不含适合打印 double 所需的 % 说明符, 则抛出 IllegalArgumentException异常;

e3)如果调用malloc 失败, 则抛出 OutOfMemoryException;

Attention)本文给出的荔枝只po 了最后C语言抛出异常的结果, 没有将java 调用本地方法的steps 全部po出来。 因为博主我已经po这个steps , 都po厌烦了,我的本地(JNI)博文中有相应的 steps;for detailed steps , please visit /pacosonswjtu/article/details/50618022

8) 本地方法的C语言实现(source code at a glance , maybe you should attend for annotations below)

#include "Printf4.h"#include <string.h>#include <stdlib.h>#include <float.h>/**@param format a string containing a printf format specifier(such as "%8.2f"). Substrings "%%" are skipped.@return a pointer to the format specifier (skipping the '%')or NULL if there wasn't a unique format specifier*/char* find_format(const char format[]){ char* p;char* q;p = strchr(format, '%');while (p != NULL && *(p + 1) == '%') /* skip %% */p = strchr(p + 2, '%');if (p == NULL) return NULL;/* now check that % is unique */p++;q = strchr(p, '%');while (q != NULL && *(q + 1) == '%') /* skip %% */q = strchr(q + 2, '%');if (q != NULL) return NULL; /* % not unique */q = p + strspn(p, " -0+#"); /* skip past flags */q += strspn(q, "0123456789"); /* skip past field width */if (*q == '.') { q++; q += strspn(q, "0123456789"); }/* skip past precision */if (strchr("eEfFgG", *q) == NULL) return NULL;/* not a floating-point format */return p;}JNIEXPORT void JNICALL Java_Printf4_fprint(JNIEnv* env, jclass cl, jobject out, jstring format, jdouble x){ const char* cformat;char* fmt;jclass class_PrintWriter;jmethodID id_print;char* cstr;int width;int i;if (format == NULL) { (*env)->ThrowNew(env, /* ThrowNew 仅仅是发布异常,而不是抛出异常,当函数返回时才会抛出异常 */(*env)->FindClass(env,"java/lang/NullPointerException"),"Printf4.fprint: format is null");return;}/* 创建给定格式的字符串 */cformat = (*env)->GetStringUTFChars(env, format, NULL);fmt = find_format(cformat);if (fmt == NULL){ (*env)->ThrowNew(env, /* ThrowNew 的作用同上 */(*env)->FindClass(env,"java/lang/IllegalArgumentException"),"Printf4.fprint: format is invalid");return;}width = atoi(fmt);if (width == 0) width = DBL_DIG + 10;cstr = (char*)malloc(strlen(cformat) + width);if (cstr == NULL){ (*env)->ThrowNew(env,(*env)->FindClass(env, "java/lang/OutOfMemoryError"),"Printf4.fprint: malloc failed");return;}sprintf(cstr, cformat, x);/* 当你不再需要改指针时, 必须记得要调用 ReleaseXxxArrayElements 函数 通知虚拟机; */(*env)->ReleaseStringUTFChars(env, format, cformat);/* now call ps.print(str) *//* get the class ==获取类 */class_PrintWriter = (*env)->GetObjectClass(env, out);/* get the method ID == 获取方法标识 */id_print = (*env)->GetMethodID(env, class_PrintWriter, "print", "(C)V");/* call the method == 通过方法标识调用方法 */for (i = 0; cstr[i] != 0 && !(*env)->ExceptionOccurred(env); i++)(*env)->CallVoidMethod(env, out, id_print, cstr[i]);free(cstr);}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。