框架基础--反射机制

 

Class反射机制

      初解

  • 指的是可以于运行时加载,探知和使用编译期间完全未知的类.
  • 程序在运行状态中, 可以动态加载一个只有名称的类, 对于任意一个已经加载的类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能调用他的任意一个方法和属性;
  • 加载完类之后, 在堆内存中会产生一个Class类型的对象(一个类只有一个Class对象), 这个对象包含了完整的类的结构信息,而且这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以被称之为:反射.
  • 每个类被加载进入内存之后,系统就会为该类生成一个对应的java.lang.Class对象,通过该Class对象就可以访问到JVM中的这个类.

Class类的使用

类类型

在面向对象的世界里,所有看到的事物都是对象,包括class也是对象(java.lang.Class的对象)

任何一个类都是Class类的实例对象,那么,他们是如何表示的呢?

       Foo foo=new Foo();

第一种 Class c1=Foo.Class;

任何一个类都有一个隐含的静态成员变量;

第二种 Class c2=foo.getClass();

通过该对象的getClass()方法,获取类;

此时c1/c2表示的是Foo类的类类型 (class type)

    c1==c2,因为他们表示的是同一个类的类类型,一个类只有一种类类型

第三种  Class c3=null;

    c3=Class.forName(“reflect.Foo”);

此时,需要的参数是类的全限定名;这种方法不仅表示了类的类类型,还表示了动态加载类;

    c2==c3;

last:

    Foo foo=(Foo)c1.newInstance;

需要通过无参的构造函数;

静态、动态加载类

静态加载类:编译时刻加载类; new一个对象,编译时候需要加载所有需要的类;很多执行的功能同时执行,但是只要有一个有问题,那么,所有的都会停滞,全部无用;

动态加载类:运行时刻加载类; 通过类类型创建类对象;

Class.forName(“类的全称”)
Class c=Class.forName(arg[0]); 
//加载类型未知,不要强制类型转换,但是不知道怎么转换

OfficeAble oa=( OfficeAble) c.newInstance();
//用一种规则(接口)来强制转换,因为此时不知道转换到哪一种类;

oa.start();

此时,可编译上述代码;

编写OfficeAble接口下面的各种类;

编译哪个类,就可以动态加载哪个类;

用途:利用动态加载,尽量提高功能型的类的可扩展性,尽量使用动态加载,更加方便;

常见的现象,某个运行的界面,运行到某个地方的时候提示出错,此时是个别的类出错,使用动态加载类,避免了整个界面不能启动、功能全部瘫痪。

void关键字、基本数据类型等都有自己的类类型;

获取类信息

获取类的信息,类成员变量,类方法,类构造函数等

package hdu.terence.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ClassUtil {
  @SuppressWarnings("unchecked")
  public static void printClassMessage(Object obj) {
    //获取类的信息:类类型;
    Class c = obj.getClass();
    System.out.println("类名称:" + c.getName());
    /*
     * Method类,方法对象
     * 一个成员方法就是一个Method对象
     * * getMethods()方法,获取所有public函数,包括继承过来的
     * * getDeclaredMethods()获取该类自己声明的方法(一般在自定义类中)
     * */
    Method[] ms = c.getMethods();
    for (inti = 0; i < ms.length; i++) {
      ClassreturnType = ms[i].getReturnType();
      System.out.print("  " + returnType.getName() + " ");
      System.out.print(ms[i].getName() + "(");
      Class[] paramTypes = ms[i].getParameterTypes();
      for (intj = 0; j < paramTypes.length; j++) {
        System.out.print(paramTypes[j].getName());
        if (j != paramTypes.length - 1) {
          System.out.print(",");
        }
      }
      System.out.println(")");
    }
  }

  /**
   * 获取成员变量的信息
   */
  @SuppressWarnings("unchecked")
  public static void printFieldMessage(Object obj) {
    Classc = obj.getClass();
    /**成员变量也是对象
     * java.lang.reflect.Field
     * Field类封装了关于成员变量的操作
     * getFields()方法获取的是所有的public的成员变量的信息
     * getDeclaredFields获取的是该类自己声明的成员变量的信息
     * */
    //Field[]fs = c.getFields();
    Field[] fs = c.getDeclaredFields();
    for (Field field : fs) {
      //得到成员变量的类型的类类型
      ClassfieldType = field.getType();
      tringtypeName = fieldType.getName();
      //得到成员变量的名称
      StringfieldName = field.getName();
      System.out.println("      " + typeName + " " + fieldName);
    }
  }

  /**
   * 打印对象的构造函数的信息
   */
  @SuppressWarnings("unchecked")
  public static   void printConMessage(Object obj) {
    Classc = obj.getClass();
  /**构造函数也是对象
   * * java.lang. Constructor中封装了构造函数的信息
   * * getConstructors获取所有的public的构造函数
   * getDeclaredConstructors得到所有的构造函数
   * */
   //Constructor[]cs = c.getConstructors();
    Constructor[] cs = c.getDeclaredConstructors();
    for (Constructor constructor : cs) {
      System.out.print("  " + constructor.getName() + "(");
      //获取构造函数的参数列表--->得到的是参数列表的类类型
      Class[] paramTypes = constructor.getParameterTypes();
      for (Class class1 : paramTypes) {
        System.out.print(class1.getName() + ",");
      }
      System.out.println(")");
    }
  }

  public static void main(String[] args) {
    Strings = "Hello,Terence!";
    System.out.println("获取类信息");
    ClassUtil.printClassMessage(s);
    System.out.println("获取类成员变量信息");
    ClassUtil.printFieldMessage(s);
    System.out.println("获取类构造函数信息");
    ClassUtil.printConMessage(s);
    //Integeri=1;
    // ClassUtil.printClassMessage(i);
  }
}

