博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
框架基础--反射机制
阅读量:2491 次
发布时间:2019-05-11

本文共 7784 字,大约阅读时间需要 25 分钟。

 

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
list1 = new ArrayList
(); 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(()在动态加载的时候将已经定义好的功能继承重写,改变它的执行时间和执行内容,实现功能扩展。 

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

你可能感兴趣的文章
mui 结束时间不能大于开始时间
查看>>
SQL创建表格——手写代码
查看>>
1、Canvas的基本用法
查看>>
两个链表的第一个公共结点-输入两个链表,找出它们的第一个公共结点。
查看>>
Swagger+AutoRest 生成web api客户端(.Net)
查看>>
setTimeout详解
查看>>
Nginx配置指定媒体类型文件强制下载
查看>>
gdb命令中attach使用
查看>>
Koa2 静态服务及代理配置
查看>>
网络运维调查报告
查看>>
bat-bat-bat (重要的事情说三遍)
查看>>
算法题11 字符串的所有对称子串
查看>>
bzoj1058: [ZJOI2007]报表统计
查看>>
寒假作业01
查看>>
关于“using namespace std”
查看>>
安卓模拟器bluestacks mac地址修改教程
查看>>
(转)android技巧01:Preferencescreen中利用intent跳转activity
查看>>
Beta Daily Scrum 第七天
查看>>
jq-dom操作
查看>>
Android style 继承
查看>>