本文学习如何在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对象却不需要?注意,jmethodIDjfieldID并不是jobject对象,无需转换。

IntelliJ IDEA平台下JNI编程(五)—本地C代码创建Java对象及引用的更多相关文章

  1. IntelliJ IDEA平台下JNI编程—HelloWorld篇

    转载请注明出处:[huachao1001的专栏:http://blog.csdn.net/huachao1001/article/details/53906237] JNI(Java Native I ...

  2. Java中JNI的使用详解第四篇:C/C++中创建Java对象和String字符串对象及对字符串的操作方法

    首先来看一下C/C++中怎么创建Java对象:在JNIEnv中有两种方法是用来创建Java对象的: 第一种方法: jobject  NewObject(jclass clazz  , jmethodI ...

  3. 项目管理---git----快速使用git笔记(五)------本地项目代码提交到远程仓库---新建项目

    上一篇我们已经知道了怎么从远程仓库获取项目文件代码. 项目管理---git----快速使用git笔记(四)------远程项目代码的首次获取 git还有一种使用场景是 我本来在电脑里就有一个项目,现在 ...

  4. 本地C代码中创建Java对象

    作者:唐老师,华清远见嵌入式学院讲师. 创建Java域的对象就是创建Java类的实例,再调用Java类的构造方法. 以Bitmap的构建为例,Bitmap中并没有Java对象创建的代码及外部能访问的构 ...

  5. Android NDK开发(五)--C代码回调Java代码【转】

    转载请注明出处:http://blog.csdn.net/allen315410/article/details/41862479 在上篇博客里了解了Java层是怎样传递数据到C层代码,并且熟悉了大部 ...

  6. 趣味编程:CPS风格代码(Java 8,Functional Java版)

    CPS风格代码(Java 8版) package fp; import java.util.function.IntConsumer; public class CPS { static int ad ...

  7. Windows平台下Git服务器搭建--------gitblit

    Windows(server)平台下Git服务器搭建 第一步:下载Java,安装,配置环境变量. 第二步:下载Gitblit.下载地址:http://www.gitblit.com/ 第三步:解压缩下 ...

  8. 在 JNI 编程中避免内存泄漏

    JAVA 中的内存泄漏 JAVA 编程中的内存泄漏,从泄漏的内存位置角度可以分为两种:JVM 中 Java Heap 的内存泄漏:JVM 内存中 native memory 的内存泄漏. Java H ...

  9. 【转】Windows平台下Git服务器搭建

    Windows平台下Git服务器搭建 Posted on 2015-05-18 21:29 阿祥当码农 阅读(7637) 评论(0) 编辑 收藏 该文章转自:http://www.codeceo.co ...

随机推荐

  1. show processlist命令与kill 线程

    show [full] processlist show processlist显示正在运行的线程.如果有process权限,则可以查看所有正在运行的线程.否则,只能看到自己的线程.如果不使用full ...

  2. python的__str__()和__repr__()方法

    __str__()和__repe__()方法定义一个值通过print打印时返回时的显示样式 l=list('hello') print(l) #['h', 'e', 'l', 'l', 'o'] cl ...

  3. P2590 [ZJOI2008]树的统计(树链剖分)

    P2590 [ZJOI2008]树的统计 虽然是入门树剖模板 但是我终于1A了(大哭) 懒得写啥了(逃 #include<iostream> #include<cstdio> ...

  4. Forword(请求转发)与Redirect(重定向) 区别

    1.从数据共享上 Forword是一个请求的延续,可以共享request的数据 Redirect开启一个新的请求,不可以共享request的数据 2.从地址栏 Forword转发地址栏不发生变化 Re ...

  5. Nginx启动错误:error while loading shared libraries: libpcre.so.0

    今天测试的时候,启动一个其他机器预编译好的nginx到目标测试机器(OEL 7.4)启动的时候,报了下列错误: /usr/local/nginx/sbin/nginx: error while loa ...

  6. linux 关于redis-trib.rb构建redis集群

    之前搭建集群漏下的坑, 今次再搭一次. 环境 ruby环境 yum install ruby rubygems -y redis的gem环境 gem install redis-3.2.2.gem 部 ...

  7. maven的使用记录

    maven的使用记录 使用的版本为3.6.0. maven配置部署项目 在cmd命令行中切换到Maven项目的根目录,比如:D:/xxxwork/java/maven-test,然后执行命令:$ mv ...

  8. JAVA基础部分 JDK和JRE以及JVM

    第一部分: 一.dos命令 *快捷查看电脑ip: Win + R进入cmd;输入ipconfig/all查看IPv4:192.168.0.xxx(首选); 基本命令: cd进入目录:d: 直接进入盘符 ...

  9. 【专家坐堂Q&A】在 petalinux-config 中选择外部来源时,可将符号链路添加内核来源目录树

    问题描述 作为 petalinux-config 菜单的一部分,现在可以将 Linux 内核指定为外部来源. 如果选择了该选项,可为内核来源目录树添加两个符号链路. 这会带来两个问题: 1. 符号链路 ...

  10. bzoj 4289 Tax - 最短路

    题目传送门 这是一条通往vjudge的神秘通道 这是一条通往bzoj的神秘通道 题目大意 如果一条路径走过的边依次为$e_{1}, e_{2}, \cdots , e_{k}$,那么它的长度为$e_{ ...