❶ 用java怎麼調用C語言已經編寫好的DLL
1.用System.getProperty("user.dir"),獲取程序抄的路徑,估計你的應該是 E:\\測試程序不清楚列印一下就可以了。System.getProperty("user.dir") + "\\DLL\\CardAPI"2.unsigned char 用char就可以了unsigned int 用int就可以了,java的int值很大.....C的無符號的記得是65535 unsigned char*、和unsigned int* 這兩個是指針,第一個是取1位地址內容的,第二個根據系統而定,一般是取4位地址值的,用java 的String對應,應該可以 。
❷ 如何在C/C++中調用Java
如何在C/C++中調用Java
java跨平台的特性使Java越來越受開發人員的歡迎,但也往往會聽到不少的抱怨:用Java開發的圖形用戶窗口界面每次在啟動的時候都會跳出一個控制台窗口,這個控制台窗口讓本來非常棒的界面失色不少。怎麼能夠讓通過Java開發的GUI程序不彈出Java的控制台窗口呢?其實現在很多流行的開發環境例如JBuilder、Eclipse都是使用純Java開發的集成環境。這些集成環境啟動的時候並不會打開一個命令窗口,因為它使用了JNI(Java Native Interface)的技術。通過這種技術,開發人員不一定要用命令行來啟動Java程序,可以通過編寫一個本地GUI程序直接啟動Java程序,這樣就可避免另外打開一個命令窗口,讓開發的Java程序更加專業。
JNI答應運行在虛擬機的Java程序能夠與其它語言(例如C和C++)編寫的程序或者類庫進行相互間的調用。同時JNI提供的一整套的API,答應將Java虛擬機直接嵌入到本地的應用程序中。圖1是Sun站點上對JNI的基本結構的描述。
如何在C/C++中調用Java 三聯
本文將介紹如何在C/C++中調用Java方法,並結合可能涉及到的問題介紹整個開發的步驟及可能碰到的難題和解決方法。本文所採用的工具是Sun公司創建的 Java Development Kit (JDK) 版本 1.3.1,以及微軟公司的Visual C++ 6開發環境。
環境搭建
為了讓本文以下部分的代碼能夠正常工作,我們必須建立一個完整的開發環境。首先需要下載並安裝JDK 1.3.1,其下載地址為「http://java.sun.com」。假設安裝路徑為C:JDK。下一步就是設置集成開發環境,通過Visual C++ 6的菜單Tools→Options打開選項對話框如圖2。
將目錄C:JDKinclude和C:JDKincludewin32加入到開發環境的Include Files目錄中,同時將C:JDKlib目錄添加到開發環境的Library Files目錄中。這三個目錄是JNI定義的一些常量、結構及方法的頭文件和庫文件。集成開發環境已經設置完畢,同時為了執行程序需要把Java虛擬機所用到的動態鏈接庫所在的目錄C:JDK jreinclassic設置到系統的Path環境變數中。這里需要提出的是,某些開發人員為了方便直接將JRE所用到的DLL文件直接拷貝到系統目錄下。這樣做是不行的,將導致初始化Java虛擬機環境失敗(返回值-1),原因是Java虛擬機是以相對路徑來尋找所用到的庫文件和其它一些相關文件的。至此整個JNI的開發環境設置完畢,為了讓此次JNI旅程能夠順利進行,還必須先預備一個Java類。在這個類中將用到Java中幾乎所有有代表性的屬性及方法,如靜態方法與屬性、數組、異常拋出與捕捉等。我們定義的Java程序(Demo.java)如下,本文中所有的代碼演示都將基於該Java程序,代碼如下:
package jni.test; /** * 該類是為了演示JNI如何訪問各種對象屬性等 * @author liudong */ public class Demo { //用於演示如何訪問靜態的基本類型屬性 public static int COUNT = 8; //演示對象型屬性 public String msg; PRivate int[] counts; public Demo() { this("預設構造函數"); } /** * 演示如何訪問構造器 */ public Demo(String msg) { System.out.println(":" + msg); this.msg = msg; this.counts = null; } /** * 該方法演示如何訪問一個訪問以及中文字元的處理 */ public String getMessage() { return msg; } /** * 演示數組對象的訪問 */ public int[] getCounts() { return counts; } /** * 演示如何構造一個數組對象 */ public void setCounts(int[] counts) { this.counts = counts; } /** * 演示異常的捕捉 */ public void throwExcp() throws IllegalaccessException { throw new IllegalAccessException("exception occur."); } }
初始化虛擬機
本地代碼在調用Java方法之前必須先載入Java虛擬機,而後所有的Java程序都在虛擬機中執行。為了初始化Java虛擬機,JNI提供了一系列的介面函數Invocation API。通過這些API可以很方便地將虛擬機載入到內存中。創建虛擬機可以用函數 jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args)。對於這個函數有一點需要注重的是,在JDK 1.1中第三個參數總是指向一個結構JDK1_ 1InitArgs, 這個結構無法完全在所有版本的虛擬機中進行無縫移植。在JDK 1.2中已經使用了一個標準的初始化結構JavaVMInitArgs來替代JDK1_1InitArgs。下面我們分別給出兩種不同版本的示例代碼。
在JDK 1.1初始化虛擬機:
#include int main() { JNIEnv *env; JavaVM *jvm; JDK1_1InitArgs vm_args; jint res; /* IMPORTANT: 版本號設置一定不能漏 */ vm_args.version = 0x00010001; /*獲取預設的虛擬機初始化參數*/ JNI_GetDefaultJavaVMInitArgs(&vm_args); /* 添加自定義的類路徑 */ sprintf(classpath, "%s%c%s", vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH); vm_args.classpath = classpath; /*設置一些其他的初始化參數*/ /* 創建虛擬機 */ res = JNI_CreateJavaVM(&jvm,&env,&vm_args); if (res < 0) { fprintf(stderr, "Can't create Java VM "); exit(1); } /*釋放虛擬機資源*/ (*jvm)->DestroyJavaVM(jvm); }
JDK 1.2初始化虛擬機:
/* invoke2.c */ #include int main() { int res; JavaVM *jvm; JNIEnv *env; JavaVMInitArgs vm_args; JavaVMOption options[3]; vm_args.version=JNI_VERSION_1_2;//這個欄位必須設置為該值 /*設置初始化參數*/ options[0].optionString = "-Djava.compiler=NONE"; options[1].optionString = "-Djava.class.path=."; options[2].optionString = "-verbose:jni";//用於跟蹤運行時的信息 /*版本號設置不能漏*/ vm_args.version = JNI_VERSION_1_2; vm_args.nOptions = 3; vm_args.options = options; vm_args.ignoreUnrecognized = JNI_TRUE; res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); if (res < 0) { fprintf(stderr, "Can't create Java VM "); exit(1); } (*jvm)->DestroyJavaVM(jvm); fprintf(stdout, "Java VM destory. "); }
為了保證JNI代碼的可移植性,建議使用JDK 1.2的方法來創建虛擬機。JNI_CreateJavaVM函數的第二個參數JNIEnv *env,就是貫穿整個JNI始末的一個參數,因為幾乎所有的函數都要求一個參數就是JNIEnv *env。
訪問類方法
初始化了Java虛擬機後,就可以開始調用Java的方法。要調用一個Java對象的方法必須經過幾個步驟:
1.獲取指定對象的類定義(jclass)
有兩種途徑來獲取對象的類定義:第一種是在已知類名的情況下使用FindClass來查找對應的類。但是要注重類名並不同於平時寫的Java代碼,例如要得到類jni.test.Demo的定義必須調用如下代碼:
jclass cls = (*env)->FindClass(env, "jni/test/Demo");//把點號換成斜杠
然後通過對象直接得到其所對應的類定義:
jclass cls = (*env)-> GetObjectClass(env, obj); //其中obj是要引用的對象,類型是jobject
2.讀取要調用方法的定義(jmethodID)
我們先來看看JNI中獲取方法定義的函數:
jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig); jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass class, const char *name, const char *sig);
這兩個函數的區別在於GetStaticMethodID是用來獲取靜態方法的定義,GetMethodID則是獲取非靜態的方法定義。這兩個函數都需要提供四個參數:env就是初始化虛擬機得到的JNI環境;第二個參數class是對象的類定義,也就是第一步得到的obj;第三個參數是方法名稱;最重要的是第四個參數,這個參數是方法的定義。因為我們知道Java中答應方法的多態,僅僅是通過方法名並沒有辦法定位到一個具體的方法,因此需要第四個參數來指定方法的具體定義。但是怎麼利用一個字元串來表示方法的具體定義呢?JDK中已經預備好一個反編譯工具javap,通過這個工具就可以得到類中每個屬性、方法的定義。下面就來看看jni.test.Demo的定義:
打開命令行窗口並運行 javap -s -p jni.test.Demo 得到運行結果如下:
Compiled from Demo.java public class jni.test.Demo extends java.lang.Object { public static int COUNT; /* I */ public java.lang.String msg; /* Ljava/lang/String; */ private int counts[]; /* [I */ public jni.test.Demo(); /* ()V */ public jni.test.Demo(java.lang.String); /* (Ljava/lang/String;)V */ public java.lang.String getMessage(); /* ()Ljava/lang/String; */ public int getCounts()[]; /* ()[I */ public void setCounts(int[]); /* ([I)V */ public void throwExcp() throws java.lang.IllegalAccessException; /* ()V */ static {}; /* ()V */ }
我們看到類中每個屬性和方法下面都有一段注釋。注釋中不包含空格的內容就是第四個參數要填的內容(關於javap具體參數請查詢JDK的使用幫助)。下面這段代碼演示如何訪問jni.test.Demo的getMessage方法:
/* 假設我們已經有一個jni.test.Demo的實例obj */ jmethodID mid; jclass cls = (*env)-> GetObjectClass (env, obj);//獲取實例的類定義 mid=(*env)->GetMethodID(env,cls,"getMessage"," ()Ljava/lang/String; "); /*假如mid為0表示獲取方法定義失敗*/ jstring msg = (*env)-> CallObjectMethod(env, obj, mid); /* 假如該方法是靜態的方法那隻需要將最後一句代碼改為以下寫法即可: jstring msg = (*env)-> CallStaticObjectMethod(env, cls, mid); */
3.調用方法
為了調用對象的某個方法,可以使用函數CallMethod或者CallStaticMethod(訪問類的靜態方法),根據不同的返回類型而定。這些方法都是使用可變參數的定義,假如訪問某個方法需要參數時,只需要把所有參數按照順序填寫到方法中就可以。在講到構造函數的訪問時,將演示如何訪問帶參數的構造函數。
訪問類屬性
訪問類的屬性與訪問類的方法大體上是一致的,只不過是把方法變成屬性而已。
1.獲取指定對象的類(jclass)
這一步與訪問類方法的第一步完全相同,具體使用參看訪問類方法的第一步。
2.讀取類屬性的定義(jfieldID)
在JNI中是這樣定義獲取類屬性的方法的:
jfieldID (JNICALL *GetFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig); jfieldID (JNICALL *GetStaticFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
這兩個函數中第一個參數為JNI環境;clazz為類的定義;name為屬性名稱;第四個參數同樣是為了表達屬性的類型。前面我們使用javap工具獲取類的具體定義的時候有這樣兩行:
public java.lang.String msg; /* Ljava/lang/String; */
其中第二行注釋的內容就是第四個參數要填的信息,這跟訪問類方法時是相同的。
3.讀取和設置屬性值
有了屬性的定義要訪問屬性值就很輕易了。有幾個方法用來讀取和設置類的屬性,它們是:GetField、SetField、GetStaticField、SetStaticField。比如讀取Demo類的msg屬性就可以用GetObjectField,而訪問COUNT用GetStaticIntField,相關代碼如下:
jfieldID field = (*env)->GetFieldID(env,obj,"msg"," Ljava/lang/String;"); jstring msg = (*env)-> GetObjectField(env, cls, field);//msg就是對應Demo的msg jfieldID field2 = (*env)->GetStaticFieldID(env,obj,"COUNT","I"); jint count = (*env)->GetStaticIntField(env,cls,field2);
訪問構造函數
很多人剛剛接觸JNI的時候往往會在這一節碰到問題,查遍了整個jni.h看到這樣一個函數NewObject,它應該是可以用來訪問類的構造函數。但是該函數需要提供構造函數的方法定義,其類型是jmethodID。從前面的內容我們知道要獲取方法的定義首先要知道方法的名稱,但是構造函數的名稱怎麼來填寫呢?其實訪問構造函數與訪問一個普通的類方法大體上是一樣的,惟一不同的只是方法名稱不同及方法調用時不同而已。訪問類的構造函數時方法名必須填寫「」。下面的代碼演示如何構造一個Demo類的實例:
jclass cls = (*env)->FindClass(env, "jni/test/Demo"); /** 首先通過類的名稱獲取類的定義,相當於Java中的Class.forName方法 */ if (cls == 0) jmethodID mid = (*env)->GetMethodID(env,cls,"","(Ljava/lang/String;)V "); if(mid == 0) jobject demo = jenv->NewObject(cls,mid,0); /** 訪問構造函數必須使用NewObject的函數來調用前面獲取的構造函數的定義 上面的代碼我們構造了一個Demo的實例並傳一個空串null */
數組處理
創建一個新數組
要創建一個數組,我們首先應該知道數組元素的類型及數組長度。JNI定義了一批數組的類型jArray及數組操作的函數NewArray,其中就是數組中元素的類型。例如,要創建一個大小為10並且每個位置值分別為1-10的整數數組,編寫代碼如下:
int i = 1; jintArray array;//定義數組對象 (*env)-> NewIntArray(env, 10); for(; i<= 10; i++) (*env)->SetIntArrayRegion(env, array, i-1, 1, &i);
訪問數組中的數據
訪問數組首先應該知道數組的長度及元素的類型。現在我們把創建的數組中的每個元素值列印出來,代碼如下:
int i; /* 獲取數組對象的元素個數 */ int len = (*env)->GetArrayLength(env, array); /* 獲取數組中的所有元素 */ jint* elems = (*env)-> GetIntArrayElements(env, array, 0); for(i=0; i< len; i++) printf("ELEMENT %d IS %d ", i, elems[i]);
中文處理
中文字元的處理往往是讓人比較頭疼的事情,非凡是使用Java語言開發的軟體,在JNI這個問題更加突出。由於Java中所有的字元都是Unicode編碼,但是在本地方法中,例如用VC編寫的程序,假如沒有非凡的定義一般都沒有使用Unicode的編碼方式。為了讓本地方法能夠訪問Java中定義的中文字元及Java訪問本地方法產生的中文字元串,我定義了兩個方法用來做相互轉換。
· 方法一,將Java中文字元串轉為本地字元串
/** 第一個參數是虛擬機的環境指針第二個參數為待轉換的Java字元串定義第三個參數是本地存儲轉換後字元串的內存塊第三個參數是內存塊的大小 */ int JStringToChar(JNIEnv *env, jstring str, LPTSTR desc, int desc_len) { int len = 0; if(desc==NULLstr==NULL) return -1; //在VC中wchar_t是用來存儲寬位元組字元(UNICODE)的數據類型 wchar_t *w_buffer = new wchar_t[1024]; ZeroMemory(w_buffer,1024*sizeof(wchar_t)); //使用GetStringChars而不是GetStringUTFChars wcscpy(w_buffer,env->GetStringChars(str,0)); env->ReleaseStringChars(str,w_buffer); ZeroMemory(desc,desc_len); //調用字元編碼轉換函數(Win32 API)將UNICODE轉為ASCII編碼格式字元串 //關於函數WideCharToMultiByte的使用請參考MSDN len = WideCharToMultiByte(CP_ACP,0,w_buffer,1024,desc,desc_len,NULL,NULL); //len = wcslen(w_buffer); if(len>0 && len
· 方法二,將C的字元串轉為Java能識別的Unicode字元串
jstring NewJString(JNIEnv* env,LPCTSTR str) { if(!env !str) return 0; int slen = strlen(str); jchar* buffer = new jchar[slen]; int len = MultiByteToWideChar(CP_ACP,0,str,strlen(str),buffer,slen); if(len>0 && len < slen) buffer[len]=0; jstring js = env->NewString(buffer,len); delete [] buffer; return js; }
異常
由於調用了Java的方法,因此難免產生操作的異常信息。這些異常沒有辦法通過C++本身的異常處理機制來捕捉到,但JNI可以通過一些函數來獲取Java中拋出的異常信息。之前我們在Demo類中定義了一個方法throwExcp,下面將訪問該方法並捕捉其拋出來的異常信息,代碼如下:
/** 假設我們已經構造了一個Demo的實例obj,其類定義為cls */ jthrowable excp = 0;/* 異常信息定義 */ jmethodID mid=(*env)->GetMethodID(env,cls,"throwExcp","()V"); /*假如mid為0表示獲取方法定義失敗*/ jstring msg = (*env)-> CallVoidMethod(env, obj, mid); /* 在調用該方法後會有一個IllegalAccessException的異常拋出 */ excp = (*env)->ExceptionOccurred(env); if(excp){ (*env)->ExceptionClear(env); //通過訪問excp來獲取具體異常信息 /* 在Java中,大部分的異常信息都是擴展類java.lang.Exception,因此可以訪問excp的toString 或者getMessage來獲取異常信息的內容。訪問這兩個方法同前面講到的如何訪問類的方法是相同的。 */ }
線程和同步訪問
有些時候需要使用多線程的方式來訪問Java的方法。我們知道一個Java虛擬機是非常消耗系統的內存資源,差不多每個虛擬機需要內存大約在20MB左右。為了節省資源要求每個線程使用的是同一個虛擬機,這樣在整個的JNI程序中只需要初始化一個虛擬機就可以了。所有人都是這樣想的,但是一旦子線程訪問主線程創建的虛擬機環境變數,系統就會出現錯誤對話框,然後整個程序終止。
其實這裡面涉及到兩個概念,它們分別是虛擬機(JavaVM *jvm)和虛擬機環境(JNIEnv *env)。真正消耗大量系統資源的是jvm而不是env,jvm是答應多個線程訪問的,但是env只能被創建它本身的線程所訪問,而且每個線程必須創建自己的虛擬機環境env。這時候會有人提出疑問,主線程在初始化虛擬機的時候就創建了虛擬機環境env。為了讓子線程能夠創建自己的env,JNI提供了兩個函數:AttachCurrentThread和DetachCurrentThread。下面代碼就是子線程訪問Java方法的框架:
DWord WINAPI ThreadProc(PVOID dwParam) { JavaVM jvm = (JavaVM*)dwParam;/* 將虛擬機通過參數傳入 */ JNIEnv* env; (*jvm)-> AttachCurrentThread(jvm, (void**)&env, NULL); ......... (*jvm)-> DetachCurrentThread(jvm); }
時間
關於時間的話題是我在實際開發中碰到的一個問題。當要發布使用了JNI的程序時,並不一定要求客戶要安裝一個Java運行環境,因為可以在安裝程序中打包這個運行環境。為了讓打包程序利於下載,這個包要比較小,因此要去除JRE(Java運行環境)中一些不必要的文件。但是假如程序中用到Java中的日歷類型,例如java.util.Calendar等,那麼有個文件一定不能去掉,這個文件就是[JRE]lib zmappings。它是一個時區映射文件,一旦沒有該文件就會發現時間操作上經常出現與正確時間相差幾個小時的情況。下面是打包JRE中必不可少的文件列表(以Windows環境為例),其中[JRE]為運行環境的目錄,同時這些文件之間的相對路徑不能變。
文件名目錄 hpi.dll [JRE]in ioser12.dll [JRE]in java.dll [JRE]in net.dll [JRE]in verify.dll [JRE]in zip.dll [JRE]in jvm.dll [JRE]inclassic rt.jar [JRE]lib tzmappings [JRE]lib
由於rt.jar有差不多10MB,但是其中有很大一部分文件並不需要,可以根據實際的應用情況進行刪除。例如程序假如沒有用到Java Swing,就可以把涉及到Swing的文件都刪除後重新打包。
❸ JNI傳遞類對象的問題,如何獲取long類型
你的JNI介面定義抄錯誤,在Java層傳襲入了4個參數,你的JNI介面只有一個參數。
JNIEXPORTjintJNICALLJava_org_jniInterface_CommitTask_myReceiveMsg
(JNIEnv*env,jclassobj,jinit,jint,jstring,jobjectjobj)
前兩個參數是默認的,從第三個開始是你需要對應的java介面的參數
❹ 如何用Java程序改變系統環境變數
最近在做一個項目的時候,遇到一個要在java程序中改變linux的PATH環境變數的問題。我們知道在linux中PATH環境變數保存在用戶根目錄下的「.bashrc」和「.bash_profile這兩個隱藏文件中。用戶登錄的過程中便會把這兩個文件中的PATH路徑記錄的該用戶的shell中。如果用戶已經登錄,這時可通過命令 export PATH=add_path:$PATH來增加一個路徑為add_path的路徑。但通過此種方式增加的PATH路徑只在當前shell中有效(也就是說,當用戶通過另一個shell登錄時,PATH又變成了原來的值)。在windows中用戶已經登錄的情況下則是通過命令set來更改環境變數的。
Java語言是一門跨平台的語言,具有一次編寫到處運行的特點。在java的1.0版本中有System.getenv(String key)可以來取得操作系統的環境變數,但由於getenv()具有與操作系統緊密相關的特性,這與java的跨平台的跟本特徵相沖突,所以在java1.2中該方法被不推薦使用。而程序員在編程的過程中經常需要用到getenv(),所以在java1.5中getenv()又重新回來了。雖然可以通過getenv()來取得系統的環境變數,但是卻沒有與getenv()相對應的setenv()函數來改變系統的環境變數。
C語言是一門與平台相關的語言,在多年的發展中積累了數量相當可觀的庫函數,在stdlib.h函數庫中就有getenv(參數省略)與setenv(參數省略)來獲取和改變系統的環境變數,這就給我們一個提示:能不能在java語言中調用C語言的函數庫?JNI(java本地介面)正給了我們這樣一個選擇。
1.首先生成ChangeEnv.java文件
/**
* @author sgh
* @version 1.0.0 06/07/21
*/
public class ChangeEnv {
/**
* @param args
*/
static {
try{
System.loadLibrary("change_env");//聲明欲載入的動態鏈接庫
}
catch(UnsatisfiedLinkError e ){
System.err.println("Can not load library "+e.toString());
}
}
public native void setEnv(String name ,String value, int replace);//聲明一個本地調用介面
}
說明:
1. 動態鏈接庫在windows中是.dll文件,在linux中則是.so文件,但在System.loadLibrary("change_env")中不需要把後綴寫出 ,程序會自己判斷。
2. 本地介面聲明方式為在普通函數前加native關鍵字
2. 編譯java文件 :Javac ChangeEnv.java
3. 使用命令 javah ChangeEnv 生成ChangeEnv.h文件(ChangeEnv.h文件由程序自動生成,程序員不需要作任何改動)
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class ChangeEnv */
#ifndef _Included_ChangeEnv
#define _Included_ChangeEnv
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: ChangeEnv
* Method: setEnv
* Signature: (Ljava/lang/String;Ljava/lang/String;I)V
*/
JNIEXPORT void JNICALL Java_ChangeEnv_setEnv
(JNIEnv *, jobject, jstring, jstring, jint);
#ifdef __cplusplus
}
#endif
#endif
說明:
1.JNIEXPORT void JNICALL Java_ChangeEnv_setEnv
(JNIEnv *, jobject, jstring, jstring, jint)是本地介面函數的聲明需要在.cpp文件中實現它
4.編寫ChangeEnv.cpp
#include"ChangeEnv.h"
#include<stdio.h>
#include<stdlib.h>
//與ChangeEnv.h中函數聲明相同
JNIEXPORT void JNICALL Java_ChangeEnv_setEnv(JNIEnv * env, jobject obj, jstring name, jstring value, jint replace)
{
////從instring字元串取得指向字元串UTF編碼的指針
const char * name_char =(const char *) env->GetStringUTFChars(name ,JNI_FALSE);
const char * value_char =(const char *) env->GetStringUTFChars(value ,JNI_FALSE);
//實際調用的C庫函數
setenv(name_char,value_char,replace);
//通知虛擬機本地代碼不再需要通過name_char訪問Java字元串,否則會
//造成內存泄露
env->ReleaseStringUTFChars(name,(const char *)name_char);
env->ReleaseStringUTFChars(value,(const char *)value_char);
return ;
}
5.編譯ChangeEnv.cpp文件,生成libchange_env.so文件
gcc -I/home/disk4/sgh/sgh/jrockit/include -I/home/disk4/sgh/sgh/jrockit/include/linux -shared -o libchange_env.so ChangeEnv.cpp
說明:
1. Linux下鏈接庫名稱必須以lib開頭,否則會無法識別
6. 編寫測試程序test.java
import java.io.InputStreamReader;
import java.io.LineNumberReader;
public class test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(System.getenv("PATH"));//列印改變之前的PATH路徑
String pathPer = System.getProperty("java.library.path");
pathPer+=":.";
System.setProperty("java.library.path",pathPer);//把當前目錄加到動態鏈接庫路徑中
ChangeEnv changePath = new ChangeEnv ();//生成一個ChangeEnv對象
changePath.setEnv("PATH","$PATH:/home/disk4/sgh/sgh/soft/slurm34/bin:/home/disk4/sgh/sgh/soft/slurm34/sbin",1);
System.out.println(System.getenv("PATH"));//列印改變之後的PATH路徑
}
}
說明:
1. 可以看到PATH路徑發生了變化
JNI在windows下的使用
既然所有的.h ,.cpp文件都已寫好,我們不防順便編譯以下windows下的動態鏈接庫.dll文件。
這時我們發現在windows下的vc環境中沒有setenv(char * name ,char * value ,int replace),而只有putenv(char * key_value)函數,所以我們必須重寫ChangeEnv.cpp,為了使ChangeEnv.class對外介面保持一致性,所以我們沒有改寫ChangeEnv.java中本地函數借口的聲明。
1. 重寫ChangeEnv.cpp函數
#include"ChangeEnv.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//與ChangeEnv.h中函數聲明相同
JNIEXPORT void JNICALL Java_ChangeEnv_setEnv(JNIEnv * env, jobject obj, jstring name, jstring value, jint replace)
{
if(replace==0)//如果replace==0表示不發生置換,直接退出
return ;
////從instring字元串取得指向字元串UTF編碼的指針
const char * name_char =(const char *) env->GetStringUTFChars(name ,JNI_FALSE);
const char * value_char =(const char *) env->GetStringUTFChars(value ,JNI_FALSE);
//實際調用的C庫函數,把環境變數寫成key=value格式
char * key_value=(char *)name_char;
strcat(key_value, "=");
strcat(key_value, value_char);
putenv(key_value);
//通知虛擬機本地代碼不再需要通過name_char訪問Java字元串,否則會
//造成內存泄露
env->ReleaseStringUTFChars(name,(const char *)name_char);
env->ReleaseStringUTFChars(value,(const char *)value_char);
return ;
}
2. 在vc6中新建一個動態鏈接庫工程,添加ChangeEnv.h和ChangeEnv.cpp到該工程中去
3. 在「工具」----〉「選項-」----〉「目錄」中添加java的include路徑
C:\Program Files\Java\jdk1.5.0_06\include和C:\Program Files\Java\jdk1.5.0_06\include\win32
4. 單擊「運行」,把生成的change_env.dll拷貝到ChangeEnv.java所在的工程目錄中
5. 在ChangeEnv.java所在工程中新建一個測試程序test.java
import java.io.InputStreamReader;
import java.io.LineNumberReader;
public class test {
/**
* @param args
*/
public static void main(String[] args) {
ChangeEnv changePath = new ChangeEnv();
changePath.setEnv("PATH", "%PATH%;c:\\", 1);
System.out.println(System.getenv("PATH"));
}
}
轉載
❺ 如何在 Swift 中優雅地處理 JSON
SwiftyJSON的使用十分的簡單:
典型的NSURLSessionTask抓取Twitter的API將產生dataFromNetwork: NSData!:
你首先應該做的事情是初始化JSONValue:
let json = JSONValue(dataFromNetwork)
JSONValue是一個枚舉類型表示一個典型的JSON數據結構。
你能使用subscripts檢索不同的值從原始的JSONValue中,像這樣:
let userName:JSONValue = json[0]["user"]["name"]
注意userName仍然是一個JSONValue。那怎樣得到一個字元串呢?
你能用.string屬性得到JSON數據表示的真正值。
let userNameString = userName.string!
對每一種JSON類型, JSONValue都提供了一種屬性檢索它:
var string: String?
var number: NSNumber?
var bool: Bool?
var array: Array<JSONValue>?
var object: Dictionary<String, JSONValue>?
注意每一種屬性都是一個Optional值。這是因為JSON數據能包含任何它定義的有效類型。
因此,建議的方式是用Optional綁定檢索值:
if let name = userName.string{
//This could avoid lots of crashes caused by the unexpected data types
}
if let name = userName.number{
//As the value of the userName is Not a number. It won't execute.
.number屬性產生一個NSNumber值,在Swift中這通常不是很有用。你能用.double或者.integer得到一個Double值或者一個Int值。
if let intValue = numberValue.integer{
count += intValue
}
枚舉(Enumeration)
在Swift中JSONValue實際上是一個枚舉:
enum JSONValue {
case JNumber(NSNumber)
case JString(String)
case JBool(Bool)
case JNull
case JArray(Array<JSONValue>)
case JObject(Dictionary<String,JSONValue>)
case JInvalid(NSError)
}
你可以使用一個switch子句去更有效地獲取值:
let json = JSONValue(jsonObject)
switch json["user_id"]{
case .JString(let stringValue):
let id = stringValue.toInt()
case .JNumber(let numberValue):
let id = numberValue.integerValue
default:
println("ooops!!! JSON Data is Unexpected or Broken")
下標(Subscripts)
注意,在JSON中一個數組結構被包裝成intoArray<JSONVlaue>,它意味著數組里的每一個元素都是一個JSONValue。甚至你從JSONValue中取出一個數組,你仍然可以使用基本的屬性去獲取元素的值:
if let array = json["key_of_array"].array{
if let string = array[0].string{
//The array[0] is still a JSONValue!
}
}
對象也是一樣。因此,推薦的方式是訪問每一個數組和對象時使用JSONValue的下標。
if let string = json["key_of_array"][0].string{
}
實際上,你可以用下標訪問一個JSONValue,還不用擔心運行時錯誤導致的崩潰:
let userName = json[99999]["wrong_key"]
如果你使用推薦的方式去取數據,它是安全的:
if let userName = json[99999]["wrong_key"]["name"].string{
//It's always safe
}
列印
JSONValue遵守Printable協議.所以很容易在原始字元串中得到JSON數據:
let json = JSONValue(dataFromNetwork)
println(json)
/*You can get a well printed human readable raw JSON string:
{
"url": {
"urls": [
{
"expanded_url": null,
"url": "http://bit.ly/oauth-dancer",
"indices": [
0,
26
],
"display_url": null
}
]
}
*/
如果你不想列印出來,你可以使用.description屬性來得到上述字元串。
let printableString = json.description
調試與錯誤處理
要是JSON數據出錯或者我們錯誤地檢索數據,那會怎麼樣呢?你可以使用if語句來測試:
let json = JSONValue(dataFromNetworking)["some_key"]["some_wrong_key"]["wrong_name"]
if json{
//JSONValue it self conforms to Protocol "LogicValue", with JSONValue.JInvalid stands for false and others stands true
}
如果我們嘗試使用錯誤的鍵值或索引來訪問數據,description屬性會高數你KeyPath在哪裡出錯了.
let json = JSONValue(dataFromNetworking)["some_key"]["some_wrong_key"]["wrong_name"]
if json{
} else {
println(json)
//> JSON Keypath Error: Incorrect Keypath "some_wrong_key/wrong_name"
//It always tells you where your key went wrong
switch json{
case .JInvalid(let error):
//An NSError containing detailed error information
}
}