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

【0】README

1) 本文文字描述 均转自 core java volume 2 , 旨在理解 本地方法(JNI)——访问数组元素+错误处理 的基础知识 ;
2)for source code, please visit : https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter12/chapter12_8


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

1)元素类型:

  • 1.1)Object: Get/SetObjectArrayElement
  • 1.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 http://blog.csdn.net/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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331461.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Shell入门(三)之字符串

一、单引号 字符串可以用单引号&#xff0c;也可以用双引号&#xff0c;也可以不用引号。单双引号的区别跟PHP类似。 单引号不存在转义字符&#xff0c;如&#xff1a;\a&#xff0c;\n&#xff0c;$abc #!/bin/bash aabc b${a}bc; echo $b; #结果为&#xff1a;${a}bc…

tms tck_在雅加达EE TCK中使用Arquillian的可能方法

tms tck最近&#xff0c;我们讨论了如何创建独立的Jakarta Batch测试套件&#xff08;TCK&#xff09;。 对于大多数提交者而言&#xff0c;使用Arquillian将测试从实现中如何执行抽象化是很自然的。 但是Romain提出了一个有趣的想法&#xff0c;即使用纯JUnit5引起了我的思考。…

本地方法(JNI)——使用调用API

【0】README 1&#xff09; 本文文字描述source code 均转自 core java volume 2 &#xff0c; 旨在理解 本地方法&#xff08;JNI&#xff09;——使用调用API 的基础知识 &#xff1b; 2&#xff09; for source code, please visit : https://github.com/pacosonTang/cor…

Shell入门(四)之数组

一、一维数组 bash支持一维数组&#xff08;不支持多维数组&#xff09;&#xff0c;并且没有限定数组的大小。 类似与C语言&#xff0c;数组元素的下标由0开始编号。 二、定义数组 在Shell中&#xff0c;用括号来表示数组&#xff0c;数组元素用"空格"符号分割开。…

aws cloud map_销毁AWS资源:Cloud-Nuke还是AWS-Nuke?

aws cloud map因此&#xff0c;您在开发帐户上工作&#xff0c;并且Terraform陷入了一个循环&#xff0c;难道不让您轻易销毁剩余资源吗&#xff1f; 进入nuke CLI的世界&#xff01; 在撰写本文时&#xff0c;我使用的是v0.1.16版本 用Go语言编写的《 Gruntwork》不会破坏掉…

mysql error 1045 的解决方法

【0】README 1&#xff09;以下是 解决方法的steps&#xff1a; step1&#xff09;点击 skip 和 cancel 退出 mysql 配置 step2&#xff09; 重启mysql server config wizard&#xff0c; 然后 remove instance step3&#xff09; 随后卸载mysql&#xff08;通过360或 …

Shell入门(五)之参数

一、Shell 传递参数 在执行 Shell 脚本时&#xff0c;向脚本传递参数&#xff0c;脚本内获取参数的格式为&#xff1a;$n。n 代表一个数字&#xff0c;0为执行文件名&#xff0c;1 为执行脚本的第一个参数&#xff0c;2 为执行脚本的第二个参数&#xff0c;以此类推 比如&…

openj9下载_Quarkus on OpenJ9 JVM和资源消耗

openj9下载除了本机模式&#xff0c;Quarkus在JVM模式下也能很好地运行&#xff0c;这有其自身的优势。 您可以使用替代的JVM&#xff0c;例如OpenJ9&#xff0c;它可以更好地消耗资源。 在下面的视频中&#xff0c;我将展示交换JVM有多么容易。 在视频中&#xff0c;我在最新…

Shell入门(六)之算术运算

一、数学运算 &#xff08;1&#xff09;原生bash不支持简单的数学运算&#xff0c;但是可以通过其他命令来实现&#xff0c;例如 awk 和 expr&#xff0c;expr 最常用。 &#xff08;2&#xff09;expr 是一款表达式计算工具&#xff0c;使用它能完成表达式的求值操作。 ex…

java数据库编程——执行SQL 语句

