1. 程式人生 > >Java學習筆記54(反射詳解)

Java學習筆記54(反射詳解)

pos code 重名 java學習筆記 spl catch 兩種 new fig

反射概念:

java反射機制是在運行狀態中,對於任意一個類,都能知道所有屬性和方法

對於任意一個對象都能調用它的任意一個方法和屬性,這種動態獲取和調用的功能稱為java的反射機制

實際作用:

已經完成一個java程序,但是想再添加新功能,又不能修改源碼,這時候就用到反射機制了

獲取class文件的三種方式:

簡單地自定義一個Person類:

技術分享圖片
package demo;

public class Person {
    public String name;
    private int age;

    public Person() {
    }

    public
Person(String name, int age) { this.name = name; this.age = age; } private Person(int age, String name) { this.name = name; this.age = age; } public void eat() { System.out.println("人吃飯"); } public void sleep(String s, int a, double
d) { System.out.println("人在睡覺" + s + "....." + a + "....." + d); } private void playGame() { System.out.println("人在打遊戲"); } public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } public String getName() { return name; }
public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
View Code

獲取person類class文件(三種方法本質上獲取的是同一個對象):

package demo;

public class ReflectDemo {
    public static void main(String[] args) {
        //1.對象獲取
        Person p = new Person();
        Class c = p.getClass();
        System.out.println(c);
        //2.類名獲取
        Class c1 = Person.class;
        System.out.println(c1);
        
        System.out.println(c==c1);//true
        System.out.println(c.equals(c1));//true
        //只存在一個class文件,兩種方式都是獲得同一個對象
        
        //3.Class類的靜態方法,參數註意帶著包名防止重名
        try {
            Class c2 =     Class.forName("demo.Person");
            System.out.println(c2);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

反射獲取空參構造方法:

package demo;

import java.lang.reflect.Constructor;

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c = Class.forName("demo.Person");
        function1(c);
        function2(c);
    }

    private static void function1(Class c) {
        // 獲得所有公共權限(public)的構造器
        Constructor[] cons = c.getConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
            // 輸出:
            // public demo.Person(java.lang.String,int)
            // public demo.Person()
        }
    }

    public static void function2(Class c) {
        // 獲取空參構造器並執行(toString方法)
        try {
            Constructor con = c.getConstructor();
            Object obj = con.newInstance();
            System.out.println(obj);
            // 輸出:Person [name=null, age=0]
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

反射獲取有參構造方法:

package demo;

import java.lang.reflect.Constructor;

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("demo.Person");
        Constructor con = c.getConstructor(String.class, int.class);
        System.out.println(con);
        Object object = con.newInstance("張三", 20);
        System.out.println(object);
    }
}
/*輸出:
public demo.Person(java.lang.String,int)
Person [name=張三, age=20]
*/

發現上邊的方式代碼量偏大,快捷一些的方式:

前提:被反射的類,必須具有空參構造方法,且構造方法權限必須是public

package demo;

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("demo.Person");
        // Class類中定義方法, T newInstance() 直接創建被反射類的對象實例
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}

反射獲取私有構造方法(日常開發不建議使用,這種方法了解即可):

package demo;

import java.lang.reflect.Constructor;

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("demo.Person");
        // 獲取所有構造方法(包括私有的)
        // Constructor[] cons = c.getDeclaredConstructors();
        Constructor con = c.getDeclaredConstructor(int.class, String.class);
        con.setAccessible(true);// 取消權限,破壞了封裝性
        Object object = con.newInstance(18, "張三");
        System.out.println(object);
        // Person [name=張三, age=18]
    }
}

反射獲取類的成員變量並修改:

package demo;

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

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("demo.Person");

        // 獲得所有public成員
        Field[] fields = c.getFields();
        // 獲得所有成員變量
        Field[] fields2 = c.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }

        // 獲取指定成員變量並修改
        Field field = c.getField("name");
        Object object = c.newInstance();
        field.set(object, "張三");
        System.out.println(object);
        // 輸出:Person [name=張三, age=0]
    }
}

反射獲取成員方法並執行:

package demo;

import java.lang.reflect.Method;

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class c1 = Class.forName("demo.Person");
        // 獲得所有public方法
        /*
         * Method[] methods = c1.getMethods(); 
         * for(Method method:methods){
         * System.out.println(method); }
         */

        // 獲取指定方法運行(空參)
        Method method = c1.getMethod("eat");
        Object obj = c1.newInstance();
        method.invoke(obj);
        // 輸出:人吃飯

        // 有參
        Method method1 = c1.getMethod("sleep", String.class, int.class, double.class);
        Object obj1 = c1.newInstance();
        method1.invoke(obj1, "休息", 10, 10.11);
        // 輸出:人在睡覺休息.....10.....10.11

        // 可以利用前面提到的方法暴力運行私有方法
    }
}

反射的泛型擦除:

package demo;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class ReflectTest {
    public static void main(String[] args) throws Exception {
        ArrayList<String> array = new ArrayList<String>();
        // 通常添加方式
        array.add("a");
        array.add("1");

        Class class1 = array.getClass();
        Method method = class1.getMethod("add", Object.class);
        method.invoke(array, 100);
        method.invoke(array, 666.666);
        method.invoke(array, 0.1);
        System.out.println(array);
        // 輸出:[a, 1, 100, 666.666, 0.1]
    }
}

反射實現通過配置文件運行:

有時候想改源碼,但是不能改源碼,可以這樣做:

自定義三個類:

技術分享圖片
package demo;

public class Person {
    public void eat(){
        System.out.println("人在吃飯");
    }
}
View Code 技術分享圖片
package demo;

public class Student {
    public void study(){
        System.out.println("學生在學習");
    }
}
View Code 技術分享圖片
package demo;
public class Worker {
    public void job(){
        System.out.println("上班族在工作");
    }
}
View Code

配置文件:config.properties

#className=demo.Student
#methodName=study
className=demo.Person
methodName=eat
#className=demo.Worker
#methodName=job

測試類:

package demo;

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;

/*
 *  調用Person方法,調用Student方法,調用Worker方法
 *  類不清楚,方法也不清楚
 *  通過配置文件實現此功能
 *    運行的類名和方法名字,以鍵值對的形式,寫在文本中
 *    運行哪個類,讀取配置文件即可
 *  實現步驟:
 *    1. 準備配置文件,鍵值對
 *    2. IO流讀取配置文件  Reader
 *    3. 文件中的鍵值對存儲到集合中 Properties
 *        集合保存的鍵值對,就是類名和方法名
 *    4. 反射獲取指定類的class文件對象
 *    5. class文件對象,獲取指定的方法
 *    6. 運行方法
 */
public class Test {
    public static void main(String[] args) throws Exception{
        //IO流讀取配置文件
        FileReader r = new FileReader("config.properties");
        //創建集合對象
        Properties pro = new Properties();
        //調用集合方法load,傳遞流對象
        pro.load(r);
        r.close();
        //通過鍵獲取值
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");
        //反射獲取指定類的class文件對象
        Class c = Class.forName(className);
        Object obj = c.newInstance();
        //獲取指定的方法名
        Method method = c.getMethod(methodName);
        method.invoke(obj);
    }
}

Java學習筆記54(反射詳解)