转载请注明出处:http://blog.csdn.net/xyang81/article/details/45770551

异常简介

异常,显而意见就是程序在运行期间没有按照正常的程序逻辑执行,在执行过程当中出现了某种错误,导致程序崩溃。在Java中异常分为运行时异常(RuntimeException)和编译时异常,在程序中有可能运行期间发生异常的逻辑我们会用try…catch…来处理,如果没有处理的话,在运行期间发生异常就会导致程序奔溃。而编译时异常是在编译期间就必须处理的。本章主要介绍运行时异常。 
示例1:

// 运行时异常
public static void exceptionCallback() {int a = 20 / 0;System.out.println("--->" + a);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

示例2:

// 编译期间异常
public static void testException() throws Exception {// ...System.out.println("testException() invoked!");
}
public static void main(String[] args) {exceptionCallback(); try {testException();} catch (Exception e) {e.printStackTrace();}// ....
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
在示例2中,testException方法声明时显示抛出了一个java.lang.Exception异常,所以在程序调用的地方必须用try...catch处理。

大家都知道,如果示例2中main方法执行到调用exceptionCallback方法时,方法第一行有一个除0的操作,因此该方法会抛出java.lang.ArithmeticException数学异常,而在main方法调用的时候并没有处理这个函数在运行时可能会发生的异常,所以会导致程序立即结束,而后面的代码try{testException();}catch(Exception e) {e.printStackTrace();}都不会被执行。运行示例2程序的你会看到下面的结果:

Exception in thread "main" java.lang.ArithmeticException: / by zeroat com.study.jnilearn.JNIException.exceptionCallback(JNIException.java:8)at com.study.jnilearn.JNIException.main(JNIException.java:22)
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

我们改进一下上面这个程序:

public static void main(String[] args) {try {exceptionCallback();} catch (Exception e) {e.printStackTrace();}try {testException();} catch (Exception e) {e.printStackTrace();}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这时我们运行程序,调用exceptionCallback方法时会引发java.lang.ArithmeticException: / by zero异常,由于我们用try…catch块显示处理了异常,所以程序会继续往下执行,调用testException()函数,打印testException() invoked!。运行结果如下所示:

java.lang.ArithmeticException: / by zeroat com.study.jnilearn.JNIException.exceptionCallback(JNIException.java:8)at com.study.jnilearn.JNIException.main(JNIException.java:24)
testException() invoked!
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

Java与JNI处理异常的区别

下面来小结一下: 
1、在Java中如果觉得某段逻辑可能会引发异常,用try…catch机制来捕获并处理异常即可 
2、如果在Java中发生运行时异常,没有使用try…catch来捕获,会导致程序直接奔溃退出,后续的代码都不会被执行 
3、编译时异常,是在方法声明时显示用throw声明了某一个异常,编译器要求在调用的时候必须显示捕获处理 
public static void testException() throws Exception {} 
上面这几点,写过Java的朋友都知道,而且很简单,但我为什么还要拿出来说呢,其实我想重点说明的是,在JNI中发生的异常和Java完全不一样。我们在写JNI程序的时候,JNI没有像Java一样有try…catch…final这样的异常处理机制,面且在本地代码中调用某个JNI接口时如果发生了异常,后续的本地代码不会立即停止执行,而会继续往下执行后面的代码。

异常处理示例

示例3: 
这个例子在main中调用了doit本地方法,在本地方法中会回调exceptionCallback方法,该方法中会引发一个除0的运行时异常java.lang.ArithmeticException,我们通过这个示例来学习在JNI中如何来正确处理这种异常。

package com.study.jnilearn;public class JNIException {public static native void doit();public static void exceptionCallback() {int a = 20 / 0;System.out.println("--->" + a);}public static void normalCallback() {System.out.println("In Java: invoke normalCallback.");}public static void main(String[] args) {doit();}static {System.loadLibrary("JNIException");}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_study_jnilearn_JNIException */#ifndef _Included_com_study_jnilearn_JNIException
#define _Included_com_study_jnilearn_JNIException
#ifdef __cplusplus
extern "C" {
#endif
/** Class:     com_study_jnilearn_JNIException* Method:    doit* Signature: ()V*/
JNIEXPORT void JNICALL Java_com_study_jnilearn_JNIException_doit(JNIEnv *, jclass);#ifdef __cplusplus
}
#endif
#endif// JNIException.c
#include "com_study_jnilearn_JNIException.h"
#include <stdio.h>JNIEXPORT void JNICALL Java_com_study_jnilearn_JNIException_doit(JNIEnv *env, jclass cls) {jthrowable exc = NULL;jmethodID mid = (*env)->GetStaticMethodID(env,cls,"exceptionCallback","()V");if (mid != NULL) {(*env)->CallStaticVoidMethod(env,cls,mid);}printf("In C: Java_com_study_jnilearn_JNIException_doit-->called!!!!");if ((*env)->ExceptionCheck(env)) {  // 检查JNI调用是否有引发异常(*env)->ExceptionDescribe(env);(*env)->ExceptionClear(env);        // 清除引发的异常,在Java层不会打印异常的堆栈信息(*env)->ThrowNew(env,(*env)->FindClass(env,"java/lang/Exception"),"JNI抛出的异常!");//return;}mid = (*env)->GetStaticMethodID(env,cls,"normalCallback","()V");if (mid != NULL) {(*env)->CallStaticVoidMethod(env,cls,mid);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

程序运行结果如下:

Exception in thread "main" java.lang.ArithmeticException: / by zeroat com.study.jnilearn.JNIException.exceptionCallback(JNIException.java:8)at com.study.jnilearn.JNIException.doit(Native Method)at com.study.jnilearn.JNIException.main(JNIException.java:17)
Exception in thread "main" java.lang.Exception: JNI抛出的异常!at com.study.jnilearn.JNIException.doit(Native Method)In Java: invoke normalCallback.at com.study.jnilearn.JNIException.main(JNIException.java:17)
In C: Java_com_study_jnilearn_JNIException_doit-->called!!!!
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在Main方法中调用doit本地方法后,程序的控制权即交给了JNI,在doit的本地方法中回调exceptionCallback方法,引发了一个java.lang.ArithmeticException异常,但本地接口并不会马上退出,而是会继续执行后面的代码,所以我们在调用完一个任何一个JNI接口之后,必须要做的一件事情就是检查这次JNI调用是否发生了异常,如果发生了异常不处理,而继续让程序执行后面的逻辑,将会产生不可预知的后果。在本例中,我们调用了JNI的ExceptionCheck函数检查最近一次JNi调用是否发生了异常,如果有异常这个函数返回JNI_TRUE,否则返回JNI_FALSE。当检测到异常时,我们调用ExceptionDescribe函数打印这个异常的堆栈信息,然后再调用ExceptionClear函数清除异常堆栈信息的缓冲区(如果不清除,后面调用ThrowNew抛出的异常堆栈信息会覆盖前面的异常信息),最后调用ThrowNew函数手动抛出一个java.lang.Exception异常。但在JNI中抛出未捕获的异常与Java的异常处理机制不一样,在JNI中并不会立即终止本地方法的执行,而是继续执行后面的代码。这种情况需要我们手动来处理。在例中的38行,如果你不用return马上退出方法的话,37行ThrowNew后面的代码依然会继续执行,如程序运行的结果一样,仍然会回调normalCallback方法,打印出:invoke normalCallback. 
异常检查JNI还提供了另外一个接口,ExceptionOccurred,如果检测有异常发生时,该函数会返回一个指向当前异常的引用。作用和ExceptionCheck一样,两者的区别在于返回值不一样。我们改造一下上面的例子:

// ....
jthrowable exc = NULL;
exc = (*env)->ExceptionOccurred(env);  // 返回一个指向当前异常对象的引用
if (exc) {(*env)->ExceptionDescribe(env); // 打印Java层抛出的异常堆栈信息(*env)->ExceptionClear(env);        // 清除异常信息// 抛出我们自己的异常处理jclass newExcCls;newExcCls = (*env)->FindClass(env,"java/lang/Exception");if (newExcCls == NULL) {return;}(*env)->ThrowNew(env, newExcCls, "throw from C Code.");
}// ....
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

写一个抛出异常的工具类

当需要抛出自己的异常处理逻辑时,需要二步,调用FindClass找到异常处理类,然后调用ThrowNew抛出一个异常。为了简化操作步聚,我们写一个工具函数,根据一个异常类名专门用来生成一个指定名字的异常:

void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg){// 查找异常类jclass cls = (*env)->FindClass(env, name);/* 如果这个异常类没有找到,VM会抛出一个NowClassDefFoundError异常 */if (cls != NULL) {(*env)->ThrowNew(env, cls, msg);  // 抛出指定名字的异常}/* 释放局部引用 */(*env)->DeleteLocalRef(env, cls);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

异常发生后释放资源

在异常发生后,释放资源是一件很重要的事情。下面的例子中,调用 GetStringChars 函数后,如果后面的代码发生异常,要记得调用 ReleaseStringChars 释放资源。

JNIEXPORT void JNICALL Java_pkg_Cls_f(JNIEnv *env, jclass cls, jstring jstr){const jchar *cstr = (*env)->GetStringChars(env, jstr);if (c_str == NULL) {return; }...if ((*env)->ExceptionCheck(env)) { /* 异常检查 */(*env)->ReleaseStringChars(env, jstr, cstr); // 发生异常后释放前面所分配的内存return; }.../* 正常返回 */(*env)->ReleaseStringChars(env, jstr, cstr);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

总结

1、当调用一个JNI函数后,必须先检查、处理、清除异常后再做其它 JNI 函数调用,否则会产生不可预知的结果。 
2、一旦发生异常,立即返回,让调用者处理这个异常。或 调用 ExceptionClear 清除异常,然后执行自己的异常处理代码。 
3、异常处理的相关JNI函数总结: 
1> ExceptionCheck:检查是否发生了异常,若有异常返回JNI_TRUE,否则返回JNI_FALSE 
2> ExceptionOccurred:检查是否发生了异常,若用异常返回该异常的引用,否则返回NULL 
3> ExceptionDescribe:打印异常的堆栈信息 
4> ExceptionClear:清除异常堆栈信息 
5> ThrowNew:在当前线程触发一个异常,并自定义输出异常信息 
jint (JNICALL *ThrowNew) (JNIEnv *env, jclass clazz, const char *msg); 
6> Throw:丢弃一个现有的异常对象,在当前线程触发一个新的异常 
jint (JNICALL *Throw) (JNIEnv *env, jthrowable obj); 
7> FatalError:致命异常,用于输出一个异常信息,并终止当前VM实例(即退出程序) 
void (JNICALL *FatalError) (JNIEnv *env, const char *msg);

JNI/NDK开发指南(十一)——JNI异常处理相关推荐

  1. JNI/NDK开发指南(八)——调用构造方法和父类实例方法

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/44002089 在第6章我们学习到了在Native层如何调用Java静态方法和实例方法,其中 ...

  2. JNI/NDK开发指南(三)——JNI数据类型及与Java数据类型的映射关系

    2019独角兽企业重金招聘Python工程师标准>>> 转载请注明出处:http://blog.csdn.net/xyang81/article/details/42047899 当 ...

  3. JNI/NDK开发指南(二)——JVM查找java native方法的规则

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/41854185 通过第一篇文章,大家明白了调用native方法之前,首先要调用System. ...

  4. JNI/NDK入门指南之jobject和jclass

          JNI/NDK入门指南之jobject和jclass Android JNI/NDK入门指南目录 JNI/NDK入门指南之正确姿势了解JNI和NDK JNI/NDK入门指南之JavaVM和 ...

  5. 【我的C语言学习进阶之旅】介绍一下NDK开发中关于JNI函数的两种注册方式:静态注册和动态注册

    目录 一.要介绍本篇博客的原因 二.静态注册 2.1 实现原理 2.2 实现过程 2.3 弊端 2.4 示例 三.动态注册 3.1 实现原理 3.2 实现过程 3.3 优点 3.4 示例 一.要介绍本 ...

  6. Android studio下JNI(NDK)开发

    玩智能手机的都说android手机体验.流畅差苹果太远了,一方面是苹果的硬件确实牛逼,另一个原因在于开发语言上的选择,苹果使用了Objective-C来开发,而android使用了java.程序员都知 ...

  7. Android学Jni/Ndk 开发记录(一)

    治疗拖延症的唯一办法就是:一想起些什么 / 要做些什么就 TM 立马去做! 是的,突然想起我不会 JNI.NDK 开发.解决办法:立马去学! 一:配置 NDK 环境 下载 NDK 写入到配置文件 二: ...

  8. 安卓jni(ndk)开发实战(2)开发jni第一个HellowWorld程序

    一.前言 本文将创建一个 HelloWorld 程序,程序功能很简单,就是一个 jni 方法返回"HelloWorld "字符串,然后界面展示. jni 中涉及动态注册和静态注册, ...

  9. jni java共享变量_JNI/NDK开发指南(七)——C/C++访问Java实例变量和静态变量 .

    在上一章中我们学习到了如何在本地代码中访问任意Java类中的静态方法和实例方法,本章我们也通过一个示例来学习Java中的实例变量和静态变量,在本地代码中如何来访问和修改.静态变量也称为类变量(属性), ...

最新文章

  1. 017_html图像
  2. vs2017编译QT with ssl
  3. 二进制枚举子集 CS Maxor 或运算,DP(SOS)
  4. 蓝桥杯 ADV-131算法提高 选择排序
  5. 七月在线python数据分析_七月在线Python数据分析笔记
  6. 软件人员kpi制定模板_软件科技公司绩效考核办法模板.doc
  7. 服装计算机辅助设计(CAD)技能证书
  8. android开发环境搭建——android studio
  9. Google Code checkout v8 方法
  10. The requested URL was not found on this serve
  11. YOLOX-PAI: An Improved YOLOX, Stronger and Faster than YOLOv6
  12. 勇士大战恶魔?这款桌游明明是套高质量原创手办
  13. Vue从入门到放弃(一)——指令篇
  14. 苹果电脑重置登录密码
  15. 关于root 联想ZUK z2 遇到的一系列问题
  16. JS新版激流勇进附地图详解
  17. 【Ubuntu系统重启以后串口驱动丢失的问题】
  18. 操作系统学习笔记——北京大学陈向群老师课后及习题答案(1)
  19. 多普达838,在关机状态下硬启的方法.
  20. 读书笔记-哈佛大学极简经济学1.

热门文章

  1. 打造你的“私人空间”,玩客云详细评测
  2. 计算机基础知识英文,计算机基础知识英文版-基础并不好,在电脑上学英语,什么软件最好用? 爱问知识人...
  3. 高等数学(第七版)同济大学 习题3-7 个人解答
  4. java中根遍历后根遍历构造,恳求大佬指点!!!首先标明空子树的先根遍历序列建立一棵二叉树...
  5. Class热替换与卸载
  6. 图形的设计和HTML和的CSS
  7. 大写锁定怎么解除(笔记本大写锁定怎么解除)
  8. 华为云硬盘备份(VBS)服务
  9. shell 计算磁盘使用率
  10. 最后聊聊:备案需要注意的地方和域名的解析