Java中JNI的使用详解第四篇:C/C++中创建Java对象和String字符串对象及对字符串的操作方法
首先来看一下C/C++中怎么创建Java对象:在JNIEnv中有两种方法是用来创建Java对象的:
第一种方法:
jobject NewObject(jclass clazz , jmethodID methodID, ....):
参数解释:
clazz:这个很简单,就是需要创建的Java对象的Class对象
methodID:这个是传递一个方法的ID,想一想Java对象在创建的时候,需要执行什么方法呢?对,没错那就是构造方法
第三个参数:是构造函数需要传入的参数值(默认的构造方法是不需要传入这个参数的)
所以我们在创建Java对象的时候之前要做的工作就是要获取这个对象的class对象,然后再获取该对象的构造方法,想要获取方法的id,就需要方法的签名,因为构造方法没有返回值,所以我们认为类的默认构造方法的返回值类型的签名始终是“()V”(因为默认的构造方法是没有参数的),方法的名称始终为“<init>”
例子:在C++中构造Java中的Date对象,并且调用它的getTime()方法打印当前时间
Java中的代码不需要改变,主要是C++代码中的改写:
- #include<iostream.h>
- #include "com_jni_demo_JNIDemo.h"
- JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj)
- {
- //获取Java中Date对象的Class对象
- jclass clazz_date = env->FindClass("java/util/Date");
- //获取构造方法的id
- jmethodID mid_date = env->GetMethodID(clazz_date,"<init>","()V");
- //生成Date对象
- jobject now = env->NewObject(clazz_date,mid_date);
- //获取Date对象中的getTime方法的id
- jmethodID mid_date_getTime = env->GetMethodID(clazz_date,"getTime","()J");
- //调用getTime方法返回时间
- jlong time = env->CallLongMethod(now,mid_date_getTime);
- //打印时间,这里要注意的是不能使用cout输出了,因为cout并没有对__int64的输出进行重载,要输出的话用printf("%I64d",time);
- printf("%I64d",time);
- }
编译成.dll文件,在Eclipse中运行结果如下:
这个输出的时间没有进行格式化处理。
第二种方法:
创建一个对象:就是用AllocObject
使用函数AllocObject可以根据传入的jclass创建一个Java对象,但是他的状态时非初始化的,在是哟个这个对象之前绝对要用CallNonvirtualVoidMethod来调用该jclass的构造函数,这样就可以延迟构造函数的调用,这一部分用的很少,只做简单的说明:
Java中的代码不做任何修改,C++代码中修改成如下:
- #include<iostream.h>
- #include "com_jni_demo_JNIDemo.h"
- JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj)
- {
- //获取java中的Date对象
- jclass clazz_date = env->FindClass("java/util/Date");
- jmethodID methodID_str = env->GetMethodID(clazz_date,"<init>","()V");
- jobject now = env->AllocObject(clazz_date);
- //调用构造方法
- env->CallNonvirtualVoidMethod(now,clazz_date,methodID_str);
- //获取Date对象中的getTime方法的id
- jmethodID mid_date_getTime = env->GetMethodID(clazz_date,"getTime","()J");
- //调用getTime方法返回时间
- jlong time = env->CallLongMethod(now,mid_date_getTime);
- //打印时间,这里要注意的是不能使用cout输出了,因为cout并没有对__int64的输出进行重载,要输出的话用printf("%I64d",time);
- printf("%I64d",time);
- }
这种方式是很少用的!
下面来看一下C/C++中如何操作Java中的字符串
接下来看一下JNIEnv中的一些C++方法:
1.获取字符串的长度:
jsize GetStringLength(jstring j_msg);
参数:j_msg:是一个jstring对象
2.将jstring对象拷贝到const jchar*指针字符串
//这个方法是:拷贝Java字符串并以UTF-8编码传入jstr
env->GetStringRegion(jstring j_msg , jsize start , jsize len , jchar* jstr);
//这个方法是:拷贝Java字符串并以UTF-16编码传入jstr
env->GetStringUTFRegion(jstring j_msg , jsize start , jsize len , char* jstr);
在Java1.2出来的函数,这个函数的动作时把Java字符串的内容直接拷贝到C/C++的字符串数组中,在呼叫这个函数之前必须有一个C/C++分配出来的字符串(具体看下面的例子),然后传入到这个函数中进行字符串的拷贝
由于C/C++中分配内存开销相对小,而且Java中的String内容拷贝的开销可以忽略,更好的一点是此函数不分配内存,不会抛出OutOfMemoryError异常
参数:j_msg:是一个jstring对象,start是拷贝字符串的开始位置,len是拷贝字符串的长度,jstr是目标指针字符串
3.生成一个jstring对象
jobject NewString(const jchar* jstr , int size);
参数:jstr是字符串指针,size是字符串长度
这个方法可以认为是将字符串指针jstr转化成字符串对象jstring
4.将jstring对象转化成const jchar*字符串指针
(1) const* jchar* GetStringChars(jstring j_msg , jboolean* copied);
返回一个UTF-16编码的宽字符串(jchar*)
参数:
j_msg是字符串对象
copied是指传入的是一个jboolean指针,用来标识是否对Java的String对象进行了拷贝的,如果传入的这个jboolean指针不是NULL,则它会给该指针所指向的内存传入JNI_TRUE或JNI_FALSE标识是否进行了拷贝,传入NULL表示不关心是否拷贝字符串,它就不会给jboolean* 指向的内存赋值
其对应的释放内存指针的方法:
ReleaseStringChars(jstring j_msg , const jchar* jstr);
参数:j_msg是jstring对象,jstr是字符串指针
在这里还有一个方法就是:
(2) const char* GetStringUTFChars(jstring str , jboolean* copied)
这个方法是可以取得UTF-8编码的字符串(char*)
参数的含义和GetStringChars方法是一样的
这个方法也有对应的一个释放内存的方法:
ReleaseStringUTFChars(jstring jstr , const char*str);
参数的含义和上面的ReleaseStringChars方法的参数的含义是一样的
需要注意的是:
这两个函数分别都会有两个不同的动作:
(1) 开辟一个新内存,然后在Java中的String拷贝到这个内存中,然后返回指向这个内存地址的指针
(2) 直接返回指向Java中String的内存的指针,这个时候千万不要改变这个内存的内容,这个将会破坏String在Java中始终是常量的这个原则
5.将jstring对象转化成const jchar*字符串指针
const jchar* GetStringCritical(jstring j_msg , jboolean* copied);
参数:j_msg是字符串对象,copied上面的方法已经做了解释了,这里就不多说了
这个方法的作用是为了增加直接传回指向Java字符串的指针的可能性(而不是拷贝),JDK1.2出来了新的函数GetStringCritical/ReleaseStringCritical,
在GetStringCritical/ReleaseStringCritical之间是一个关键区,在这个关键区域之间不能呼叫JNI的其他函数和会造成当前线程中断,或是会让当前线程等待的任何本地代码,否则将造成关键区代码执行期间垃圾回收器停止运作,任何触发垃圾回收器的线程也会暂停,其他的触发垃圾回收器的线程不能前进直到当前线程结束而激活垃圾回收器
在关键区域中千万不要出现中断操作,或是在JVM中分配任何新对象,否则会造成JVM死锁
虽说这个函数会增加直接传回指向Java字符串的指针的可能性,不过还是会根据情况传回拷贝过的字符串
不支持GetStringUTFCritical,没有这样的函数,由于Java字符串用的是UTF-16,要转成UTF-8编码的字符串始终需要进行一次拷贝,所以没有这样的函
这个方法和第四个方法是一样的功能
其对应的释放内存指针的方法:
env->ReleaseStringCritical(jstring j_msg , const jchar* jstr);
下面来看一下实例:在Java中定义一个String属性,通过控制台输入值,然后定义一个本地方法callCppFunction,在C++中这个方法的实现就是:获取到Java中这个字符串属性,将其进行倒序操作然后,然后再Java中输出:
先来看一下Java中的代码:
- package com.jni.demo;
- import java.io.BufferedReader;
- import java.io.InputStreamReader;
- public class JNIDemo {
- //定义一个本地方法
- public native void callCppFunction();
- //定义一个String属性
- public String msg = null;
- public static void main(String[] args)throws Exception{
- //调用动态链接库
- System.loadLibrary("JNIDemo");
- //从控制台中获取值
- BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
- String str = reader.readLine();
- JNIDemo jniDemo = new JNIDemo();
- jniDemo.msg = str;
- jniDemo.callCppFunction();
- System.out.println(jniDemo.msg);
- }
- }
在来看一下C++代码:
- #include<iostream>
- #include"com_jni_demo_JNIDemo.h"
- #include"windows.h"
- #include<string>
- #include<algorithm>
- using namespace std;
- JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_callCppFunction (JNIEnv * env, jobject obj)
- {
- //获取java中的属性:msg
- jfieldID fid_msg = env->GetFieldID(env->GetObjectClass(obj),"msg","Ljava/lang/String;");
- //获取属性msg的对象
- jstring j_msg = (jstring)env->GetObjectField(obj,fid_msg);
- /**第一种方式START*/
- /*
- //获得字符串指针
- const jchar* jstr = env->GetStringChars(j_msg,NULL);
- //转换成宽字符串
- wstring wstr((const wchar_t*)jstr);
- //释放指针
- env->ReleaseStringChars(j_msg,jstr);
- */
- /**第一种方式END*/
- /**第二种方式START*/
- /*
- //获取字符串指针
- const jchar* jstr = env->GetStringCritical(j_msg,NULL);
- //转换成宽字符串
- wstring wstr((const wchar_t*)jstr);
- //释放指针
- env->ReleaseStringCritical(j_msg,jstr);
- */
- /**第二种方式END*/
- /**第三种方式START*/
- //获取字符串的长度
- jsize len = env->GetStringLength(j_msg);
- //生成长度为len的字符串指针
- jchar* jstr = new jchar[len+1];
- //C++中字符串以'\0'结尾,不然会输出意想不到的字符
- jstr[len] = L'\0';
- //将字符串j_msg复制到jstr中
- env->GetStringRegion(j_msg,0,len,jstr);
- //转换成宽字符串
- wstring wstr((const wchar_t*)jstr);
- //释放指针
- delete[] jstr;
- /**第三种方式END*/
- //将字符串进行倒序
- reverse(wstr.begin(),wstr.end());
- //获取倒序后新的字符串
- jstring j_new_str = env->NewString((const jchar*)wstr.c_str(),(jint)wstr.size());
- //将新的字符串设置变量中
- env->SetObjectField(obj,fid_msg,j_new_str);
- }
这里使用了三种方式实现功能。这里要注意的是还有一个方法就是将const jchar*转换成wstring,因为reverse方法接受的参数是wstring。在Eclipse中运行结果如下:
到这里这一篇的内容就讲到着了,后面的内容更精彩!
Java中JNI的使用详解第四篇:C/C++中创建Java对象和String字符串对象及对字符串的操作方法的更多相关文章
- Java中JNI的使用详解第三篇:JNIEnv类型中方法的使用
转自: http://blog.csdn.net/jiangwei0910410003/article/details/17466369 上一篇说道JNIEnv中的方法的用法,这一篇我们就来通过例子来 ...
- Java中JNI的使用详解第五篇:C/C++中操作Java中的数组
在Java中数组分为两种: 1.基本类型数组 2.对象类型(Object[])的数组(数组中存放的是指向Java对象中的引用) 一个能通用于两种不同类型数组的函数: GetArrayLength(ja ...
- Java中JNI的使用详解第六篇:C/C++中的引用类型和Id的缓存
首先来看一下C/C++中的引用 从Java虚拟机创建的对象传到本地C/C++代码时会产生引用,根据Java的垃圾回收机制,只要有引用存在就不会触发该引用指向的Java对象的垃圾回收 第一.局部引用: ...
- Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理
Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理 转自:https://www.jianshu.com/p/2b71ea919d49 本系列文章首发于我的个人博 ...
- Java网络编程和NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector
Java网络编程与NIO详解4:浅析NIO包中的Buffer.Channel 和 Selector 转自https://www.javadoop.com/post/nio-and-aio 本系列文章首 ...
- IIS负载均衡-Application Request Route详解第四篇:使用ARR实现三层部署架构(转载)
IIS负载均衡-Application Request Route详解第四篇:使用ARR实现三层部署架构 系列文章链接: IIS负载均衡-Application Request Route详解第一篇: ...
- Java中JNI的使用详解第二篇:JNIEnv类型和jobject类型的解释
上一篇说的是一个简单的应用,说明JNI是怎么工作的,这一篇主要来说一下,那个本地方法sayHello的参数的说明,以及其中方法的使用 首先来看一下C++中的sayHello方法的实现: JNIEXPO ...
- Java中JNI的使用详解第一篇:HelloWorld
转自: http://blog.csdn.net/jiangwei0910410003/article/details/17465085 今天开始研究JNI技术,首先还是老套路,输出一个HelloWo ...
- Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector
微信公众号[黄小斜]作者是蚂蚁金服 JAVA 工程师,目前在蚂蚁财富负责后端开发工作,专注于 JAVA 后端技术栈,同时也懂点投资理财,坚持学习和写作,用大厂程序员的视角解读技术与互联网,我的世界里不 ...
随机推荐
- 【LeetCode】Hash
[451] Sort Characters By Frequency [Medium] 给一个字符串,要求返回按照字母出现频率的排序后的字符串.(哈希表+桶排) 有个技巧是Hash用Value作为In ...
- AtomicInteger 、Synchronized 和 volatile 之间的区别?
AtomicInteger:无锁的线程安全整数??? Synchronized:同步 volatile:挥发性??? 参考文档:
- String的static方法
//String concat(String str) 拼接字符串 String concat_str0 = "abc"; String concat_str1 = "b ...
- 阿里云高磊:API网关加速能力聚合与生态集成
导读:本文中,阿里云高级技术专家高磊(花名:埃兰)将聚焦API网关加速能力聚合与生态集成,讲述API如何实现系统间的衔接和API网关的产品升级进程,重点展示了一些新功能.新体验和新变化. 大家下午好, ...
- bzoj1047题解
[解题思路] (p.s.:刚看的时候一脸懵逼..没看见N已经给定了,还以为要用某些高明的方法..果然还是太naive了..) 两遍预处理,第一遍处理出f[i][j][0/1]表示第i行从j-n+1~j ...
- 密码学之RSA基础
预备数论知识 互质关系 如果两个正整数,除了1以外,没有其他公因子,那么就称这两个数是互质关系 比如:4和7,13和61 欧拉函数 思考:任意给定整数n,在小于等于n的正整数中,有多少个数与n构成互质 ...
- P1537 弹珠
P1537 弹珠 题目描述 玛莎和比尔各自有自己的弹珠收藏.他们想重新分配收藏品,使两人能平等拥有弹珠.如果所有的弹珠的价值相同,那么他们就可以平分.但不幸的是,有一些弹珠更大,或者更美丽,所以,玛莎 ...
- nginx代理一个服务器上所有域名
1. 主配置文件不需要更改任何配置2. 在vhosts目录下需要建立两个文件,一个是servername 列表文件,一个是虚拟主机配置文件两个文件内容分别为 #() servername server ...
- Naive RNN vs LSTM vs GRU、attention基础
原文地址:https://www.jianshu.com/p/b8653f8b5b2b 一.Recurrent Neural Network 二.Naive RNN Naive RNN更新参数时易出现 ...
- 31-Ubuntu-用户权限-02-ls输出信息介绍
ls -l 查看文件夹下文件或目录的详细信息 1 2 3 4 5 6 7 8 9 10 d/- rwx rwx r-x 2 summmer summmer 12288 2月 25 13:34 Ente ...
