(二)JNI 的数据类型

本文详细介绍了JNI的数据类型,并通过实例演示了如何使用C通过JNI调用Java的属性、静态属性、方法、静态方法、构造方法及父类方法。同时,文章还探讨了JNI字符串乱码问题及数组处理,包括传入和返回数组的操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

温馨提示

写博客是为了记录在开发过程中所涉及到的技术以及遇到的问题的解决,如果该博客对您有所帮助,希望可以点个关注;如果您对文章中的内容有什么不同的见解,欢迎留言进行讨论。谢谢!

JNI 的数据类型

一、JNI 数据类型

Java基本数据类型与JNI数据类型的映射关系

Java数据类型JNI数据类型C/C++数据类型
booleanjbooleanunsigned char
bytejbytesigned char
charjcharunsigned short
shortjshortshort
intjintint
longjlonglong long
floatjfloatfloat
doublejdoubledouble
voidvoidvoid

Java引用数据类型与JNI数据类型的映射关系

Java数据类型JNI数据类型
Stringjstring
Objectjobject
基本数据类型数组如: byte[]jbyteArray
对象数组如: Object[],String[]jobjectArray

二、C通过JNI调用Java

1、访问Java属性

GETField

  • 编写JNITest.java文件
public class JNITest {
    public String key = "World!";
    //访问属性,返回修改之后的属性内容
    public native String accessField();

    public static void main(String[] args){
        JNITest t = new JNITest();
        System.out.println("key修改前:"+t.key);
        t.accessField();
        System.out.println("key修改后:"+t.key);
    }

    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    accessField
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_accessField
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_accessField
(JNIEnv *jEnv, jobject jobj) {
	//jobj是t对象,JniTest.class
	jclass cls = (*jEnv)->GetObjectClass(jEnv, jobj);
	//jfieldID
	//属性名称,属性签名
	jfieldID fid = (*jEnv)->GetFieldID(jEnv, cls, "key", "Ljava/lang/String;");

	//World! >> Hello World!
	//获取key属性的值
	//Get<Type>Field
	jstring jstr = (*jEnv)->GetObjectField(jEnv, jobj, fid);
	printf("jstr:%#x\n", &jstr);

	//jstring -> c字符串
	//isCopy 是否复制(true代表复制,false不复制)
	char *c_str = (*jEnv)->GetStringUTFChars(jEnv, jstr, NULL);
	//拼接得到新的字符串
	char text[20] = "Hello ";
	strcat(text, c_str);

	//c字符串 ->jstring
	jstring new_jstr = (*jEnv)->NewStringUTF(jEnv, text);

	//修改key
	//Set<Type>Field
	(*jEnv)->SetObjectField(jEnv, jobj, fid, new_jstr);

	printf("new_jstr:%#x\n", &new_jstr);

	return new_jstr;
}
  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

2、访问Java静态属性

GETField

  • 编写JNITest.java文件
public class JNITest {
    public static int count = 5;
    //访问属性,返回修改之后的属性内容
    public native int accessStaticField();

    public static void main(String[] args){
        JNITest t = new JNITest();
        System.out.println("count 修改前:"+t.count);
        t.accessStaticField();
        System.out.println("count 修改后:"+t.count);
    }

    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    accessField
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jint JNICALL Java_com_example_jni_JNITest_accessStaticField
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

JNIEXPORT jint JNICALL Java_com_example_jni_JNITest_accessStaticField
(JNIEnv *jEnv, jobject jobj) {
	//jobj是t对象,JniTest.class
	jclass cls = (*jEnv)->GetObjectClass(jEnv, jobj);
	//jfieldID
	//属性名称,属性签名
	jfieldID fid = (*jEnv)->GetStaticFieldID(jEnv, cls, "count", "I");

	//获取 count 属性的值
	//Get<Type>Field
	jint count = (*jEnv)->GetStaticIntField(jEnv, jobj, fid);
	printf("jstr:%#x\n", &jstr);
    count++;
    (*jEnv)->SetStaticIntField(jEnv, jobj, fid,count);
	return count;
}
  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

3、访问java方法

CallMethod

  • 编写JNITest.java文件
public class JNITest {
  
    public native void accessMethod();

    public static void main(String[] args){
        JNITest t = new JNITest();
        t.accessMethod();
    }

