Java的反射机制

今天在慕课网上看了一些有关Java反射的视频,感觉收获不小。反射(Reflection)能够让运行于JVM中的程序检测和修改运行时的行为。自从学Java以来就一直对反射这块内容不是很理解,今天就觉得自己突然开窍了,好像理解了些什么,于是就趁热打铁记录下来吧。

一切皆是对象

要学习面向对象编程,首先要理解什么是对象,面向对象更是一种思想,而不仅仅是一种技术。在Java中,对象是某个类的实例,那么类本身是不是一个对象呢?根据一切皆是对象的思想,当然,类也是对象。Java中任何一个类都是java.lang.Class类的实例对象。如

String hello = new String("hello");

其中,hello是一个String对象的引用,而String这个类则是java.lang.Class类的一个实例对象。类中的方法实际上也是对象,类中每一个方法都是Method类的实例对象。

java.lang.Class

下图是java.lang.Class类源码中构造方法部分的截图。可以看到该类的构造方法是私有方法,注释中指出只有Java虚拟机才能创建这个类的实例。
Class类的构造方法

通过下面的代码来了解一下Class类:

Class s1 = String.class;
String hello = new String("hello");
Class s2 = hello.getClass();
System.out.println(s1 == s2);//true
  1. 任何一个类都有一个隐含的静态成员变量class,可以通过这个成员变量取得该类的类类型
  2. 也可以通过任何一个对象的getClass()方法,取得该对象所属类的类类型
  3. s1 == s2为true,因为s1和s2都是Class类的对象(String的类类型)

Class类中有一个静态方法forName(“ClassFullName”),Class.forName(“ClassFullName”)不仅表示了类的类类型,还代表了动态加载该类。Java类在被使用之前需要先被类加载器加载,在编译时加载类属于静态加载,在运行时加载类便属于动态加载。通过new创建类的实例时,类是静态加载的,在编译的时候就需要加载所有可能用到的类。关于Java类的静态加载详情请见我的这篇博客深入理解Java类加载原理。而动态加载类是在运行时刻进行类的加载,如果类没有找到则会抛出异常。
总的来说,一个类的类类型,即Class类的一个实例对象,包含了这个类的信息(好比String.class包含了String类的信息),也可以通过这个类的类类型,对这个类进行操作,比如创建一个该类的对象,调用某个方法等等。

java.lang.reflect.Method

上面我们简单介绍了java.lang.Class类的一些信息,接下来我们继续了解下java.lang.reflect.Method类,下图是该类源码中构造方法部分的截图。
Method类的构造方法
我们已经说明,方法也是对象:
1. 一个成员方法就是一个Method对象。
2. java.lang.Class类中的getMethods()方法可以获取所有的访问控制为public的方法,包括从父类继承而来的。
3. java.lang.Class类中的getDeclaredMethods()方法可以获取该类自己声明的所有方法。
4. 也可以通过java.lang.Class类中的getMethod(“functionName”,classType…)即方法名+参数类型列表的形式唯一取得一个方法。

下面是一个实例代码

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;

    public class Student {

        public String printName(String familyName,String lastName){
            System.out.println("familyName: " + familyName + " lastName: " + lastName);
            return "Hello "+lastName;
        }

        public static void main(String[] args) {

            Student student = new Student();
            // 取得student对象的类类型
            Class studentClass = student.getClass();

            try {
                // 通过方法名和参数类类型列表取得方法对象
                // 其中pringName是方法名,Class数组中为参数类类型列表
                Method method = studentClass.getMethod("printName", new Class[]{String.class,String.class});

                // 调用这个方法,效果与调用student.printName("Wang","Colin")相同
                // object为方法的返回值。如果方法的返回类型为void,则object = null
                Object object = method.invoke(student, new Object[]{"Wang","Colin"});

                System.out.println(object);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

总之,Method对象包含了一个方法的信息,可以通过这个对象,对方法进行操作。

Java中关于集合泛型的本质

编译后的集合泛型是去泛型化的,编译前集合泛型的作用是防止向一个集合中放入不同类型的对象,否则将不能通过编译。但是编译之后,通过反射动态的向一个集合中放入不同类型的对象就没有问题,说明在编译之后

ArrayList<String> al = new ArrayList<String>();
ArrayList al = new ArrayList();

这两条语句是一样的。反射的操作都是编译之后的操作,发生在运行时刻,所以通过反射可以绕过编译。另一种方式是把编译之后的Java字节码用jdk中提供的工具反编译成Java代码,就会发现编译后的集合泛型都是去泛型化的。

以上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值