Java反射

Java基礎 - 反射

1、做用

「反射是框架設計的靈魂」,它能夠將類的各個組成部分封裝爲一個個的對象,經過 Java 的反射機制,程序員能夠更深刻地控制程序的運行過程。java

2、好處

  1. 能夠在程序運行過程當中操做這些封裝的對象程序員

  2. 能夠解耦,提升程序的可擴展性api

3、使用

首先準備一個Person類

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

    public String a;
    protected String b;
    String c;
    private String d;

    public Person() {
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    private void eat(String food){
        System.out.println("吃" + food);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
}

1. 獲取Class對象的方式

@Test
public void test() throws ClassNotFoundException {
    //1. Class.forName("全類名"):將字節碼文件加入進內存,獲取Class對象
    Class<?> clazz1 = Class.forName("com.sjl.Person");
    System.out.println(clazz1);

    //2. 類名.class:經過類的屬性class獲取
    Class<Person> clazz2 = Person.class;
    System.out.println(clazz2);

    //3. 類對象.getClass():經過Object中的方法getClass()獲取
    Person person = new Person();
    Class<? extends Person> clazz3 = person.getClass();
    System.out.println(clazz3);

    //同一字節碼文件(*.class)在程序運行過程當中只被加載一次,因此不管哪一種方式得到的Class對象都是同一個
    System.out.println(clazz1 == clazz2); //true
    System.out.println(clazz2 == clazz3); //true
}

2. Class中的API

Class類的API,大體分爲一下幾類(參考jdk1.8 api)框架

  1. 獲取類名
    • String getName()
  2. 獲取成員變量們
    • Field getField(String name) 獲取指定名稱的公有方法
    • Field[] getFields() 獲取公有方法列表
    • Field getDeclaredField(String name) 獲取指定名稱的方法
    • Field[] getDeclaredFields() 獲取全部方法列表
  3. 獲取構造方法們
    • Constructor< T > getConstructor(Class<?>... parameterTypes)
    • Constructor<?>[] getConstructors()
    • Constructor< T > getDeclaredConstructor(Class<?>... parameterTypes)
    • Constructor<?>[] getDeclaredConstructors()
  4. 獲取成員方法們
    • Method getMethod(String name, Class<?>... parameterTypes)
    • Method[] getMethods()
    • Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    • Method[] getDeclaredMethods()

3. 代碼實現

@Test
public void test1(){
    //獲取Class對象
    Class<Person> clazz = Person.class;
    //獲取類的全名稱
    String name = clazz.getName();
    System.out.println("類的全名稱:" + name);
}

@Test
public void test2() throws Exception {
    //獲取Class對象
    Class<?> clazz = Class.forName("com.sjl.Person");
    //獲取Person的公有屬性列表
    Field[] fields = clazz.getFields();
    for(Field field : fields){
        System.out.println("公共屬性:" + field);
    }
    //獲取指定的公有屬性
    Field a = clazz.getField("a");
    System.out.println("指定公共屬性a:" + a);

    System.out.println("================================");

    //獲取Person的全部屬性列表,不論權限修飾符
    Field[] declaredFields = clazz.getDeclaredFields();
    for (Field declaredField : declaredFields) {
        System.out.println("全部屬性:" + declaredField);
    }

    //獲取指定的屬性
    Field d = clazz.getDeclaredField("d");
    System.out.println("指定屬性d:" + d);

    System.out.println("================================");

    //爲私有屬性 設置值 和 獲取值
    Field name = clazz.getDeclaredField("name");
    //暴力反射,忽略類的權限修飾符
    name.setAccessible(true);
    //設值,須要傳一個person對象
    Person person = new Person();
    name.set(person, "張三");
    //取值,也須要傳一個person對象
    Object value = name.get(person);
    System.out.println(value);
}

@Test
public void test3() throws Exception {
    //獲取Class對象
    Class<?> clazz = Class.forName("com.sjl.Person");
    //獲取公有構造方法
    Constructor<?>[] constructors = clazz.getConstructors();
    for (Constructor<?> constructor : constructors) {
        System.out.println(constructor);
    }

    //獲取空參構造器
    Constructor<?> constructor1 = clazz.getConstructor();
    //經過構造器建立對象 newInstance
    Object c1 = constructor1.newInstance();
    System.out.println(c1);

    //若是是空參構造器還能夠簡寫,直接用Class對象建立
    Object c2 = clazz.newInstance();
    System.out.println(c2);
}

@Test
public void test4() throws Exception {
    //獲取Class對象
    Class<?> clazz = Class.forName("com.sjl.Person");
    //獲取成員方法
    Method eat_method = clazz.getDeclaredMethod("eat", String.class);
    //eat是私有方法,所以須要暴力反射
    eat_method.setAccessible(true);
    //執行方法,傳入對象和參數
    Person person = new Person();
    eat_method.invoke(person, "火鍋");
}

4、應用小案例

實現:在不改變當前代碼的狀況下,建立任意一個類的實例並調用它的方法ide

思路:this

  1. 編寫一個配置文件存放要建立的類的全類名,方法名
  2. 讀取配置文件內容得到對應的信息
  3. 經過反射,根據全類名得到Class對象,建立其實例,調用其方法

配置文件:設計

className=com.sjl.Person
methodName=eat

Java代碼:code

public class ReflectDemo {
    //實現:在不改變當前代碼的狀況下,建立任意一個類的實例並調用它的方法
    public static void main(String[] args) throws Exception {
        //獲取Class對象
        Class<?> clazz = Class.forName(getValue("className"));
        //建立實例
        Object o = clazz.newInstance();
        //獲取方法對象
        Method eat_method = clazz.getDeclaredMethod(getValue("methodName"), String.class);
        //執行
        eat_method.setAccessible(true);
        eat_method.invoke(o, "火鍋");
    }

    //根據key值讀取配置文件內容
    public static String getValue(String key) throws IOException {
        Properties properties = new Properties();
        InputStream is = ReflectDemo.class.getClassLoader().getResourceAsStream("config.properties");
        properties.load(is);
        assert is != null;
        is.close();
        return properties.getProperty(key);
    }
}

運行結果:對象

吃火鍋

新建一個類Person2,將eat方法的輸出稍做修改,將配置文件修改成內存

className=com.sjl.Person2
methodName=eat

運行結果

吃火鍋,我是二號選手
相關文章
相關標籤/搜索