方法反射

如何获取某个方法

方法的名称和方法的参数列表可以唯一决定某个方法

方法的反射操作

Method.invoke(对象,参数列表)

package hdu.terence.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MethodDemo1 {
  @SuppressWarnings("unchecked")
  public static void main(String[] args) {
    /**
     *要获取方法
     *1.首先要获取该方法所在的类,即获取类的信息
     *要获取类的信息,首先要获取类的类类型;
     */
    A a1 = new A();
    Class c = a1.getClass();
    /**
     *2.获取方法名称和参数列表来表决
     *getMethod获取的是public方法
     *getDeclaredMethod自己声明的方法
     * /
    /* Methodm=c.getDeclaredMethod("print",new Class[]{int.class,int.class}); */
    try {
      Method m = c.getDeclaredMethod("print", int.class, int.class);
      /**方法的反射;正常调用:a1.print(10,20);
       *方法的反射操作时通过m对象来进行方法调用,效果和正常调用是一样的;
       *反射操作有返回值,返回值类型是Object类型,如果返回值类型是void则obj=null;
       */
      Object obj1 = m.invoke(a1, new Object[]{10, 20});  /* 反射传参1 */
      Objectobj2 = m.invoke(a1, 10, 20);   /* 反射传参2 */
      System.out.println(obj1 + "\t" + obj2);
      Methodm1 = c.getDeclaredMethod("print", String.class, String.class); 
      Object obj3 = m1.invoke(a1, "Hello", "Terence");
      Object obj4 = m1.invoke(a1, new Object[]{"Hello", "Terence"});
    } catch (SecurityException e) {
      e.printStackTrace();
    } catch (NoSuchMethodException e) { 
      e.printStackTrace();
    } catch (IllegalArgumentException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    }
  }
}

class A {
  public void print(int a, int b) {
    System.out.println(a + b);
  }
  public void print(String a, String b) {
    System.out.println(a.toUpperCase() + "," + b.toLowerCase());
  }
}
通过反射来理解泛型的本质
package hdu.terence.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class MethodDemo2 {
  public static void main(String[] args) {
    ArrayListlist = new ArrayList();
    ArrayList<String> list1 = new ArrayList<String>();
    list.add("hello");
    list.add(10);
    list1.add("tttttt");
    /* list1.add(10);提示错误;              */
    Classc1 = list.getClass();
    Classc2 = list1.getClass();
    System.out.println("listand list1 是否类型相同:" + (c1 == c2));

    /*
     *上述结果提示:true;表明编译之后的集合ArrayList是去除泛型化的
     *只是泛型的限制不同,所以,泛型是用来提示输入错误的,只有在编译阶段才有效,绕过编译就无效了;
     *反射的操作都是在编译之后的操作,此处可以用方法反射来证明上述观点;
     */
    try {
      Methodm = c2.getMethod("add", Object.class);
      m.invoke(list1, 20); /* 此时转化为对象传参,绕过了编译,避免了泛型的判断;不提示错误 */
      System.out.println("list1的长度:" + list1.size());
      System.out.println("list1:" + list1);                     
      System.out.println("使用泛型 遍历集合(出错)");
      /* 此时不能用泛型再遍历了,应该使用对象Object遍历 */
/*for(Stringstr:list1) {
System.out.println(str+" ");
 *此时遍历到整形的时候会出错
 }
  */
      System.out.println("使用Object类型 遍历集合:");
      for (Object obj : list1) {
        System.out.print(obj + "\t");
      }
    } catch (SecurityException e) {
      e.printStackTrace();
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    } catch (IllegalArgumentException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    }
  }
}

梳理总结

(2016-10-17) 

  1. 反射机制是利用类的类型,跳过了编译阶段,在运行阶段需要的时候来加载相应的类,也叫作动态加载 ; 在运行的时候加载到了这个类,可以探查到这个类里面的各种信息,包括属性和方法,将里面的各种变量属性和成员方法都看做了对象,也就是说类中包含了不同类型的对象 。
  2.  一种表面化的理解就是:这种能够探知类的结构和详细信息的方式就像人坐在一面镜子前面能够反观自己,看到自己的信息一样,就是我们通常所说的反射。
  3. 反射一般用reflect表示,跟随它出现的还有一个词:introspect(反观,向里面看,内视),二者意思差不多;
  4. 出现反射肯定有自己的理由,也就是反射带来的好处:
  • 利用反射跳过编译阶段实现运行时状态的动态加载一种显而易见的好处就是,如果程序里面存在错误,在运行阶段运行到相应错误处才会停止出错,如果不用动态加载,那么所有的东西都跳不过编译阶段,编译阶段会排查错误,一旦遇到错误,所有的程序全部运行不出来,界面会全部瘫痪,启动不了。 
  •  还有一个主要的作用的就是,java里面很多的应用功能都是打包好的,放在APIs里面,要执行的内容是固定的不能改变的,但是我们在设计功能、编码的时候要根据需要改变它的执行时间和执行内容,也就是实现功能扩展,这个时候可以根据自己的需要 用反射函数invoke(()在动态加载的时候将已经定义好的功能继承重写,改变它的执行时间和执行内容,实现功能扩展。 

:如果看到的人发现了里面有错误,请帮忙指正一下,我修改一下自己的理解,共同学习,只求让我这只菜鸟快快成长奋斗提到反射我就晕晕乎不知其所以然

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值