java反射

1.java的反射机制可让我们在编译期之外的运行期检查类,接口,变量以及方法的信息,还可在运行期实例化对象,
   调用方法,通过get/set获取变量的值
2.反射时需要先获取类的Class对象,可反射所有类型包括基本类型(int等),即使是数组与之关联的Class对象
   Class c=Class.forName("类的全名"),找不到类就会抛出ClassNotFoundException
   c.getName()            //获取完整包路径名
   c.getSimpleName() //只获取类名
   c.getModifiers()     //获取修饰符
   c.getSuperclass() //获取父类对象
   c.getInterfaces()     //获取接口数组
   c.getConstructors() //获取构造数组
   c.getMethods()    //获取所有方法数组
   c.getFields()        //获取成员变量数组
   c.getAnnotation() //获取类的注解
   c.getConstructor.getParameterTypes()  //获取构造方法的参数数组
   c.getConstructor().newInstance("可变参数")  //实例化一个类
3.反射方法:Method[] methods=MyObject.class.getMethods();
4.变量
   a.c.getField().getName() //获取变量名称
   b.c.getField().getType()  //获取变量类型
   c.当获取Field引用时,可通过调用Field.get()或Field.set()获取或设置变量的值
5.方法
   a.c.getMethods() //返回的数组中包含指定类中声明为public的所有变量集合
   b.c.getMthod("doSth",null)  //获取的方法没有参数,在第二个参数传入null即可
   c.c.getMthod(“doSth”).getParameterTypes()  //获取指定方法的参数
   d.c.getMethod("doSth").getReturnType()  //获取指定方法的返回值类型
   调用一个方法采用c.getMethod("doSth").invoke(null,"parameter-value") //null为要调用方法的对象,
   如果是静态方法调用,则可用null代替指定对象作为invoke()的参数;(doSth不是静态方法时,就需要传入真正的实例,而不是null)
6.私有变量和方法
   a.Field  privateField=c.getDeclaredField("privateVar")   //获取一个当前类的私有变量,而不是父类中的
   b.privateField.setAccessible(true)   //关闭指定类Field实例的反射访问检查,设置true之后不论私有、受保护的以及
      包访问的作用域,不论在任何地方都可以被访问到
   c.c.getDeclaredMethod(String name,Class[] paramTypes)或者c.getDeclaredMethods()可获取私有方法
   d.c.getMethod(String name,Class[] paramTypes)和c.getMethod()获取公有方法
   e.c.getMethod().setAccessible(true)  //关闭指定Method实例的反射访问检查
7.注解:插入代码中的一种注释或者说是一种元数据
    a.@interface 表名这是一个注解    @Retention(RetentionPolicy.RUNTIME)  //表示注解可以在运行期通过反射访问
    b.@Target(ElementType.TYPE)  表示注解只能用在类型上(比如类和接口)  Type也可改成Field或Method
    类注解:
           Annotation[] annotations=c.getAnnotations();  //获取类注解   
          或者c.getAnnotation(Aaaa.class)获取指定访问类的注解
    方法注解:
          Annotation[] annotations=c.getMethod().getDeclaredAnnotations();//获取方法上的注解    
          或者c.getMethod("name",null).getAnnotation(Aaaa.class);   
     参数注解:
          Annotation[][] paramAnnotation=c.getMethod("name",null).getParameterAnnotations();
          Class[] paramType=c.getMethod("name",null).getParameterTypes();
          通过instanceof判断是否为某个注解
      变量注解:
          c.getDeclaredField("name").getDeclaredAnnotations();   
          或者c.getDeclaredField("name").getAnnotation(Aaa.class);
8.泛型:Java泛型信息并非完全无法在运行期获得,部分情况是可以获取得到的(通过被参数化类型的方法及变量中找)
       a.使用泛型的场景:
          1.声明一个需要被参数化的类/接口
          2.使用一个参数化类
       b.
 9.数组:java.lang.reflect.Array处理数组
       a.int[] arr=(int[])Array.newInstance(int.class,3);
       b.不通过反射获取数组的Class对象
           Class stringClass=String[].class;
       c.通过反射获取对象
           Class intArr=Class.forName("[I");  //jvm中I代表int类型,[代表数组,同样使用其他原生数据类型
       d.普通对象类型数组
           Class stringClass=Class.forName("[Ljava.lang.String;");  //获取一个数组的Class对象后,
           则可通过stringClass.getComponentType();获取数组的成员类型(数组存储的数据类型)
10.动态代理:可在运行期动态创建接口的实现
        Proxy.newProxyInstance()创建动态代理,参数:
        a.类加载器(ClassLoader)用来加载动态代理类
        b.待实现的接口数组
        c.一个InvocationHandler把所有方法的调用都转到代理上
   InvocationHandler handler = new MyInvocationHandler();
    MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                            MyInterface.class.getClassLoader(),
                            new Class[] { MyInterface.class },
                            handler);
d.使用场景
           1.数据库连接及事务管理
           2.单元测试中的动态Mock(生成)对象
           3.自定义工厂与依赖注入(DI)容器之间的适配器
           4.类似AOP的方法拦截器
11.动态类加载与重载:一个类只能被同一个ClassLoader实例加载一次
    a.类加载过程是一个递归的模式,一个类所有相关的类都会被加载
    b.一个类加载器被用来加载一个类的时候,会首先调用该类加载器的父加载器来加载,
       当父加载器服务找到该类时,才会尝试加载该类
    c.类加载顺序:c.getClassLoader()
       1.检查类是否已经被加载
       2.未被加载,先调用父加载器加载
       3.父加载器不能加载该类,则尝试加载该类
     d.动态类重载:java内置的类加载器在加载一个类之前会检查其是否已经被加载,
        因此重载一个类时无法使用Java内置的类加载器(不能使用已经加载过类的类加载器来重载一个雷),
        想要重新动态加载一个类需要手动继承ClassLoader,另外c.getClassLoader().resolve()完成被加载类需要的相关链接,
        但是resolve是一个final方法,因此没重载一个类都需要一个新的ClassLoader的子类
     e.自定义重载:Java中类的全名(包名+类名)作为一个唯一标识来呗ClassLoader实例加载
        一个类A分别被两个雷加载器加载,则会被认为是两个不同的类,尽管类全名的完全一样的,如果进行类的实例转换会报ClassCastException
        解决:
             1.标记变量类型为一个接口,只重载其实现类
             2.标记变量类型为一个超类(父类),只重载其子类
     参考:https://github.com/mjiderhamn/classloader-leak-prevention











































posted on 2018-03-06 18:18  xiaojiayu0011  阅读(167)  评论(0)    收藏  举报

导航