天天看點

反射機制總結 - Java

文章目錄

  • ​​一、反射的作用​​
  • ​​二、反射的用法​​
  • ​​2.1 類​​
  • ​​2.1.1 修飾符​​
  • ​​2. 1.2 類名​​
  • ​​2.1.3 繼承的父類​​
  • ​​2.1.3 實作的接口​​
  • ​​2.1.4 包資訊​​
  • ​​2.2 變量​​
  • ​​2.2.1 特定變量​​
  • ​​2.2.2 變量類型和變量名​​
  • ​​2.3 構造器​​
  • ​​2.3.1 擷取Constructor對象以及對象的參數清單​​
  • ​​2.3.2 利用Constructor對象執行個體化一個類​​
  • ​​2.4 方法​​
  • ​​2.4.1 擷取類中所有方法​​
  • ​​2.4.2 調用指定方法​​
  • ​​2.5 數組​​
  • ​​2.5.1 一維數組指派​​
  • ​​2.5.1 多元數組指派​​
  • ​​2.6 泛型​​
  • ​​2.6.1 方法的傳回類型的泛型​​
  • ​​2.7 注解​​
  • ​​三、源碼檢視位址​​

一、反射的作用

可以通過反射來擷取類的各個組成部分,還可以通過反射來建立對象,甚至構造器是私有的也可以;調用方法,傳參以及擷取方法的傳回值;總體來說可以對構造器,字段,方法,注釋等進行一系列操作。

二、反射的用法

下面将會通過對類,字段,構造器,方法,數組,泛型分别進行描述反射的使用

将對這個類進行案例分析

Animal類

package top.clearlight.coretech.reflect;

public abstract class Animal {
    public abstract void move();
    abstract void shout();

    public void Help() {
        System.out.println("Help Ohter");
    }
}      

People類

package top.clearlight.coretech.reflect;

public class People  extends Animal implements Comparable<People> {

    private String name;
    public int age;
    static String sex;
 
    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public People() {
    }

    public People(String name) {
        this.name = name;
    }

    private People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(People o) {
        return Double.compare(this.age, o.age);
    }
    
  @Override
    public void move() {
        System.out.println("行走");
    }

    @Override
    void shout() {
        System.out.println("交流");
    }
    
    public List<String> getStringList() {
        ArrayList<String> list = new ArrayList<>();
        list.add("hhhh");
        return list;
    }
}      

2.1 類

擷取類對象的方式有三種.對象.
  1. 對象.getClass()
  2. Class.forName(“全限定類名”); (需要try-catch,類名有可能寫錯)
  3. 類名.class

2.1.1 修飾符

Class clazz = People.class;
  int modifiers = clazz.getModifiers(); // modifiers : 1 (public)      
反射機制總結 - Java

Modifier源碼 : ​​https://gitee.com/huangtianyu/jdk-source/blob/master/java/lang/reflect/Modifier.java​​

2. 1.2 類名

String name = clazz.getName(); // top.clearlight.coretech.reflect.People

String simpleName = clazz.getSimpleName(); // People      

2.1.3 繼承的父類

Class superclass = clazz.getSuperclass(); // class java.lang.Object      

2.1.3 實作的接口

類可以實作多個接口,是以傳回Class數組.且隻會傳回目前類所實作的接口 , 父類所實作的接口并不會在傳回的Class數組中.
Class<?>[] interfaces = clazz.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println(anInterface);
        }      

輸出結果 : ​

​interface java.lang.Comparable​

2.1.4 包資訊

Package aPackage = clazz.getPackage(); // package top.clearlight.coretech.reflect      

2.2 變量

擷取一個類的成員變量

Field[] fields = clazz.getFields(); // 隻能擷取修飾符為public的變量

 Field[] declaredFields = clazz.getDeclaredFields(); // 擷取類中所有的變量,包括私有,預設,公有..      

2.2.1 特定變量

通過變量名來擷取指定變量,并且對該變量進行指派
  • 擷取指定變量
如果是public修飾的變量,可以使用​

​getField(String)​

​​來擷取;但是對于其他修飾符需要使用​

​getDeclaredField(String)​

​方法
Field field = clazz.getDeclaredField("name");      
  • 為特定對象的變量指派

    首先建立對象

People p = new People();
        field.setAccessible(true); // 将域設定為可通路的
        field.set(p, "Jack"); // 若變量為static, 第一個參數設為null,即不需要傳入對象,隻需直接設定值即可.
        System.out.println(p.getName()); // Jack      
  • 為靜态變量指派
