天天看点

反射机制总结 - 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