    //产生指定范围的随机数
	public int genRandomInt(int max){
		System.out.println("genRandomInt 执行了...");
		return new Random().nextInt(max); 
	}
	
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest

产生的结果中有对应的属性和方法的签名

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    accessMethod
 * Signature: (I)I;
 */
JNIEXPORT void JNICALL Java_com_example_jni_JNITest_accessMethod
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

JNIEXPORT void JNICALL Java_com_example_jni_JNITest_accessMethod
(JNIEnv *env, jobject jobj){
	//jclass
	jclass cls = (*env)->GetObjectClass(env, jobj);
	//jmethodID
	jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");
	//调用
	//Call<Type>Method
	jint random = (*env)->CallIntMethod(env, jobj, mid, 200);
	printf("random num:%ld",random);

	//.....
}
  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

4、访问静态方法

CallStaticMethod

  • 编写JNITest.java文件
public class JNITest {
  
    public native void accessStaticMethod();

    public static void main(String[] args){
        JNITest t = new JNITest();
        t.accessStaticMethod();
    }

    //产生UUID字符串
	public static String getUUID(){
		return UUID.randomUUID().toString();
	}
	
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest

产生的结果中有对应的属性和方法的签名

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    accessStaticMethod
 * Signature: ()Ljava/lang/String;;
 */
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_accessStaticMethod
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_accessStaticMethod
(JNIEnv *env, jobject jobj){
	//jclass
	jclass cls = (*env)->GetObjectClass(env, jobj);
	//jmethodID	
	jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");
	
	//调用
	//CallStatic<Type>Method
	jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);

	//随机文件名称 uuid.txt
	//jstring -> char*
	//isCopy ,代表java和c操作的是同一个字符串
	char *uuid_str = (*env)->GetStringUTFChars(env, uuid, NULL);
	//拼接
	char filename[100];
	sprintf(filename, "D://%s.txt",uuid_str);
	FILE *fp = fopen(filename,"w");
	fputs("How are you?", fp);
	fclose(fp);
}

  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

5、访问构造方法

通过FindClass找到对应的类

  • 编写JNITest.java文件
public class JNITest {
    //java.util.Date
    public native Date accessConstructor();

    public static void main(String[] args){
        JNITest t = new JNITest();
        t.accessConstructor();
    }

	
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest

产生的结果中有对应的属性和方法的签名

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    accessConstructor
 * Signature: 
 */
JNIEXPORT jobject JNICALL Java_com_example_jni_JNITest_accessConstructor
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

//使用java.util.Date产生一个当前的时间戳
JNIEXPORT jobject JNICALL Java_com_example_jni_JNITest_accessConstructor
(JNIEnv *env, jobject jobj){
	jclass cla = (*env)->FindClass(env,"java/util/Date");
	//jmethodID,构造方法的方法名称固定为"<init>"
	jmethodID constructor_mid = (*env)->GetMethodID(env,cls,"<init>","()V");
	//实例化一个Date对象
	jobject date_obj = (*env)->NewObject(env,cls,constructor_mid);
	//调用getTime方法
	jmehoodID mid = (*env)->GetMethodID(env,cls,"getTime","()J");
	//调用
	jlong time = 
	(*env)->CallLongMethod(env,date_obj,mid);
	//time 为long long类型
	printf("time: %lld\n",time);
	return date_obj;
}

  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

6、访问父类的方法


  • 编写父类文件
public class Human {
    public void sayHi(){
        System.out.println("Human say Hi...");
    }
}


public class man extends Human{
    @override
    public void sayHi(){
        System.out.println("Man say Hi,How are you?");
    }
}
  • 编写JNITest.java文件
public class JNITest {
    public Human human = new Man();
    public native void accessNonvirtualMethod();

    public static void main(String[] args){
        JNITest t = new JNITest();
        t.accessNonvirtualMethod();
    }

	
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest

产生的结果中有对应的属性和方法的签名

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    accessNonvirtualMethod
 * Signature: 
 */
JNIEXPORT jobject JNICALL Java_com_example_jni_JNITest_accessNonvirtualMethod
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

//调用父类的方法 
JNIEXPORT jobject JNICALL Java_com_example_jni_JNITest_accessNonvirtualMethod
(JNIEnv *env, jobject jobj){
    jclass cls = (*env)->GetObjectClass(env,jobj);
	//获取Man属性
    jfieldID fid =	(*env)->GetFieldID(env,cls,"human","Lcom/example/jni/Human;");
    jobject human_obj = (*env)->GetObjectField(env,jobj,fid);
    //执行sayHi方法
    //传父类的父类
    jclass human_cls = (*env)->FindClass(env,"com/example/jni/Human");
    jmethodID  mid = (*env)->GetMethodID(env,human_cls,"sayHi","()V");
    //执行子类方法
    (*env)->CallObjectMethod(env,human_obj,mid);
    
    //执行父类方法
    (*env)->CallNonvirtualObjectMethod(env,human_obj,human_cls,mid);
    
}

  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

三、JNI字符串乱码问题

