反射详解

Lou.Chen
大约 9 分钟

一、什么是反射?

在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

二、与反射相关的类

类名用途
Class类代表类的实体,在运行的Java应用程序中表示类和接口
Field类代表类的成员变量(成员变量也称为类的属性
Method类代表类的方法
Constructor类代表类的构造方法

三、Class类

public final class Class<T> implements java.io.Serializable, GenericDeclaration,Type,AnnotatedElement{}

Class类表示正在运行的Java应用程序中的类和接口

1、获取Class类实例

以下知识点代码:

public class Person {
    //私有属性
    private String name;
    //公有属性
    public int age;

    /**
     * 公有无参构造
     */
    public Person() {
    }

    /**
     * 私有构造
     * @param name
     */
    private Person(String name) {
        this.name = name;
    }

    /**
     * 公有构造
     * @param name 姓名
     * @param age 年龄
     */
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    /**
     * 公有方法
     */
    public void show() {
        System.out.println("I am a good gay!");
    }

    /**
     * 私有方法
     * @return 国籍
     */
    private String showNation() {
        System.out.println("I am an American");
        return "America";
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
1)通过类的Class属性
Class pClass1=Person.class;
//class org.lc.reflect_test.Person
System.out.println(pClass1);
2)通过类的实例的getClass()方法
Person person=new Person();
Class pClass2 = person.getClass();
//class org.lc.reflect_test.Person
System.out.println(pClass2);
3)通过Class.forName获取
Class pClass3= Class.forName("org.lc.reflect_test.Person");
//class java.lang.String
Class sClass= Class.forName("java.lang.String");
System.out.println(sClass);
//class org.lc.reflect_test.Person
4)通过类加载器获取
//通过当前线程获取类加载器
Class pClass4 = Thread.currentThread().getContextClassLoader().loadClass("org.lc.reflect_test.Person");
//通过当前启动类获取类加载器
Class pClass5 = T1.class.getClassLoader().loadClass("org.lc.reflect_test.Person");
//class org.lc.reflect_test.Person
System.out.println(pClass4);
//class org.lc.reflect_test.Person
System.out.println(pClass5);
5)总结
//true
System.out.println((pClass1==pClass2) & (pClass3==pClass4));

通过反射获取到的运行时的类的Class对象时,它们的实例都是相同的。在内存中保存一份,无需重复创建。

2、可以获取Class的结构实例

    public static void main(String[] args) throws ClassNotFoundException {
        //类
        Class c1 = Object.class;
        //接口
        Class c2 = Comparable.class;
        //一维数组
        Class c3 = String[].class;
        //二维数组
        Class c4 = int[][].class;
        //枚举
        Class c5 = ElementType.class;
        //基本类型
        Class c6 = int.class;
        //无返回类型
        Class c7 = void.class;
        //获取Class类的自身Class
        Class c8 = Class.class;

        int[] a = new int[10];
        int[] b = new int[100];
        //true  只要元素类型和维度一致,就是同一个Class
        System.out.println(a.getClass()==b.getClass());
    }

3、获取运行时类的实例对象

通过Class中的newInstance()方法获取实例对象

  • 注意 该无参构造器不能为私有,因为newInstance()方法默认的还是调用的无参构造器
    public static void main(String[] args) throws IOException, IllegalAccessException, InstantiationException {
        Class personClass = Person.class;
        Person o = (Person)personClass.newInstance();
        //Person{name='null', age=0}
        System.out.println(o);
    }

以下知识点代码:

Person.java

@MyAnnotation(value = "哈哈哈")
public class Person extends Creature<String> implements Comparable<String>,MyInterface{

    private String name;
    protected Date birthday;
    int age;
    public int id;

    public Person() {
    }
    @MyAnnotation(value = "呵呵呵")
    private Person(String name) {
        this.name=name;
    }

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

    @MyAnnotation(value = "啧啧啧")
    private String showNation(String nation) throws NullPointerException,IndexOutOfBoundsException{
        System.out.println("my nationality is:" + nation);
        return nation;
    }

    public String play(String interest) {
        System.out.println("my interest is :"+interest);
        return interest;
    }

