1. 得到某個(gè)對(duì)象的屬性
1 public Object getProperty(Object owner, String fieldName) throws Exception {
2 Class ownerClass = owner.getClass();
3
4 Field field = ownerClass.getField(fieldName);
5
6 Object property = field.get(owner);
7
8 return property;
9 }
Class ownerClass = owner.getClass():得到該對(duì)象的Class。
Field field = ownerClass.getField(fieldName):通過(guò)Class得到類聲明的屬性。
Object property = field.get(owner):通過(guò)對(duì)象得到該屬性的實(shí)例,如果這個(gè)屬性是非公有的,這里會(huì)報(bào)IllegalAccessException。
2. 得到某個(gè)類的靜態(tài)屬性
1 public Object getStaticProperty(String className, String fieldName)
2 throws Exception {
3 Class ownerClass = Class.forName(className);
4
5 Field field = ownerClass.getField(fieldName);
6
7 Object property = field.get(ownerClass);
8
9 return property;
10 }
Class ownerClass = Class.forName(className) :首先得到這個(gè)類的Class。
Field field = ownerClass.getField(fieldName):和上面一樣,通過(guò)Class得到類聲明的屬性。
Object property = field.get(ownerClass) :這里和上面有些不同,因?yàn)樵搶傩允庆o態(tài)的,所以直接從類的Class里取。
3. 執(zhí)行某對(duì)象的方法
1 public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {
2
3 Class ownerClass = owner.getClass();
4
5 Class[] argsClass = new Class[args.length];
6
7 for (int i = 0, j = args.length; i < j; i++) {
8 argsClass[i] = args[i].getClass();
9 }
10
11 Method method = ownerClass.getMethod(methodName, argsClass);
12
13 return method.invoke(owner, args);
14 }
Class owner_class = owner.getClass() :首先還是必須得到這個(gè)對(duì)象的Class。
5~9行:配置參數(shù)的Class數(shù)組,作為尋找Method的條件。
Method method = ownerClass.getMethod(methodName, argsClass):通過(guò)Method名和參數(shù)的Class數(shù)組得到要執(zhí)行的Method。
method.invoke(owner, args):執(zhí)行該Method,invoke方法的參數(shù)是執(zhí)行這個(gè)方法的對(duì)象,和參數(shù)數(shù)組。返回值是Object,也既是該方法的返回值。
4. 執(zhí)行某個(gè)類的靜態(tài)方法
1 public Object invokeStaticMethod(String className, String methodName,
2 Object[] args) throws Exception {
3 Class ownerClass = Class.forName(className);
4
5 Class[] argsClass = new Class[args.length];
6
7 for (int i = 0, j = args.length; i < j; i++) {
8 argsClass[i] = args[i].getClass();
9 }
10
11 Method method = ownerClass.getMethod(methodName, argsClass);
12
13 return method.invoke(null, args);
14 }
基本的原理和實(shí)例3相同,不同點(diǎn)是最后一行,invoke的一個(gè)參數(shù)是null,因?yàn)檫@是靜態(tài)方法,不需要借助實(shí)例運(yùn)行。
5. 新建實(shí)例
1
2 public Object newInstance(String className, Object[] args) throws Exception {
3 Class newoneClass = Class.forName(className);
4
5 Class[] argsClass = new Class[args.length];
6
7 for (int i = 0, j = args.length; i < j; i++) {
8 argsClass[i] = args[i].getClass();
9 }
10
11 Constructor cons = newoneClass.getConstructor(argsClass);
12
13 return cons.newInstance(args);
14
15 }
這里說(shuō)的方法是執(zhí)行帶參數(shù)的構(gòu)造函數(shù)來(lái)新建實(shí)例的方法。如果不需要參數(shù),可以直接使用newoneClass.newInstance()來(lái)實(shí)現(xiàn)。
Class newoneClass = Class.forName(className):第一步,得到要構(gòu)造的實(shí)例的Class。
第5~第9行:得到參數(shù)的Class數(shù)組。
Constructor cons = newoneClass.getConstructor(argsClass):得到構(gòu)造子。
cons.newInstance(args):新建實(shí)例。
6. 判斷是否為某個(gè)類的實(shí)例
1 public boolean isInstance(Object obj, Class cls) {
2 return cls.isInstance(obj);
3 }
7. 得到數(shù)組中的某個(gè)元素
1 public Object getByArray(Object array, int index) {
2 return Array.get(array,index);
3 }
其中,反射機(jī)制最重要的部分是允許你檢查類的結(jié)構(gòu)。java.lang.reflect包中的三個(gè)類Field,Method,Constructor類分別描述類的字段,方法和構(gòu)造器,它們都有一個(gè)getName方法,用來(lái)返回相應(yīng)條目的名稱。
2。使用java.util.ResourceBundle類的getBundle()方法
示例: ResourceBundle rb = ResourceBundle.getBundle(name, Locale.getDefault());
3。使用java.util.PropertyResourceBundle類的構(gòu)造函數(shù)
示例: InputStream in = new BufferedInputStream(new FileInputStream(name));
ResourceBundle rb = new PropertyResourceBundle(in);
4。使用class變量的getResourceAsStream()方法
示例: InputStream in = JProperties.class.getResourceAsStream(name);
Properties p = new Properties();
p.load(in);
5。使用class.getClassLoader()所得到的java.lang.ClassLoader的getResourceAsStream()方法
示例: InputStream in = JProperties.class.getClassLoader().getResourceAsStream(name);
Properties p = new Properties();
p.load(in);
6。使用java.lang.ClassLoader類的getSystemResourceAsStream()靜態(tài)方法
傳遞引用
既然說(shuō)java中的參數(shù)傳遞只有by value一種,為什么還要說(shuō)傳遞引用呢?實(shí)際上,java與C++一樣,同樣存在對(duì)一個(gè)對(duì)象的引用進(jìn)行傳遞的問(wèn)題,但java中的引用傳遞機(jī)制是,把原來(lái)變量中保存的內(nèi)存地址傳遞作為一個(gè)參數(shù)進(jìn)行傳遞,而不是直接把引用傳過(guò)去。所以在java中仍把它稱做按值傳參。
同樣采用例子的方式來(lái)解釋java中的引用傳遞的問(wèn)題:
.......
public static void changAge(User user){
User temp = user;//這時(shí),temp中存放了和原對(duì)象相同的內(nèi)存地址
temp.age = temp.age + 20;//對(duì)user的年齡進(jìn)行增加的操作,
/*是對(duì)temp所指向的內(nèi)存空間中的值直接進(jìn)行操作,所以會(huì)對(duì)原對(duì)象的值造成影響。就相當(dāng)于是傳遞了原對(duì)象的引用*/
}
......
User user = new User("li",25);//仍采用上例中的User
changAge(user);//改變user的年齡
System.out.println(user.age);//45
.......
有了上面例子的說(shuō)明,我們可能就會(huì)想到,如果我們的程序中私有變量是一個(gè)對(duì)象類型的變量時(shí),在主程序中有了這個(gè)私有變量的拷貝,是不是就有可能在修改這個(gè)拷貝時(shí)不小心把原來(lái)的私有變量的值也給改變了呢?
我們來(lái)看下面的例子:
class Test{
private Date date=new Date();
public Date getDate(){
return date;
}
public static void main(String[] args){
Test tt=new Test();
Date myDate=tt.getDate();//返回了一個(gè)私有變量的拷貝
System.out.println(myDate);
myDate.setTime(new Date().getTime()-(long)(10*365.25*24*3600*1000));//對(duì)新產(chǎn)生的拷貝進(jìn)行修改
System.out.println(tt.getDate());
System.out.println(myDate);
}
}
先來(lái)猜一下運(yùn)行的結(jié)果,是前兩句輸出一樣呢還是后兩句輸出一樣(最后一句輸出比第一句輸出的日期早十年)?很多人都會(huì)說(shuō)是前兩句輸出一樣.實(shí)際上,你會(huì)驚奇地發(fā)現(xiàn),輸出結(jié)果顯示后兩者輸出一樣,私有變量在程序外部被改變了,程序的封裝性遭到了破壞。
出錯(cuò)的原因很微妙.因?yàn)閙yDate和tt指向了同一個(gè)對(duì)象,對(duì)myDate的引用更改方法自動(dòng)地改變了這個(gè)類的私有方法狀態(tài)。經(jīng)過(guò)測(cè)試可以知道,如果myDate被重新賦值(比如myDate=new Date()),就不會(huì)出現(xiàn)上面的結(jié)果。但是現(xiàn)在的這個(gè)程序,私有變量還是在程序外部被改變了。
如果需要返回一個(gè)指向可變對(duì)象的引用,我們就需要克隆它,這樣就不會(huì)導(dǎo)致上面的私有變量被更改。
上面程序就應(yīng)更改為:return (Date)date.clone();就可以防止私有變量被修改的麻煩了。
再來(lái)執(zhí)行上面的程序,就會(huì)出現(xiàn)不一樣的結(jié)果。
我之前為了給一個(gè)java項(xiàng)目添加IC卡讀寫功能,曾經(jīng)查了很多資料發(fā)現(xiàn)查到的資料都是只說(shuō)到第二步,所以剩下的就只好自己動(dòng)手研究了.下面結(jié)合具體的代碼來(lái)按這三個(gè)步驟分析.
1>假設(shè)廠商提供的.h文件中定義了一個(gè)我們需要的方法:
__int16 __stdcall readData( HANDLE icdev, __int16 offset, __int16 len, unsigned char *data_buffer );
a.__int16定義了一個(gè)不依賴于具體的硬件和軟件環(huán)境,在任何環(huán)境下都占16 bit的整型數(shù)據(jù)(java中的int類型是32 bit),這個(gè)數(shù)據(jù)類型是vc++中特定的數(shù)據(jù)類型,所以我自己做的dll也是用的vc++來(lái)編譯.
b.__stdcall表示這個(gè)函數(shù)可以被其它程序調(diào)用,vc++編譯的DLL欲被其他語(yǔ)言編寫的程序調(diào)用,應(yīng)將函數(shù)的調(diào)用方式聲明為_(kāi)_stdcall方式,WINAPI都采用這種方式.c/c++語(yǔ)言默認(rèn)的調(diào)用方式是__cdecl,所以在自己做可被java程序調(diào)用的dll時(shí)一定要加上__stdcall的聲明,否則在java程序執(zhí)行時(shí)會(huì)報(bào)類型不匹配的錯(cuò)誤.
c.HANDLE icdev是windows操作系統(tǒng)中的一個(gè)概念,屬于win32的一種數(shù)據(jù)類型,代表一個(gè)核心對(duì)象在某一個(gè)進(jìn)程中的唯一索引,不是指針,在知道這個(gè)索引代表的對(duì)象類型時(shí)可以強(qiáng)制轉(zhuǎn)換成此類型的數(shù)據(jù).
這些知識(shí)都屬于win32編程的范圍,更為詳細(xì)的win32資料可以查閱相關(guān)的文檔.
這個(gè)方法的原始含義是通過(guò)設(shè)備初始時(shí)產(chǎn)生的設(shè)備標(biāo)志號(hào)icdev,讀取從某字符串在內(nèi)存空間中的相對(duì)超始位置offset開(kāi)始的共len個(gè)字符,并存放到data_buffer指向的無(wú)符號(hào)字符類型的內(nèi)存空間中,并返回一個(gè)16 bit的整型值來(lái)標(biāo)志這次的讀設(shè)備是否成功,這里真正需要的是unsigned char *這個(gè)指針指向的地址存放的數(shù)據(jù),而java中沒(méi)有指針類型,所以可以考慮定義一個(gè)返回字符串類型的java方法,原方法中返回的整型值也可以按經(jīng)過(guò)一定的規(guī)則處理按字符串類型傳出,由于HANDLE是一個(gè)類型于java中的Ojbect類型的數(shù)據(jù),可以把它當(dāng)作int類型處理,這樣java程序中的方法定義就已經(jīng)形成了:
String readData( int icdev, int offset, int len );
聲明這個(gè)方法的時(shí)候要加上native關(guān)鍵字,表明這是一個(gè)與本地方法通信的java方法,同時(shí)為了安全起見(jiàn),此文方法要對(duì)其它類隱藏,使用private聲明,再另外寫一個(gè)public方法去調(diào)用它,同時(shí)要在這個(gè)類中把本地文件加載進(jìn)來(lái),最終的代碼如下:
package test;
public class LinkDll
{
//從指定地址讀數(shù)據(jù)
private native String readData( int icdev, int offset, int len );
public String readData( int icdev, int offset, int len )
{
return this.readDataTemp( icdev, offset, len );
}
static
{
System.loadLibrary( "TestDll" );//如果執(zhí)行環(huán)境是linux這里加載的是SO文件,如果是windows環(huán)境這里加載的是dll文件
}
}
2>使用JDK的javah命令為這個(gè)類生成一個(gè)包含類中的方法定義的.h文件,可進(jìn)入到class文件包的根目錄下(只要是在classpath參數(shù)中的路徑即可),使用javah命令的時(shí)候要加上包名javah test.LinkDll,命令成功后生成一個(gè)名為test_LinkDll.h的頭文件.
文件內(nèi)容如下:
/* DO NOT EDIT THIS FILE - it is machine generated*/
#include <jni.h>
/* Header for class test_LinkDll */
#ifndef _Included_test_LinkDll #define
Included_test_LinkDll
#ifdef __cplusplus extern "C" { #endif
/*
* Class: test_LinkDll
* Method: readDataTemp
* Signature: (III)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_test_LinkDll_readDataTemp(JNIEnv *, jobject, jint, jint, jint);
#ifdef __cplusplus } #endif
#endif
可以看出,JNI為了實(shí)現(xiàn)和dll文件的通信,已經(jīng)按它的標(biāo)準(zhǔn)對(duì)方法名/參數(shù)類型/參數(shù)數(shù)目作了一定的處理,其中的JNIEnv*/jobjtct這兩個(gè)參數(shù)是每個(gè)JNI方法固有的參數(shù),javah命令負(fù)責(zé)按JNI標(biāo)準(zhǔn)為每個(gè)java方法加上這兩個(gè)參數(shù).JNIEnv是指向類型為JNIEnv_的一個(gè)特殊JNI數(shù)據(jù)結(jié)構(gòu)的指針,當(dāng)由C++編譯器編譯時(shí)JNIEnv_結(jié)構(gòu)其實(shí)被定義為一個(gè)類,這個(gè)類中定義了很多內(nèi)嵌函數(shù),通過(guò)使用"->"符號(hào),可以很方便使用這些函數(shù),如:
(env)->NewString( jchar* c, jint len )
可以從指針c指向的地址開(kāi)始讀取len個(gè)字符封裝成一個(gè)JString類型的數(shù)據(jù).
其中的jchar對(duì)應(yīng)于c/c++中的char,jint對(duì)應(yīng)于c/c++中的len,JString對(duì)應(yīng)于java中的String,通過(guò)查看jni.h可以看到這些數(shù)據(jù)類型其實(shí)都是根據(jù)java和c/c++中的數(shù)據(jù)類型對(duì)應(yīng)關(guān)系使用typedef關(guān)鍵字重新定義的基本數(shù)據(jù)類型或結(jié)構(gòu)體.
具體的對(duì)應(yīng)關(guān)系如下:
Java類型 本地類型 描述
boolean jboolean C/C++8位整型
byte jbyte C/C++帶符號(hào)的8位整型
char jchar C/C++無(wú)符號(hào)的16位整型
short jshort C/C++帶符號(hào)的16位整型
int jint C/C++帶符號(hào)的32位整型
long jlong C/C++帶符號(hào)的64位整型e
float jfloat C/C++32位浮點(diǎn)型
double jdouble C/C++64位浮點(diǎn)型
Object jobject 任何Java對(duì)象,或者沒(méi)有對(duì)應(yīng)java類型的對(duì)象
Class jclass Class對(duì)象
String jstring 字符串對(duì)象
Object[] jobjectArray 任何對(duì)象的數(shù)組
boolean[] jbooleanArray 布爾型數(shù)組
byte[] jbyteArray 比特型數(shù)組
char[] jcharArray 字符型數(shù)組
short[] jshortArray 短整型數(shù)組
int[] jintArray 整型數(shù)組
long[] jlongArray 長(zhǎng)整型數(shù)組
float[] jfloatArray 浮點(diǎn)型數(shù)組
double[] jdoubleArray 雙浮點(diǎn)型數(shù)組
更為詳細(xì)的資料可以查閱JNI文檔.
需要注意的問(wèn)題:test_LinkDll.h文件包含了jni.h文件;
3>使用vc++ 6.0編寫TestDll.dll文件,這個(gè)文件名是和java類中l(wèi)oadLibrary的名稱一致.
a>使用vc++6.0 新建一個(gè)Win32 Dynamic-Link Library的工程文件,工程名指定為TestDll
b>把源代碼文件和頭文件使用"Add Fiels to Project"菜單加載到工程中,若使用c來(lái)編碼,源碼文件后綴名為.c,若使用c++來(lái)編碼,源碼文件擴(kuò)展名為.cpp,這個(gè)一定要搞清楚,因?yàn)閷?duì)于不同的語(yǔ)言,使用JNIEnv指針的方式是不同的.
c>在這個(gè)文件里調(diào)用設(shè)備商提供的dll文件,設(shè)備商一般提供三種文件:dll/lib/h,這里假設(shè)分別為A.dll/A.lib/A.h.
這個(gè)地方的調(diào)用分為動(dòng)態(tài)調(diào)用和靜態(tài)調(diào)用靜態(tài)調(diào)用即是只要把被調(diào)用的dll文件放到path路徑下,然后加載lib鏈接文件和.h頭文件即可直接調(diào)用A.dll中的方法:
把設(shè)備商提供的A.h文件使用"Add Fiels to Project"菜單加載到這個(gè)工程中,同時(shí)在源代碼文件中要把這個(gè)A.h文件使用include包含進(jìn)來(lái);
然后依次點(diǎn)擊"Project->settings"菜單,打開(kāi)link選項(xiàng)卡,把A.lib添加到"Object/library modules"選項(xiàng)中.
具體的代碼如下:
//讀出數(shù)據(jù),需要注意的是如果是c程序在調(diào)用JNI函數(shù)時(shí)必須在JNIEnv的變量名前加*,如(*env)->xxx,如果是c++程序,則直接使用(env)->xxx
#include<WINDOWS.H>
#include<MALLOC.H>
#include<STDIO.H>
#include<jni.h>
#include "test_LinkDll.h"
#include "A.h"
JNIEXPORT jstring JNICALL Java_test_LinkDll_readDataTemp( JNIEnv *env, jobject jo, jint ji_icdev, jint ji_len )
{
//*************************基本數(shù)據(jù)聲明與定義******************************
HANDLE H_icdev = (HANDLE)ji_icdev;//設(shè)備標(biāo)志符
__int16 i16_len = (__int16)ji_len;//讀出的數(shù)據(jù)長(zhǎng)度,值為3,即3個(gè)HEX形式的字符
__int16 i16_result;//函數(shù)返回值
__int16 i16_coverResult;//字符轉(zhuǎn)換函數(shù)的返回值
int i_temp;//用于循環(huán)的中間變量
jchar jca_result[3] = { 'e', 'r', 'r' };//當(dāng)讀數(shù)據(jù)錯(cuò)誤時(shí)返回此字符串
//無(wú)符號(hào)字符指針,指向的內(nèi)存空間用于存放讀出的HEX形式的數(shù)據(jù)字符串
unsigned char* uncp_hex_passward = (unsigned char*)malloc( i16_len );
//無(wú)符號(hào)字符指針,指向的內(nèi)存空間存放從HEX形式轉(zhuǎn)換為ASC形式的數(shù)據(jù)字符串
unsigned char* uncp_asc_passward = (unsigned char*)malloc( i16_len * 2 );
//java char指針,指向的內(nèi)存空間存放從存放ASC形式數(shù)據(jù)字符串空間讀出的數(shù)據(jù)字符串
jchar *jcp_data = (jchar*)malloc(i16_len*2+1);
//java String,存放從java char數(shù)組生成的String字符串,并返回給調(diào)用者
jstring js_data = 0;
//*********讀出3個(gè)HEX形式的數(shù)據(jù)字符到uncp_hex_data指定的內(nèi)存空間**********
i16_result = readData( H_icdev, 6, uncp_hex_data );//這里直接調(diào)用的是設(shè)備商提供的原型方法.
if ( i16_result != 0 )
{
printf( "讀卡錯(cuò)誤......\n" );
//這個(gè)地方調(diào)用JNI定義的方法NewString(jchar*,jint),把jchar字符串轉(zhuǎn)換為JString類型數(shù)據(jù),返回到j(luò)ava程序中即是String
return (env)->NewString( jca_result, 3 );
}
printf( "讀數(shù)據(jù)成功......\n" );
//**************HEX形式的數(shù)據(jù)字符串轉(zhuǎn)換為ASC形式的數(shù)據(jù)字符串**************
i16_coverResult = hex_asc( uncp_hex_data, uncp_asc_data, 3 );
if ( i16_coverResult != 0 )
{
printf( "字符轉(zhuǎn)換錯(cuò)誤!\n" );
return (env)->NewString( jca_result, 3 );
}
//**********ASC char形式的數(shù)據(jù)字符串轉(zhuǎn)換為jchar形式的數(shù)據(jù)字符串***********
for ( i_temp = 0; i_temp < i16_len; i_temp++ )
jcp_data[i_temp] = uncp_hex_data[i_temp];
//******************jchar形式的數(shù)據(jù)字符串轉(zhuǎn)換為java String****************
js_data = (env)->NewString(jcp_data,i16_len);
return js_data;
}
動(dòng)態(tài)調(diào)用,不需要lib文件,直接加載A.dll文件,并把其中的文件再次聲明,代碼如下:
#include<STDIO.H>
#include<WINDOWS.H>
#include "test_LinkDll.h"
//首先聲明一個(gè)臨時(shí)方法,這個(gè)方法名可以隨意定義,但參數(shù)同設(shè)備商提供的原型方法的參數(shù)保持一致.
typedef int ( *readDataTemp )( int, int, int, unsigned char * );//從指定地址讀數(shù)據(jù)
//從指定地址讀數(shù)據(jù)
JNIEXPORT jstring JNICALL Java_readDataTemp( JNIEnv *env, jobject jo, jint ji_icdev, jint ji_offset, jint ji_len )
{
int i_temp;
int i_result;
int i_icdev = (int)ji_icdev;
int i_offset = (int)ji_offset;
int i_len = (int)ji_len;
jchar jca_result[5] = { 'e', 'r', 'r' };
unsigned char *uncp_data = (unsigned char*)malloc(i_len);
jchar *jcp_data = (jchar *)malloc(i_len);
jstring js_data = 0;
//HINSTANCE是win32中同HANDLE類似的一種數(shù)據(jù)類型,意為Handle to an instance,常用來(lái)標(biāo)記App實(shí)例,在這個(gè)地方首先把A.dll加載到內(nèi)存空間,以一個(gè)App的形式存放,然后取
得它的instance交給dllhandle,以備其它資源使用.
HINSTANCE dllhandle;
dllhandle = LoadLibrary( "A.dll" );
//這個(gè)地方首先定義一個(gè)已聲明過(guò)的臨時(shí)方法,此臨時(shí)方法相當(dāng)于一個(gè)結(jié)構(gòu)體,它和設(shè)備商提供的原型方法具有相同的參數(shù)結(jié)構(gòu),可互相轉(zhuǎn)換
readDataTemp readData;
//使用win32的GetProcAddress方法取得A.dll中定義的名為readData的方法,并把這個(gè)方法轉(zhuǎn)換為已被定義好的同結(jié)構(gòu)的臨時(shí)方法,
//然后在下面的程序中,就可以使用這個(gè)臨時(shí)方法了,使用這個(gè)臨時(shí)方法在這時(shí)等同于使用A.dll中的原型方法.
readData = (readDataTemp) GetProcAddress( dllhandle, "readData" );
i_result = (*readData)( i_icdev, i_offset, i_len, uncp_data );
if ( i_result != 0 )
{
printf( "讀數(shù)據(jù)失敗......\n" );
return (env)->NewString( jca_result, 3 );
}
for ( i_temp = 0; i_temp < i_len; i_temp++ )
{
jcp_data[i_temp] = uncp_data[i_temp];
}
js_data = (env)->NewString( jcp_data, i_len );
return js_data;
}
4>以上即是一個(gè)java程序調(diào)用第三方dll文件的完整過(guò)程,當(dāng)然,在整個(gè)過(guò)程的工作全部完成以后,就可以使用java類LinkDll中的public String radData( int, int, int )方法了,效果同直接使用c/c++調(diào)用這個(gè)設(shè)備商提供的A.dll文件中的readData方法幾乎一樣.
總結(jié):JNI技術(shù)確實(shí)是提高了java程序的執(zhí)行效率,并且擴(kuò)展了java程序的功能,但它也確確實(shí)實(shí)破壞了java程序的最重要的優(yōu)點(diǎn):平臺(tái)無(wú)關(guān)性,所以除非必須(不得不)使用JNI技術(shù),一般還是提倡寫100%純java的程序.根據(jù)自己的經(jīng)驗(yàn)及查閱的一些資料,把可以使用JNI技術(shù)的情況羅列如下:
1>需要直接操作物理設(shè)備,而沒(méi)有相關(guān)的驅(qū)動(dòng)程序,這時(shí)候我們可能需要用C甚至匯編語(yǔ)言來(lái)編寫該設(shè)備的驅(qū)動(dòng),然后通過(guò)JNI調(diào)用;
2>涉及大量數(shù)學(xué)運(yùn)算的部分,用java會(huì)帶來(lái)些效率上的損失;
3>用java會(huì)產(chǎn)生系統(tǒng)難以支付的開(kāi)銷,如需要大量網(wǎng)絡(luò)鏈接的場(chǎng)合;
4>存在大量可重用的c/c++代碼,通過(guò)JNI可以減少開(kāi)發(fā)工作量,避免重復(fù)開(kāi)發(fā).
另外,在利用JNI技術(shù)的時(shí)候要注意以下幾點(diǎn):
1>由于Java安全機(jī)制的限制,不要試圖通過(guò)Jar文件的方式發(fā)布包含本地化方法的Applet到客戶端;
2>注意內(nèi)存管理問(wèn)題,雖然在本地方法返回Java后將自動(dòng)釋放局部引用,但過(guò)多的局部引用將使虛擬機(jī)在執(zhí)行本地方法時(shí)耗盡內(nèi)存;
3>JNI技術(shù)不僅可以讓java程序調(diào)用c/c++代碼,也可以讓c/c++代碼調(diào)用java代碼.
注:有一個(gè)名叫Jawin開(kāi)源項(xiàng)目實(shí)現(xiàn)了直接讀取第三方dll文件,不用自己辛苦去手寫一個(gè)起傳值轉(zhuǎn)換作用的dll文件,有興趣的可以研究一下.但是我用的時(shí)候不太順手,有很多規(guī)則限制,像自己寫程序時(shí)可以隨意定義返回值,隨意轉(zhuǎn)換類型,用這個(gè)包的話這些都是不可能的了,所以我的項(xiàng)目還沒(méi)開(kāi)始就把它拋棄了.
轉(zhuǎn)自:http://hi.baidu.com/dinguangx/blog/item/c6f5003ddcd688c19e3d6279.html
其中plugin.txt 文件中的內(nèi)容為(自定義的內(nèi)容,可以根據(jù)自己的習(xí)慣設(shè)置):
s = select * from
sc* = select count(*) from
w = where
ss = select /*+parallel(a,6)*/ * from
打開(kāi)pl/sql developer進(jìn)入[首選項(xiàng)]->[用戶界面]->[編輯器]->[autoreplace],選中enabled復(fù)選框,再查找 plugin.txt所在的路徑,點(diǎn)擊[應(yīng)用]即完成插件的安裝。
重啟pl/sql developer,打開(kāi)sql編輯窗口,輸入s,再按空格,就可以出現(xiàn)select * from,這樣就可以不必每次都輸入這段經(jīng)常使用而又經(jīng)常打錯(cuò)的語(yǔ)句了。
vi ~/.emacs
然后重新啟動(dòng) emacs ,效率很低 ,暗自嘟囔, emacs 怎么沒(méi)有這種功能,不重起,就自動(dòng)更新 .emacs 的設(shè)置 呢?
后來(lái)我發(fā)現(xiàn),這個(gè)功能完全沒(méi)有必要,我的做法是:
都是立即生效,可以馬上試驗(yàn)一條語(yǔ)句的效果。 例如,在任何一個(gè)文件中,寫
(setq frame-title-format "emacs@%b")
把光標(biāo)停在在這條語(yǔ)句后面, C-x C-e ,馬上看到 emacs 的 標(biāo)題欄上發(fā)生變化。
我用這種方法調(diào)試我的每一個(gè)小的配置文件,按上篇文章說(shuō)的方法, 把他放在 ~/Emacs/myconfig/my-site-start.d 中。
轉(zhuǎn)自:http://ann77.emacser.com/Emacs/EmacsDotEmacsWithoutRestart.html
C-x C-c : 退出Emacs
C-x C-f : 打開(kāi)一個(gè)文件,如果文件不存在,則創(chuàng)建一個(gè)文件
C-g : 取消未完成的命令
C-z (redefined): Undo;原來(lái)C-z是掛起Emacs(然后用fg命令調(diào)出);C-x u 是默認(rèn)的命令; 移動(dòng)一下光標(biāo),再C-z就可以redo
M-d : 刪除光標(biāo)后的詞語(yǔ)
C-v : 向前翻頁(yè)
M-v : 向后翻頁(yè)
M-r : 將光標(biāo)移動(dòng)到屏幕中間那行
C-a : 移到行首
M-a : 移到句首,從行首到句首之間可能有空格
C-e : 移到行尾
M-e : 移到句尾
M-{ : 向上移動(dòng)一段
M-} : 向下移動(dòng)一段
C-right : 向前移動(dòng)一個(gè)單詞
C-left : 向后移動(dòng)一個(gè)單詞
C-up : 向前移動(dòng)一段
C-down : 向后移動(dòng)一段
M-< : 移到整個(gè)文本開(kāi)頭
M-> : 移到整個(gè)文本末尾
C-u 數(shù)字 命令 : 執(zhí)行多次(數(shù)字表示次數(shù))該命令;“M-數(shù)字 命令” 也可以
M-x goto-line : 移動(dòng)到某一行
C-l : 重繪屏幕,效果就是當(dāng)前編輯行移動(dòng)窗口中央
C-x k : 關(guān)閉當(dāng)前buffer
C-x b : 切換到前一個(gè)編輯的buffer
C-x C-b : 列出當(dāng)前所有buffer
C-x C-s : 保存當(dāng)前buffer
C-x s : 保存所有未保存的buffer,會(huì)提示你是否需要保存
C-x C-w : 文件另存為
M-space (redefined): 設(shè)置mark; C-@ 是默認(rèn)命令
C-w (redefined) : 剪切一塊區(qū)域;如果沒(méi)有設(shè)置mark,則是剪切一行
M-w (redefined) : 拷貝一塊區(qū)域;如果沒(méi)有設(shè)置mark, 則是拷貝一行
C-k : 從當(dāng)前位置剪切到行尾
C-y : 粘貼
M-y : 用C-y拉回最近被除去的文本后,換成 M-y可以拉回以前被除去的文本。鍵入多次的M-y可以拉回更早以前被除去的文本。
C-x r k : 執(zhí)行矩形區(qū)域的剪切
C-x r y : 執(zhí)行矩形區(qū)域的粘貼
C-x 0 : 關(guān)閉當(dāng)前窗口
C-x 1 : 將當(dāng)前窗口最大化
C-x 2 : 垂直分割窗口
C-x 3 : 水平分割窗口
M-o (redefined) : 在窗口之間切換; C-x o 是默認(rèn)命令
C-x 5 1/2/3/0 : 對(duì)frame類似的操作
C-x < : 窗口內(nèi)容右卷
C-x > : 窗口內(nèi)容左卷(這兩個(gè)命令在垂直分割窗口后比較有用)
(C-u) C-x ^ : 加高當(dāng)前窗口,如果有C-u,則每次加高4行
(C-u) C-x } : 加寬當(dāng)前窗口
(C-u) C-x { : 壓窄當(dāng)前窗口
ESC C-v : 在其它窗口進(jìn)行卷屏操作
C-s : 向前搜索(增量式搜索);連續(xù)C-s,跳到下一個(gè)搜索到的目標(biāo)
C-s RET : 普通搜索
C-r : 向前搜索
C-s RET C-w : 按單詞查詢
M-% : 查詢替換,也就是替換前會(huì)詢問(wèn)一下
M-x replace-string : 普通替換
M-! etags .c .h : 創(chuàng)建TAGS文件
M-. : 跳到tag所在位置
M-x list-tags : 列出tags
C-x r m : 設(shè)置書簽bookmark
C-x r b : 跳到bookmark處
C-h ? : 查看幫助信息
C-h f : 查看一個(gè)函數(shù)
C-h v : 查看一個(gè)變量
C-h k : 查看一個(gè)鍵綁定 (C-h(huán) c 也是查看鍵綁定,但是信息較簡(jiǎn)略)
C-h C-f : 查看一個(gè)函數(shù)的Info,非常有用
C-h i : 看Info
C-M-\ : 對(duì)選中區(qū)域,按照某種格式(比如C程序)進(jìn)行格式化
C-x h : 全部選中
M-! : 執(zhí)行外部shell命令
M-x shell : 模擬shell的buffer
M-x term : 模擬terminal, C-c k 關(guān)閉terminal
C-x C-q : 修改buffer的只讀屬性
翻頁(yè)
C-v 下一頁(yè)
M-v 上一頁(yè)
選擇
M-h 選擇段落
C-x h 全部選擇
普通區(qū)塊
C-SPC M-x set-mark-command 單個(gè)位置set mark
C-@ 同上
M-@ 對(duì)word進(jìn)行set Mark
M-w 先set Mark,移到光標(biāo),M-w就可以復(fù)制
C-w 剪切
矩形區(qū)塊
用這些快捷鍵要先關(guān)閉cua-mode
C-x r t 用串填充矩形區(qū)域
C-x r o 插入空白的矩形區(qū)域
C-x r y 插入之前刪除的矩形區(qū)域, 粘貼時(shí),矩形左上角對(duì)齊光標(biāo)
C-x r k 刪除矩形區(qū)域
C-x r c 將當(dāng)前矩形區(qū)域清空
寄存器
----------------------------------------------------------------------
光標(biāo)位置和窗口狀態(tài)
C-x r SPC <寄存器名> 存貯光標(biāo)位置
C-x r w <寄存器名> 保存當(dāng)前窗口狀態(tài)
C-x r f <寄存器名> 保存所有窗口狀態(tài)
C-x r j <寄存器名> 光標(biāo)跳轉(zhuǎn)
文本和數(shù)字
C-x r s <寄存器名> 將連續(xù)區(qū)塊拷貝到寄存器中
C-x r r <寄存器名> 將矩形區(qū)塊拷貝到寄存器中
C-u <數(shù)字> C-x r n <寄存器名> 將數(shù)字拷貝到寄存器中
C-x r i <寄存器名> 在緩沖區(qū)中插入寄存器內(nèi)容
M-x view-register 查看寄存器內(nèi)容
M-x list-registers 查看寄存器列表
宏模式
C-x ( 開(kāi)始一個(gè)宏的定義
C-x ) 結(jié)束一個(gè)宏的定義
C-x e 執(zhí)行宏
M-x name-last-kbd-macro 給最后一個(gè)宏命名
M-x insert-kbd-macro 在當(dāng)前文件中插入一個(gè)已定義并命名過(guò)的宏
書簽
C-x r m <name> 設(shè)置書簽
C-x r b <name> 跳轉(zhuǎn)到書簽
C-x r l 書簽列表
M-x bookmark-delete 刪除書簽
M-x bookmark-load 讀取存儲(chǔ)書簽文件
M-x bookmark-save 保存到文件
目錄模式
----------------------------------------------------------------------
C-x d M-x dired 啟動(dòng)目錄模式
C-x C-d 簡(jiǎn)單目錄
程序
C-x C-z 掛起程序
C-c C-x 退出程序
C-c k 關(guān)閉buffer
C-l 重畫屏幕
C-g 結(jié)束命令,或者假死中恢復(fù),也可以按3次ESC
文件
C-x C-s 保存
C-x C-w 另存為
C-x C-f 打開(kāi)文件
C-x C-r 只讀方式打開(kāi)
C-x C-v 讀入另外一個(gè)文件代替當(dāng)前buffer的文件
C-x s 保存所有
C-x i 將文件的內(nèi)容插入
M-x revert-buffer 恢復(fù)到原始狀態(tài)
跳轉(zhuǎn)
前/后 單位
C-f/b 字
M-f/b 詞
C-a/e 行內(nèi)
M-a/e 句
M-</> 文檔
C-p/n 行間
M-{/} 段落
C-x ]/[ 頁(yè)
C-x C-x 文件內(nèi),mark之間
M-g g 跳到指定行
M-x goto-char 跳到指定字符
編輯
M-u 后面單詞變?yōu)榇髮?
M-l 后面單詞變?yōu)樾?
M-c 后面單詞的首字母變大寫
M-/ 補(bǔ)全
C-j 從當(dāng)前位置分成兩行,相當(dāng)于RET + tab
M-( 插入()
C-q tab 插入tab
C-q C-m 插入^M
M-; 插入注釋
C-o 回車
刪除
M-d 后一詞
C-d 后一字
M-del 前一詞
M-k 到句尾
M-" 前面的所有空白
M-z 刪到指定字母處
C-k 刪除到行尾
文本換位
C-t 字符
M-t 單詞
C-x C-t 行
M-x transpose-* 其他命令
撤銷
C-/
C-x u
C-_
C-z
重做
C-g M-x undo
C-g C-/
C-g C-z
C-g C-_
粘貼
C-y
C-v
tab/空格轉(zhuǎn)換
M-x tabify
M-x untabify
讓選擇的區(qū)塊自動(dòng)對(duì)齊
M-x indent-region
其他命令
C-u <數(shù)字> <命令> 重復(fù)命令n次
M-<數(shù)字> <命令> 同上
M-! 運(yùn)行shell命令
C-u M-! 執(zhí)行一條外部命令,并輸出到光標(biāo)位置
M-x cd 改變工作目錄
M-x pwd 當(dāng)前工作目錄
C-" 啟動(dòng)輸入法
M-` 菜單
F10 菜單
M-x eval-buffer 在.emacs的buffer中運(yùn)行,重新加載emacs配置
查找替換
----------------------------------------------------------------------
C-r 向上查找
C-s 向下查找
C-s C-w 向下查找,光標(biāo)位置的單詞作為查找字符串
C-s C-y 向下查找,光標(biāo)位置到行尾作為查找字符串
C-s RET <查找字符串> RET 非遞增查找
C-s RET C-w 不受換行、空格、標(biāo)點(diǎn)影響
C-M-s 正則式向下查找
用向上查找命令就將上面命令的s替換為r
M-% 替換
C-M-% 正則式替換
y 替換當(dāng)前的字符串并移動(dòng)到下一個(gè)字符串
n 不替換當(dāng)前字符串,直接移動(dòng)到下一個(gè)字符串
! 進(jìn)行全局替換,并要求不再顯示
. 替換當(dāng)前字符串,然后退出查找替換操作
q 退出查找替換操作,光標(biāo)定位到操作開(kāi)始時(shí)的位置
其他命令
M-x replace-*
M-x search-*
窗口
C-x 0 關(guān)掉當(dāng)前窗口
C-x 1 關(guān)掉其他窗口
C-x o 切換窗口
C-x 2 水平兩分窗口
C-x 3 垂直兩分窗口
C-x 5 2 新frame
buffer
C-x C-b 查看
C-x b 切換
C-x C-q 設(shè)為只讀
C-x k 刪除
C-x left/right 切換
一,配置JSF
把JSF2.0內(nèi)的兩個(gè)開(kāi)發(fā)包jsf-api.jar,jsf-impl.jar拷貝到Eclipse項(xiàng)目的lib目錄中(建立web項(xiàng)目略). 在web.xml中添加以下內(nèi)容:
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<context-param>
<param-name>com.sun.faces.resourceUpdateCheckPeriod</param-name>
<param-value>-1</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.do</url-pattern> <!--此處名字可以隨便起,可以是*.action,*.faces 等 我習(xí)慣用*.do-->
</servlet-mapping>
這樣jsf框架已經(jīng)添加進(jìn)項(xiàng)目中了.接下來(lái)就要建立Bean了.如下所示:
package com.joy.jsf.beans;
//@ManagedBean(name="users") jsf2.0可以不用配置文件來(lái)管理bean
public class User {
private String userName;
private String userPassword;
private String errorMessage;
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String Check() {
if (!this.userName.equals("JOY") || !this.userPassword.equals("123456")) {
this.errorMessage = "名稱或密碼錯(cuò)誤";
return "failure";
} else {
return "success";
}
}
}
之后要定制導(dǎo)航規(guī)則,新建face-config.xml 如下所示:
<faces-config>
<!-- <application>
<variable-resolver>
org.springframework.web.jsf.DelegatingVariableResolver
</variable-resolver>
</application> -->
<navigation-rule>
<from-view-id>/index.jsp</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/pages/welcome.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>failure</from-outcome>
<to-view-id>/index.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<!--<managed-bean> <managed-bean-name>users</managed-bean-name> <managed-bean-class>
com.joy.jsf.beans.User </managed-bean-class> <managed-bean-scope>request</managed-bean-scope>
</managed-bean> -->
</faces-config>
配置文件建立完了,添加view頁(yè)面. index.jsp和welcome.jsp
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="f" uri=">
<%@ taglib prefix="h" uri=">
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>首頁(yè)</title>
</head>
<body>
<f:view>
<h:form>
姓名:<h:inputText value="#{users.userName }" /><br/>
密碼:<h:inputText value="#{users.userPassword }"></h:inputText><br/>
<h:commandButton value="submit" action="#{users.Check}"></h:commandButton>
<br/>
<h:outputText value="#{users.errorMessage}"/><p>
</h:form>
</f:view>
</body>
</html>
welcome.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="f" uri=">
<%@ taglib prefix="h" uri=">
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>歡迎回來(lái)</title>
</head>
<body>
<f:view>
<h:form>
Hello <h:outputText value="#{users.userName }"></h:outputText> ,Welcome to here!
</h:form>
</f:view>
</body>
</html>
保存,部署,運(yùn)行http://localhost:8080/JSF/index.do
二,整合Spring
添加spring開(kāi)發(fā)包,spring.jar,stadard.jar,commons-logging.jar,jstl.jar
web.xml 添加如下配置
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<description>添加Srping支持</description>
<display-name>Spring</display-name>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
然后在face-config.xml中添加
<application>
<variable-resolver>
org.springframework.web.jsf.DelegatingVariableResolver
</variable-resolver>
</application>
從spring工廠中獲取bean ,如果和jsf的托管bean一起使用,則托管bean的優(yōu)先級(jí)要高于spring.
添加applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<bean id="users" class="com.joy.jsf.beans.User">
重新部署,運(yùn)行. OK.
<beans xmlns="
xmlns:xsi="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
</bean>
</beans>