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

    1.反射机制由来

    1.MVC设计模式 ,单标CRUD , 分页,上传
    2.传统的代码开发之中,发现所有的操作只能够使用硬编码的方式进行操作,例如:MVC设计模式的请求操作类型都需要额外传递一个附加参数,这样的操作本来就很麻烦
    3.每一个Servlet 即使使用了Annotation 进行配置,实际上代码也没少多少
    4.java语言新增了反射机制

    2.反射机制应用

    1.反射机制的基本概念
    2.分析简单Java类与反射的关系
    3.利用反射解决Servlet代码过多的问题
    4.利用反射来解决工厂设计模式,代理设计模式的操作
    5.动态代理模式,CGLIB实现的动态代理设计模式
    6.使用Annotation 编写注解操作

    3.具体内容

    3.1 反射的基本概念
    如果在正常的情况下,如果是使用一个类,则必须按照如下的步骤操作:
                        使用import导入类所在的包
                       明确的使用类名称或接口定义对象
    通过关键字 new 进行类对象实例化(构造方法: java.lang.reflect.Constructor)
    产生对象可以使用”对象.属性” 进行类中属性的使用(属性: java.lang.reflect.Field)
    通过”对象.方法()” 调用类中的方法(方法: java.lang.reflect.Method)

    而反射的过程,不需要明确类型的对象,所有的对象使用Object表示:
             可以利用Object与反射机制的混合调用类中的方法


    3.1.1 Class类
    Class类是整个反射操作的源头,而这个类的定义如下:

    public final class Class<T> //反射的泛型几乎无用 使用的时候就用 “?”
                     extends Object
                     implements Serializable, GenericDeclaration, Type, AnnotatedElement

    但如果想要使用Class类进行操作,那么就必须首先产生Class类的实例化对象.

    有三种方式产生Class类的实例化对象:

    方式一.Object类提供一个返回Class类对象的方法: public Class<?> getClass()

                    Class class = new 类名().getClass();
    方式二.利用 “类.class” 获得,日后见得最多的是Hibernate上
                    Class class = 类名.class
    方式三.利用Class类的static方法取得:public static Class<?> forName(String className) throwsClassNOtFoundException;
                    Class class = Class.forName(“类的路径”);
    如果是程序设计人员,使用最多的一定是forName()方法,但如果是使用者,肯定会使用”类.class”.通过之前的分析知道,工厂设计模式最好利用反射机制解决 耦合问题


    面试题: 请解释Object类之中的所有方法以及每一个方法使用上的注意事项
      1.对象克隆:

         public Object clone() throws CloneNotSupportedException
         克隆对象所在的类一定要实现java.lang.Cloneable接口,而且子类只需要继续调用Object类的Clone()就可以实现克隆操作.
      2.对象输出:

          public String toString()
          直接输出对象时会默认调用toString()方法
      3.对象比较:

          public boolean equals()
          当保存Set集合时,会依靠hashCode()和equals()判断是否为重复对象
      4.获取hash码,

           public int bashCode()
           可以理解为为每一个类对象的唯一编码,比较时会先判断编码是否相同,而后比较equals()里面的内容
       5.取得Class类对象

          public Class<?> getClass()
           通过一个已经实例化好的对象进行对象的反射操作
      6.线程等待:

          public void wait() throws IntercepterException
         执行到此代码时线程要等待执行,一直到执行notify() 或 notifyAll()
         唤醒第一个线程:notify()
         唤醒所有线程:notifyAll()
     7.垃圾回收前释放:

        public void finalized() throws Throwable
        当使用GC回收无用的垃圾空间时,默认调用.
         
    3.1.2 利用反射 实例化对象

      class类如果使用了forName()方法之后,就可以使用Class类定义的newInstance()方法默认去掉用类之中的无参构造方法进行操作:

    public T newInstance()
                  throws InstantiationException,
                         IllegalAccessException

     此泛型使用不到

    在我们整个程序的编写之中,即使完全不知道类的结构,即使不导入包.类,也可以对类进行实例化操作.

    但是如果我们使用反射实例化类对象,必须满足类中有无参构造方法,因为默认使用newInstance()方法只能够找到无参构造方法.

    在Class类里面定义了可以获得一个类之中构造方法的操作:
  

    1.  取得类之中的全部构造方法:

    public Constructor<?>[] getConstructors()
                                     throws SecurityException

     2.取得类之中指定的构造方法:

    public Constructor<T> getConstructor(Class<>... parameterTypes)
                                  throws NoSuchMethodException,
                                         SecurityException

    所以如果现在想要进行制定构造方法的调用,就必须将关注点放在 Constructor 类之中,在此类中定义了一个对象实例化方法: (前提:反射的类中必须要有 无参构造方法)
         

      public T newInstance(Object... initargs)
                  throws InstantiationException,
                         IllegalAccessException,
                         IllegalArgumentException,
                         InvocationTargetException

    因为如果是通过构造方法实例化对象 规格不统一,所以在进行简单 java类操作的时候 必须有无参构造方法;;

    3.1.3调用类种方法
     取得了一个类的实例化对象之后,下面主要的任务是要调用类之中可以使用的构造方法,对于一个类之中的方法,实际上有两类:
      1. 取得我们父类继承而来的方法
       取得全部方法:

    public Method[] getMethods()
                        throws SecurityException

      2.取得指定方法:

    public Method getMethod(String name,Class<?>... parameterTypes)
                     throws NoSuchMethodException,
    SecurityException

      2.取得本类中定义的方法
          取得全部方法:

    public Method[] getDeclaredMethods()
                                throws SecurityException

         取得指定方法:

    public Method getDeclaredMethod(String name,
                                    Class<?>... parameterTypes)
                             throws NoSuchMethodException,
                                    SecurityException

    两种方法定义上区别不大,因为方法大都是public,所以两种方式取得的结果是没有区别的.

    程序直接调用Method类之中的toString()实现输出,如果用户想要自己的输出格式,就需要调用Method类中的如下方法:
        1.取得方法修饰符:public intgetModifiers();
              .程序之中找的不是public等关键字,而是关键字对应的数字,例如:
                public --> 1
                static --> 200
                final --> 30
               --->  public static  ---> 201
               --->  public static final  ---> 231
             但是在程序之中,我们不需将其更换成能读懂的信息,可以借助使用Modifier类完成,此类中可以直接利用toString()方法将数字转化为字符串,
            转换:public static String  toString(int mod).

        2.取得方法的返回参数类型:

           public Class<?> getReturnTypes()                            

           met[i].getReturnType().getSimpleName()  去包名


         3.取得方法的参数:

           public Class<?> getParameterTypes()
         4.获取方法的抛出异常:

            public Class<?> getExceptionTypes()

    package com.reflect;
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    
    
    interface Message{
        void get();
    }
    class Student implements Message{
    
    
        public void get(){}
        public void fun(){}
        public void print(){}
    }
    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            Class<?> cls = Class.forName("com.reflect.Student");
            Method met[] = cls.getMethods();
            for (int i = 0; i < met.length; i++) {
                System.out.print(Modifier.toString(met[i].getModifiers()) + " ");//获取修饰符
                System.out.print(met[i].getReturnType().getSimpleName() + " ");//获取返回值类型
                System.out.print(met[i].getName() + "(");
                Class<?> params[] = met[i].getParameterTypes();//取得全部参数
                if (params.length>0) {  //有参数
                    for (int i1 = 0; i1 < params.length; i1++) {
                        System.out.print(params[i1].getSimpleName() + " arg-"+i1);
                        if (i1<params.length-1){ //还有参数
                            System.out.print(",");
                        }
                    }
                }
                System.out.print(")");
                Class<?> exps[] = met[i].getExceptionTypes();
                if (exps.length>0){ //有异常抛出
                    System.out.print("throws");
                    for (int i1 = 0; i1 < exps.length; i1++) {
                        System.out.print(exps[i1].getSimpleName());
                        if (i1<exps.length-1){
                            System.out.print(",");
                        }
                    }
                }
                System.out.println(); //换行
            }
        }
    }

     

    此类代码只会在编写开发工具中使用,而我们使用的所谓的随笔提示功能就是依据以上的代码实现的( . 提示功能),但是与我们开发者密切关联的一定是利用Method调用类中的方法,而且在Method类里面提供了一个重要的方法
             
     调用:

    public Object invoke(Object obj,
                         Object... args)
                  throws IllegalAccessException,
                         IllegalArgumentException,
                         InvocationTargetException

     

    package com.reflect;
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    
    
    
    class StudentA {
       private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    public class Reflect2 {
        public static void main(String[] args) throws Exception {
            Class<?> cls = Class.forName("com.reflect.StudentA");
           Object obj = cls.newInstance();  //实例化对象
            Method setNameMethod = cls.getMethod("setName",String.class);
            Method getNameMethod = cls.getMethod("getName");
            setNameMethod.invoke(obj,"SMITH");//对象.setName("SMITH")
            System.out.println(getNameMethod.invoke(obj));
        }
    }

    正因如此,所以开发之中才强调 setter,getter 方法必须按照严格的要求编写;

    3.1.5 调用类中的一个属性(尽量不要使用)

    关于类之中的属性也可以直接利用反射操作,二支持的方法有两类:

      1. 取得所有继承而来的属性
            取得全部属性:

     public Field[] getFields()
                      throws SecurityException

            取得指定属性:

     public Field getField(String name)
                   throws NoSuchFieldException,
                          SecurityException


     2.  取得本类定义的属性
            取得全部属性:

    public Field[] getDeclaredFields()
                              throws SecurityException

            取得指定属性:

    public Field getDeclaredField(String name)
                           throws NoSuchFieldException,
                                  SecurityException

    范例:取得一个类中的全部属性

    package com.reflect;
    
    import java.lang.reflect.Field;
    
    
    interface MessageA{
        public static final String INFO = "Hello worg.";
    }
    class Person {
        private String name;
        private Integer age;
    }
    class StudentB extends Person implements MessageA{
        private String school;
        private Double price;
    }
    public class GetMethods {
        public static void main(String[] args) throws Exception {
           Class<?> cls = Class.forName("com.reflect.StudentB");
            { //取得继承而来的全部属性
                 Field fields[] = cls.getFields();
                  for (int i = 0; i < fields.length; i++) {
                    System.out.println(fields[i]);
                }
    
            }
            { //取得本类中的全部属性
                Field fields[] = cls.getDeclaredFields();
                for (int i = 0; i < fields.length; i++) {
                    System.out.println(fields[i]);
                }
    
            }
            { //取得父类定义的属性
                Field fields[] = cls.getSuperclass().getDeclaredFields();
                for (int i = 0; i < fields.length; i++) {
                    System.out.println(fields[i]);
                }
    
            }
        }
    }


    在Field类里还定义有进行属性调用的方法:
    设置属性内容:

    public void set(Object obj,Object value)
             throws IllegalArgumentException,
                    IllegalAccessException

        取得属性内容:

    public Object get(Object obj)
               throws IllegalArgumentException,
                      IllegalAccessException

    在Constructor,Method,Field类有一个共同的父类Accessible里面定义了可以取消封装的操作

    范例:直接操作属性

    package com.reflect;
    
    import java.lang.reflect.Field;
    
    class StudentC{
        private String school;
    }
    public class FieldDemo {
        public static void main(String[] args) throws Exception {
            Class<?> cls = Class.forName("com.reflect.StudentC");
            Object obj = cls.newInstance();
            Field schoolField = cls.getDeclaredField("school");
            schoolField.setAccessible(true);
            schoolField.set(obj,"清华大学");
            System.out.println(schoolField.get(obj));
        }
    }

    在开发中,灵活使用Class,Constructor,Method,Field,就可以使用反射进行一系列代码的操作.

  • 相关阅读:
    多线程实际应用踩坑
    SpringBoot远程接口调用-RestTemplate使用
    Python测试Post请求
    Ubuntu基于zsh自定义设置shell主题
    github-share报错无法读取远程仓库
    JVM是如何处理异常的
    springboot之父pom操作
    php 后台json 转 js数组
    tp 外连接查询
    原生关联查询语句
  • 原文地址:https://www.cnblogs.com/luke-liuqi/p/9372901.html
Copyright © 2011-2022 走看看