反射

注意:下面的文章是收藏的:

原文url: http://blog.csdn.net/hbcui1984/archive/2008/07/27/2719089.aspx

 

本篇文章为在工作中使用JAVA反射的经验总结,也可以说是一些小技巧,以后学会新的小技巧,会不断更新。本文不准备讨论JAVA反射的机制,网上 有很多,大家随便google一下就可以了。

        在开始之前,我先定义一个测试类Student,代码如下:

  1. package  chb.test.reflect;   
  2.   
  3. public   class  Student {   
  4.      private   int  age;   
  5.      private  String name;   
  6.      public   int  getAge() {   
  7.          return  age;   
  8.     }   
  9.      public   void  setAge( int  age) {   
  10.          this .age = age;   
  11.     }   
  12.      public  String getName() {   
  13.          return  name;   
  14.     }   
  15.      public   void  setName(String name) {   
  16.          this .name = name;   
  17.     }   
  18.        
  19.      public   static   void  hi( int  age,String name){   
  20.         System.out.println( "大家好,我叫" +name+ ", 今年" +age+ "岁" );   
  21.     }   
  22. }<PRE></PRE>  

一、JAVA反射的常规使用步骤

    反射调用一般分为3个步骤:

  • 得到要调用类的class
  • 得到要调用的类中的方法(Method)
  • 方法调用(invoke)

     代码示例:

  1. Class cls = Class.forName( "chb.test.reflect.Student" );   
  2. Method m = cls.getDeclaredMethod( "hi" , new  Class[]{ int . class ,String. class });   
  3. m.invoke(cls.newInstance(),20, "chb" );<PRE></PRE>  

二、方法调用中的参数类型

        在方法调用中,参数类型必须正确,这里需要注意的是不能使用包装类替换基本类型,比如不能使用Integer.class代替int.class。

       如我要调用Student的setAge方法,下面的调用是正确的:

 

  1. Class cls = Class.forName( "chb.test.reflect.Student" );   
  2. Method setMethod = cls.getDeclaredMethod( "setAge" , int . class );   
  3. setMethod.invoke(cls.newInstance(),  15 );<PRE></PRE>  

 

       而如果我们用Integer.class替代int.class就会出错,如:

  1. Class cls = Class.forName( "chb.test.reflect.Student" );   
  2. Method setMethod = cls.getDeclaredMethod( "setAge" ,Integer. class );   
  3. setMethod.invoke(cls.newInstance(),  15 );<PRE></PRE>  

 

       jvm会报出如下异常:

  1. java.lang.NoSuchMethodException: chb.test.reflect.Student.setAge(java.lang.Integer)   
  2.     at java.lang.Class.getDeclaredMethod(Unknown Source)   
  3.     at chb.test.reflect.TestClass.testReflect(TestClass.java:23) < PRE > </ PRE >   

 

三、static方法的反射调用

 

       static方法调用时,不必得到对象示例,如下:

  1. Class cls = Class.forName( "chb.test.reflect.Student" );   
  2. Method staticMethod = cls.getDeclaredMethod( "hi" , int . class ,String. class );   
  3. staticMethod.invoke(cls, 20 , "chb" ); //这里不需要 newInstance   
  4. //staticMethod.invoke(cls.newInstance(),20,"chb");<PRE></PRE>   

四、private的成员变量赋值

    如果直接通过反射给类的private成员变量赋值,是不允许的,这时我们可以通过setAccessible方法解决。代码示例:

  1. Class cls = Class.forName( "chb.test.reflect.Student" );   
  2. Object student = cls.newInstance(); //得到一个实例   
  3. Field field = cls.getDeclaredField( "age" );   
  4. field.set(student,  10 );   
  5. System.out.println(field.get(student));<PRE></PRE>  

 

     运行如上代码,系统会报出如下异常:

  1. java.lang.IllegalAccessException: Class chb.test.reflect.TestClass can not access a member of class chb.test.reflect.Student with modifiers "private"   
  2.     at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)   
  3.     at java.lang.reflect.Field.doSecurityCheck(Unknown Source)   
  4.     at java.lang.reflect.Field.getFieldAccessor(Unknown Source)   
  5.     at java.lang.reflect.Field.set(Unknown Source)   
  6.     at chb.test.reflect.TestClass.testReflect(TestClass.java:20) < PRE > </ PRE >   

    解决方法:

  1. Class cls = Class.forName( "chb.test.reflect.Student" );   
  2. Object student = cls.newInstance();   
  3. Field field = cls.getDeclaredField( "age" );   
  4. field.setAccessible( true ); //设置允许访问   
  5. field.set(student,  10 );   
  6. System.out.println(field.get(student));<PRE></PRE>  

    其实,在某些场合下(类中有get,set方法),可以先反射调用set方法,再反射调用get方法达到如上效果,代码示例:

  1. Class cls = Class.forName( "chb.test.reflect.Student" );   
  2. Object student = cls.newInstance();   
  3.   
  4. Method setMethod = cls.getDeclaredMethod( "setAge" ,Integer. class );   
  5. setMethod.invoke(student,  15 ); //调用set方法   
  6.                
  7. Method getMethod = cls.getDeclaredMethod( "getAge" );   
  8. 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写文件

public   void  StringBufferDemo()  throws  IOException... {
        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文件

     /** 从目录中读取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;
    }

 

七.创建文件(文件夹)

1.创建文件夹
  /** 创建文件夹
     * 
@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();
    }

八.删除文件(目录)

1.删除文件
     /** 删除文件
     * 
@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();
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值