java反射

参考:尚硅谷

Class类:

对象照镜子后可以得到的信息:

某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含 了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。

Class本身也是一个类

 Class 对象只能由系统建立对象

 一个加载的类在 JVM 中只会有一个Class实例

一个Class对象对应的是一个加载到JVM中的一个.class文件

 每个类的实例都会记得自己是由哪个 Class 实例所生成

 通过Class可以完整地得到一个类中的所有被加载的结构

 Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的 Class对象

Class类常用方法

 获取Class类实例方法:

1)Class clazz = String.class

2)Class clazz = 某类实例.getClass();

3)Class clazz = Class.forName(“java.lang.String”); //最为常用

拥有Class对象的类型:
各种类 基本数据类型 接口 数组 枚举 注解 

对于数组数组只要元素类型与维度一样,就是同一个Class

对这些基本类型Class延申参考https://www.cnblogs.com/jpfss/p/11382043.html

Class与class字节码的联系在类的加载阶段有体现:

加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时 数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的 过程需要类加载器参与。

三种类加载器ClassLoader:

引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类 库。该加载器无法直接获取

扩展类加载器:负责jre/lib/ext目录下的jar包或 – D java.ext.dirs 指定目录下的jar包装入工作库

系统类加载器:负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工 作 ,是最常用的加载器

private Person(String name) {
        this.name = name;
    }

创建运行时类的对象:

1)无参构造器,类的构造器的访问权限需要足够:

直接调用Class对象的newInstance()方法,比较简单。

2)有参构造器,足够访问权限

先获取带有指定参数类型的构造器,在创建实例

Class clazz = Person.class;
//1.通过反射,创建Person类的对象
Constructor cons = clazz.getConstructor(String.class,int.class);
Object obj = cons.newInstance("Tom", 12);
Person p = (Person) obj;

3)有参构造器,私有权限

private Person(String name) {
        this.name = name;
    }

使用反射,强行开放权限(注意 Declared 后面马上解释)

Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person) cons1.newInstance("Jerry");

调用运行时类指定结构:

i 调用指定方法:

通过反射,调用类中的方法,通过Method类完成。步骤:

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

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

getMethod 返回所有public方法, getDeclaredMethod返回所有方法。

Object invoke(Object obj, Object[] args)

说明:

1.Object 对应原方法的返回值,若原方法无返回值,此时返回null

2.若原方法若为静态方法,此时形参Object obj可为null

3.若原方法形参列表为空,则Object[] args为null

4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

private String showNation(String nation){
        System.out.println("我的国籍是:" + nation);
        return nation;
    }
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);
        String nation = (String) showNation.invoke(p1,"中国");//相当于String nation = p1.showNation("中国")
        System.out.println(nation);

ii 调用指定属性:

在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和 get()方法就可以完成设置和取得属性内容的操作。

 public Field getField(String name) 返回此Class对象表示的类或接口的指定的 public的Field。

 public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。

获取到Filed后,执行Field上面的方法:

 public Object get(Object obj) 取得指定对象obj上此Field的属性内容,注意get不用传入Class对象

 public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容

Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1,"HanMeimei");
System.out.println(p1);

关于于setAccessible方法:

 Method和Field、Constructor对象都有setAccessible()方法。

 setAccessible启动和禁用访问安全检查的开关。

 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。

 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被 调用,那么请设置为true。  使得原本无法访问的私有成员也可以访问

 参数值为false则指示反射的对象应该实施Java语言访问检查。

一句话就是设置为True,私有就不私有了

 

InvocationHandler接口:

InvocationHandler是由代理实例的调用处理程序实现的接口 。
每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

​ 1)InvocationHandler接口(调用处理器):就一个方法 invoke()
​ invoke():表示代理对象要执行的功能代码。你的代理类要完成的功能就写在 invoke()方法中。

参数,一般来说写法是固定的:

object proxy:jdk创建的代理对象,无需赋值。

Method method:目标类中的方法,jdk提供method对象的

object[]args:目标类中方法的参数,jdk提供的。

 

proxy类:

核心的对象,创建代理对象。之前创建对象都是new类的构造方法()

现在我们是使用proxy类的方法,代替new的使用。

方法:静态方法 newProxyInstance()

作用是:创建代理对象

 对象原型;

    public static Object newProxyInstance( ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)   throws IllegalArgumentException

参数:

  1. ClassLoader loader 类加载器,负责向内存中加载对象的,使用反射机制获取对象的classLoader,

