为了解决这些问题,程序需要在运行时发现对象和类的真实信息。解决该问题有以下两种做法。
①第一种做法是假设在编译时和运行时都完全知道类型的具体信息,在这种情况下,可以先使用instanceof运算符进行判断,再利用强制类型转换将其转换成其运行时类型的变量即可。
②第二种做法是编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射。
- 获得Class对象
- 从Class中获取信息
- Java8新增的方法参数反射
1.获得Class对象
每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。
在Java程序中获得Class对象通常有如下三种方式。
①使用Class类的forName(String clazzName)静态方法。其中clazzName是某个类的全限定类名(必须添加完整包名);
②调用某个类的class属性来获取该类对应的Class对象。
③调用某个对象的getClass()方法。
一旦获得了某个类所对应的Class对象之后,程序就可以调用Class对象的方法来获得该对象和该类的真实信息了。
2.从Class中获取信息
Class类提供了大量的实例方法来获取该Class对象所对应类的详细信息,用的时候查阅API即可。eg:可以获取Class对应类所包含的构造器、方法、成员变量、Annotation、内部类、外部类、实现的接口、继承的父类、修饰符、所在包、类名等基本信息、判断该类是否接口、枚举、注解类型等;
通过Class对象可以得到大量的Method、Constructor、Field等对象,这些对象分别代表该类所包括的方法、构造器和成员变量等信息,程序还可以通过这些对象来执行实际的功能,例如调用方法、创建实例。
3.Java8新增的方法参数反射
Java8在java.lang.reflect包下新增了一个Executable抽象基类,该对象代表可执行的类成员,该类派生了Constructor、Method两个子类。Executable基类提供了大量方法来获取修饰该方法或构造器的注解信息;还提供了isVarArgs()方法用于判断该方法或构造器是否包含数量可变的形参,以及通过getModifiers()方法来获取该方法或构造器的修饰符。除此之外,Executable提供了如下两个方法来获取该方法或参数的参数个数及形参名。
int getParameterCount():获取该构造器或方法的所有形参;
Parameter[] getParameters():获取该构造器或方法的所有形参;
上面第二个方法返回了一个Parameter[]数组,Parameter也是Java8新增的API,每个Parameter对象代表方法或构造器的一个参数。
Parameter也提供了大量方法来获取声明该参数的泛型信息,还提供了如下常用方法来获取参数信息;
getModifiers():获取修饰该形参的修饰符。
String getName():获取形参名
Type getParameterizedType():获取带泛型的形参类型
Class<?> getType():获取形参类型
boolean isNamePresent():该方法返回该类的class文件中是否包含了方法的形参名信息
boolean isVarArgs():该方法用于判断该参数是否为个数可变的形参
需要指出的是,使用javac命令编译Java源文件时,默认生成的class文件并不包含方法的形参名信息,因此调用isNamePresent()方法将返回false,
调用getName()方法也不能得到该参数的形参名。如果希望javac命令编译java源文件是可以保留形参信息,则需要为该命令指定-parameters选项。
class Test
{
public void replace(String str, List<String> list){}
}
public class MethodParameterTest
{
public static void main(String[] args)throws Exception
{
// 获取String的类
Class<Test> clazz = Test.class;
// 获取String类的带两个参数的replace()方法
Method replace = clazz.getMethod("replace"
, String.class, List.class);
// 获取指定方法的参数个数
System.out.println("replace方法参数个数:" + replace.getParameterCount());
// 获取replace的所有参数信息
Parameter[] parameters = replace.getParameters();
int index = 1;
// 遍历所有参数
for (Parameter p : parameters)
{
if (p.isNamePresent())
{
System.out.println("---第" + index++ + "个参数信息---");
System.out.println("参数名:" + p.getName());
System.out.println("形参类型:" + p.getType());
System.out.println("泛型类型:" + p.getParameterizedType());
}
}
}
}