反射

142 阅读3分钟

首先定义一个类方便于解释

public class Person{
    private String name;
    private int age;
    public Person(){};

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public int add(int a,int b){
        return a+b;
    }
    public void update(){
        System.out.println("update方法");
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
1、反射的定义

将类的各个组成部分封装成Class对象

2、Class类

Person.java 文件编译成 Person.class字节码文件,Person.class字节码文件由类加载器被加载进内存之后会生成一个Class类对象,这个过程有以下几步

  • 1.使用I/O读取Person.class文件

  • 2.构建字节数组

  • 3.根据字节数组构建Class对象

  • 4.在内存中出现Class对象

Class类中结构

Class类结构实际上是和Person.java中的类的信息一一对应的,Person.java中有成员变量,成员方法,构造函数,所以Class类对象也有这些内容。即Person.java中的信息被一一映射到Class类对象

用来描述成员变量 Field fields[]
用来描述成员方法 Method methods[]
用来描述构造函数 Constructor constructor[]

3、获取Class对象的方式

Java 中的 java.lang.reflect 包提供了反射功能。java.lang.reflect 包中的类都没有 public 构造方法。所以只能由JVM进行创建

Class c1=Class.forName(“Person类的全类名”);

Class c1=类名.class;
即Class c1=Person.class;

Class c1=对象.getClass(); 
即Class c1=person.getClass();
4、Class对象的API介绍
4.1 获取Constructor

无参构造 newInstance

Person p= (Person) c1.newInstance();
System.out.println(p);  //Person{name='null', age=0}  

有参构造 getDeclaredConstructor

Constructor constructor = c1.getDeclaredConstructor(String.class, int.class);
Person person = (Person) constructor.newInstance("kallensun", 21);
System.out.println(person); //Person{name='kallensun', age=21}
4.2 获取Fields[]

获取所有public修饰的变量getFields()

Field[] fields = c1.getFields();
for (Field field:fields){
  System.out.println(field);
}  //这里因为都是private,所以打印为空

获取所有private修饰的变量getDeclaredFields()

Field[] fields1 = c1.getDeclaredFields();
for (Field field:fields1){
  System.out.println(field);
}  
//private Person.name
//private Person.age

获取指定名称的public成员变量

Field f=c1.getField("name");
System.out.println(f);  //报错,因为是private属性
4.3 获取Method[]

获取所有的public方法 getMethods()

Method[] methods=c1.getMethods();
for (Method method:methods){
  System.out.println(method);
} //这里会打印所有方法
public Person.add(int,int)
public Person.toString()
public void Person.update()
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

获取在类中实现的方法getDeclaredMethods()

public int Person.add(int,int)
public toString()
public void Person.update()

获取指定名称的方法getMethod(String name, Class<?>... parameterTypes)

 Method add = c1.getMethod("add", int.class, int.class);
 Object invoke = add.invoke(new Person(), 1, 1);
 System.out.println(invoke);

上面的invoke方法用于在根据名字获取Method对象后对方法进行调用,invoke方法定义如下

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException,InvocationTargetException

这里的第二个参数使用了剩余参数类型,是将输入的参数封装成了String数组

总结:

通过反射的机制,在内存中可以获取一个类的各个信息,所以反射在框架的应用很广

5、反射的应用
5.1 创建实例

在JDBC中我们可以看到这一句

Class.forName("com.mysql.cj.jdbc.Driver");

在执行这一句时,JVM会将Driver类加载到内存中进行执行,通过Driver中的静态代码块,会将mysql注入到DriverManager中,这就是反射的作用。在Spring框架中也经常用到这种设计,例如IOC(下篇介绍)

5.2 Method.invoke执行方法