反射机制

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,就可以使用反射进行一系列代码的操作.

posted on 2018-07-26 17:03  楓与楪  阅读(117)  评论(0编辑  收藏  举报

导航