如何获取? 类 a, a.getCalss().getClassLoader(),目标对象的类加载器

这里我们细分:每一个类都继承Object类,在Object中有一个getClass方法,表示 类对象的运行时类的Class对象。 而Class类里面有一个public ClassLoader getClassLoader()方法

  1. Class<?>[] interfaces: 接口,目标对象实现的接口,也是反射获取的
  2. InvocationHandler h : 我们自己写的,代理类要完成的功能
  3. public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h){
    final Class<?>[] intfs = interfaces.clone();
    Class<?> cl = getProxyClass0(loader, intfs);
    return cons.newInstance(new Object[]{h});
    }

返回值也就是代理对象  类型是: class com.sun.proxy. $ProxyO

 

关于Proxy 与 InvocationHandler 更多展开参考 java8 api内容:


 

Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

为某个接口创建代理Foo :

  InvocationHandler handler = new MyInvocationHandler(...);
     Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
     Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).
                     newInstance(handler);

或更简单地:

  Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class<?>[] { Foo.class },
                                          handler); 

动态代理类 (以下简称为代理类 )是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为。 代理接口是由代理类实现的接口。 代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象,它实现了接口InvocationHandler 。 通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke方法,传递代理实例, java.lang.reflect.Method被调用方法的java.lang.reflect.Method对象以及包含参数的类型Object Object的数组。 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。

代理类具有以下属性:

  • 代理类是公共的,最终的,而不是抽象的,如果所有代理接口都是公共的。
  • 如果任何代理接口是非公开的,代理类是非公开的,最终的,而不是抽象的 。
  • 代理类的不合格名称未指定。 然而,以字符串"$Proxy"开头的类名空间应该保留给代理类。
  • 一个代理类扩展了java.lang.reflect.Proxy 。
  • 代理类完全按照相同的顺序实现其创建时指定的接口。
  • 如果一个代理类实现一个非公共接口,那么它将被定义在与该接口相同的包中。 否则,代理类的包也是未指定的。 请注意,程序包密封不会阻止在运行时在特定程序包中成功定义代理类,并且类也不会由同一类加载器定义,并且与特定签名者具有相同的包。
  • 由于代理类实现了在其创建时指定的所有接口, getInterfaces在其对象上调用getInterfaces将返回一个包含相同列表接口的数组(按其创建时指定的顺序),在其对象上调用getMethods将返回一个数组的方法对象,其中包括这些接口中的所有方法,并调用getMethod将在代理接口中找到可以预期的方法。
  • Proxy.isProxyClass方法将返回true,如果它通过代理类 - 由Proxy.getProxyClass返回的类或由Proxy.newProxyInstance返回的对象的类 - 否则为false。
  • 所述java.security.ProtectionDomain代理类的是相同由引导类装载程序装载系统类,如java.lang.Object ,因为是由受信任的系统代码生成代理类的代码。 此保护域通常将被授予java.security.AllPermission 。
  • 每个代理类有一个公共构造一个参数,该接口的实现InvocationHandler ,设置调用处理程序的代理实例。 而不必使用反射API来访问公共构造函数,也可以通过调用Proxy.newProxyInstance方法来创建代理实例,该方法将调用Proxy.getProxyClass的操作与调用处理程序一起调用构造函数。

代理实例具有以下属性:

  • 给定代理实例proxy和其代理类Foo ,以下表达式将返回true:
       proxy instanceof Foo 
    并且以下操作将会成功(而不是throws一个ClassCastException ):
       (Foo) proxy 
  • 每个代理实例都有一个关联的调用处理程序,它被传递给它的构造函数。 静态Proxy.getInvocationHandler方法将返回与作为其参数传递的代理实例关联的调用处理程序。
  • 代理实例上的接口方法调用将被编码并分派到调用处理程序的invoke方法,如该方法的文档所述。
  • hashCode , equals在代理实例上的toString中声明的java.lang.ObjecttoStringtoStringtoString方法将被编码并分派到调用处理程序的invoke方法,方法与接口方法调用被编码和调度相同。 传递给invoke方法对象的声明类将为java.lang.Object 。 从java.lang.Object的代理实例的其他公共方法不会被代理类覆盖,因此这些方法的调用与java.lang.Object 。

 

 

反射最重要的应用是在动态代理,请移步:https://www.cnblogs.com/wangid3/p/14159821.html

 

posted @ 2020-12-19 16:43  wangid3  阅读(85)  评论(0)    收藏  举报