    private static void selfDescription() {
        System.out.println(" i am a good gay");
    }

    @Override
    public int compareTo(String o) {
        return 0;
    }

    @Override
    public void info() {
        System.out.println("i am a people");
    }

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

Creature.java

public class Creature<T> implements Serializable {
    private char gender;
    public double weight;

    private void breath() {
        System.out.println("animals breath");
    }

    public void eat() {
        System.out.println("animals eating");
    }
}

MyInterface.java

public interface MyInterface {
    void info();
}

MyAnnotation.java

//target:可用在类上,字段,方法,参数,构造器 ,本地局部变量上
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
//在运行时可以获得此注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    //定义属性value ,默认值hello
    String value() default "hello";
}

4、 获取属性

①获取当前类和父类的所有public属性
Class personClass = Person.class;
//获取所有的属性
//只能获取属性为public的,且从父类继承的属性也为public的
Field[] fields = personClass.getFields();
for (Field field : fields) {
    //public int org.lc.reflect_01.Person.id
    //public double org.lc.reflect_01.Creature.weight
    System.out.println(field);
}
②获取所有本类的所有(任意访问修饰符)属性(不包括父类的属性)
Class personClass = Person.class;
//获取所有属性
//只能获取本类的中的所有属性(任意访问修饰符)
Field[] declaredFields = personClass.getDeclaredFields();
for (Field field : declaredFields) {
    //private java.lang.String org.lc.reflect_01.Person.name
    //protected java.util.Date org.lc.reflect_01.Person.birthday
    //int org.lc.reflect_01.Person.age
    //public int org.lc.reflect_01.Person.id
    System.out.println(field);
}

5、获取方法

①获取所有本类中和父类中的所有public方法 (只能为public)
 Class personClass=Person.class;
 Method[] methods = personClass.getMethods();
 for (Method method : methods) {
     //public int org.lc.reflect_01.Person.compareTo(java.lang.String)
     //public int org.lc.reflect_01.Person.compareTo(java.lang.Object)
     //public void org.lc.reflect_01.Person.info()
     //public java.lang.String org.lc.reflect_01.Person.play(java.lang.String)
     //....
     System.out.println(method);
 }
②获取本类中的所有(任意访问修饰符)方法(不包括父类的方法)
Class personClass=Person.class;
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method method : declaredMethods) {
    //public int org.lc.reflect_01.Person.compareTo(java.lang.String)
    //public int org.lc.reflect_01.Person.compareTo(java.lang.Object)
    //public java.lang.String org.lc.reflect_01.Person.play(java.lang.String)
    //public void org.lc.reflect_01.Person.info()
    //private java.lang.String org.lc.reflect_01.Person.showNation(java.lang.String)
    System.out.println(method);
}

6、获取构造器

①获取本类中所有为public的构造器
        Class personClass = Person.class;
        //获取当前运行类的public修饰的构造器
        Constructor[] constructors = personClass.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
public org.lc.reflect_01.Person(java.lang.String,int)
public org.lc.reflect_01.Person()
②获取本类中所有的构造器(任意访问修饰符)
		Class personClass = Person.class;        
		//获取当前类的所有的构造器(任何访问修饰符)
        Constructor[] declaredConstructors = personClass.getDeclaredConstructors();
        for (Constructor constructor : declaredConstructors) {
            System.out.println(constructor);
        }
public org.lc.reflect_01.Person(java.lang.String,int)
private org.lc.reflect_01.Person(java.lang.String)
public org.lc.reflect_01.Person()

7、获取父类

8、获取带泛型的父类

9、获取带泛型父类的泛型

        Class personClass = Person.class;
        //1、获取父类
        Class superclass = personClass.getSuperclass();
        //class org.lc.reflect_01.Creature
        System.out.println(superclass);

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

        //2、取带泛型的父类
        Type genericSuperclass = personClass.getGenericSuperclass();
        //org.lc.reflect_01.Creature<java.lang.String>
        System.out.println(genericSuperclass.getTypeName());
        System.out.println("-----");

        //获取带泛型父类的泛型
        Type genericSuperclass1 = personClass.getGenericSuperclass();
        ParameterizedType parameterizedType= (ParameterizedType) genericSuperclass1;
        //父类的泛型,可能有多个  Creature<T,V,N>
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        //[class java.lang.String]
        System.out.println(Arrays.toString(actualTypeArguments));

11、 获取类实现的接口

        Class personClass = Person.class;
        //1、获取当前类的实现接口
        Class[] interfaces = personClass.getInterfaces();
        //[interface java.lang.Comparable, interface org.lc.reflect_01.MyInterface]
        System.out.println(Arrays.toString(interfaces));
        //2、获取当前类的父类实现的接口
        Class[] interfaces1 = personClass.getSuperclass().getInterfaces();
        //[interface java.io.Serializable]
        System.out.println(Arrays.toString(interfaces1));

12、获取当前类的包名

13、获取当前类的所有注解

        Class personClass = Person.class;
        //1、获取当前类的包名
        Package aPackage = personClass.getPackage();
        //package org.lc.reflect_01
        System.out.println(aPackage);
        System.out.println("----------");
        //2、获取当前类的所有注解
        Annotation[] annotations = personClass.getAnnotations();
        //[@org.lc.reflect_01.MyAnnotation(value=哈哈哈)]
        System.out.println(Arrays.toString(annotations));

四、Field属性类

1、获取访问修饰符

getModifiers()

2、获取数据类型

getType()

3、获取变量名

getName()

    public static void main(String[] args) {
        Class personClass = Person.class;
        for (Field field : personClass.getDeclaredFields()) {
            //1、访问修饰符
            //访问修饰符对应的整数
            int modifiers = field.getModifiers();
            //private  对应的整数为2
            //protected 对应的整数4
            // 默认不写        对应的整数为0
            //public  对应的整数为1
            //将访问修饰符转为真正的字符串
            System.out.print("访问修饰符:"+modifiers+"="+Modifier.toString(modifiers));

            //2、获取数据类型
            Class type = field.getType();
            System.out.print("\t类型名:"+type.getName());

            //3、获取变量名
            String name = field.getName();
            System.out.println("\t变量名:"+name);
        }
    }
访问修饰符:2=private	类型名:java.lang.String	变量名:name
访问修饰符:4=protected	类型名:java.util.Date	变量名:birthday
访问修饰符:0=	类型名:int	变量名:age
访问修饰符:1=public	类型名:int	变量名:id

五、Method方法类

1、获取在该方法上的所有注解

2、获取访问修饰符

3、返回值类型

4、方法名

5、获取形参列表

6、获取所有的异常

    public static void main(String[] args) {
        Class personClass=Person.class;
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            //1、获取方法上所有的注解
            //注意这里声明的注解 必须 @Retention(RetentionPolicy.RUNTIME) 运行时可获取
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.print("注解:"+annotation);
            }

            //2、获取访问修饰符
            System.out.print("\t"+Modifier.toString(method.getModifiers()));

            //3、返回值类型
            System.out.print("\t"+method.getReturnType().getName());

            //4、方法名
            System.out.print("\t"+method.getName());


            //5、获取形参列表
            Class[] parameterTypes = method.getParameterTypes();
            //若没有参数则不获取参数
            if (!(parameterTypes == null || parameterTypes.length == 0)) {
                System.out.print("(");
                for (int i = 0; i < parameterTypes.length; i++) {
                    //变量到最后一个参数换行
                    if (i == parameterTypes.length - 1) {
                        System.out.print(parameterTypes[i].getName()+" arg_"+i+")");
                        break;
                    }
                    System.out.print(parameterTypes[i].getName()+" arg_"+i+",");
                }
            }else{
                System.out.println();
            }

            //6、获取所有的异常
            Class[] exceptionTypes = method.getExceptionTypes();
            if (!(exceptionTypes == null || exceptionTypes.length == 0)) {
                System.out.print(" throws ");
                for (int i = 0; i < exceptionTypes.length; i++) {
                    if (i == exceptionTypes.length - 1) {
                        System.out.println(exceptionTypes[i].getName());
                        break;
                    }
                    System.out.print(exceptionTypes[i].getName()+",");
                }
            }else{
                System.out.println();
            }
        }
    }
	public	int	compareTo(java.lang.String arg_0)
	public volatile	int	compareTo(java.lang.Object arg_0)
	public	java.lang.String	play(java.lang.String arg_0)
	public	void	info
注解:@org.lc.reflect_01.MyAnnotation(value=啧啧啧)	private	java.lang.String	showNation(java.lang.String arg_0,int arg_1) throws java.lang.NullPointerException,java.lang.IndexOutOfBoundsException

六、调用运行时类中指定结构:属性、方法、构造器

1、调用属性

①通过getDeclaredField(String name) (不适用)
        //1、获取Class对象
        Class personClass = Person.class;
        //2、通过Class对象创建类的实例对象
        Person person = (Person)personClass.newInstance();
        //3、获取属性(获取的为本类和父类的public属性)   注意:此时获取的属性必须为public
        Field id = personClass.getField("id");
        //4、为该属性设置值
        id.set(person, 11);
        //5、获取该属性的值
        int x = (int)id.get(person);
        System.out.println(x);
②通过getField(String name) (常用)
        //1、获取Class对象
        Class personClass = Person.class;
        //2、通过Class对象创建类的实例对象
        Person person = (Person)personClass.newInstance();
        //3、获取属性(获取的为本类的所有属性)
        Field name = personClass.getDeclaredField("name");
        //3'当获取的属性为private私有的属性时,进行访问设值的时候需要设置访问权限setAccessible为true
        name.setAccessible(true);
        //4、为该属性设置值
        name.set(person, "张三");
        //5、获取该属性的值
        String x = (String)name.get(person);
        System.out.println(x);

2、调用方法

  • public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

    • name:要获得的方法名称
    • parameterTypes:参数列表对应的Class类
  • public Object invoke(Object obj, Object... args)

    • 参数

      • obj:实例对象

      • args:传入的实际的方法参数

    • 返回值

      • 调用该方法的返回值
①调用实例方法
        //1、获取Class对象
        Class personClass = Person.class;
        //2、通过Class对象创建类的实例对象
        Person person = (Person)personClass.newInstance();
        //3、获取方法
        Method showNation = personClass.getDeclaredMethod("showNation", String.class);
        //3' 当获取的方法为private私有类型时,同理也必须设置setAccessible为true.代表可访问
        showNation.setAccessible(true);
        //4、回调方法并获取返回值
        Object cn = showNation.invoke(person, "CN");
        //CN
        System.out.println(cn);
②调用静态方法
        //1、获取Class对象
        Class personClass = Person.class;
        //2、通过Class对象创建类的实例对象
        Person person = (Person)personClass.newInstance();
        //3、获取方法
        Method selfDescription = personClass.getDeclaredMethod("selfDescription");
        //3'、使private方法可访问
        selfDescription.setAccessible(true);
        //回调方法的实例类型可传null或者selfDescription.invoke(null);
        Object invoke = selfDescription.invoke(Person.class);
        //若方法没有返回值则返回 null
        //null
        System.out.println(invoke);
③调用构造器
1)使用Class的newInstance()方法 (常用)
        //1、获取Class对象
        Class personClass = Person.class;
        //2、直接通过newInstance回去默认的无参构造器
        Person person = (Person) personClass.newInstance();
        System.out.println(person);
2)使用getDeclaredConstructor (不常用)
        //1、获取Class对象
        Class personClass = Person.class;
        //2、获取无参的构造器
//        Constructor declaredConstructor = personClass.getDeclaredConstructor();
        //2、获取有参构造器,传入指定的参数的Class类
        Constructor declaredConstructor = personClass.getDeclaredConstructor(String.class);
        //3若构造器为private私有的,则需要设置访问权限为true
        declaredConstructor.setAccessible(true);
        //4、若为无参构造器则无需传入参数,有参构造器则需要传入实参值
        Person person = (Person) declaredConstructor.newInstance("张三");
        //Person{name='张三', birthday=null, age=0, id=0}
        System.out.println(person);