不藏了,這些Java反射用法總結都告訴大家

摘要:Java反射是一種很是強大的機制,它能夠在同一個系統中去檢測內部的類的字段、方法和構造函數。它很是多的Java框架中,都大量應用了反射技術,如Hibernate和Spring。能夠說,反射機制的特徵讓Java能夠構建異常強大,具有柔性的系統。

本文分享自華爲雲社區《JAVA編程不可不知的反射用法總結丨【奔跑吧!JAVA】》,原文做者:jackwangcumt 。java

Java反射是一種很是強大的機制,它能夠在同一個系統中去檢測內部的類的字段、方法和構造函數。它很是多的Java框架中,都大量應用了反射技術,如Hibernate和Spring。能夠說,反射機制的特徵讓Java能夠構建異常強大,具有柔性的系統。編程

雖然Java反射機制存在效率低、速度慢和安全性不高等弊端,但在不少場景下,這些特徵並非主要的因素,或者能夠經過緩存或者JVM優化等來逐步提高執行效率。數組

根據網上的說法,反射技術可以檢查或修改在JVM中應用程序在運行時的行爲,這是一個比較高級的語言特性和一種強大的技術,反射可使應用程序實現原本不可能的操做。緩存

下面對Java反射的基礎知識進行說明和總結:安全

首先定義一個MyBase類,其中有私有字段,也有公有字段。同時也有公有方法和私有方法。MyBase類示例以下:框架

package com.hwdev.demo;
/**
 * 基類示例
 * @author wangming
 */
public class MyBase {
    //公有字段  
    public int version  = 1;
    //私有字段
    private String date = "2021-05-18" ;
    //公有方法   
    public void say2(String msg){
	System.out.println("Hello " + msg);
    }
    //私有方法
    private String getData(){
        return this.date;
    }
}

這裏再定義一個Hello類,它繼承自MyBase類,經過繼承主要用於驗證一下反射對於父類、子類的反射用法。函數

package com.hwdev.demo;
/**
 *
 * @author wangming
 */
public class Hello extends MyBase {
 
    public String author = "JackWang" ;
    public int version  = 1;
    private String company = "kzcloud" ;
 
    public void say(String msg){
	     System.out.println("Hello " + msg);
    }
    public void setAuthor(String author){
	    this.author = author;
    }
    public String getAuthor(){
        return this.author;
    }
    private int getVersion(){
        return this.version;
    }
}

關於Java反射,功能強大的就是能夠經過字符串配置來動態從系統中調用方法或者修改其中某個對象的字段值,而Class.forName方法便可以經過傳入類全路徑字符串名稱來獲取對應的Class對象,很是的方便。另外經過getField方法和GetMethod方法能夠獲取指定字段和方法,並動態調用。優化

package com.hwdev.demo;
import java.lang.reflect.*;
import java.util.Arrays;
/**
 * 反射第一種用法 Class.forName
 * @author wangming
 */
public class ReflectDemo01 {
 
