反射-基础

一. 什么是反射:

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

假如你写了一段代码: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类型,但是可以看到仍然可以添加到集合中,验证了以上两点
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值