Java反射機制

概述

 Java反射機制指的是在Java程序運行狀態中,對於任何一個類,都可以獲得這個類的所有屬性和方法;對於給定的一個對象,都能夠調用它的任意一個屬性和方法。這種動態獲取類的內容以及動態調用對象的方法稱爲反射機制。

意義

(1)反射機制極大的提高了程序的靈活性和擴展性,降低模塊的耦合性,提高自身的適應能力。
(2)通過反射機制可以讓程序創建和控制任何類的對象,無需提前硬編碼目標類。
(3)使用反射機制能夠在運行時構造一個類的對象、判斷一個類所具有的成員變量和方法、調用一個對象的方法。
(4)反射機制是構建框架技術的基礎所在,使用反射可以避免將代碼寫死在框架中。

反射機制的相關類

與Java反射相關的類如下:

與Java反射相關的類如下:

類名用途
Class類 代表類的實體,在運行的Java應用程序中表示類和接口
Field類 代表類的成員變量(成員變量也稱爲類的屬性)
Method類 代表類的方法
Constructor類 代表類的構造方法

Class類

  • 獲得類相關的方法
方法用途
asSubclass(Class<U> clazz) 把傳遞的類的對象轉換成代表其子類的對象
Cast 把對象轉換成代表類或是接口的對象
getClassLoader() 獲得類的加載器
getClasses() 返回一個數組,數組中包含該類中所有公共類和接口類的對象
getDeclaredClasses() 返回一個數組,數組中包含該類中所有類和接口類的對象
forName(String className) 根據類名返回類的對象
getName() 獲得類的完整路徑名字
newInstance() 創建類的實例
getPackage() 獲得類的包
getSimpleName() 獲得類的名字
getSuperclass() 獲得當前類繼承的父類的名字
getInterfaces() 獲得當前類實現的類或是接口
  • 獲得類中屬性相關的方法
方法用途
getField(String name) 獲得某個公有的屬性對象
getFields() 獲得所有公有的屬性對象
getDeclaredField(String name) 獲得某個屬性對象
getDeclaredFields() 獲得所有屬性對象
  • 獲得類中註解相關的方法
方法用途
getAnnotation(Class<A> annotationClass) 返回該類中與參數類型匹配的公有註解對象
getAnnotations() 返回該類所有的公有註解對象
getDeclaredAnnotation(Class<A> annotationClass) 返回該類中與參數類型匹配的所有註解對象
getDeclaredAnnotations() 返回該類所有的註解對象
  • 獲得類中構造器相關的方法
方法用途
getConstructor(Class...<?> parameterTypes) 獲得該類中與參數類型匹配的公有構造方法
getConstructors() 獲得該類的所有公有構造方法
getDeclaredConstructor(Class...<?> parameterTypes) 獲得該類中與參數類型匹配的構造方法
getDeclaredConstructors() 獲得該類所有構造方法
  • 獲得類中方法相關的方法
方法用途
getMethod(String name, Class...<?> parameterTypes) 獲得該類某個公有的方法
getMethods() 獲得該類所有公有的方法
getDeclaredMethod(String name, Class...<?> parameterTypes) 獲得該類某個方法
getDeclaredMethods() 獲得該類所有方法
  • 類中其他重要的方法
方法用途
isAnnotation() 如果是註解類型則返回true
isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果是指定類型註解類型則返回true
isAnonymousClass() 如果是匿名類則返回true
isArray() 如果是一個數組類則返回true
isEnum() 如果是枚舉類則返回true
isInstance(Object obj) 如果obj是該類的實例則返回true
isInterface() 如果是接口類則返回true
isLocalClass() 如果是局部類則返回true
isMemberClass() 如果是內部類則返回true

Field類

Filed代表類的成員變量(成員變量也稱爲類的屬性)。

方法用途
equals(Object obj) 屬性與obj相等則返回true
get(Object obj) 獲得obj中對應的屬性值
set(Object obj, Object value) 設置obj中對應屬性值

Method類

Method代表類的方法。

方法用途
invoke(Object obj, Object... args) 傳遞object對象及參數調用該對象對應的方法

Constructor類

Constructor代表類的構造方法。

方法用途
newInstance(Object... initargs) 根據傳遞的參數創建類的對象

示例

目錄結構圖

Student.class

package com.lzh.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * @author lzh
 * create 2019-10-27-10:38
 */
public class Student {

    private String name;
    private Integer age;