     public static void Test() {
        try
        {
            //經過字符串全路徑類名查找Class
            Class helloC = Class.forName("com.hwdev.demo.Hello"); 
            //獲取全部公有的字段數組,私有的沒法獲取   
            Field [] fields = helloC.getFields();
            //打印字段數組內容
            System.out.println(Arrays.toString(fields));
            //[public java.lang.String com.hwdev.demo.Hello.author, public int com.hwdev.demo.Hello.version]
            //實例化
            Object obj = helloC.newInstance();
            //獲取特定字段,比遍歷Field[]效率更高
            Field f = helloC.getField("author");
            if (f != null){
                //關閉安全檢查,提升效率
                f.setAccessible(true);
                //獲取字段author內容
                String author = (String)f.get(obj);
                System.out.println("author=" + author);
                //author=JackWang
            }
            //獲取全部公有的方法數組,私有的沒法獲取 
            Method [] methods = helloC.getMethods();
            //打印方法數組內容,子類等方法也能夠獲取到
            System.out.println(Arrays.toString(methods));
            //本類全部方法
            Method [] methods2 = helloC.getDeclaredMethods();
            //打印方法數組內容
            System.out.println(Arrays.toString(methods2));
            //獲取特定方法,第二個參數String.class爲say方法的參數類型
            //say(java.lang.String)
            Method m = helloC.getDeclaredMethod("say",String.class); 
            if (m != null){
                //關閉安全檢查,提升效率
                m.setAccessible(true);
                //獲取字段author內容
                Object returnValue = m.invoke(obj, new Object[]{"Java"});
                //Hello Java
                if (returnValue!=null){
                    System.out.println("returnValue =" + returnValue);    
                }
            }
 
        }catch(ClassNotFoundException | SecurityException ex){
            ex.printStackTrace();
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
    }    
}

這裏須要注意:xxx.getMethods()方法默認狀況下,會返回本類、父類、父接口的公有方法,而xxx.getDeclaredMethods()返回本類的 全部方法,包括私有的方法。同理,反射API中其餘getXXX和getDeclaredXXX的用法相似。this

package com.hwdev;
import com.hwdev.demo.ReflectDemo01;
/**
 * 
 * @author wangming
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        //反射第一種用法 Class.forName
        ReflectDemo01.Test();
    }    
}

執行程序,輸出的結果以下:url

[public java.lang.String com.hwdev.demo.Hello.author, public int com.hwdev.demo.Hello.version, public int com.hwdev.demo.MyBase.version]
author=JackWang
[public void com.hwdev.demo.Hello.say(java.lang.String), public void com.hwdev.demo.Hello.setAuthor(java.lang.String), public java.lang.String com.hwdev.demo.Hello.getAuthor(), public void com.hwdev.demo.MyBase.say2(java.lang.String), public final void java.lang.Object.wait() throws java.lang.InterruptedException, public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException, public boolean java.lang.Object.equals(java.lang.Object), public java.lang.String java.lang.Object.toString(), public native int java.lang.Object.hashCode(), public final native java.lang.Class java.lang.Object.getClass(), public final native void java.lang.Object.notify(), public final native void java.lang.Object.notifyAll()]
[public void com.hwdev.demo.Hello.say(java.lang.String), public void com.hwdev.demo.Hello.setAuthor(java.lang.String), public java.lang.String com.hwdev.demo.Hello.getAuthor(), private int com.hwdev.demo.Hello.getVersion()]
Hello Java

從輸出結果上來看,Field [] fields = helloC.getFields();不但能夠獲取Hello類的公有字段,還能夠獲取到父類MyBase的公有字段:com.hwdev.demo.MyBase.version

而Method [] methods2 = helloC.getDeclaredMethods();則能夠獲取本類,即Hello類全部的方法,包括公有的方法和私有的方法。所以,Java反射能夠訪問類的私有字段和方法,從而暴露內部的信息,這也是Java反射有安全問題的緣由。

因爲Java方法支持重載,所以同名的方法能夠存在多個,即參數不一樣,所以在用反射調用方法時,須要指定方法的參數類型,這樣就能夠明確調到的具體是哪一個方法簽名,如Method m = helloC.getDeclaredMethod("say",String.class); 調用的是public void com.hwdev.demo.Hello.say(java.lang.String) 。

除了能夠用Class.forName來進行反射外,還能夠經過以下方式來獲取反射對象:

Hello hello = new Hello();
Class helloC = hello.getClass();
Field [] fields = helloC.getFields();
//////////////////////////////////////////
Class helloC = Hello.class; 
Field [] fields = helloC.getFields();

下面介紹一下如何用Java反射修改私有字段和調用私有方法的示例:

package com.hwdev.demo;
import java.lang.reflect.*;
/**
 * 反射訪問私有字段和方法
 * @author wangming
 */
public class ReflectDemo02 {
 
     public static void Test() {
        try
        {
            //經過已有類查找Class         
            Class helloC = Hello.class; 
            //實例化
            Object obj = helloC.newInstance();
            //獲取特定私有字段
            Field f = helloC.getDeclaredField("company");
            if (f != null){
                //私有必須開啓
                f.setAccessible(true);
                //設置私有字段值
                f.set(obj, "newKZ");
                //獲取字段author內容
                String fv = (String)f.get(obj);
                System.out.println("company=" + fv);
                //company=newKZ
            } 
            //獲取私有方法
            Method m = helloC.getDeclaredMethod("getVersion", null); 
            if (m != null){
                //私有必須開啓
                m.setAccessible(true);
                Object returnValue = m.invoke(obj, null);
                if (returnValue!=null){
                    //returnValue =1
                    System.out.println("returnValue =" + returnValue);    
                }
            }
 
        }catch(SecurityException ex){
            ex.printStackTrace();
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
    }    
}

另外,Java反射能夠獲取註解信息,這個對於ORM框架來說,用的很是多。

package com.hwdev.demo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 註解示例
 * @author wangming
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ORMAnnotation {
    public String FieldName();
    public String FieldType();
}

其中,@Retention(RetentionPolicy.RUNTIME)表示註解能夠在運行時經過反射訪問。@Target(ElementType.FIELD) 表示這個註解只能用在字段上面。同理,能夠把FIELD改成Type或者Method等。

package com.hwdev.demo;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
/**
 * 反射或者字段註解
 * @author wangming
 */
public class ReflectDemo03 {
 
