JAVA和C++混编
2013-11-16 18:10
337 查看
注:此文主要参考了CSDN中火山哥的专栏的文章和另一位网友的文章
C++编译JAVA的程序例子加说明
JAVA编译C++程序:
1、先在java程序中声明native方法(public类型)
2、静态载入DLL库 static{
System.loadLibrary("Filename");
}
3、编译该JAVA程序,并生成class文件
4、利用JAVAH编译class文件生成头文件
5、在vc中新建工程-》win32工程-》DLL
6、在头文件中导入上面生成的.h文件
7、在附加包含路径里面设置java jdk的include路径
8、在相应的cpp文件中写自己的函数,如:
创建虚拟机JVM, 在程序结束的时候销毁虚拟机JVM
寻找class对象
创建class对象的实例
调用方法和修改属性
虚拟的创建
与之相关的有这样几个变量
JavaVMOption options[1]; JNIEnv *env; JavaVM *jvm; JavaVMInitArgs vm_args; JavaVM就是我们需要创建的虚拟机实例
JavaVMOption相当于在命令行里传入的参数
JNIEnv在Java调用C/C++中每个方法都会有的一个参数, 拥有一个JNI的环境
JavaVMInitArgs就是虚拟机创建的初始化参数, 这个参数里面会包含JavaVMOption
下面就是创建虚拟机
options[0].optionString = "-Djava.class.path=."; memset(&vm_args, 0, sizeof(vm_args)); vm_args.version = JNI_VERSION_1_4; vm_args.nOptions = 1; vm_args.options = options; // 启动虚拟机 status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); "-Djava.class.path=."看着眼熟吧,
这个就是传入当前路径, 作为JVM寻找class的用户自定义路径, 我们的Sample2.class就在当前路径(当然也可以不在当前路径, 你可以随便修改).
vm_args.version是Java的版本, 这个应该是为了兼容以前的JDK, 可以使用旧版的JDK, 这个宏定义是在jni.h中, 有以下四种
#define JNI_VERSION_1_1 0x00010001 #define JNI_VERSION_1_2 0x00010002 #define JNI_VERSION_1_4 0x00010004 #define JNI_VERSION_1_6 0x00010006 vm_args.nOptions的含义是, 你传入的options有多长, 我们这里就一个, 所以是1。
vm_args.options = options把JavaVMOption传给JavaVMInitArgs里面去。
然后就是启动虚拟机了status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)。
可以通过这个返回值status , 知道虚拟机是否启动成功
#define JNI_OK 0 /* success */ #define JNI_ERR (-1) /* unknown error */ #define JNI_EDETACHED (-2) /* thread detached from the VM */ #define JNI_EVERSION (-3) /* JNI version error
*/ #define JNI_ENOMEM (-4) /* not enough memory */ #define JNI_EEXIST (-5) /* VM already created */ #define JNI_EINVAL (-6) /* invalid arguments */ 寻找class对象, 并实例化
JVM在Java中都是自己启动的, 在C/C++中只能自己来启动了, 启动完之后的事情就和在Java中一样了, 不过要使用C/C++的语法.
获取class对象比较简单, FindClass(env, className).
cls = (*env)->FindClass(env, "Sample2"); 在Java中的类名格式是java.lang.String, 但是className的格式有点不同, 不是使用'.'作为分割, 而是'/', 即java/lang/String.
我们知道Java中构造函数有两种, 一种是默认的没有参数的, 一种是自定义的带有参数的. 对应的在C/C++中, 有两种调用构造函数的方法.
调用默认构造函数
// 调用默认构造函数 obj = (*env)->AllocObjdect(env, cls); 构造函数也是方法, 类似调用方法的方式.
// 调用指定的构造函数, 构造函数的名字叫做<init> mid = (*env)->GetMethodID(env, cls, "<init>", "()V"); obj = (*env)->NewObject(env, cls, mid); 调用方法和修改属性
关于方法和属性是有两个ID与之对应, 这两个ID用来标识方法和属性.
jmethodID mid; jfieldID fid; 方法分为静态和非静态的, 所以对应的有
mid = (*env)->GetStaticMethodID(env, cls, "sayHello", "(Ljava/lang/String;)Ljava/lang/String;"); mid = (*env)->GetMethodID(env, cls, "sayHello", "()Ljava/lang/String;"); 上面两个方法是同名的, 都叫sayHello, 但是签名不同, 所以可以区分两个方法.
JNI的函数都是有一定规律的, Static就表示是静态, 没有表示非静态.
方法的调用如下
jstring result = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid, arg); jstring result = (jstring)(*env)->CallObjectMethod(env, obj, mid); 我们可以看到静态方法是只需要class对象, 不需要实例的, 而非静态方法需要使用我们之前实例化的对象.
属性也有静态和非静态, 示例中只有非静态的.
获取属性ID
fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;"); 改属性的值
(*env)->SetObjectField(env, obj, fid, arg); // 修改属性 关于jstring的说明
java的String都是使用了unicode, 是双字节的字符, 而C/C++中使用的单字节的字符。
从C转换为java的字符, 使用NewStringUTF方法
jstring arg = (*env)->NewStringUTF(env, name); 从java转换为C的字符, 使用GetStringUTFChars
const char* str = (*env)->GetStringUTFChars(env, result, 0); const char* str = (*env)->GetStringUTFChars(env, result, 0);
编译和运行
编译需要头文件, 头文件在这两个目录中%JAVA_HOME%\include和%JAVA_HOME%\include\win32, 第一个是与平台无关的, 第二个是与平台有关的, 由于笔者的系统是windows, 所以是win32.
编译的时候还要一个lib文件, 是对虚拟机的支持, 保证编译通过.
cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 Sample2.c %JAVA_HOME%\lib\jvm.lib 我们可以看到在当前目录下Sample2.exe, 运行的时候需要jvm.dll(不要将其复制到当前目录下, 这样不可以运行, 会导致jvm创建失败) set PATH=%JAVA_HOME%\jre\bin\client\;%PATH% Sample2 jvm.dll在%JAVA_HOME%\jre\bin\client\目录下,
所以我把这个目录加入到PATH中, 然后就可以运行 Result of sayHello: Hello, World! Result of sayHello: Hello, icejoywoo! 关于C++的说明
本示例的C++版本, 请自行下载后面的源代码来查看, 区别不是很大.
主要是JNIEnv和JavaVM两个对象, 在C中是结构体, 是函数指针的集合, 在C++中结构体拥有类的能力, 使用起来更为简便, 与Java之间的差异更小一些.
C++编译JAVA的程序例子加说明
#include <Windows.h> #include "jni.h" #include <string> #include <iostream> #include <Mqoai.h> using namespace std; int main () { //为获得启动虚拟机函数名称做准备,具体可参考MSDN中GetProcAddress()函数的说明 //这一句的意思就是讲jint类型重新定义为*PFuncCreateJavaVM类型,加上typedef可以直接定义变量 //*PFuncCreateJavaVM是一个WINAPI函数,后面是他的参数列表 typedef jint (WINAPI* PFunCreateJavaVM) (JavaVM**, void**, void*); int res; JavaVMInitArgs vm_args;//java虚拟机初始化所需要的参数 //相当于在命令行里传入的参数 JavaVMOption options[3]; //创建一个Java虚拟机 JavaVM* jvm; //java虚拟机环境指针 JNIEnv* env; options[0].optionString = options[0].optionString = "-Djava.compiler=NONE"; //设置JAVA类文件的路径 options[1].optionString = "-Djava.class.path=.\\"; options[2].optionString = "-verbose:NONE"; vm_args.version = JNI_VERSION_1_6; vm_args.nOptions = 3; vm_args.options = options; vm_args.ignoreUnrecognized = JNI_TRUE; //动态载入JAVA程序编译所需要的DLL,并获得库实例 HINSTANCE hInstance = ::LoadLibrary("E:\\Java\\jdk\\jre\\bin\\client\\jvm.dll"); if (hInstance == NULL) return 1; //获得启动虚拟机函数在DLL库中的函数名称(具体可以参看MSDN(GetProcAddress()函数)),注意获得后必须利用强制类型转换,转换成前面定义的类型 PFunCreateJavaVM funCreateJavaVM = (PFunCreateJavaVM)::GetProcAddress(hInstance, "JNI_CreateJavaVM"); //启动虚拟机(JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)) res = (*funCreateJavaVM)(&jvm, (void**)&env, &vm_args); if (res < 0) { return -1; } //获得类对象 jclass cls = env->FindClass("Demo"); //调用默认的构造函数 jobject obj = env->AllocObject(cls); //获得方法ID(对不同的方法有不同的获取函数) jmethodID mid = env->GetMethodID(cls, "append","(Ljava/lang/String;I)Ljava/lang/String;"); //要传入JAVA程序中的参数,传入之前必须进行转码,也就是说要进行两次转码,否则会出现异常 const char szTest[] = "HELLO WORLD"; jstring arg = (*env).NewStringUTF(szTest); //方法的调用,方法调用之后的返回值也要进行转码才可以输出来 jstring msg = (jstring) env->CallObjectMethod(obj, mid, arg, 12); string str = env->GetStringUTFChars(msg,0); cout<<str<<endl; //调用指定的构造函数 jmethodID mid1 = env->GetMethodID(cls,"<init>","(Ljava/lang/String;)V"); jvalue val[1]; val[0].l = arg; jobject obj1 = env->NewObjectA(cls,mid1,val); jmethodID get = env->GetMethodID(cls,"getMessage","()Ljava/lang/String;"); jstring getstr = (jstring)env->CallObjectMethod(obj1,get); string getst = env->GetStringUTFChars(getstr,0); cout<<endl; cout<<"调用指定的构造函数"<<getst<<endl; //卸载虚拟机 jvm->DestroyJavaVM(); //释放库 ::FreeLibrary(hInstance); return 0; } public class Demo { public static int COUNT = 8; private String msg; private int[] counts; public Demo () { System.out.println ("default constructor!"); } public Demo (String msg) { System.out.println("your constructor!"); this.msg = msg; this.counts = null; } public String getMessage () { return msg; } public static String getHelloWorld () { return "hello world!"; } public String append (String str, int i) { return str + i; } public int[] getCounts () { return counts; } public void setCounts (int[] counts) { this.counts = counts; } public void throwExcp () throws IllegalAccessException { throw new IllegalAccessException ("exception occurs"); } }
JAVA编译C++程序:
1、先在java程序中声明native方法(public类型)
2、静态载入DLL库 static{
System.loadLibrary("Filename");
}
3、编译该JAVA程序,并生成class文件
4、利用JAVAH编译class文件生成头文件
5、在vc中新建工程-》win32工程-》DLL
6、在头文件中导入上面生成的.h文件
7、在附加包含路径里面设置java jdk的include路径
8、在相应的cpp文件中写自己的函数,如:
JNIEXPORT void JNICALL Java_Test_hello(JNIEnv * env, jclass obj, jstring jMsg) //这一句的函数名不固定,第二个下划线后面的字符才是函数名-就是java中要声明为native的函数名 { //这一句是字符转码,C++编译JAVA程序一样,需要转码 const char *strMsgPtr = env->GetStringUTFChars( jMsg , 0); MessageBox( 0, strMsgPtr,"Message box from VC++ ", 0 ); env->ReleaseStringUTFChars( jMsg, strMsgPtr); }9、编译生成DLL文件,然后拷贝到java类文件目录下编译即可。
创建虚拟机JVM, 在程序结束的时候销毁虚拟机JVM
寻找class对象
创建class对象的实例
调用方法和修改属性
虚拟的创建
与之相关的有这样几个变量
JavaVMOption options[1]; JNIEnv *env; JavaVM *jvm; JavaVMInitArgs vm_args; JavaVM就是我们需要创建的虚拟机实例
JavaVMOption相当于在命令行里传入的参数
JNIEnv在Java调用C/C++中每个方法都会有的一个参数, 拥有一个JNI的环境
JavaVMInitArgs就是虚拟机创建的初始化参数, 这个参数里面会包含JavaVMOption
下面就是创建虚拟机
options[0].optionString = "-Djava.class.path=."; memset(&vm_args, 0, sizeof(vm_args)); vm_args.version = JNI_VERSION_1_4; vm_args.nOptions = 1; vm_args.options = options; // 启动虚拟机 status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); "-Djava.class.path=."看着眼熟吧,
这个就是传入当前路径, 作为JVM寻找class的用户自定义路径, 我们的Sample2.class就在当前路径(当然也可以不在当前路径, 你可以随便修改).
vm_args.version是Java的版本, 这个应该是为了兼容以前的JDK, 可以使用旧版的JDK, 这个宏定义是在jni.h中, 有以下四种
#define JNI_VERSION_1_1 0x00010001 #define JNI_VERSION_1_2 0x00010002 #define JNI_VERSION_1_4 0x00010004 #define JNI_VERSION_1_6 0x00010006 vm_args.nOptions的含义是, 你传入的options有多长, 我们这里就一个, 所以是1。
vm_args.options = options把JavaVMOption传给JavaVMInitArgs里面去。
然后就是启动虚拟机了status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)。
可以通过这个返回值status , 知道虚拟机是否启动成功
#define JNI_OK 0 /* success */ #define JNI_ERR (-1) /* unknown error */ #define JNI_EDETACHED (-2) /* thread detached from the VM */ #define JNI_EVERSION (-3) /* JNI version error
*/ #define JNI_ENOMEM (-4) /* not enough memory */ #define JNI_EEXIST (-5) /* VM already created */ #define JNI_EINVAL (-6) /* invalid arguments */ 寻找class对象, 并实例化
JVM在Java中都是自己启动的, 在C/C++中只能自己来启动了, 启动完之后的事情就和在Java中一样了, 不过要使用C/C++的语法.
获取class对象比较简单, FindClass(env, className).
cls = (*env)->FindClass(env, "Sample2"); 在Java中的类名格式是java.lang.String, 但是className的格式有点不同, 不是使用'.'作为分割, 而是'/', 即java/lang/String.
我们知道Java中构造函数有两种, 一种是默认的没有参数的, 一种是自定义的带有参数的. 对应的在C/C++中, 有两种调用构造函数的方法.
调用默认构造函数
// 调用默认构造函数 obj = (*env)->AllocObjdect(env, cls); 构造函数也是方法, 类似调用方法的方式.
// 调用指定的构造函数, 构造函数的名字叫做<init> mid = (*env)->GetMethodID(env, cls, "<init>", "()V"); obj = (*env)->NewObject(env, cls, mid); 调用方法和修改属性
关于方法和属性是有两个ID与之对应, 这两个ID用来标识方法和属性.
jmethodID mid; jfieldID fid; 方法分为静态和非静态的, 所以对应的有
mid = (*env)->GetStaticMethodID(env, cls, "sayHello", "(Ljava/lang/String;)Ljava/lang/String;"); mid = (*env)->GetMethodID(env, cls, "sayHello", "()Ljava/lang/String;"); 上面两个方法是同名的, 都叫sayHello, 但是签名不同, 所以可以区分两个方法.
JNI的函数都是有一定规律的, Static就表示是静态, 没有表示非静态.
方法的调用如下
jstring result = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid, arg); jstring result = (jstring)(*env)->CallObjectMethod(env, obj, mid); 我们可以看到静态方法是只需要class对象, 不需要实例的, 而非静态方法需要使用我们之前实例化的对象.
属性也有静态和非静态, 示例中只有非静态的.
获取属性ID
fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;"); 改属性的值
(*env)->SetObjectField(env, obj, fid, arg); // 修改属性 关于jstring的说明
java的String都是使用了unicode, 是双字节的字符, 而C/C++中使用的单字节的字符。
从C转换为java的字符, 使用NewStringUTF方法
jstring arg = (*env)->NewStringUTF(env, name); 从java转换为C的字符, 使用GetStringUTFChars
const char* str = (*env)->GetStringUTFChars(env, result, 0); const char* str = (*env)->GetStringUTFChars(env, result, 0);
编译和运行
编译需要头文件, 头文件在这两个目录中%JAVA_HOME%\include和%JAVA_HOME%\include\win32, 第一个是与平台无关的, 第二个是与平台有关的, 由于笔者的系统是windows, 所以是win32.
编译的时候还要一个lib文件, 是对虚拟机的支持, 保证编译通过.
cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 Sample2.c %JAVA_HOME%\lib\jvm.lib 我们可以看到在当前目录下Sample2.exe, 运行的时候需要jvm.dll(不要将其复制到当前目录下, 这样不可以运行, 会导致jvm创建失败) set PATH=%JAVA_HOME%\jre\bin\client\;%PATH% Sample2 jvm.dll在%JAVA_HOME%\jre\bin\client\目录下,
所以我把这个目录加入到PATH中, 然后就可以运行 Result of sayHello: Hello, World! Result of sayHello: Hello, icejoywoo! 关于C++的说明
本示例的C++版本, 请自行下载后面的源代码来查看, 区别不是很大.
主要是JNIEnv和JavaVM两个对象, 在C中是结构体, 是函数指针的集合, 在C++中结构体拥有类的能力, 使用起来更为简便, 与Java之间的差异更小一些.
相关文章推荐
- 【Android性能优化】使用NDK进行Java和C++混编
- 编程设计:cosos2d-x之C++与Java混编通用接口
- java与c/c++之间的数据交互
- C/C++/Java/C#的基础类型模型的定义
- 从java到C++ 面向对象(一)
- 用C++设计一个不能被继承的类实现java final的作用
- C++与Java多态的区别
- 替换字符串中的空格(C++/Java实现)
- java调用C/C++的过程
- java访问c/c++---开篇
- C++程序员学Java系列之二一:final 关键字
- JAVA调用C++ dll库
- C++程序员学Java系列之二三:匿名对象
- Qt安卓JNI交互之(1) C++注册函数给JAVA调用
- 配置Sublime Test 3在控制台编译运行java,c,c++
- java与c++语法区别
- c++ && JAVA 的资源回收机制-对比
- [闭月羞花猫]C++ OR JAVA ? 暂别CSDN前,给各位初学者的一点建议。(大家不要悲伤,过两天我还会回来的)
- java(JNI)调用c/c++动态链接库 linux
- c++ 与java引用比较