反射-基础

来源:互联网 发布:python 2.7 idle 编辑:程序博客网 时间:2024/05/21 14:40

一. 什么是反射:

反射之中包含了一个“反”的概念,所以要想解释反射就必须先从“正”开始解释,一般而言,当用户使用一个类的时候,应该先知道这个类,而后通过这个类产生实例化对象,但是“反”指的是通过对象找到类。
所以,为什么要反向来找这个类呢?

假如你写了一段代码:Object o=new Object(); 运行了起来!
首先JVM会启动,你的代码会编译成一个.class文件,然后被类加载器加载进jvm的内存中,你的类Object加载到方法区中,创建了Object类的class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。jvm创建对象前,会先检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,进行初始化也就是代码: new Object()。

上面的程序对象是自己new的,是静态加载类,相当于写死了给jvm去跑。在编译时进行加载,假如一个服务器上突然遇到某个请求要用到某个类,但没加载进jvm,是不是要停下来自己写段代码,new一下,启动一下服务器呢??显然。这很不科学…而且如果程序中其他的类有问题,那么没有问题的类也是无法执行的,解决这个问题可以使用动态加载

反射是什么呢?当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载,这样的好处对于服务器来说不言而喻,举个例子我们的项目底层有时是用mysql,有时用oracle,需要动态地根据实际情况加载驱动类,这个时候反射就有用了,假设 com.java.dbtest.myqlConnection,com.java.dbtest.oracleConnection这两个类我们要用,这时候我们的程序就写得比较动态化,通过Class tc = Class.forName(“com.java.dbtest.TestConnection”);通过类的全类名让jvm在服务器中找到并加载这个类,而如果是oracle则传入的参数就变成另一个了。这时候就可以看到反射的好处了,这个动态性就体现出java的特性了!

二. 反射的基本使用

1. 获取class对象,即类类型

        Demo d = new Demo();        //方法1:通过该类的对象获取Demo类的类类型        Class d1 = d.getClass();        //方法2:Demo类的类类型d2指的是Class的对象        //实际在告诉我们任何一个类都有一个隐含的静态成员变量class        Class d2 = Demo.class;        //方法3:使用Class类的forName静态方法        Class d3 = Class.forName("com.nic.reflect.Demo");

2. 根据得到的类类型,创建该类的实例

通过反射来生成对象主要有两种方式。
(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。前提是需要有无参数的构造方法

Class c = String.class;Object str = c.newInstance();

(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。

//获取String所对应的Class对象Class c = String.class;//获取String类带一个String参数的构造器Constructor constructor = c.getConstructor(String.class);//根据构造器创建实例Object obj = constructor.newInstance("23333");System.out.println(obj);

3. 获取成员变量对象及其信息

成员变量是java.lang.reflect.Field的对象

  • Field类封装了关于成员变量的操作
  • Field[] fs = c.getFields()方法获取所有public的成员变量Field[]信息
  • c.getDeclaredFields() 获取的是该类自己声明的成员变量信息
  • field.getType()获得成员类型的类类型
  • field.getName()获得成员的名称

4. 获取构造函数对象及其信息

构造函数是java.lang.Constructor类的对象

  • 通过Class.getConstructor()获得Constructor[]所有公有构造方法信息
  • 建议getDeclaredConstructors()获取自己声明的构造方法
  • Constructor.getName():String
  • Constructor.getParameterTypes():Class[]

5. 获取方法对象及方法信息

package com.nic.reflect;import java.lang.reflect.Method;public class ClassUtil {    public static void pringClassMessage(Object object){        //要获取类的信息,首先要获取类的类型        Class c=object.getClass();//传递的是哪个子类的对象,c就是该子类的类类型        //获取类的名称        System.out.println("类的名称是:"+c.getName());        /*         * Method类,即方法对象         * 一个成员方法就是一个Method对象         * --getMethods()方法获取的是所有public函数,包括父类继承而来的         * --getDeclaredMethods() 返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法         * */        Method[] ms=c.getMethods();        for (int i = 0; i < ms.length; i++) {            //获取返回值类型-->得到的是方法的返回值类型的类类型            Class returnType=ms[i].getReturnType();            System.out.println(returnType.getName());            //得到方法名            System.out.println(ms[i].getName()+"(");            //获取参数类型-->得到的是参数列表的类型的类类型            Class[] paramType=ms[i].getParameterTypes();            for (Class class1: paramType) {                System.out.println(class1.getName()+",");            }            System.out.println(")");        }     }}

6. 方法反射的基本操作

public class MethodDemo {    public static void main(String[] args) throws Exception {        Class c = Class.forName("A");        /*         * 获取方法,方法的名称和方法的参数列表可以唯一决定一个方法         * getMethod()返回的是public的方法         * getDeclaredMethod()返回的是自己声明的方法         */        Method method = c.getMethod("print", int.class,int.class);        /*         * 方法的反射操作         */        method.invoke(c, 20,30);    }}class A{    public static void print(int a,int b){        System.out.println(a+b);    }}

方法的反射的好处就是解耦,比如说a,b,c对象都要调用 print()方法,正常的想法就是要创建每个对象,并且a.print() b.print() c.print() ,但是使用反射的话,就可以通过 print()方法的对象.invoke(对象,参数列表)想要用哪个对象就用哪个对象

三. 通过反射了解集合泛型的本质

public class MethodDemo {    public static void main(String[] args) throws Exception {        ArrayList list1 = new ArrayList();         ArrayList<String> list2 = new ArrayList<String>();        System.out.println(list1.getClass()==list2.getClass());         //结果为true说明编译之后的集合是去泛型化的        Class c = list2.getClass();        Method m =c.getMethod("add", Object.class);        m.invoke(list2, 20);        System.out.println(list2.size()); //1        //反射的操作都是编译之后的操作;就是运行阶段        //java中集合的泛型是防止错误输入的;只在编译阶段有效,只要绕过编译就无效了        //这里的例子违背了list2只能输入String类型,但是可以看到仍然可以添加到集合中,验证了以上两点    }}