反射机制

反射机制

类对象

所有的类,都存在一个类对象,这个类对象用于提供类本身的信息,比如有几种构造方法, 有多少属性,有哪些普通方法。通过class实例获取class信息的方法就是反射

获取类对象有3种方式

  • Class.forName
  • 类名.class
  • new 类.getClass()

在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的

获取类对象的时候,会导致类属性被初始化

静态方法被修饰为synchronized的时候,其同步对象就是当前类的类对象

创建对象

与传统的通过new 来获取对象的方式不同,反射机制会先拿到Hero的“类对象”,然后通过类对象实例化一个对象构造器,再通过构造器对象创建一个对象

			String className = "类所在位置";
            //类对象
            Class pClass=Class.forName(className);
            //构造器
            Constructor c= pClass.getConstructor();
            //通过构造器实例化
            Hero h2= (Hero) c.newInstance();

访问字段

通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)来访问非public字段。

 //获取类Hero的名字叫做name的字段
        Field f1= h.getClass().getDeclaredField("name");
                //修改这个字段的值,第一个Object参数是指定的实例,第二个Object参数是待修改的值
                f1.set(h, "teemo");
Field getField(name):根据字段名获取某个public的field(包括父类)
Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
Field[] getFields():获取所有public的field(包括父类)
Field[] getDeclaredFields():获取当前类的所有field(不包括父类)

getField和getDeclaredField的区别:

  • getField 只能获取public的,包括从父类继承来的字段。
  • getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (: 这里只能获取到private的字段,但并不能访问该private字段的,除非加上setAccessible(true))

一个Field对象包含了一个字段的所有信息:

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

获取一个实例对应的该字段的值:

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

调用方法

// 获取这个名字叫做setName,参数类型是String的方法
 Method m = h.getClass().getMethod("setName", String.class);
            // 对h对象,调用这个方法,对Method实例调用invoke就相当于调用该方法,invoke的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致
            m.invoke(h, "盖伦");

Class类提供了以下几个方法来获取Method

  • Method getMethod(name, Class...):获取某个publicMethod(包括父类)
  • Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有publicMethod(包括父类)
  • Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

一个Method对象包含一个方法的所有信息:

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

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

调用构造方法

调用非publicConstructor时,必须首先通过setAccessible(true)设置允许访问

通过Class实例获取Constructor的方法如下:

  • getConstructor(参数类型):获取某个publicConstructor
  • getDeclaredConstructor(参数类型):获取某个Constructor
  • getConstructors():获取所有publicConstructor
  • getDeclaredConstructors():获取所有Constructor

然后调用newInstance(Object... parameters)创建实例对象

获取继承关系

通过Class对象可以获取继承关系:

  • Class getSuperclass():获取父类类型;
  • Class[] getInterfaces():获取当前类实现的所有接口

动态代理

在运行期动态创建一个interface实例的方法如下:

  1. 定义一个InvocationHandler实例,它负责实现接口的方法调用;
  2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
    1. 使用的ClassLoader,通常就是接口类的ClassLoader
    2. 需要实现的接口数组,至少需要传入一个接口进去;
    3. 用来处理接口方法调用的InvocationHandler实例。
  3. 将返回的Object强制转型为接口。
   InvocationHandler handler = new InvocationHandler() {
            @Override
       /*
			 * proxy:把代理对象自身传递进来
			 * method:代表当前调用的方法
			 * args:调用方法的参数
			 */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method);
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                return null;
            }
        };
/*
		 * 第一个参数:为了产生某个对象的代理对象,需要一个类装载器。(固定写法)
		 * 第二个参数:产生谁的代理对象。(基于接口进行代理,所以要拿到接口)
		 * 第三个参数:产生的代理对象干什么事情,什么事情是通过一个对象来指定的,也就是说这个地方必须要new一个InvocationHandler接口类型的对象
		 */
        Hello hello = (Hello) Proxy.newProxyInstance(
            Hello.class.getClassLoader(), 
            new Class[] { Hello.class }, 
            handler); 
        hello.morning("Bob");

interface Hello {
    void morning(String name);
}
posted @ 2020-09-01 16:33  知南而北游  阅读(38)  评论(0编辑  收藏  举报