    public Student() {
    }

    private Student(String name){
        this.name = name;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

 Employee.class

package com.lzh.model;

/**
 * @author lzh
 * create 2019-10-27-16:32
 */
public class Employee {
    public void show(){
        System.out.println("is show()");
    }
}

Employee2

package com.lzh.model;

/**
 * @author lzh
 * create 2019-10-27-16:32
 */
public class Employee2 {
    public void show2(){
        System.out.println("is show2()");
    }
}

Dog.class

package com.lzh.model;

/**
 * @author lzh
 * create 2019-10-27-10:36
 */
public class Dog {

    private String name;
    private Integer age;

    public String type;
    public Integer height;

    private Dog() {
    }

    private Dog(String name, Integer height) {
        this.name = name;
        this.height = height;
    }

    public Dog(String name, String type, Integer height, Integer age) {
        this.name = name;
        this.type = type;
        this.height = height;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", type='" + type + '\'' +
                ", height=" + height +
                '}';
    }
}

Teacher.class

package com.lzh.model;

/**
 * @author lzh
 * create 2019-10-27-16:14
 */
public class Teacher {

    public void show1(String s) {
        System.out.println("調用了:公有的,String參數的show1(): s = " + s);
    }

    protected void show2() {
        System.out.println("調用了:受保護的,無參的show2()");
    }

    void show3() {
        System.out.println("調用了:默認的,無參的show3()");
    }

    private String show4(int age) {
        System.out.println("調用了,私有的,並且有返回值的,int參數的show4(): age = " + age);
        return "abcd";
    }
}

測試類,反射可獲取私有屬性和私有方法並進行操作

package com.lzh.classreflect;

import com.lzh.model.Dog;
import com.lzh.model.Student;
import org.junit.Test;

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

/**
 * @author lzh
 * create 2019-10-27-10:39
 */
public class TestClass {

    @Test
    public void getClassTest() {

        //1、獲得Class:主要有三種方法:

        //第一種方式獲取Class對象
        Student student = new Student();
        Class stuClass1 = student.getClass();
        System.out.println(stuClass1.getName());


        //第二種方式獲取Class對象
        Class stuClass2 = Student.class;
        System.out.println(stuClass2.getName());
        System.out.println(stuClass1 == stuClass2);


        //第三種方式獲取Class對象
        try {
            Class stuClass3 = Class.forName("com.lzh.model.Student");//注意此字符串必須是真實路徑,就是帶包名的類路徑,包名.類名
            System.out.println(stuClass3 == stuClass2);//判斷三種方式是否獲取的是同一個Class對象
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void isInstanceTest(){

        Student student = new Student();

        //判斷是否爲某個類
        System.out.println(student instanceof Student);

    }

    @Test
    public void newObjectTest() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //創建實例:通過反射來生成對象主要有兩種方法

        //1.使用Class對象的newInstance()方法來創建Class對象對應類的實例。
        Class c = Student.class;
        Object stu1 = c.newInstance();

        //2.先通過Class對象獲取指定的Constructor對象,再調用Constructor對象的newInstance()方法來創建對象,這種方法可以用指定的構造器構造類的實例。
        Class<?> stu2 = Student.class;
        //通過Class對象獲取指定的Constructor構造器對象

        Constructor<?> constructor = stu2.getConstructor(String.class, Integer.class);
        Object raicho = constructor.newInstance("raicho", 21);
        System.out.println(raicho);

    }

    @Test
    public void getConstructorTest() throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {

        Class<?> stu = Student.class;

        //所有"公有的"構造方法
        Constructor<?> constructor = stu.getConstructor(String.class, Integer.class);
        Object stu1 = constructor.newInstance("raicho_one", 21);
        System.out.println(stu1);
        System.out.println("----------------------------------");

        //獲取所有的構造方法(包括私有、受保護、默認、公有)
        Constructor<?>[] constructors=stu.getDeclaredConstructors();
        for (Constructor<?> constructor1 : constructors) {
            System.out.println(constructor1);
        }
        //根據構造器創建實例:
        Object obj = constructors[0].newInstance("小王",21);
        System.out.println(obj);
        System.out.println("----------------------------------");

        //獲取"某個構造方法"可以是私有的,或受保護、默認、公有;
        Constructor<?> declaredConstructor = stu.getDeclaredConstructor(String.class);
        //System.out.println("declaredConstructor="+declaredConstructor);
        //設置越過安全檢查
        declaredConstructor.setAccessible(true);
        Object stu2 = declaredConstructor.newInstance("raicho");
        System.out.println(stu2);
        System.out.println("獲取所有構造器,包括私有");
        Constructor<?>[] declaredConstructors = stu.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor1 : declaredConstructors) {
            System.out.println(declaredConstructor1);
        }
        System.out.println("----------------------------------");
    }

    @Test
    public void getsetPropertyTest() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {

        Class<?> dogClass = Class.forName("com.lzh.model.Dog");

        System.out.println("************獲取所有公有的字段********************");
        Field[] fields = dogClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        System.out.println("************獲取所有的字段(包括私有、受保護、默認的)********************");
        Field[] declaredFields = dogClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        System.out.println("*************設置私有屬性值***********************************");
        Constructor<?> declaredConstructor = dogClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Dog dog1 = (Dog) declaredConstructor.newInstance();
        System.out.println(dog1);
        //dog1.setName("阿拉斯");
        Field field = Dog.class.getDeclaredField("name");
        field.setAccessible(true);
        field.set(dog1,"阿拉斯");
        System.out.println(dog1);

    }

    @Test
    public void method() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> teacherClass = Class.forName("com.lzh.model.Teacher");

        System.out.println("***************獲取所有的”公有“方法*******************");
        Method[] methods = teacherClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        System.out.println("***************獲取所有的方法,包括私有的*******************");
        Method[] declaredMethods = teacherClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }

        System.out.println("***************獲取私有的show4()方法******************");
        Method show4 = teacherClass.getDeclaredMethod("show4", int.class);
        System.out.println(show4);
        Object obj = teacherClass.getConstructor().newInstance();
        //越過安全檢查
        show4.setAccessible(true);
        Object result = show4.invoke(obj, 21);
        System.out.println("返回值:"+result);
    }
}

