1. 程式人生 > >Java語法之反射

Java語法之反射

數組 私有 declare put 需要 out div inter oca

一、反射機制

在前面Java語法之註解自定義註解時我們也有提到反射,要獲取類方法和字段的註解信息,必須通過Java的反射技術來獲取 Annotation對象。那什麽是反射呢?JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。它有點類似照妖鏡的作用,不管是什麽妖魔鬼怪(類或對象)都能看到它的真面目(獲取類的屬性方法、調用對象的屬性方法)。

二、Class理解

反射機制可以動態獲取類信息以及調用對象方法,那它是通過什麽實現的呢?這就要介紹下Class類了。首先明確Class也是一個類,只是它是一個描述類的類,它也可以生成對象。對於每個類而言,在JRE中有且僅有一個不變的 Class 類型的對象,而這個Class 類型的對象只能由系統建立,封裝了當前對象所對應的類的信息,有哪些屬性,方法,構造器,實現了哪些接口等等。而且每個類的實例都會記得自己是由哪個Class實例所生成。

那要獲取類信息或調用對象方法,肯定首先要獲取到該類或對象對應的Class類的實例。一般獲取Class對象有三種方式。

1. 通過類名獲取 類名.class

2. 通過對象獲取 對象.getClass()

3. 通過全類名獲取 Class.forName(全類名)

這裏我們可以使用用字符串來做驗證。輸出結果都是class java.lang.String。

package Reflection;

public class ReflectionTest {
    
    public static void main(String[] args) throws ClassNotFoundException {
       
//字符串的例子 Class clazz = null; //類名.class clazz = String.class; System.out.println(clazz); //對象.getClass() clazz = "ReflectionTest".getClass(); System.out.println(clazz); //Class.forName(全類名) clazz = Class.forName("java.lang.String"); System.out.println(clazz); } }
class java.lang.String
class java.lang.String
class java.lang.String

上面通過三種方式能獲取到Class實例,然後再了解一下Class類常用的方法

方法名

功能說明

forName(String name)

返回指定類名 name Class 對象

newInstance()

調用缺省構造函數,返回該Class對象的一個實例

newInstance(Object []args)

調用當前格式構造函數,返回該Class對象的一個實例

getName()

返回此Class對象所表示的實體(類、接口、數組類、基本類型或void)名稱

getSuperClass()

返回當前Class對象的父類的Class對象

getInterfaces()

獲取當前Class對象的接口

getClassLoader()

返回該類的類加載器

getSuperclass()

返回表示此Class所表示的實體的超類的Class

getFields()

獲取類中public類型的屬性

getField(String name)

獲取類特定的方法,name參數指定了屬性的名稱

getDeclaredFields()

獲取類中所有的屬性(publicprotecteddefaultprivate),但不包括繼承的屬性

getDeclaredField(String name)

獲取類特定的方法,name參數指定了屬性的名稱

getConstructors()

獲取類中的公共方法

getConstructor(Class[] params)

獲取類的特定構造方法,params參數指定構造方法的參數類型

getDeclaredConstructors()

獲取類中所有的構造方法(publicprotecteddefaultprivate)

getDeclaredConstructor(Class[] params)

獲取類的特定構造方法,params參數指定構造方法的參數類型

getMethods()

獲得類的public類型的方法

getMethod(String name, Class[] params)

獲得類的特定方法,name參數指定方法的名字,params參數指定方法的參數類型

getDeclaredMethods()

獲取類中所有的方法(publicprotecteddefaultprivate)

getDeclaredMethod(String name, Class[] params)

獲得類的特定方法,name參數指定方法的名字,params參數指定方法的參數類型

三、反射的使用

這裏要著重介紹下上面API的使用,因為在後面要學習的Spring中IOC的原理就是反射加工廠模式。學好反射API有助於理解Spring框架內部實現。為了演示Class方法的使用,在註解demo的基礎上對Person、Student類進行了修改。

Person類:

技術分享圖片
package Reflection;

@CustomDescription(description="基類")
@CustomDescription(description="人")
public class Person {
    
    private String Name;
    
    public String getName() {
        return Name;
    }
    
    public void setName(String name) {
        Name = name;
    }
    
    public String PersonPublicMethod(String str)
    {
        return str;
    }
    
    public Person(String name) {
        Name = name;
    }

    public String PersonPrivateMethod(String str)
    {
        return str;
    }

    public Person() {
        super();
    }
    
}
View Code

Student類:

技術分享圖片
package Reflection;

@CustomDescription(description="學生")
@CustomDescription(description="人")
public class Student extends Person {
    public String StudentId;

    public String getStudentId() {
        return StudentId;
    }

    public void setStudentId(String studentId) {
        StudentId = studentId;
    }
    
    public String StudentPublicMethod(String str)
    {
        return str;
    }
    
    private String StudentPrivateMethod(String str)
    {
        return str;
    }
    
    public Student(String name, String studentId) {
        super(name);
        StudentId = studentId;
    }

    public Student() {
        
    }
}
View Code

一、描述方法Method

描述方法的主要是4個獲取方法getMethods、getMethod、getDeclaredMethods、getDeclaredMethod和1個調用方法invoke。
getMethods:獲取clazz對應類中的所有方法,不能獲取private方法,且獲取從父類繼承來的所有方法包括私有父類的私有方法
getMethod:獲取clazz對應類中指定方法名和參數類型的方法,不能獲取private方法,且獲取從父類繼承來的所有方法包括私有父類的私有方法,因為存在同方法名不同參數這種情況,所以只有同時指定方法名和參數類型才能唯一確定一個方法。
getDeclaredMethods:獲取所有方法,包括私有方法,所有聲明的方法,都可以獲取到,且只獲取當前類的方法。
getDeclaredMethod:獲取clazz對應類中指定方法名和參數類型的方法,包括私有方法,所有聲明的方法,都可以獲取到,且只獲取當前類的方法。
Invoke:執行方法,第一個參數表示執行哪個對象的方法,剩下的參數是執行方法時需要傳入的參數,私有方法的執行,必須在調用invoke之前加上一句method.setAccessible(true);

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

        Class clazz = Class.forName("Reflection.Student");
        Method method=null;
        Method[] methods=null;
        
        methods = clazz.getMethods();
        for(Method mth:methods){
            System.out.print(mth.getName()+" ");
        }
        System.out.println();
        
        method = clazz.getMethod("StudentPublicMethod",String.class);
        System.out.print(method.getName()+" ");
        System.out.println();

        methods = clazz.getDeclaredMethods();
        for(Method mth:methods){
            System.out.print(mth.getName()+" ");
        }
        System.out.println();
        
        method = clazz.getDeclaredMethod("StudentPrivateMethod",String.class);
        System.out.print(method.getName()+" ");
        System.out.println();
        
        Object obje = clazz.newInstance();
        method.setAccessible(true);
        String result=(String) method.invoke(obje,"inputParams");
        System.out.println(result);
    }

輸出結果:

StudentPublicMethod setStudentId getStudentId getName setName PersonPrivateMethod PersonPublicMethod wait wait wait equals toString hashCode getClass notify notifyAll 
StudentPublicMethod 
StudentPrivateMethod StudentPublicMethod setStudentId getStudentId 
StudentPrivateMethod 
inputParams

二、描述字段Filed
描述字段Filed方法的使用和描述方法Method中方法的使用有點類似,也是4個獲取字段的方法:getFields、getField、getDeclaredFields、getDeclaredField。
getFields:獲得某個類的所有的公共(public)的字段,包括父類中的字段。
getField:獲取某個類public成員變量中指定變量名的字段,包括基類。
getDeclaredFields:獲得某個類的所有聲明的字段,即包括public、private和proteced,但是不包括父類的申明字段。
getDeclaredField:獲取某個類的所有成員變量指定變量名的字段,不包括基類。

1.字段獲取

