原文出處
JNI之C++調(diào)用Java類(lèi) —— java.lang.String
為什么要用C++調(diào)用Java類(lèi)?很難回答,寫(xiě)著文章只是覺(jué)得JNI很有意思。于是開(kāi)始編寫(xiě)一段使用VC++在Windows系統(tǒng)里調(diào)用java的String類(lèi),在C++里調(diào)用String類(lèi)內(nèi)的一些方法。
JNI已經(jīng)被開(kāi)發(fā)了很多年,而在我2年多的Java編程時(shí)間里從來(lái)沒(méi)有接觸過(guò)。直到最近研究JVM實(shí)現(xiàn)原理才注意到JNI。 JNI既Java Native Interface,Native這個(gè)詞我見(jiàn)過(guò)我認(rèn)為最恰當(dāng)?shù)姆g就是原生。原生的意思就是來(lái)自系統(tǒng)自己的,原汁原味的東西,例如Win32 API。Java類(lèi)需要在虛擬機(jī)上運(yùn)行,也就不是原生的,同樣.NET Framework也不是原生的。JNI也就是Java原生接口。關(guān)于JNI的規(guī)范,以及為什么要使用它,它能做些什么,都在http://java.sun.com/j2se/1.4.2/docs/guide/jni/spec/jniTOC.html里記述著。
JNI是規(guī)范,它規(guī)定了虛擬機(jī)的接口,而把具體的實(shí)現(xiàn)留給開(kāi)發(fā)者。
JVM的實(shí)現(xiàn)不是唯一的,目前存在很多種Java虛擬機(jī),Sun Hotspot,IBM JDK,還有HP的,Kaffe等等。最流行的就是Sun的Hotspot,最復(fù)雜的就是IBM JDK,這是IBM的一貫作風(fēng)。本文不討論JVM的實(shí)現(xiàn),只關(guān)注JNI。如果您安裝了Sun的JDK,您就能在[JAVA_HOME]\include目錄下找到j(luò)ni.h。這個(gè)頭文件就是虛擬機(jī)的唯一接口,你可以調(diào)用它聲明的函數(shù)創(chuàng)建一個(gè)JVM。
在說(shuō)明C++調(diào)用Java類(lèi)之前,我想先演示一下如果編寫(xiě)Java Native Method。
1.編寫(xiě)帶有Native方法的Java類(lèi)
package org.colimas.jni.test;
public class JniTest {
static { System.loadLibrary("JniTestImpl"); } //JVM調(diào)用JniTestImpl.dll
public JniTest(){
}
//原生方法
public native void print(String str);
/**
* @param args
*/
public static void main(String[] args) {
JniTest test=new JniTest();
test.print("hello JVM"); //調(diào)用原生方法
}
}
2.使用javah生成c語(yǔ)言頭文件。
javah -jni org.colimas.jni.test.JniTest
目錄里多了一個(gè)org_colimas_jni_test_JniTest.h文件,打開(kāi)文件,內(nèi)容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_colimas_jni_test_JniTest */
#ifndef _Included_org_colimas_jni_test_JniTest
#define _Included_org_colimas_jni_test_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: org_colimas_jni_test_JniTest
* Method: print
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_org_colimas_jni_test_JniTest_print
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
其中的Java_org_colimas_jni_test_JniTest_print就是JniTest類(lèi)里面的print原生方法的C語(yǔ)言聲明。
3.編寫(xiě)C代碼實(shí)現(xiàn)原生方法print
#include <jni.h>
#include "org_colimas_jni_test_JniTest.h" //javah生成的頭文件
#include <stdio.h>
JNIEXPORT void JNICALL Java_org_colimas_jni_test_JniTest_print
(JNIEnv *env, jobject object,jstring str)
{
//獲得字符串
const char * txt=(*env)->GetStringUTFChars(env,str,0);
printf("%s\n",txt); //打印到控制臺(tái)
return;
}
參數(shù)JNIEnv *env,是JNI里最重要的變量。Java.exe創(chuàng)建JVM,之后JVM生成一個(gè)env,該env相當(dāng)于JVM內(nèi)的Session,可以完成創(chuàng)建Java對(duì)象,調(diào)用類(lèi)方法,獲得類(lèi)的屬性等等。
在這里env將方法的參數(shù)Str從JNI的jstring類(lèi)型轉(zhuǎn)換為常數(shù)char數(shù)組。
4.編譯
cl /Ic:\j2sdk1.4.2_10\include /Ic:\j2sdk1.4.2_10\include\win32 /c JniTestImpl.c
5.連接為DLL
link /dll JniTestImpl.obj
6.設(shè)置PATH
set PATH=C:\MyProject\Colimas\CD\JNI\MyJNI;%PATH%
7.運(yùn)行
java org.colimas.jni.test.JniTest
返回結(jié)果
hello JVM
結(jié)束
以上是實(shí)現(xiàn)Java原生方法的開(kāi)發(fā)過(guò)程,下面進(jìn)入正題,使用C++調(diào)用Java的java.lang.String類(lèi)。
1. Object類(lèi)出創(chuàng)建JVM。
使用Java類(lèi)之前必須要?jiǎng)?chuàng)建JVM環(huán)境。JDK由java.exe來(lái)完成。本文有Object類(lèi)的靜態(tài)方法BeginJVM來(lái)創(chuàng)建,用EndJVM來(lái)關(guān)閉。
創(chuàng)建JVM之后會(huì)在創(chuàng)建2個(gè)變量,分別是JNIEnv* env和JavaVM* jvm,JNIEnv上文已經(jīng)說(shuō)明,JavaVM,顧名思義,代表Java虛擬機(jī),用它來(lái)關(guān)閉JVM。
Object類(lèi)的頭文件
#include "jni.h"
class Object
{
public:
static bool BeginJVM();
static bool EndJVM();
Object();
virtual ~Object();
protected:
static JNIEnv* env;
static JavaVM* jvm;
};
object.cpp代碼
#include "stdafx.h"
#include "JavaClasses.h"
#include "Object.h"
Object::Object()
{}
Object::~Object()
{}
JNIEnv* Object::env=NULL;
JavaVM* Object::jvm=NULL;
//創(chuàng)建JVM
bool Object::BeginJVM()
{
JavaVMOption options[3];
JavaVMInitArgs vm_args;
//各種參數(shù)
options[0].optionString="-Xmx128m";
options[1].optionString="-Verbose:gc";
options[2].optionString="-Djava.class.path=.";
vm_args.version=JNI_VERSION_1_2;
vm_args.options=options;
vm_args.nOptions=3;
//創(chuàng)建JVM,獲得jvm和env
int res = JNI_CreateJavaVM(&jvm,(void **)&env, &vm_args);
return true;
}
bool Object::EndJVM()
{
//關(guān)閉JVM
jvm->DestroyJavaVM();
return true;
}
2. C++的String類(lèi)調(diào)用java.lang.String類(lèi)方法
編寫(xiě)C++版的String類(lèi),調(diào)用java String類(lèi)方法。調(diào)用的方法如下:
String replaceAll(String regex, String replacement);
boolean endsWith(String str);
int indexOf(String str);
int compareTo(String anotherString);
char charAt(int i);
String的頭文件:
class String :public Object
{
public:
//與要調(diào)用的Java方法名一致。
const char * replaceAll(char *regex,char *replacement);
bool endsWith(char * str);
int indexOf(char * str);
int compareTo(char *anotherString);
char charAt(int i);
String(char *str);
virtual ~String();
};
實(shí)現(xiàn):
#include "stdafx.h"
#include "String.h"
#include "jni.h"
using namespace std;
jclass clazz; //全局變量,用來(lái)傳遞class
jobject object; //全局變量,用來(lái)傳遞object
String::String(char *str)
{
jstring jstr;
if (Object::env ==NULL){
cout << "JVM is not created" << endl;
exit(-1);
}
//獲得java.lang.String類(lèi)
clazz=Object::env->FindClass("java/lang/String");
if (clazz ==0 ){
cout << "Class is not found" << endl;
exit(-1);
}
//獲得String(String str)構(gòu)造體
jmethodID mid= Object::env->GetMethodID(clazz,"<init>", "(Ljava/lang/String;)V");
if (mid==0){
cerr<< "GetMethodID Error for class" << endl;
exit(-1);
}
//將字符串封裝為jstring。
jstr = Object::env->NewStringUTF(str);
if (jstr == 0) {
cerr << "Out of memory" <<endl;
exit(-1);
}
cout << "invoking method" << endl;
//創(chuàng)建一個(gè)java.lang.String對(duì)象。
object=Object::env->NewObject(clazz,mid,jstr);
}
String::~String()
{}
char String::charAt(int i)
{
if (Object::env ==NULL){
cout << "JVM is not created" << endl;
exit(-1);
}
if (clazz ==0 ){
cout << "Class is not found" << endl;
exit(-1);
}
if (object ==0 ){
cout << "String object is not created" << endl;
exit(-1);
}
jmethodID mid;
//獲得charAt方法,(I)C表示 參數(shù)為int型,返回char型。詳細(xì)參見(jiàn)JNI規(guī)范
mid = Object::env->GetMethodID(clazz,"charAt", "(I)C");
if (mid==0){
cerr<< "GetMethodID Error for class" << endl;
exit(-1);
}
jint ji=i;
cout << "invoking method" << endl;
//調(diào)用charAt
jchar z = Object::env->CallCharMethod(object,mid,i);
//返回結(jié)果。
return z;
}
int String::compareTo(char *anotherString)
{
if (Object::env ==NULL){
cout << "JVM is not created" << endl;
exit(-1);
}
if (clazz ==0 ){
cout << "Class is not found" << endl;
exit(-1);
}
if (object ==0 ){
cout << "String object is not created" << endl;
exit(-1);
}
jmethodID mid;
//(Ljava/lang/String;)I表示參數(shù)為java.lang.String,返回int
mid= Object::env->GetMethodID(clazz,"compareTo", "(Ljava/lang/String;)I");
if (mid==0){
cerr<< "GetMethodID Error for class" << endl;
exit(-1);
}
jstring jstr = Object::env->NewStringUTF(anotherString);
cout << "invoking method" << endl;
//調(diào)用方法
jint z=Object::env->CallIntMethod(object,mid,jstr);
//返回結(jié)果
return z;
}
int String::indexOf(char *str)
{
if (Object::env ==NULL){
cout << "JVM is not created" << endl;
exit(-1);
}
if (clazz ==0 ){
cout << "Class is not found" << endl;
exit(-1);
}
if (object ==0 ){
cout << "String object is not created" << endl;
exit(-1);
}
jmethodID mid;
mid= Object::env->GetMethodID(clazz,"indexOf", "(Ljava/lang/String;)I");
if (mid==0){
cerr<< "GetMethodID Error for class" << endl;
exit(-1);
}
jstring jstr = Object::env->NewStringUTF(str);
cout << "invoking method" << endl;
jint z=Object::env->CallIntMethod(object,mid,jstr);
return z;
}
bool String::endsWith(char *str)
{
if (Object::env ==NULL){
cout << "JVM is not created" << endl;
exit(-1);
}
if (clazz ==0 ){
cout << "Class is not found" << endl;
exit(-1);
}
if (object ==0 ){
cout << "String object is not created" << endl;
exit(-1);
}
jmethodID mid;
mid= Object::env->GetMethodID(clazz,"endsWith", "(Ljava/lang/String;)Z");
if (mid==0){
cerr<< "GetMethodID Error for class" << endl;
exit(-1);
}
jstring jstr = Object::env->NewStringUTF(str);
cout << "invoking method" << endl;
bool z = Object::env->CallBooleanMethod(object,mid,jstr);
return z;
}
const char * String::replaceAll(char *regex, char *replacement)
{
if (Object::env ==NULL){
cout << "JVM is not created" << endl;
exit(-1);
}
if (clazz ==0 ){
cout << "Class is not found" << endl;
exit(-1);
}
if (object ==0 ){
cout << "String object is not created" << endl;
exit(-1);
}
jmethodID mid;
mid= Object::env->GetMethodID(clazz,"replaceAll", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
if (mid==0){
cerr<< "GetMethodID Error for class" << endl;
exit(-1);
}
jvalue array[2];
jstring jreg = Object::env->NewStringUTF(regex);
jstring jstr = Object::env->NewStringUTF(replacement);
array[0].l=jreg;
array[1].l=jstr;
cout << "invoking method" << endl;
//傳入?yún)?shù),調(diào)用replaceAll方法
jobject z=Object::env->CallObjectMethodA(object,mid,array);
const char *result=Object::env->GetStringUTFChars((jstring)z, 0);
return (const char *)result;
}
3.測(cè)試
編寫(xiě)測(cè)試代碼
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)){
cerr << _T("Fatal Error: MFC initialization failed") << endl;
nRetCode = 1;
} else{
//創(chuàng)建JVM
Object::BeginJVM();
String test("hello");
//調(diào)用replaceAll
const char *result = test.replaceAll("l","z");
//返回結(jié)果
cout<< result <<endl;
//關(guān)閉JVM
Object::EndJVM();
}
return nRetCode;
}
4.運(yùn)行
編譯需要 jni.h和jvm.lib文件。
jni.h在[JAVA_HOME]\include
jvm.lib在[JAVA_HOME]\lib
運(yùn)行需要jvm.dll
jvm.dll在[JAVA_HOME]\ jre\bin\client
運(yùn)行結(jié)果如下:
invoking method
invoking method
hezzo
Press any key to continue
盡管本文的代碼很有意思,但我還沒(méi)有想到有什么價(jià)值,以及應(yīng)用到實(shí)際項(xiàng)目中的理由。
本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/onlymilan/archive/2006/07/20/947652.aspx