JAVA 反射

除了int等基本类型外,Java的其他类型全部都是class(interface,Runnable,Exception)

class的本质是数据类型(Type

class是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class类型时,将其加载进内存。

每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。这里的Class类型是一个名叫Classclass

public final class Class {
    private Class() {}
}

由于JVM为每个加载的class(小写)创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。

如何获取一个classClass实例?有三个方法:

方法一:直接通过一个class的静态变量class获取:

Class cls = String.class;

方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:

String s = "Hello";
Class cls = s.getClass();

方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:

Class cls = Class.forName("java.lang.String");

因为Class实例在JVM中是唯一的,所以,上述方法获取的Class实例是同一个实例。可以用==比较两个Class实例(相同)

instanceof不但匹配指定类型,还匹配指定类型的子类。 用==判断class实例可以精确地判断数据类型,但不能作子类型比较。

动态加载

JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载

public class Main {
    public static void main(String[] args) {
        if (args.length > 0) {
            create(args[0]);
        }
    }

    static void create(String name) {
        Person p = new Person(name);
    }
}

当执行Main.java时,由于用到了Main,因此,JVM首先会把Main.class加载到内存。然而,并不会加载Person.class,除非程序执行到create()方法,JVM发现需要加载Person类时,才会首次加载Person.class。如果没有执行create()方法,那么Person.class根本就不会被加载。

访问字段

  • Field getField(name):根据字段名获取某个public的field(包括父类)
  • Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
  • Field[] getFields():获取所有public的field(包括父类)
  • Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
xxxField.setAccessible(true);获取权限使其可访问private

Field

  • getName():返回字段名称,例如,"name"
  • getType():返回字段类型,也是一个Class实例,例如,String.class
  • getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。

获取字段值:先获取Class实例,再获取Field实例,然后,用Field.get(Object)获取指定实例的指定字段的值。

f.get(p); 

设置字段值:Field.set(Object, Object)实现,第一个Object参数是指定的实例,第二个Object参数是待修改的。 

 f.set(p, "test"); 

 

访问方法

  • Method getMethod(name, Class...):获取某个publicMethod(包括父类)
  • Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有publicMethod(包括父类)
  • Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)
Method.setAccessible(true);允许其调用private方法

Method

  • getName():返回方法名称,例如:"getScore"
  • getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class
  • getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}
  • getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。

调用方法

Method m=c.getMethod("方法名", int.class)//获取方法

m.invoke(s, 6);//执行方法

调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null

多态

一个Person类定义了hello()方法,并且它的子类Student也覆写了hello()方法

那么,从Person.class获取的Method,作用于Student实例时,调用的方法到底是哪个?
        Method h = Person.class.getMethod("hello");
        h.invoke(new Student());

发现打印出的是Student:hello

因此,使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)。

 

访问构造方法

  • getConstructor(Class...):获取某个publicConstructor
  • getDeclaredConstructor(Class...):获取某个Constructor
  • getConstructors():获取所有publicConstructor
  • getDeclaredConstructors():获取所有Constructor
c.setAccessible(true);允许其调用private方法

调用构造方法

Say test= (Say) c.newInstance("123");

获取继承关系

获取父类的Class:c.getSuperclass()

获取interface列表:c.getInterfaces();

是否继承关系(本类及父类):c instanceof xxx 返回bool

动态代理

未理解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值