Field sex = clazz.getDeclaredField("sex");
        sex.set(null, "girl"); // 當然,你傳入類的執行個體效果相同
        System.out.println(People.sex); // girl      

若對私有變量使用getField(String)方法的話,将會抛出NoSuchFieldException ; 或者傳入的String确實沒有該字段同樣會抛出該異常

2.2.2 變量類型和變量名

Field ageField = clazz.getField("age");
        Class<?> type = ageField.getType(); // int
        
        String name1 = ageField.getName(); // age      

2.3 構造器

對于構造器,不僅可以擷取所有Constructor對象,來列出參數清單,還可以擷取指定參數清單的Constructor對象;同樣可以通過獲得的Constructor對象來執行個體化一個類

擴充閱讀 : ​​Java反射 - 通過反射建立有參與無參構造函數的對象​​

2.3.1 擷取Constructor對象以及對象的參數清單

Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); // 傳回所有的Constructor對象
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("構造器:" + declaredConstructor);
            Class<?>[] parameterTypes = declaredConstructor.getParameterTypes(); // 傳回該Constructor對象的參數清單
            for (Class<?> parameterType : parameterTypes) {
                System.out.println(parameterType);
            }
        }      

輸出結果:

反射機制總結 - Java

2.3.2 利用Constructor對象執行個體化一個類

1、擷取指定的構造器對象

Constructor<People> declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class);      

2、将私有構造器設為可通路的

declaredConstructor.setAccessible(true);      

3、建立執行個體并為構造器傳入相應的參數

People mary = declaredConstructor.newInstance("Mary", 18);      

對于上述,擷取構造器對象的方法有​

​getConstructor​

​​與​

​getDeclaredConstructor​

​,兩者的差別與擷取字段是的兩個方法一樣,一個隻能擷取public,而另一個還可以擷取預設,甚至是private所修飾的,是以可以建立私有構造器的執行個體。

2.4 方法

與構造器的使用類似,其中不同的是擷取所有方法:
  • ​getMethods​

    ​傳回的方法集是本類以及父類還有Object的所有​

    ​public​

    ​修飾的方法;
  • ​getDeclaredMethods​

    ​傳回的方法集隻是本類所有的方法集合。

2.4.1 擷取類中所有方法

Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("方法名:" + declaredMethod.getName());
            int modifiers1 = declaredMethod.getModifiers();
            System.out.println("修飾符:" + modifiers);
            System.out.println("傳回類型:" + declaredMethod.getReturnType());
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            System.out.println("參數清單:");
            for (Class<?> parameterType : parameterTypes) {
                System.out.println(parameterType);
            }
            System.out.println();
        }      

輸出結果 :

方法名:move

修飾符:1

傳回類型:void

參數清單:

方法名:getAge

修飾符:1

傳回類型:int

參數清單:

… …

2.4.2 調用指定方法

Constructor<People> declaredConstructor1 = People.class.getDeclaredConstructor(String.class, int.class);
        declaredConstructor1.setAccessible(true);
        People p2 = declaredConstructor1.newInstance("Bob", 21);
        p.setAge(23);


        Method compareTo = clazz.getMethod("compareTo", People.class);
        Object comPareMethod = compareTo.invoke(p, p2);
        System.out.println(comPareMethod); // 1      
  • ​getMethod​

    ​​方法

    第一個參數 : 方法名

    第二個參數 : (可變參數)方法的參數類型

  • ​invoke​

    ​​方法

    第一個參數 : 非靜态方法的話傳入調用該方法的執行個體對象 ; 若是靜态方法的話傳入 null

    第二個參數 : (可變參數) 對應類型的參數的值

2.5 數組

擴充閱讀 :

​Java反射 - 建立數組執行個體​​Java中的反射機制(三) 反射與數組

Java反射機制通過​

​java.lang.reflect.Array​

​這個類來處理數組.

2.5.1 一維數組指派

// 建立int類型的數組,長度為10
        int[] ints = (int[]) Array.newInstance(int.class, 10);
        System.out.println(Arrays.toString(ints));

        Array.set(ints, 3, 18);
        int i = (int) Array.get(ints, 3);
        System.out.println(i);      

輸出結果:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

18

2.5.1 多元數組指派

