IntelliJ IDEA平台下JNI编程(五)—本地C代码创建Java对象及引用

简介: IntelliJ IDEA平台下JNI编程(五)—本地C代码创建Java对象及引用

IntelliJ IDEA平台下JNI编程(五)—本地C代码创建Java对象及引用


转载请注明出处:【huachao1001的专栏:http://blog.csdn.net/huachao1001/article/details/65446070

本文学习如何在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对象,无需转换。

相关文章
|
6天前
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
144 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
|
1月前
|
SQL Java 数据库连接
如何在 Java 代码中使用 JSqlParser 解析复杂的 SQL 语句?
大家好,我是 V 哥。JSqlParser 是一个用于解析 SQL 语句的 Java 库,可将 SQL 解析为 Java 对象树,支持多种 SQL 类型(如 `SELECT`、`INSERT` 等)。它适用于 SQL 分析、修改、生成和验证等场景。通过 Maven 或 Gradle 安装后,可以方便地在 Java 代码中使用。
239 11
|
1月前
|
JSON Java 数据挖掘
利用 Java 代码获取淘宝关键字 API 接口
在数字化商业时代,精准把握市场动态与消费者需求是企业成功的关键。淘宝作为中国最大的电商平台之一,其海量数据中蕴含丰富的商业洞察。本文介绍如何通过Java代码高效、合规地获取淘宝关键字API接口数据,帮助商家优化产品布局、制定营销策略。主要内容包括: 1. **淘宝关键字API的价值**:洞察用户需求、优化产品标题与详情、制定营销策略。 2. **获取API接口的步骤**:注册账号、申请权限、搭建Java开发环境、编写调用代码、解析响应数据。 3. **注意事项**:遵守法律法规与平台规则,处理API调用限制。 通过这些步骤,商家可以在激烈的市场竞争中脱颖而出。
|
28天前
|
前端开发 Java 开发工具
Git使用教程-将idea本地Java等文件配置到gitte上【保姆级教程】
本内容详细介绍了使用Git进行版本控制的全过程,涵盖从本地仓库创建到远程仓库配置,以及最终推送代码至远程仓库的步骤。
38 0
|
2月前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
74 3
|
3月前
|
Java
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
75 24
|
2月前
|
前端开发 Java 测试技术
java日常开发中如何写出优雅的好维护的代码
代码可读性太差,实际是给团队后续开发中埋坑,优化在平时,没有那个团队会说我专门给你一个月来优化之前的代码,所以在日常开发中就要多注意可读性问题,不要写出几天之后自己都看不懂的代码。
78 2
|
2月前
|
Java 编译器 数据库
Java 中的注解(Annotations):代码中的 “元数据” 魔法
Java注解是代码中的“元数据”标签,不直接参与业务逻辑,但在编译或运行时提供重要信息。本文介绍了注解的基础语法、内置注解的应用场景,以及如何自定义注解和结合AOP技术实现方法执行日志记录,展示了注解在提升代码质量、简化开发流程和增强程序功能方面的强大作用。
137 5
|
2月前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
93 5
|
3月前
|
Java API 开发者
Java中的Lambda表达式:简洁代码的利器####
本文探讨了Java中Lambda表达式的概念、用途及其在简化代码和提高开发效率方面的显著作用。通过具体实例,展示了Lambda表达式如何在Java 8及更高版本中替代传统的匿名内部类,使代码更加简洁易读。文章还简要介绍了Lambda表达式的语法和常见用法,帮助开发者更好地理解和应用这一强大的工具。 ####

热门文章

最新文章