  • 编写JNITest.java文件
public class JNITest {
    
    public native String chineseChars(String in);

    public static void main(String[] args){
        JNITest t = new JNITest();
        String outStr = t.chineseChars("传入一串中文");
        System.out.println(outStr);
    }

	
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest

产生的结果中有对应的属性和方法的签名

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    chineseChars
 * Signature: 
 */
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_chineseChars
(JNIEnv *, jobject,jstring);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

//中文问题
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_chineseChars
(JNIEnv *env, jobject jobj, jstring in) {
	char *c_str = (*env)->GetStringUTFChars(env, in, NULL);
	printf("%s\n", c_str);
	char *c_str_out = "传出一段文字";
	//NewStringUTF 是utf-16编码
	jstring j_str = (*env)->NewStringUTF(env, c_str_out);
	//使用Java处理乱码问题
	jclass jcls = (*env)->FindClass(env, "java/lang/String");
	//jmethodID
	jmethodID c_mid = (*env)->GetMethodID(env, jcls, "<init>", "([BLjava/lang/String;)V");
	//byte[]数组
	//jbyteArray -> char[]
	jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str_out));
	//byte数组赋值
	(*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str_out), c_str_out);
	//字符编码
	jstring charsetName = (*env)->NewStringUTF(env, "GB2312");
	//调用构造函数,返回编码之后的字符串

	return (*env)->NewObject(env, jcls, c_mid, bytes, charsetName);
}

  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

四、JNI数组处理,传入一个数组,返回另一个数组

  • 编写JNITest.java文件
public class JNITest {
    
    public native int[] sortArray(int[] array);

    public static void main(String[] args){
        JNITest t = new JNITest();
        int[] arr = new int[]{6,8,1,17,23,12,65,32};
        int newArr = t.sortArray();
        for (int i : arr) {
            System.out.println(i);
        }
        System.out.println("返回的新数组为:");
         for (int i : newArr) {
            System.out.println(i);
        }
    }

	
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest

产生的结果中有对应的属性和方法的签名

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    sortArray
 * Signature: 
 */
JNIEXPORT jintArray JNICALL Java_com_example_jni_JNITest_sortArray
(JNIEnv *, jobject,jintArray);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"
#include <stdlib.h>

int compare(int *a,int *b){
    return (*a) - (*b);
}

//数组处理
JNIEXPORT jintArray JNICALL Java_com_example_jni_JNITest_sortArray
(JNIEnv *env, jobject jobj, jintArray arr) {
	//拿到 jintArray 的指针 -> 得到 c int 数组
	jint *elems = (*env)->GetIntArrayElements(env,arr,NULL);
	//数组的长度
	int len = (*env)->GetArrayLength(env,arr);
	
	//排序
	qsort(elems, len, sizeof(jint), compare);
	//同步,第4个参数:
	//JNI_OK(0):Java数组进行更新,并且释放C/C++数组
	//JNI_COMMIT(1):Java数组进行更新,不释放C/C++数组(函数执行完数组会释放)
	//JNI_ABOUT:Java数组不进行更新,但释放C/C++数组
	(*env)->ReleaseIntArrayElements(env, arr, elems,JNI_COMMIT);
	
	
	//将原数组倒序生成一个新的数组
	jintArray jint_new_arr = (*env)->NewIntArray(env,len);
	jint *new_elems = (*env)->GetIntArrayElements(env,jint_new_arr,NULL);
	int i = len-1;
	for(; i >= 0; i--){
	    int j = len-1-i;
	    new_elems[j] = elems[i];
	}
	//同步
	(*env)->ReleaseIntArrayElements(env, jint_new_arr, new_elems, JNI_COMMIT);
	return jint_new_arr;
}

  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值