IntelliJ IDEA平台下JNI编程(五)—本地C代码创建Java对象及引用
本文学习如何在C代码中创建Java对象和对象数组,前面我们学习了C代码中访问Java对象的属性和方法,其实在创建对象时本质上也就是调用构造函数,因此本文知识学习起来也很轻松。有了前面学习数组创建的方法后,C代码创建对象数组同样很容易,下面开始学习吧~
1. C代码创建Java对象
创建Java对象本质就是调用构造函数,这与上一篇文章中提到的调用方法使用方法一致。下面直接贴代码:
package com.huachao.java;
/**
* Created by HuaChao on 2017/03/23.
*/
public class HelloJNI {
static {
System.loadLibrary("HelloJNI");
}
private String name;
private HelloJNI(String name) {
this.name = name;
}
public static native HelloJNI getInstance();
public static void main(String[] args) {
HelloJNI obj = HelloJNI.getInstance();
System.out.println(obj.name);
}
}
接下来在c代码中完成对getInstance()的实现。
#include<jni.h>
#include <stdio.h>
#include "com_huachao_java_HelloJNI.h"
JNIEXPORT jobject JNICALL Java_com_huachao_java_HelloJNI_getInstance
(JNIEnv * env,jobject thisObj){
jclass cls = (*env)->FindClass(env, "com/huachao/java/HelloJNI");
//获取构造函数的ID
jmethodID midInit = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
if (NULL == midInit) return NULL;
// 调用构造函数
jstring name=(*env)->NewStringUTF(env, "HuaChao");
jobject newObj = (*env)->NewObject(env, cls, midInit, name);
return newObj;
}
有一点需要注意,在调用HellJNI(String name)
构造函数时,需要先将c的本地字符串转为jstring类型,即调用NewStringUTF函数,再作为参数传入。运行结果如下:
HuaChao
2. C代码创建对象数组
创建对象数组其实就是结合对象的创建和数组的创建来实现,都是前面学过的知识。在HelloJNI.java中加入getArray函数,并且修改main函数如下:
public static native HelloJNI[] getArray(String[] names);
public static void main(String[] args) {
String[] names = {"HuaChao", "Lianjin"};
HelloJNI[] arr = HelloJNI.getArray(names);
for (HelloJNI obj : arr) {
System.out.println("name:" + obj.name);
}
}
在本地C代码中,对应getArray函数的实现如下:
JNIEXPORT jobjectArray JNICALL Java_com_huachao_java_HelloJNI_getArray
(JNIEnv * env,jobject thisObj,jobjectArray names){
// 获取HelloJNI类的引用
jclass clazz = (*env)->FindClass(env, "com/huachao/java/HelloJNI");
//获取构造函数的ID
jmethodID midInit = (*env)->GetMethodID(env, clazz, "<init>", "(Ljava/lang/String;)V");
// 获取数组长度
jsize length = (*env)->GetArrayLength(env, names);
jobjectArray outJNIArray = (*env)->NewObjectArray(env, length, clazz, NULL);
//遍历names数组
int i;
for (i = 0; i < length; i++) {
jstring name = (*env)->GetObjectArrayElement(env, names, i);
if (NULL == name)
return NULL;
jobject newObj = (*env)->NewObject(env, clazz, midInit, name);
(*env)->SetObjectArrayElement(env, outJNIArray, i, newObj);
}
return outJNIArray;
}
最后,运行如下:
name:HuaChao
name:Lianjin
3. C代码对Java类的局部和全局引用
与Java代码类似,在C代码函数里面创建的对象时,对对象的引用为局部引用,当函数执行结束时,引用无效。但是如果在函数外对对象进行引用,引用会一直有效,直到程序结束。前面我们频繁地用到了jclass和jmethodID以及jfieldID,下面我们尝试将其作为c代码的全局引用。
首先看看Java中代码。
package com.huachao.java;
/**
* Created by HuaChao on 2017/03/23.
*/
public class HelloJNI {
static {
System.loadLibrary("HelloJNI");
}
private String name;
private HelloJNI(String name) {
this.name = name;
}
public static native HelloJNI getInstance();
public static void main(String[] args) {
HelloJNI obj1 = HelloJNI.getInstance();
HelloJNI obj2 = HelloJNI.getInstance();
System.out.println("obj1:--->>name=" + obj1.name);
System.out.println("obj2:--->>name=" + obj2.name);
}
}
然后在C代码中,将jclass和jmethodID对象作为全局变量
#include<jni.h>
#include <stdio.h>
#include "com_huachao_java_HelloJNI.h"
static jclass cls;
static jmethodID midInit;
JNIEXPORT jobject JNICALL Java_com_huachao_java_HelloJNI_getInstance
(JNIEnv * env,jobject thisObj){
//如果cls为null,则调用FindClass赋值
if(NULL==cls){
cls = (*env)->FindClass(env, "com/huachao/java/HelloJNI");
}
if(NULL==cls) return NULL;
//如果midInit为NULL,获取构造函数的ID
if(NULL==midInit){
midInit = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
}
if (NULL == midInit) return NULL;
// 调用构造函数
jstring name=(*env)->NewStringUTF(env, "HuaChao");
jobject newObj = (*env)->NewObject(env, cls, midInit, name);
return newObj;
}
点击运行,发现报错!!!!!
为什么会出现这样的情况呢?主要是第一次调用FindClass()函数时,能正常获取到com.huachao.java.HelloJNI
类对象,并作为全局静态变量存储。但是,在接下来调用中,引用不再有效,虽然此时cls
变量不为NULL
,主要原因是FindClass()返回的是一个局部引用,当函数执行结束时,引用将变为无效的引用。
为了解决这个问题,可以通过调用NewGlobalRef函数将局部引用转为全局引用。完成转换后,记得将局部引用删除。修改c代码如下:
#include<jni.h>
#include <stdio.h>
#include "com_huachao_java_HelloJNI.h"
static jclass cls;
static jmethodID midInit;
JNIEXPORT jobject JNICALL Java_com_huachao_java_HelloJNI_getInstance
(JNIEnv * env,jobject thisObj){
//如果cls为null,则调用FindClass赋值
if(NULL==cls){
jclass clsLocal = (*env)->FindClass(env, "com/huachao/java/HelloJNI");
//局部引用转为全局引用
cls = (*env)->NewGlobalRef(env, clsLocal);
//删除局部引用
(*env)->DeleteLocalRef(env, clsLocal);
}
if(NULL==cls) return NULL;
//如果midInit为NULL,获取构造函数的ID
if(NULL==midInit){
midInit = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
}
if (NULL == midInit) return NULL;
// 调用构造函数
jstring name=(*env)->NewStringUTF(env, "HuaChao");
jobject newObj = (*env)->NewObject(env, cls, midInit, name);
return newObj;
}
运行结果如下:
obj1:--->>name=HuaChao
obj2:--->>name=HuaChao
细心的同学会发现,同为全局引用,为什么jclass
全局引用需要将本地引用转为全局引用,而jmethodID
对象却不需要?注意,jmethodID
和jfieldID
并不是jobject
对象,无需转换。
相关文章
- [Java基础] Java中List.remove报错UnsupportedOperationException
- JAVA学习(五):Java面向对象编程基础
- Java实现 LeetCode 816 模糊坐标(暴力)
- Java实现 LeetCode 441 排列硬币
- Java实现 LeetCode 160 相交链表
- Java实现信用卡校验
- Java实现One-way traffic(单向交通)
- Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
- Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
- Java -- JDBC 学习--PreparedStatement
- [Linux] Install java and add JAVA_HOME, PATH
- 【JAVA】java中split以"." 、""、“|”分隔字符串
- 【JAVA】 03-Java中的异常和包的使用
- Atitit web httphandler的实现 java python node.js c# net php 目录 1.1. Java 过滤器 servelet1 1.2. Python的
- Atitit 搜索蓝牙设备 powershell的实现 java noede.js python 先用脚本语言python nodejs,不好实现。。Java 也不好实现。。 Netcore可以,
- atitit.软件开发GUI 布局管理优缺点总结java swing wpf web html c++ qt php asp.net winform
- 求最大公约数和最小公倍数的做法(Java实现)
- 在浏览器进行大文件分片上传(java服务端实现)
- Java爬虫:使用WebMagic构建最简单的爬虫项目
- Java-idea-FindBugs、PMD和CheckStyle对比
- 解决办法:IntelliJ IDEA运行Junit5错误 : Exception in thread "main" java.lang.NoSuchMethodError: org.junit.pla
- 华为校招机试 - 数组取最小值(Java & JS & Python)
- IDEA又双叒叕抽风了,怎样避免一拳把屏幕打穿 原创 Django Java工程师成长日记
- 【java】Java 内存模型
- java Intellij IDEA设置中文