java反射机制

java反射机制

反射机制就是在运行的状态下,通过一个类可以知道这个类的所有的属性和方法,或者通过一个类的对象可以调用它的任意一个属性和方法;这种动态获取信息及动态调用对象的方法和属性的功能就是反射机制(其实就是我们的属性.的时候自动跳出方法和属性供选择的这个功能。)
说白了反射机制其实就是由小知大,既然你是某个类的对象,或者我知道了这个类的名字,那么我jvm就可以知道这个类的所有信息。这不是很正常吗?

实例化的过程

在正常情况下,实例化一个类是需要知道这个类的路径的,流程如下
引入包–>new 类名称–>实例化完成
反射机制的流程:
类名称–>类所在的路径

通过反射机制完成类的实例化

  1. getName()方法获取类名称的使用:这个方法是Class类里面的,所以获取的流程如下:

     User u=new User();
    u.getClass().getName();
    
  2. Class的静态方法:

    Class<?> u=Class.forName("com.User");
    u.getName();//因为此时的u刚好是Class对象。所有所以不需要getClass();
    
  3. CLass的newInstance()方法:
    ` User1  tes2=u.getClass().newInstance();`
    

    Class类的使用

    获取Class实例的三种方式:
    运用.class的方式来获取Class实例。对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例。例如,Integer.TYPE 与 int.class是等效的,都是int。
    利用对象调用getClass()方法获取该对象的Class实例;
    使用Class类的静态方法forName(),用类的名字获取一个Class实例(static Class forName(String className) ),这种方式灵活性最高,根据类的字符串全名即可获取Class实例,可以动态加载类,框架设计经常用到;
    Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass方法自动构造的,因此不能显式地声明一个Class对象。
    虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
    基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
    每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
    一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。
    (JVM加载一个类的流程,查看虚拟机里面是否已经加载,如果没有,就去找对应的.class字节码文件进行加载,之后利用这个Class类的对象进行这个类的实例化操作。整个流程就一份.class字节码文件,一个类对应的Class对象,这个Class对象是JVM自行创建的。)

    java反射机制的深入研究

  4. 通过public Object invoke(Object obj,Object… args);可以调用特定的类里面的方法,只需要传入一个类的实例。注意Method的实例化如下:Method method=tes2.getClass().getMethod("sayHello", null);在实例化的时候要知道自己需要调用的方法的名称。注意这个方法的返回类型是Object,你需要自己将数据提取出来。

  5. 静态代理技术的实现:什么是代理技术?代理技术就是完成被代理对象所需要的业务,代理对象需要有代理很多对象的方法,也就是每个类的实例传进来后,可以动态的调用对应的方法实现需要的功能。所有代理对象需要实例化一个最大的接口,利用接口的多态性进行设计。被代理对象,代理对象都需要实现我们的同一个接口哦。代理对象在覆写我们的接口的方法的时候,需要将被代理对象需要的业务织入到覆写方法里面。

  6. 类加载器:对于java,我们的类加载器一共有三种类的加载。
  7. 由于静态代理技术在实现的时候,是指定了继承的类,也就是这个代理对只能代理一个接口,这样不好,如果有很多的接口需要代理的话,那么依旧解决不了代码的重用性,利用java的反射机制我们来实现动态的代理技术
  8. 动态代理技术的实现:

    public interface Subject {
    public void rent();
    public String sayHello(String name);
    }

实现接口的需要被代理的类:

public class RealSubject implements Subject {

@Override
public void rent() {
    // TODO Auto-generated method stub
    System.out.println("我是被代理对象里面的rent()方法");
}

@Override
public String sayHello(String name) {
    // TODO Auto-generated method stub
    return "我是被代理对象,我的名字是:"+name;
}

}

代理对象的产生:使用的是实现了InvocationHandler接口的一个类

public class DynamicProxy implements InvocationHandler{
private Object subject;//需要被代理的对象
public DynamicProxy(Object subject) {  
this.subject = subject;  
}  
 //这个方法会被Proxy生成的代理对象自动调用,也就是说,Proxy生成的代理对象调用对应的方法时候,我们这里的method就实例化成该方法的对象。
@Override
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
    // TODO Auto-generated method stub
    System.out.println("before rent()方法");
    System.out.println("Method方法是:"+method);
    method.invoke(subject, args);
    return null;
  }
 //这类其实只是一个桥接工具,因为在newProxyInstance()的构造方法里面使用到;

}

这个类我们其实就写了一个实例化而已。同时调用方法的实例method.invoke(代理对象的实例);这样其实就是将代理对象直接传到我们之前写的method对象的产生。
接下来就是产生我们的动态代理对象,使用的是我们Proxy类提供的一个静态的方法,我们可以直接调用就行。
具体代码如下:

  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Proxy;
  public class TestDynamicProxy {
    public static void main(String[] args) { 
       //1.找到需要代理的对象
       Subject subject=new RealSubject();
       InvocationHandler inHandler=new DynamicProxy(subject);
       Subject sub=(Subject)Proxy.newProxyInstance(inHandler.getClass().getClassLoader(), subject.getClass().getInterfaces(), inHandler);
       sub.rent();
       sub.sayHello("林金福");
       RealSayBye realSayBye=new RealSayBye();
       InvocationHandler inHandler2=new DynamicProxy(realSayBye);
       TestFace getIt=(TestFace)Proxy.newProxyInstance(inHandler2.getClass().getClassLoader(), realSayBye.getClass().getInterfaces(), inHandler2);
       getIt.sayBye();
    }
}

Proxy实例的产生返回的是接口的实例,因此我们写的是接口而不是代理类!
所以是(接口名)对象=(接口名)Proxy.newProxyInstance(实现了INvocationHandle接口的代理对象的类加载器,被代理对象的接口名称,实现了InvocationHandle接口的代理对象的实例)
由于InvocationHandle接口的实现,我们的代理对象得的是被代理对象的

还有一点:
可能我以为返回的这个代理对象会是Subject类型的对象,或者是InvocationHandler的对象,结果却不是,首先我们解释一下为什么我们这里可以将其转化为Subject类型的对象?原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Subject类型,所以就可以将其转化为Subject类型了。
同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

接着我们来看看上面d代码里面的这两句
subject.rent();
subject.hello(“world”);
这里是通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行,而我们的这个 handler 对象又接受了一个 RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用 handler 中的invoke方法去执行。