Java反射機制——學習總結


前幾天上REST課,因為涉及到Java的反射機制,之前看過一直沒有用過,有些遺忘了,周末找了些資料來重新學習,現在總結一下,加深印象。

什么是反射機制?

參考百度百科對java反射機制的定義:

“JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。”

我們通過一些例子,更好理解反射機制。

Class類

我們知道Java是一門面向對象語言,在Java的世界里,萬物皆為對象,比如我們有一個Person類:

public class Person {

}

我們創建一個Person類的實例,Person person = new Person();  那么這個person,就是Person類的實例對象。

那么既然萬物都為對象,所以類也是對象。

類是什么的對象呢?類是Class類的對象,表示方式有三種:

//第一種,任何一個類都有一個隱含的靜態成員變量class
Class c1 = Person.class;

//第二種,已經知道該類的對象,通過getClass()獲得
Class c2 = person.getClass();

//第三種,Class類的forName()方法
Class c3 = Class.forName("Person");

//這里,c1,c2,c3都是Class類的實例,我們稱c1, c2 ,c3為Person類的類類型
//不難看出,c1 == c2結果是true, c2 == c3結果也是true

通過類的類類型,我們經常會用到的方法就是newInstance()方法,通過該方法可以創建該類的實例:

Person personA = new Person(); //直接new一個實例
Person personB = Person.class.newInstance(); //通過newInstance()方法獲得Person的實例

//在學習JAVAEE時候,newInstance()方法我們最常見於獲取數據庫驅動
Class.forName("com.mysql.jdbc.Driver").newInstance();

//需要注意的是,在使用newInstance()方法的前提是該類必須要有無參構造方法

動態加載類:

編譯時刻加載類稱為靜態加載,運行時刻加載類稱為動態加載,使用new方法新建實例即為靜態加載類,在編譯時候就要加載全部類。這里我們舉一個例子:

為了更好的區分編譯和運行,我們不適用IDE工具,而使用記事本來實現這個例子:

//我們舉女媧造人的例子,創建一個CreatePerson類
public class CreatePerson {
    public static void main(String[] args) {
        if(args[0].equalsIgnoreCase("man")) {  //如果從命令行傳入的參數為man 則創建Man的實例並調用say()方法
            new Man().say();
        }
        if(args[0].equalsIgnoreCase("woman")) {  //如果從命令行傳入的參數為woman 則創建Woman的實例並調用say()方法
            new Woman().say();
        }
    }
}

//CreatePerson類用到了2個類,分別是Man和Woman。
//但是我們現在只創建Man類
public class Man {
    public void say() {
        System.out.println("I am a man !");
    }
}

我們在CMD中編譯CreatePerson,看看會發生什么:

提示我們找不到Woman這個類。這就是靜態加載,在編譯時刻需要加載全部類。那么問題來了,如果我要寫一個程序,里面有100個功能,這100個功能分別由100個類實現,那么一旦缺少一個類,這整個程序是不是就不能用了。

為了解決這個問題,我們可以使用動態加載。我們把上面這個例子改一下:

//創建一個Person接口
public interface Person {
    void say();
}

//修改Man類,繼承Person接口
public class Man implements Person{
    public void say() {
        System.out.println("I am a man !");
    }
}

//修改CreatePerson類,實現動態加載類
public class CreatePerson{
    public static void main(String[] args) {
        
        try{
            //動態加載類
            Class c = Class.forName(args[0]);
            //通過類的類類型,創建該類實例對象
            Person person = (Person)c.newInstance();
            person.say();
            
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

這個時候,我們再進行編譯:

沒有報錯了,那么我們繼續把Man編譯,再運行一下:

如果我們在命令行中輸入的參數是Woman呢?

 

拋出ClassNotFoundException異常。因為我們並沒有創建Woman類,所以如果以后需要創建Woman類實例,我們只需要新建一個Woman類,並實現Person接口就行了。

 

通過反射機制,獲得類的信息

 在Java反射機制的定義中有:JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法。下面是一個實例:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ClassUtil {
    /**
     * 獲取成員函數的信息
     */
    public static void getClassMethodMessage(Object obj) {
        Class c = obj.getClass();
        //獲取類的名稱
        //System.out.println(c.getName());
        
        /**
         * 
         * Method類,方法對象
         *getMethods方法獲得所有public的方法,包括繼承而來
         *getDeclaredMethods是獲取該類自己的聲明的方法
         */
        Method[] ms = c.getMethods();
        for(int i = 0; i < ms.length; i++) {
            //得到方法的返回值類型的類類型
            Class returnType = ms[i].getReturnType();
            System.out.print(returnType.getName() + " ");
            //得到方法的名稱
            System.out.print(ms[i].getName() + "(");
            //獲取參數類型
            Class[] paramTypes = ms[i].getParameterTypes();
            for(Class class1:paramTypes) {
                System.out.print(class1.getName() + ",");
            }
            System.out.println(")");
        }
        
        
    }



    public static void getFieldMessage(Object obj) {
        Class c = obj.getClass();
        
        /**
         * 成員變量也是對象
         * java.lang.reflect.Field
         * 
         */
        Field[] fs = c.getDeclaredFields();
        for(Field field:fs) {
            //得到成員變量類型的類類型
            Class fieldType = field.getType();
            String typeName = fieldType.getName();
            //得到成員變量的名稱
            String fieldName = field.getName();
            System.out.println(typeName + " " + fieldName);
            
        }
    }
    
    public static void printConMessage(Object obj) {
        Class c = obj.getClass();
        /**
         * 構造函數也是對象
         * 
         */
        Constructor[] cs = c.getConstructors();
        for (Constructor constructor : cs) {
            System.out.print(constructor.getName() + "(");
            //獲取構造函數參數列表------>參數列表的參數類型
            Class[] paramType = constructor.getParameterTypes();
            for (Class class1 : paramType) {
                System.out.print(class1.getName() + ",");
            }
            System.out.println(")");
        }
        
    }
    
    public static void main(String[] args) {
        String s = "hello";
        getFieldMessage(s);
        ClassUtil.printConMessage(s);
        getClassMethodMessage(s);
    }
}

方法的反射操作

可以通過方法的反射操作實現方法的調用:

 

import java.lang.reflect.Method;

public class MethodDemo1 {
    public static void main(String[] args) {
        //要獲取print(int, int)
        //要獲取類的方法就要獲取類的信息,獲取類的信息就要獲取類的類類型
        A a1 = new A();
        Class c = a1.getClass();
        //2,獲取方法 名稱和參數列表
        //getMethod獲取的是public的方法
        try {
            Method m = c.getDeclaredMethod("print", int.class,int.class);
            
            //方法的反射操作
            //a1.print(10, 20);方法的反射操作,用m來進行方法調用和前者效果一致
            Object obj = m.invoke(a1, 10,20);//如果方法有返回值返回值,沒有就null
            
            
        } catch (Exception e) {
            
            e.printStackTrace();
        }
    }
    
    
}

class A {
    public void print(int a , int b) {
        System.out.println(a + b);
    }
    
    public void print(String a , String b) {
        System.out.println(a.toUpperCase() + "," + b.toUpperCase());
    }
}

 

以上就是我對Java反射機制的學習和理解,如果有錯誤,望指出,以便及時更正!謝謝!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM