1. 获取运行时类的完整结构

通过反射获取运行时类的完整结构Field, Method, Constructor,Superclass,Interface, Annotation。
我们可以通过反射获得如下内容:
[1] 实现的全部接口
[2] 所继承的父类
[3] 全部的构造器
[4] 全部的方法
[5] 全部的Field
[6] 注解

相关实例代码

package com.gs.reflection;

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

public class Test08 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("com.gs.reflection.User");

        //获取类的名字
        System.out.println(c1.getName()); //获取包名+类名
        System.out.println(c1.getSimpleName()); //获取类名
        
        //获取类的属性
        System.out.println("=====获取类的属性====");
        Field[] fields = c1.getFields(); //只能找到public属性
        for (Field field : fields) {
            System.out.println(field);
        }

        //找到全部的属性
        fields  = c1.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        //获得指定属性的值
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        //获得类的方法
        System.out.println("====获得类的方法=====");
        //获得本类及其父类的全部public方法
        Method[] methods = c1.getMethods();
        for (Method method : methods) {
            System.out.println("public方法:"+method);
        }
        //获得本类的所有方法
        methods = c1.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("getDeclaredMethods:"+method);
        }

        //获得指定的方法(因为有重载,获得方法时,需要传递参数类型)
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println( getName);
        System.out.println(setName);

        //获得所有的构造器
        System.out.println("====获得指定的构造器=====");
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        constructors = c1.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("#Declared:"+constructor);
        }
        //获得指定的构造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println("指定:"+declaredConstructor);

    }
}

小结: 从上面的例子我们不难发现
getXXXs() : 获得public相关的全部东西。如: getMethods() 获得该类的所有public方法。
getDeclaredXXXs(): 获得包括private,public等相关的全部东西。如: getDeclaredMethods(): 获得该类所有的方法(包括private类型的)
getXXX(): 获得public相关的指定东西。如: getMethod(名字,参数类型),获得该类指定的public方法
getDeclaredXXX():获取包括private,public等相关的指定东西。如:getDeclaredMethod(名字,参数类型),获得该类指定的方法(包括private 类型的)

既然我们能通过反射获得指定的方法,属性。那我们应该怎么进行调用呢?

2. Class对象

在了解如何调用之前,我们先来聊一聊如何通过反射的Class类来参加类的对象呢?
[1] 创建类的对象:先获取Class对象,再调用newInstance() 方法 ===>在这个大前提,原来的类必须具备:
1.1】类必须要有一个无参构造器
1.2】类的构造器的访问权限需要足够(即需要是public类型)
通过一个实例说明:
我们有一个实体类User

// 实体类 pojo,entity
class User{
    private String name;
    private int id;
    private int age;

    public User() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

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

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

通过反射new出对象

public class Test09 {

    public static void main(String[] args) throws Exception,  {
        //获得Class对象
        Class c1 = Class.forName("com.gs.reflection.User");

        //构造一个对象
        User user = (User) c1.newInstance(); //本质上是调用了无参构造器
        System.out.println(user);
      }

[2] 思考:通过上面的例子我们可以通过Class对象调用类的无参构造器新建对象,那我们没有无参构造器就不能创建对象吗?
答案肯定是否定的,只要在操作的时候明确的调用类的构造器,并将参数传递进去之后,便可以完成实例化操作。
相关的步骤如下;

  • 通过Class类的getDeclaredConstructor(Class… parameterTypes)取得奔雷指定形参类型的构造器
  • 向构造器的形参中传递一个对象数据进去,里面包含了构造器中所需要的各个参数。
  • 通过Constructor实例化对象

上面的User对象还可以通过下面方法创建

public class Test09 {

    public static void main(String[] args) throws Exception {
       
        //通过构造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User user2 = (User) constructor.newInstance("zs", 001, 18);
        System.out.println(user2);

[3] 上面我们已经了解了Class类的方法,以及如何通过Class类new出一个对象,那接下来就是我们应该如何去调用这些方法。

调用指定的方法

通过反射,调用类中的方法,通过Method类实现

3.1】通过Class类的getMethod(String name, Class …parameterTypes) 方法取得一个Method对象,并设置此方法操作时所需要的参数类型。

3.2 】之后使用Object inovke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

java通过方法上的注解获取值 java如何通过注解获取类_System


下面通过一个实例进行说明

package com.gs.reflection;

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


//动态创建对象,通过反射
public class Test09 {

    public static void main(String[] args) throws Exception {
            //获得Class对象
            Class c1 = Class.forName("com.gs.reflection.User");

	        //通过反射调用普通方法
	        User user3 = (User) c1.newInstance();
	        //通过反射获取一个方法
	        Method setName = c1.getDeclaredMethod("setName", String.class);
	        //invoke(对象,"方法的值") ===>表示激活调用的意思
	        setName.invoke(user3, "zs");
	        System.out.println(user3.getName());
	
	        //通过反射操作属性
	        System.out.println("=====通过反射操作属性=====");
	        User user4 = (User) c1.newInstance();
	        Field name = c1.getDeclaredField("name");
	        //不能直接操作私有属性,我们需要关闭程序的安全检测,属性或方法的setAccessible(true)
	        name.setAccessible(true);
	        name.set(user4,"ls");
	        System.out.println(user4.getName());
    }
}

相关解析:
Object invoke(Object obj, Object… args)

  • 第一个Object对应原方法的返回值,若原方法无返回值,此处为null
  • 若原方法为静态方法,此时形参Object obj可为null
System.out.println("====== 静态方法=======");
 //其中test01为静态方法
 Method test01 = c1.getDeclaredMethod("test01");
 Object invoke1 = test01.invoke(null);
 System.out.println(invoke1);
  • 若原方法形参列表为空,则Object[] args为null
  • 若原方法声明为private,则需要在调用invoke()方法前,先调用方对象的setAcessible(true).这才可以访问private方法

setAccessible

  • Method和Field,Constructor对象都有setAcessible() 方法
  • setAcessible的作用就是启动和禁用访问安全检查的开关
  • 参数值为true则指示反射对象在使用时取消java语言访问检查。
  • 参数值为false则提示反射对象应该实施java语言访问检查

作用:
【1】提高反射效率,如果代码中必须使用反射,而该句代码需要频繁的被调用,则设置为true
【2】使得原本无法访问的私有成员可以访问