反射的基本原理
『反射』就是指程序在运行时能够动态的获取到一个类的类型信息的一种操作。它是现代框架的灵魂,几尽所有的框架能够提供的一些自动化机制都是靠反射实现的,这也是为什么各类框架都不允许你覆盖掉默认的无参构造器的原因,因为框架需要以反射机制利用无参构造器创建实例。
总的来说,『反射』是很值得大家花时间学习的,尽管大部分人都很少有机会去手写框架,但是这将有助于你对于各类框架的理解。不奢求你通过本篇文章的学习对于『反射』能够有多么深层次的理解,但至少保证你了解『反射』的基本原理及使用。
Class 类型信息
虚拟机的类加载机制:每一种类型都会在初次使用时被加载进虚拟机内存的『方法区』中,包含类中定义的属性字段,方法字节码等信息。
Java 中使用类 java.lang.Class 来指向一个类型信息,通过这个 Class 对象,我们就可以得到该类的所有内部信息。而获取一个 Class 对象的方法主要有以下三种。
类名.class
这种方式就比较简单,只要使用类名点 class 即可得到方法区该类型的类型信息。例如:
Object.class;
Integer.class;
int.class;
String.class;
//等等
getClass 方法
这是一个本地方法,并且不允许子类重写,所以理论上所有类型的实例都具有同一个 getClass 方法。具体使用上也很简单:
Obejct object = new object();
object.getClass();
forName 方法
forName 是获取 Class 类型的一个最常用的方法,它允许你传入一个全类名,该方法会返回方法区代表这个类型的 Class 对象,如果这个类还没有被加载进方法区,forName 会先进行类加载。
由于方法区 Class 类型信息由类加载器和类全限定名唯一确定,所以想要去找这么一个 Class 就必须提供类加载器和类全限定名,这个 forName 方法默认使用调用者的类加载器。(用一句话来说,就是调用时,要用类的全名称,包含包名)
例子:
package edu.cn.test;
class Circle {
private Double radius;
public Circle(Double r){
radius=r;
}
public Double circumference(){
return 2*Math.PI*radius;
}
}
public static void main(String[] args) throws ClassNotFoundException{
String className="edu.cn.test.Circle";//Circle类的全名称
Class<?> myClass=Class.forName(className);//加载类
//通过forName方法获取相应的Class对象
}
通过字符串的形式获取Class对象,这样就实现了动态实例化对象(反射的主要原理)
反射方法
同样的,Class 类也提供了四种方法来获取其中的方法属性:
public Method[] getMethods():返回所有的 public 方法,包括父类中的
public Method getMethod(String name, Class<?>… parameterTypes):返回指定的方法
public Method[] getDeclaredMethods():返回本类申明的所有方法,包括非 public 修饰的,但不包括父类中的
public Method getDeclaredMethod(String name, Class<?>… parameterTypes):同理
Method 抽象地代表了一个方法,同样有描述这个方法基本信息的字段和方法,例如方法名,方法的参数集合,方法的返回值类型,异常类型集合,方法的注解等。
除此之外的还有一个 invoke 方法用于间接调用其他实例的该方法,例如:
package edu.cn.test;
class Rectangle{
public Double area(Double width,Double length){
return