        Class clazz = Class.forName("Reflection.Student");
        System.out.println("---------getDeclaredFields---------");
        Field[] fields = clazz.getDeclaredFields();
        for(Field field: fields){
            System.out.print(field.getName()+" ");
        }
        System.out.println();
        System.out.println("---------getFields---------");
        fields = clazz.getFields();
        for(Field field: fields){
            System.out.print(field.getName()+" ");
        }
        System.out.println();
       
        System.out.println("---------getDeclaredField---------");
        Field field = clazz.getDeclaredField("StudentId");
        field.setAccessible(true);
        System.out.println(field.getName());
        
        System.out.println("---------getField--------");
        
        field = clazz.getField("StudentId");
        System.out.println(field.getName());
---------getDeclaredFields---------
StudentId 
---------getFields---------
StudentId 
---------getDeclaredField---------
StudentId
---------getField--------
StudentId

2.字段的使用

    Class clazz = Class.forName("Reflection.Person");
    Person person = new Person("CYW");
        //獲取私有字段的值
        Field field = clazz.getDeclaredField("Name");
        //由於是私有字段,需要使用setAccessible(true)
        field.setAccessible(true);
        Object val = field.get(person);
        System.out.println(val);
        //改變私有字段的值
        field.set(person, "ivan");
        System.out.println(person.getName());

計劃在未來6個月內寫一本Spring入門的書,本篇和Java語法註解篇都是書的一部分,博友有什麽建議或意見可以留言,我也好在後面的章節進行改進。未完,待續!

Java語法之反射