zoukankan      html  css  js  c++  java
  • 17.Java 反射机制

    1.反射的定义

    反射之中包含了一个“反”的概念,所以要想解释反射就必须先从“正”开始解释,一般而言,当用户使用一个类的时候,应该先知道这个类,而后通过这个类产生实例化对象,但是“反”指的是通过对象找到类。                                                              

    2.Class对象

    2.1 概述

    Class对象是反射的起点,可以利用类的Class对象创建类的实例(newInstance),同时提供了操作类的工具,可以获取类的详细信息,并且使用特殊的技术实现类的加载(运行时加载,在编译的时候还没有该类的class文件)。

    2.2 Class对象的获取方式

    • a.使用类的字面常量
        • Class<Integer> c=Integer.class;     备注:编译时会进行检查,不需要捕获ClassNotFoundException,Class对象的类型也是明确的。
    • b.使用forName函数
        • Class<?> c=Class.forName("Integer");              备注:需要捕获异常,而且Class对象的类型是Class<?>,类型不明确,可以使用asSubClass进行转换,但是只能转换到Class<? extends Integer>。
        • Class<? extends Integer> u=c.asSubClass(Integer.class);
        • Class<? exnteds Integer> u1=c.getClass(new Integer(10).getClass());
    • c.使用getClass()函数
        • Integer integer=new Integer(10);
        • Class<? extends Integer> c=intger.getClass();   备注:使用对象实例获取Class对象。

    3.类型检查

    3.1概述

    Class对象提供了很多获取类信息的工具,可以获取类的方法,属性,进行类型判断,创建类的对象实例等等。

    3.2 相关API介绍

    • a.获取类的构造函数
    • public class Main {
          public static void main(String[] atgs) throws ClassNotFoundException {
              printTypeInfo("MyClass");
          }
         /**
         获取类的构造函数
      */ public static void printTypeInfo(String className){ try { Class<?> c = Class.forName(className); Constructor<?>[] constructors=c.getConstructors(); for(Constructor<?> constructor:constructors){ System.out.println(constructor); } }catch(ClassNotFoundException e){ e.printStackTrace(); } } } class MyClass { private int num=10; public MyClass(int num){ this.num=num; } public MyClass(){ } @Override public String toString(){ return String.format("%d",num); } } 
    • 备注:1.使用反射创建类的实例,Class.newInstance(),类中必须又一个无参数的构造函数,否则会报错。

    •          2.输出:public MyClass(int)

                       public MyClass()

    • 利用构造函数获取相关的对象
    • 用带参数构造函数,创建实例
    • Constructor<?> constructor=c.getConstructor(int.class);
      MyClass myClass=(MyClass)constructor.newInstance(5);
      System.out.println(myClass); 
    • 备注:1.上述的代码没有捕获异常,不能直接执行
    •          2.Class.newInstance()其实也就是获取类的无参构造函数,然后创建对象。
    • b.获取类的一般函数
    • public static void printTypeInfo(String className) {
          try {
               Class<?> c=Class.forName(className);
               for(Method method:c.getMethods()){
                  System.out.println(method);
               }
          } catch (ClassNotFoundException e) {
               e.printStackTrace();
          }
      } 
    • 备注:
    • 只能获取构造函数之外的函数,但是还包括从父类中继承来的函数
    • 获取特定的函数
    • getMethod(String methodName,Class<?> ...);
    • 例子:获取特定函数并执行Method.invoke(Obj,Class<?>...)---------动态执行Java的函数
    • public static void main(String[] args){
          Class<?> c=Class.forName(className);
          Method method=c.getMethod("getInfo",String.class);
          System.out.println(method.invoke(c.newInstance(),new String("123")));
      }
      
      class MyClass {
          private int num=10;
          public MyClass(int num){
              this.num=num;
          }
      
          public MyClass(){
          }
      
          public String getInfo(String string){
              return String.format("get : %s",string);
          }
      
          @Override
          public String toString(){
              return String.format("%d",num);
          }
      }
    • 备注:不能直接执行,没有捕获异常,为了看一来比较清楚
    • c.获取类的属性
    • public static void printTypeInfo(String className) {
              try {
                  Class<?> c=Class.forName(className);
                  Field[] fields=c.getDeclaredFields();
                  for(Field field:fields){
                      System.out.println(field);
                  }
      
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              }
          }
    • 备注:还可以获取制定的属性 :field = c.getDeclaredField("num");
    • 操作类的属性,赋值-取值
    • 可能会有访问权限的问题,比如private的属性就不能进行操作,此时可以使用field.setAccessible(true)进行“破坏”面向对象的封装。
    • field.setAccessible(true);
      field.set(myClass,123);
      
    • d.备注
    • 反射还有很所用处,例如获取泛型的参数化类型,操作注解等等。

    4.代理

    • a.一般的代理设计模式
    • public class Main {
          public static void main(String[] atgs) {
              proxy proxy=new ProxyServer(new RealServer());
              proxy.doSomthing();
          }
      }
      
      interface proxy{
          void doSomthing();
      }
      
      class RealServer implements proxy{
          @Override
          public void doSomthing(){
              System.out.println("doSomething from RealServer");
          }
      }
      
      class ProxyServer implements proxy{
          private proxy realServer;
          public ProxyServer(proxy realServer){
              this.realServer=realServer;
          }
      
          @Override
          public void doSomthing(){
              realServer.doSomthing();
          }
      }
      
    • b.利用反射的动态代理设计模式
    • import java.lang.reflect.*;
      
      /**
       * Created by yangyun on 2016/11/30.
       */
      public class Main {
          public static void main(String[] atgs) {
              myProxy myProxyInterface=(myProxy) Proxy.newProxyInstance(
                      RealServer.class.getClassLoader(),
                      RealServer.class.getInterfaces(),
                      new ProxyHandler(new RealServer())
              );
              myProxyInterface.doSomthing();
          }
      }
      
      interface myProxy{
          void doSomthing();
      }
      
      class RealServer implements myProxy{
      
          public RealServer(){
          }
      
          @Override
          public void doSomthing(){
              System.out.println("from RealServer doSomething()");
          }
      }
      
      class ProxyHandler implements InvocationHandler{
          private Object realProxy;
          public ProxyHandler(Object realProxy){
              this.realProxy=realProxy;
          }
      
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              System.out.println("before invoke()");
              Object result=method.invoke(realProxy,args);
              System.out.println("after invoke()");
              return result;
          }
      }
      
    •  有什么意义?

    • 1.如果需要在调用代理服务器的函数之前或者之后做一些别的工作,可以不修改服务器端的代码,只需要修改InvocationHandler的invloke()函数就可以。
    • 2.动态代理还是AOP(Spring 面向切面编程的基础)
    • 3.静态代理每增加一个服务类就应该添加一个代理类,动态代理的话只需要一个InvokationHandler就可以了
    • 备注:
    • System.out.println(myProxyInterface.getClass().getName());//$Proxy0
    • 通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号
    • 我自己的理解可能会有错
    • proxy.newProxyInstance()产生的其实是代理对象,这个代理对象会实现被“代理类RealServer”的所有接口,这里我们把它转型为proxy接口。
    • 当我通过代理对象来调用方法的时候,起实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的,输出method可以看到其实就是doSomething函数。
    • 这就是我们的java动态代理机制,代理对象执行函数全部都通过InvokationHandler的invoke()函数。

    参考文献

    https://www.zhihu.com/question/24304289

    http://blog.csdn.net/xu__cg/article/details/52882023

    http://blog.csdn.net/liujiahan629629/articzle/details/18013523

    http://www.cnblogs.com/xiaoluo501395377/p/3383130.html

  • 相关阅读:
    02 re模块
    24 内置模块re 正则表达式
    pickle 模块
    json模块
    命名元组
    os模块
    24 内置函数 命名元组(namedtuple) ,os ,sys,序列化,pickle,json
    functools
    ccc 音乐播放
    ccc prefab
  • 原文地址:https://www.cnblogs.com/yangyunnb/p/6126109.html
Copyright © 2011-2022 走看看