int[][] ints2 = (int[][]) Array.newInstance(int.class, 5, 10);
        System.out.println(Arrays.toString(ints2));
        int[] ints3 = (int[]) Array.get(ints2, 3);
        Array.setInt(ints3, 5, 21);

        for (int[] ints4 : ints2) {
            System.out.println(Arrays.toString(ints4));
        }      

輸出結果:

反射機制總結 - Java

2.6 泛型

在一些情況下是可以在運作期擷取到泛型的資訊

2.6.1 方法的傳回類型的泛型

Method getStringList = People.class.getMethod("getStringList", null);

        Type genericReturnType = getStringList.getGenericReturnType();

        if (genericReturnType instanceof ParameterizedType) {
            ParameterizedType type1 = (ParameterizedType) genericReturnType;
            Type[] actualTypeArguments = type1.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }      

輸出結果:

class java.lang.String

2.7 注解

擷取注解的方式無論是擷取類注解,方法注解還是參數注解,方式都是相似的,隻要掌握一種就可以快速學會其他擷取注解内容的方法.

關于注解的學習請看: ​​注解知識點總結 - Java​​

任意位置的自定義注解

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Container {
    Person[] value();
}      

自定義的變量注解

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Inherited
public @interface FieldAnnotation {
    String value();
}      

容器注解

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Container {
    Person[] value();
}      

通過@Repeatable修飾的注解

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Container.class)
public @interface Person {
    String role() default "artist";
}      

定義方法來擷取字段以及指定方法的注解

package top.clearlight.blog.annotation.reflecttest;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;


// @MyAnnotation(msg = "type annotation", id = 1)
public class ReflectTest1 extends SuperClass{
    @FieldAnnotation(value = "file annotation")
    private int age;

    @Person(role = "student")
    @Person(role = "cooker")
    @Person(role = "superman")
    public void test() {
        System.out.println("用來測試Repeatable注解");
    }

    @MyAnnotation(id = 4)
    public void test2() {
        System.out.println("直接擷取多個方法的注解");
    }

    public static void main(String[] args) {
        Class<ReflectTest1> rt = ReflectTest1.class;

        // 獲得的父類的類注解,該注解已經通過@Inherited修飾
        System.out.println("擷取類注解");
        Annotation[] typeAns = rt.getAnnotations();
        for (Annotation typeAn : typeAns) {
            if (typeAn instanceof MyAnnotation) {
                MyAnnotation man = (MyAnnotation) typeAn;
                System.out.println("msg(): " + man.msg());
                System.out.println("id(): " + man.id());
            }通過@Repeatable修飾的方法的注解
        }

        // 擷取的字段的注解
        System.out.println("\n擷取字段注解");
        try {
            Field age = rt.getDeclaredField("age");
            // 通路private修飾的字段, 需要修改通路權限
            age.setAccessible(true);
            // 獲得指定的注解
            FieldAnnotation fieldAn = age.getAnnotation(FieldAnnotation.class);
            if (fieldAn != null) {
                System.out.println("字段的value(): " + fieldAn.value());
            }

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        // 擷取所有方法的注解
        System.out.println("\n擷取方法注解");
        Method[] methodsAns = rt.getDeclaredMethods();
        for (Method methodsAn : methodsAns) {
            Annotation[] ans = methodsAn.getAnnotations();
              System.out.println(methodsAn.getName() + "方法的注解:");
            if (ans.length == 0) {
                System.out.println("無");
            }
            for (Annotation an : ans) {
                System.out.println(an);
            }
        }

         // 擷取通過@Repeatable修飾的方法的注解,
        System.out.println("\n擷取指定方法的注解");
        try {
            Method test = rt.getDeclaredMethod("test", null);
            Container conAn = test.getAnnotation(Container.class);
            System.out.println("人的角色: ");
            for (Person person : conAn.value()) {
                System.out.println(person.role());
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

    }
}      

結果:

擷取類注解

msg(): 由父類而得

id(): 2

擷取字段注解

字段的value(): file annotation

擷取方法注解

test2方法的注解:

@top.clearlight.blog.annotation.reflecttest.MyAnnotation(msg=“hhhh”, id=4)

main方法的注解:

test方法的注解:

@top.clearlight.blog.annotation.reflecttest.Container(value={@top.clearlight.blog.annotation.reflecttest.Person(role=“student”), @top.clearlight.blog.annotation.reflecttest.Person(role=“cooker”), @top.clearlight.blog.annotation.reflecttest.Person(role=“superman”)})

擷取指定方法的注解

人的角色:

student

cooker

superman