反射

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

原文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();
   

發佈了45 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章