注意:下面的文章是收藏的:
原文url: http://blog.csdn.net/hbcui1984/archive/2008/07/27/2719089.aspx
本篇文章爲在工作中使用JAVA反射的經驗總結,也可以說是一些小技巧,以後學會新的小技巧,會不斷更新。本文不準備討論JAVA反射的機制,網上 有很多,大家隨便google一下就可以了。
在開始之前,我先定義一個測試類Student,代碼如下:
- package chb.test.reflect;
- public class Student {
- private int age;
- private String name;
- public int getAge() {
- return age;
- }
- public void setAge( int age) {
- this .age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this .name = name;
- }
- public static void hi( int age,String name){
- System.out.println( "大家好,我叫" +name+ ", 今年" +age+ "歲" );
- }
- }<PRE></PRE>
一、JAVA反射的常規使用步驟
反射調用一般分爲3個步驟:
-
得到要調用類的class
-
得到要調用的類中的方法(Method)
-
方法調用(invoke)
代碼示例:
- Class cls = Class.forName( "chb.test.reflect.Student" );
- Method m = cls.getDeclaredMethod( "hi" , new Class[]{ int . class ,String. class });
- m.invoke(cls.newInstance(),20, "chb" );<PRE></PRE>
二、方法調用中的參數類型
在方法調用中,參數類型必須正確,這裏需要注意的是不能使用包裝類替換基本類型,比如不能使用Integer.class代替int.class。
如我要調用Student的setAge方法,下面的調用是正確的:
- Class cls = Class.forName( "chb.test.reflect.Student" );
- Method setMethod = cls.getDeclaredMethod( "setAge" , int . class );
- setMethod.invoke(cls.newInstance(), 15 );<PRE></PRE>
而如果我們用Integer.class替代int.class就會出錯,如:
- Class cls = Class.forName( "chb.test.reflect.Student" );
- Method setMethod = cls.getDeclaredMethod( "setAge" ,Integer. class );
- setMethod.invoke(cls.newInstance(), 15 );<PRE></PRE>
jvm會報出如下異常:
- java.lang.NoSuchMethodException: chb.test.reflect.Student.setAge(java.lang.Integer)
- at java.lang.Class.getDeclaredMethod(Unknown Source)
- at chb.test.reflect.TestClass.testReflect(TestClass.java:23) < PRE > </ PRE >
三、static方法的反射調用
static方法調用時,不必得到對象示例,如下:
- Class cls = Class.forName( "chb.test.reflect.Student" );
- Method staticMethod = cls.getDeclaredMethod( "hi" , int . class ,String. class );
- staticMethod.invoke(cls, 20 , "chb" ); //這裏不需要 newInstance
- //staticMethod.invoke(cls.newInstance(),20,"chb");<PRE></PRE>
四、private的成員變量賦值
如果直接通過反射給類的private成員變量賦值,是不允許的,這時我們可以通過setAccessible方法解決。代碼示例:
- Class cls = Class.forName( "chb.test.reflect.Student" );
- Object student = cls.newInstance(); //得到一個實例
- Field field = cls.getDeclaredField( "age" );
- field.set(student, 10 );
- System.out.println(field.get(student));<PRE></PRE>
運行如上代碼,系統會報出如下異常:
- java.lang.IllegalAccessException: Class chb.test.reflect.TestClass can not access a member of class chb.test.reflect.Student with modifiers "private"
- at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)
- at java.lang.reflect.Field.doSecurityCheck(Unknown Source)
- at java.lang.reflect.Field.getFieldAccessor(Unknown Source)
- at java.lang.reflect.Field.set(Unknown Source)
- at chb.test.reflect.TestClass.testReflect(TestClass.java:20) < PRE > </ PRE >
解決方法:
- Class cls = Class.forName( "chb.test.reflect.Student" );
- Object student = cls.newInstance();
- Field field = cls.getDeclaredField( "age" );
- field.setAccessible( true ); //設置允許訪問
- field.set(student, 10 );
- System.out.println(field.get(student));<PRE></PRE>
其實,在某些場合下(類中有get,set方法),可以先反射調用set方法,再反射調用get方法達到如上效果,代碼示例:
- Class cls = Class.forName( "chb.test.reflect.Student" );
- Object student = cls.newInstance();
- Method setMethod = cls.getDeclaredMethod( "setAge" ,Integer. class );
- setMethod.invoke(student, 15 ); //調用set方法
- Method getMethod = cls.getDeclaredMethod( "getAge" );
- System.out.println(getMethod.invoke(student));
一.獲得控制檯用戶輸入的信息
可以返回用戶輸入的信息,不足之處在於不支持中文輸入,有待進一步改進。 二.複製文件
1.以文件流的方式複製文件 該方法經過測試,支持中文處理,並且可以複製多種類型,比如txt,xml,jpg,doc等多種格式 2.利用FileChannel和ByteBuffer來複制文件 但是,在實際操作中,上述方法並不是處理該類操作的最佳方法,我們可以用
transferTo方法或transferFrom來實現。 3.利用transferTo方法實現文件複製 三.寫文件
1.利用PrintStream寫文件 2.利用StringBuffer寫文件 該方法可以設定使用何種編碼,有效解決中文問題。 3.利用BufferedWriter寫入文件內容 利用Buffer操作IO速度會稍微快一點。 四.文件重命名
注:如果重命名的目標文件已經存在,則不會進行任何操作
五.轉移文件目錄
轉移文件目錄不等同於複製文件,複製文件是複製後兩個目錄都存在該文件,而轉移文件目錄則是轉移後,只有新目錄中存在該文件。 六.讀文件 1.利用FileInputStream讀取文件
2.利用BufferedReader讀取
在IO操作,利用BufferedReader和BufferedWriter效率會更高一
點 3.利用dom4j讀取xml文件 七.創建文件(文件夾) 八.刪除文件(目錄)
*
@return
*
@throws
IOException
*/
public
String getInputMessage()
throws
IOException
{
System.out.println(
"
請輸入您的命令∶
"
);
byte
buffer[]
=
new
byte
[
1024
];
int
count
=
System.in.read(buffer);
char
[] ch
=
new
char
[count
-
2
];
//
最後兩位爲結束符,刪去不要
for
(
int
i
=
0
;i
<
count
-
2
;i
++
)
ch[i]
=
(
char
)buffer[i];
String str
=
new
String(ch);
return
str;
}
*
@param
src 文件源目錄
*
@param
dest 文件目的目錄
*
@throws
IOException
*/
public
void
copyFile(String src,String dest)
throws
IOException
{
FileInputStream in
=
new
FileInputStream(src);
File file
=
new
File(dest);
if
(
!
file.exists())
file.createNewFile();
FileOutputStream out
=
new
FileOutputStream(file);
int
c;
byte
buffer[]
=
new
byte
[
1024
];
while
((c
=
in.read(buffer))
!=-
1
)
{
for
(
int
i
=
0
;i
<
c;i
++
)
out.write(buffer[i]);
}
in.close();
out.close();
}
*
@author
崔紅保
*
@param
oldpath 以前的目錄
*
@param
newpath 新目錄
*
@param
filename 文件名
*
@throws
IOException
*/
public
void
copyFile(String oldpath,String newpath,String filename)
throws
IOException
{
FileChannel in
=
new
FileInputStream(oldpath
+
File.separator
+
filename).getChannel();
FileChannel out
=
new
FileOutputStream(newpath
+
File.separator
+
filename).getChannel();
ByteBuffer buffer
=
ByteBuffer.allocate(
1024
);
while
(in.read(buffer)
!=-
1
)
{
buffer.flip();
out.write(buffer);
buffer.clear();
}
in.close();
out.close();
}
*
@author
崔紅保
*
@param
oldpath 以前的目錄
*
@param
newpath 新目錄
*
@param
filename 文件名
*
@throws
IOException
*/
public
void
copyFile(String oldpath,String newpath,String filename)
throws
IOException
{
FileChannel in
=
new
FileInputStream(oldpath
+
File.separator
+
filename).getChannel();
FileChannel out
=
new
FileOutputStream(newpath
+
File.separator
+
filename).getChannel();
in.transferTo(
0
,in.size(),out);
in.close();
out.close();
}
* 文件輸出示例
*/
public
void
PrintStreamDemo()
{
try
{
FileOutputStream out
=
new
FileOutputStream(
"
D:/test.txt
"
);
PrintStream p
=
new
PrintStream(out);
for
(
int
i
=
0
;i
<
10
;i
++
)
p.println(
"
This is
"
+
i
+
"
line
"
);
}
catch
(FileNotFoundException e)
{
e.printStackTrace();
}
}
File file
=
new
File(
"
/root/sms.log
"
);
if
(
!
file.exists())
file.createNewFile();
FileOutputStream out
=
new
FileOutputStream(file,
true
);
for
(
int
i
=
0
;i
<
10000
;i
++
)...
{
StringBuffer sb
=
new
StringBuffer();
sb.append(
"
這是第
"
+
i
+
"
行:
前面介紹的各種方法都不關用,爲什麼總是奇怪的問題
"
);
out.write(sb.toString().getBytes(
"
utf-8
"
));
}
out.close();
}
*
@param
filename
*/
public
void
writeFile(String filename)
{
File file
=
new
File(filename);
try
{
if
(
!
file.exists())
file.createNewFile();
FileWriter fw
=
new
FileWriter(file,
true
);
//
傳入true表示如果該文件存在,則將新內容添加到文件末尾
BufferedWriter bw
=
new
BufferedWriter(fw);
for
(
int
i
=
0
;i
<
1000
;i
++
)
bw.write(
"
這是第
"
+
(i
+
1
)
+
"
行,
應該沒錯哈
"
);
//
關閉
bw.close();
bw
=
null
;
fw.close();
fw
=
null
;
}
catch
(IOException e)
{
e.printStackTrace();
}
}
*
@param
path 文件目錄
*
@param
oldname 原來的文件名
*
@param
newname 新文件名
*/
public
void
renameFile(String path,String oldname,String newname)
{
if
(
!
oldname.equals(newname))
{
//
新的文件名和以前文件名不同時,纔有必要進行重命名
File oldfile
=
new
File(path
+
"
/
"
+
oldname);
File newfile
=
new
File(path
+
"
/
"
+
newname);
if
(newfile.exists())
//
若在該目錄下已經有一個文件和新文件名相同,則不允許重命名
System.out.println(newname
+
"
已經存在!
"
);
else
{
oldfile.renameTo(newfile);
}
}
}
*
@param
filename 文件名
*
@param
oldpath 舊目錄
*
@param
newpath 新目錄
*
@param
cover 若新目錄下存在和轉移文件具有相同文件名的文件時,是否覆蓋新目錄下文
件,cover=true將會覆蓋原文件,否則不操作
*/
public
void
changeDirectory(String filename,String oldpath,String newpath,
boolean
cover)
{
if
(
!
oldpath.equals(newpath))
{
File oldfile
=
new
File(oldpath
+
"
/
"
+
filename);
File newfile
=
new
File(newpath
+
"
/
"
+
filename);
if
(newfile.exists())
{
//
若在待轉移目錄下,已經存在待轉移文件
if
(cover)
//
覆蓋
oldfile.renameTo(newfile);
else
System.out.println(
"
在
新目錄下已經存在:
"
+
filename);
}
else
{
oldfile.renameTo(newfile);
}
}
}
*
@param
path 文件目錄
*
@return
*
@throws
DocumentException
*
@throws
IOException
*/
public
Document readXml(String path)
throws
DocumentException, IOException
{
File file
=
new
File(path);
BufferedReader bufferedreader
=
new
BufferedReader(
new
FileReader(file));
SAXReader saxreader
=
new
SAXReader();
Document document
=
(Document)saxreader.read(bufferedreader);
bufferedreader.close();
return
document;
}
*
@param
path 目錄
*/
public
void
createDir(String path)
{
File dir
=
new
File(path);
if
(
!
dir.exists())
dir.mkdir();
}
2.創建新文件
*
@param
path 目錄
*
@param
filename 文件名
*
@throws
IOException
*/
public
void
createFile(String path,String filename)
throws
IOException
{
File file
=
new
File(path
+
"
/
"
+
filename);
if
(
!
file.exists())
file.createNewFile();
}
*
@param
path 目錄
*
@param
filename 文件名
*/
public
void
delFile(String path,String filename)
{
File file
=
new
File(path
+
"
/
"
+
filename);
if
(file.exists()
&&
file.isFile())
file.delete();
}
2.刪除目錄
要利用File類的
delete()方法刪除目錄時,必須保證該目錄下沒有文件或者子目錄,否則刪除失敗,因此在實際應用中,我們要刪除目錄,必須利用遞歸刪除該目錄下的所
有子目錄和文件,然後再刪除該目錄。
*
@param
path
*/
public
void
delDir(String path)
{
File dir
=
new
File(path);
if
(dir.exists())
{
File[] tmp
=
dir.listFiles();
for
(
int
i
=
0
;i
<
tmp.length;i
++
)
{
if
(tmp[i].isDirectory())
{
delDir(path
+
"
/
"
+
tmp[i].getName());
}
else
{
tmp[i].delete();
}
}
dir.delete();
}
}
*
@param
path
*
@return
*
@throws
IOException
*/
public
String BufferedReaderDemo(String path)
throws
IOException
{
File file
=
new
File(path);
if
(
!
file.exists()
||
file.isDirectory())
throw
new
FileNotFoundException();
BufferedReader br
=
new
BufferedReader(
new
FileReader(file));
String temp
=
null
;
StringBuffer sb
=
new
StringBuffer();
temp
=
br.readLine();
while
(temp
!=
null
)
{
sb.append(temp
+
"
"
);
temp
=
br.readLine();
}
return
sb.toString();
}
*
@param
path
*
@return
*
@throws
IOException
*/
public
String FileInputStreamDemo(String path)
throws
IOException
{
File file
=
new
File(path);
if
(
!
file.exists()
||
file.isDirectory())
throw
new
FileNotFoundException();
FileInputStream fis
=
new
FileInputStream(file);
byte
[] buf
=
new
byte
[
1024
];
StringBuffer sb
=
new
StringBuffer();
while
((fis.read(buf))
!=-
1
)
{
sb.append(
new
String(buf));
buf
=
new
byte
[
1024
];
//
重新生成,避免和上次讀取的數據重複
}
return
sb.toString();