初涉Java反射机制

来源:互联网 发布:昆仑 知乎 编辑:程序博客网 时间:2024/05/29 18:16

1、Class类

1)任何一个类都是(java.lang.)Class的实例,这个实例对象有三个表示对象。如下所示:

public class ClassDemo1 {    public static void main(String[] args) {        Foo f = new Foo();        //第一种方式:通过类名获得(由这种方式可得任何一个类都有一个隐含的静态成员变量class)        Class c1=Foo.class;        //第二种方式:通过该类的对象名,用getClass方法获得        Class c2=f.getClass();        //第三种方式:通过 Class.forName("类全称");获得        try {               Class c3 = Class.forName("com.java.demo.Foo");        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        /**         * c1、c2表示Foo类的类类型(class type)         * 类也是对象,是(java.lang.)Class的实例对象         * 该类的类类型可以创建该类型的对象实例,如通过c1、c2创建Foo的实例         * eg:         */        try {            Foo foo = (Foo)c2.newInstance();//注意:要用无参数的构造方法才能newInstance()        }catch (Exception e) {            e.printStackTrace();        }    }}class Foo{} 

2)Class.forName(“类全名”);不仅表示类的类类型,还代表了动态加载。

3)编译时刻加载的类是静态加载类、运行时加载类是动态加载类。new创建对象是静态加载类,在编译时刻就需要加载所有可能使用到的类。

4)基本的数据类型、void关键字都存在类类型。
eg:这里写图片描述

5)Method、Field、Constructor类分别封装了关于方法、成员变量、构造函数的操作。

import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class ClassUtils {    /*     * 获取类的方法信息     */    public static void printClassMethod(Object obj) {        System.out.println("-----------打印方法信息------------");        Class c = obj.getClass();// 首先要获取类的类类型        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.print(returnType.getName() + " ");// 打印返回值类型的名称            System.out.print(ms[i].getName());// 得到方法的名称            // 获取参数类型,得到的是参数类表的类型的类类型            Class[] paramTypes = ms[i].getParameterTypes();            System.out.print("(");            for (Class class1 : paramTypes) {                System.out.print(class1.getTypeName() + ",");            }            System.out.println(")");        }    }    /*     * 获取类的成员变量信息     */    public static void printClassMenbers(Object obj) {        System.out.println("-----------打印成员变量信息------------");        Class c = obj.getClass();        /**         * 成员变量也是对象,是java.lang.reflect.Field的对象 Field封装了关于成员变量的操作         * getFields()方法获取的是所有的public的成员信息 getDeclaredFields获取的是该类自己声明的成员变量的信息         */        Field[] fs = c.getFields(); // c.getDeclaredFields()        for (Field field : fs) {            Class fieldType = field.getType();// 得到成员变量的类型的类类型            String typeName = fieldType.getName();// 得到成员变量的名称            String fieldName = field.getName();// 得到成员变量的名称            System.out.println(typeName + " " + fieldName);        }    }    /**     * 构造函数也是对象,是java.lang.Constructor中封装了构造函数的信息     * getConstructors()方法获取到所有public的构造函数 getDeclaredConstructors()得到所有的构造函数     */    public static void printClassConstructor(Object obj) {        System.out.println("-----------打印构造函数信息------------");        Class c = obj.getClass();        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.getTypeName() + "   "                        + class1.getName());            }            System.out.println(")");        }    }}

2、方法的反射操作

1)反射调用一般分为3个步骤:
(1)得到要调用类的类类型(可从上面介绍的3种方式获得)。
(2)得到要调用的类中的方法(Method)。
(3)调用Method的invoke()方法。
eg:

A a = new A();//要获取类中的方法,也就是要获取类的信息,先要获取类的类类型Class c = a.getClass();//获取A类的类类型Method m = c.getMethod("print", int.class, int.class);//获取类的print(int a, int b);方法m.invoke(a, new Object[]{1,2});//也可以写成m.invoke(a, 1,2);

说明:当使用非反射方式调用方法时,是a.print(1,2),即通过类对象调用方法;而反射操作:方法的反射操作时用Method实例对象来进行方法调用,这a.print(1,2)的作用是一模一样的;方法如果没用返回值则返回null,有返回值则返回具体的返回值。

完整例子:

public class MethodReflectDemo {    public static void main(String[] args) {        A a = new A();        //要获取类中的方法,也就是要获取类的信息,先要获取类的类类型        Class c = a.getClass();//获取A类的类类型        //这个是Method[] ms = c.getMethods();是获取类的所有的public方法,而getMethod()获取的是某一个方法,具体获取哪个方法看getMethod()中传入了什么参数        try {            //Method m = c.getMethod("print",new Class[]{int.class, int.class});可以写成:            Method m = c.getMethod("print", int.class, int.class);//获取类的print(int a, int b);方法            m.invoke(a, new Object[]{1,2});//也可以写成m.invoke(a, 1,2);            Method m1 = c.getMethod("print", new Class[]{String.class, String.class});//获取类的print(String a, String b)方法            m1.invoke(a, "hi","hello");//反射操作,调用方法            Method m2 = c.getMethod("print");//获取类的print()方法;或者写成:Method m2 = c.getMethod("print",new Class[]{});                       m2.invoke(a);//反射操作,调用方法;或者写成:m2.invoke(a, new int[]{});        } catch (Exception e) {            e.printStackTrace();        }    }}class A {    public void print(){        System.out.println("haha");    }    public void print(int a, int b){        System.out.println(a+b);    }    public void print(String a, String b){        System.out.println(a + "," + b);    }}

3、通过反射了解泛型的本质

java中集合的泛型,是防止错误输入的,只在编译时有效,绕过编译就无效了。
eg:

public class GenericList {    public static void main(String[] args) {        ArrayList list1 = new ArrayList();//实例一个无泛型的ArrayList对象list1        ArrayList<Integer> list2 = new ArrayList<Integer>();//实例一个Integer型的ArrayList对象list2        list1.add("abc");//在无泛型的list1中添加字符串“abc”        list1.add(123);//在无泛型的list1中添加整型数据123        list2.add(123);//在泛型位Integer的list1中添加整型数据123        //list2.add("abc");//在泛型位Integer的list1中添加字符串数据“abc”,Eclipse提示出错        Class c1 = list1.getClass();//获得list1的类类型        Class c2 = list2.getClass();//获得list2的类类型        System.out.println(c1==c2);//打印“true”,说明编译之后集合的泛型是去泛型化的        /**         * java中集合的泛型,是防止错误输入的,只在编译时有效,绕过编译就无效了         * 下面进行验证:通过方法的反射来操作,绕过编译         */        try {            Method m = c1.getMethod("add",Object.class);//获得ArrayList中的add()方法            m.invoke(list1,11);//list1,反射调用add()方法,为list1添加整型数据            m.invoke(list2, "dfdf");//list2,反射调用add()方法,为list2添加字符串            m.invoke(list2, "dfdf123");//list2,反射调用add()方法,为list2添加字符串            System.out.println(list2.size());//运行后打印"3",说明字符串"dfdf123"插入、list2成功。        } catch (Exception e) {            e.printStackTrace();        }    }}

说明:本来list2只能存放int型数据的,但由于这里用的是反射操作,就绕过了编译,就绕过泛型,所以list2也能存String类的数据。也就说明了java中集合的泛型,只在编译时有效,绕过编译就无效了。

1 0
原创粉丝点击