     public static void Test() {
        try
        {
 
            Class helloC = Class.forName("com.hwdev.demo.HelloAnnotation");      
            Field[] fields = helloC.getDeclaredFields();
            for(Field f : fields){
                 //關閉安全檢查,提升效率
                f.setAccessible(true);
                Annotation ann = f.getAnnotation(ORMAnnotation.class);
                if(ann instanceof ORMAnnotation){
                    ORMAnnotation ormAnn = (ORMAnnotation) ann;
                    System.out.println("FieldName=" + ormAnn.FieldName());
                    System.out.println("FieldType=" + ormAnn.FieldType());
                }
 
            }          
 
        }catch(ClassNotFoundException | SecurityException ex){
            ex.printStackTrace();
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
    }    
}

執行此示例,則輸出以下:

FieldName=f_author
FieldType=varchar(50)
FieldName=f_ver
FieldType=int

再次,介紹一下如何用反射獲取方法參數個數和類型,其中包含泛型的信息獲取:

package com.hwdev.demo;

import java.util.ArrayList;
import java.util.List;

/**
 * 泛型示例
 * @author wangming
 */
public class GenericCls {
   protected List<String> myList = new ArrayList(); 
   public GenericCls(int size){      
       for(int i = 0;i<size;i++){
           myList.add("item"+i);
       }
   }
   public  List<String> getList(){ 
      return this.myList;
   }
   public String getList(int idx){ 
      return this.myList.get(idx);
   }
}
package com.hwdev.demo;
import java.lang.reflect.*;
/**
 * 反射獲取方法參數
 * @author wangming
 */
public class ReflectDemo05 {
 
     public static void Test() {
        try
        {
 
            Class helloC = Class.forName("com.hwdev.demo.GenericCls"); 
            //構造函數調用
            Object obj = helloC.getConstructor(int.class).newInstance(3);
            Method method = helloC.getMethod("getList", int.class);
            Class<?> returnType = method.getReturnType();
            System.out.println("ReturnType = " + returnType.getName());
            Parameter[] params = method.getParameters();
            for(Parameter p : params){    
                System.out.println("ParameterizedType = " + p.getParameterizedType());
                System.out.println("getModifiers = " + p.getModifiers());
                System.out.println("getName = " + p.getName());
                System.out.println("getType = " + p.getType());
            }
           //調用方法
           Object ret =  method.invoke(obj, new Object[]{2});
           System.out.println("ret = " + ret.toString());
            Method method2 = helloC.getMethod("getList", null);
            Type greturnType = method2.getGenericReturnType();
            System.out.println("getGenericReturnType = " + returnType.getName());
            if(greturnType instanceof ParameterizedType){
                ParameterizedType type = (ParameterizedType) greturnType;
                System.out.println("type = " + type.getTypeName());
                Type[] typeArguments = type.getActualTypeArguments();
                for(Type typeArgument : typeArguments){
                    Class typeArgClass = (Class) typeArgument;
                    System.out.println("typeArgClass = " + typeArgClass);
                }
            }
        }catch(ClassNotFoundException | SecurityException ex){
            ex.printStackTrace();
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
    }    
}

執行上述示例,輸出以下所示。

ReturnType = java.lang.String
ParameterizedType = int
getModifiers = 0
getName = arg0
getType = int
ret = item2
getGenericReturnType = java.lang.String
type = java.util.List<java.lang.String>
typeArgClass = class java.lang.String

關於反射還有很是多的知識點能夠講解,好比利用反射技術實現插件的動態加載等。反射的效率問題,能夠經過使用高效的第三方反射庫,或者加入緩衝機制來解決,這裏再也不贅述。

 

點擊關注,第一時間瞭解華爲雲新鮮技術~

相關文章
相關標籤/搜索