初涉Java反射机制

这篇博客介绍了Java反射机制,包括Class类的使用,如Class.forName()动态加载类,以及静态和动态加载的区别。接着讨论了如何通过反射进行方法调用,包括Method对象的invoke()方法的应用。最后,通过实例揭示了反射如何揭示泛型的本质,即Java中泛型只在编译时有效,反射可以绕过这一限制。
摘要由CSDN通过智能技术生成

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中集合的泛型,只在编译时有效,绕过编译就无效了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值