注意:下面的文章是收藏的:
原文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));
一.获得控制台用户输入的信息
* @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;
}
可以返回用户输入的信息,不足之处在于不支持中文输入,有待进一步改进。
二.复制文件
1.以文件流的方式复制文件
* @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();
}
该方法经过测试,支持中文处理,并且可以复制多种类型,比如txt,xml,jpg,doc等多种格式
2.利用FileChannel和ByteBuffer来复制文件
* @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();
}
但是,在实际操作中,上述方法并不是处理该类操作的最佳方法,我们可以用 transferTo方法或transferFrom来实现。
3.利用transferTo方法实现文件复制
* @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();
}
三.写文件
1.利用PrintStream写文件
* 文件输出示例
*/
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();
}
}
2.利用StringBuffer写文件
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();
}
该方法可以设定使用何种编码,有效解决中文问题。
3.利用BufferedWriter写入文件内容
* @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();
}
}
利用Buffer操作IO速度会稍微快一点。
四.文件重命名
* @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);
}
}
}
六.读文件
1.利用FileInputStream读取文件
2.利用BufferedReader读取
在IO操作,利用BufferedReader和BufferedWriter效率会更高一 点
3.利用dom4j读取xml文件
* @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();