通過反射越過集合泛型檢查,爲集合添加不同類型的數據

package com.lzh.collectionreflect;

import org.junit.Test;

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

/**
 * @author lzh
 * create 2019-10-27-17:15
 */
public class CollectionTest {

    @Test
    public void collectionTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //通過反射越過泛型檢查
        //有一個String泛型的集合,向這個集合中添加一個Integer類型的值
        List<String> list = new ArrayList<String>();
        list.add("aaa");
        list.add("bbb");
        //list.add(100); //報錯
        //獲得集合Class對象
        Class listClass = list.getClass();
        //獲得add()方法
        Method add = listClass.getMethod("add", Object.class);
        //調用add()方法
        add.invoke(list,100);
        for (Object s : list) {
            System.out.println(s);
        }

    }
}

創建pro.properties文件,通過反射

className = com.lzh.model.Employee2
methodName = show2

加載配置文件,通過反射機制創建對象,在不修改代碼的情況下動態修改配置文件創建新對象

package com.lzh.filereflect;

import org.junit.Test;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
import java.util.ResourceBundle;

/**
 * @author lzh
 * create 2019-10-27-16:30
 */
public class FileTest {

    @Test
    public void fileTest() throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.通過反射獲取Class對象
        Class<?> employeeclass = Class.forName(getValue("className"));
//        String className = getValue("className");
//        System.out.println(className);
        //2.獲取show()方法
        Method method = employeeclass.getMethod(getValue("methodName"));
        //3.調用show()方法
        method.invoke(employeeclass.getConstructor().newInstance());

        //當我們升級這個系統時,不要Student類,而需要新寫一個Student2的類時,這時只需要更改pro.properties的文件內容就可以了。代碼就一點不用改動。
    }

    public String getValue(String key) throws IOException {

        Properties properties = new Properties();
        // 使用ClassLoader加載properties配置文件生成對應的輸入流
        InputStream in = new FileInputStream("F:\\idea2018WorkSpace\\reflect-mechanism\\src\\main\\java\\com\\lzh\\filereflect\\pro.properties");
        // 使用properties對象加載輸入流
        properties.load(in);
        //獲取key對應的value值
        String property = properties.getProperty(key);

        return property;
    }

}

代碼

github代碼:https://github.com/LZHDonald/reflect-mechanism

參考

百度百科:https://baike.baidu.com/item/JAVA%E5%8F%8D%E5%B0%84%E6%9C%BA%E5%88%B6/6015990?fr=aladdin

簡書:https://www.jianshu.com/p/9be58ee20dee

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章