【0】README 1&#xff09; 本文文字描述source code 均转自 core java volume 2 &#xff0c; 旨在理解 java数据库编程——执行SQL 语句 的基础知识 &#xff1b; 2&#xff09;for source code, please visit &#xff1a; https://github.com/pacosonTang/core-java-vol…

php cdi_Quarkus的其他(非标准)CDI功能

php cdiQuarkus支持CDI&#xff08;上下文和依赖注入&#xff09;2.0&#xff0c;但并非全部&#xff0c;仅支持最常见的功能。 但是&#xff0c;Quarkus确实包含一些非标准功能&#xff0c;这些功能对于开发人员非常方便&#xff0c;我想在下面的视频中进行展示。 不管您是否…

Shell入门(七)之关系运算

一、关系运算 shell关系运算符只支持数字&#xff0c;不支持字符串&#xff0c;除非字符串的值是数字。 a10 b20 运算符说明举例-eq检测两个数是否相等&#xff0c;相等返回 true。[ $a -eq $b ] 返回 false。-ne检测两个数是否相等&#xff0c;不相等返回 true。[ $a -ne $…

java数据库编程——Insert and Retrieve Images from MySql Table Using Java

【0】README0.1&#xff09;本文翻译自 http://harmeetsingh13.blogspot.jp/2013/03/insert-and-retrieve-images-from-mysql.html【1】正文如下&#xff1a;段1&#xff09;演示 从数据库表中插入和查询出图片。大多数情况下&#xff0c;图片数据都存储在数据库外部的一些文件…

Shell入门(八)之布尔运算

一、常规的布尔运算 常规的布尔运算符有&#xff1a;!、&&、|| 使用语法 ! exp exp && exp exp || exp [[ n op m && a op b]] ... exp为[ n op m ]或test n op m或true或false 二、条件测试的布尔运算 条件测试的布尔运算有&#xff1a;!、…

java switch语句_Java 14:查看更新的switch语句

java switch语句于2020年3月发布的JDK 14带有switch语句的更新版本。 这是JDK 12和JDK 13中的预览功能。 要了解差异&#xff0c;让我们看一个简单的示例。 假设我们要基于DayOfWeek枚举来计算每日工作时间。 使用旧的使用switch语句的方法&#xff0c;我们的解决方案可能如下…

java数据库编程——执行查询操作(一)

【0】README 1&#xff09; 本文部分文字描述和source code 均转自 core java volume 2 &#xff0c; 旨在理解 java数据库编程——执行查询操作 的基础知识 &#xff1b; 2&#xff09; 本文和 java数据库编程——执行查询操作&#xff08;二&#xff09; 是姊妹篇&#xff…

Shell入门(九)之字符串比较

一、字符串比较 字符串比较符&#xff1a;、!、-z、-n、str 实际上&#xff0c;shell不区分数值与字符串类型&#xff0c;数值也可以使用上面比较。 a"mk" b"maokun" 运算符说明举例检测两个字符串是否相等&#xff0c;相等返回 true。[ $a $b ] 返回…

java中将毫秒转换成时间_在Java中将时间单位转换为持续时间

java中将毫秒转换成时间java.util.concurrent.TimeUnit以给定的粒度单位表示Java中的持续时间&#xff0c;并提供跨单位转换的实用方法。 java.util.concurrent.TimeUnit最早是在Java早期&#xff08;1.5&#xff09;引入的&#xff0c;但自那时以来已经扩展了好几次。 在此博客…

java数据库编程——执行查询操作(二)

【0】README 1&#xff09; 本文部分文字描述和source code 均转自 core java volume 2 &#xff0c; 旨在理解 java数据库编程——执行查询操作&#xff08;二&#xff09; 的基础知识 &#xff1b; 2&#xff09; 本文和 java数据库编程——执行查询操作&#xff08;一&…

Shell入门(十)之echo

一、echo参数 echo [参数选项] 字符串 参数选项 -e 解析字符串中的转义字符&#xff0c;如\n -E 这是默认设置&#xff0c;不解析转义字符 -n 不输出换行&#xff0c;可以使用echo -e 字符串"\c" 代替 #!/bin/bash a"abc\n" echo $a echo -e…