1.Class类
1.1有三种方式获取到Class的对象
1.2 可以使用 "=="
1.3 newInstance() 方法
1.4 forName 与 newInstance() 配合
2. 分析类
2.1 Field、Method 和 Constructor
3. 运行时获取域的值
4. 调用方法
1.Class类
在java运行时系统中,每当成功加载了一个类,都会为之创建一个Class类型的对象来存储这个类的信息。比如:
在Person类被加载成功时,java运行时系统会为之创建一个Class对象,这个对象存储了Person类的相关信息。
public Person
{
private String name;
public Person(String name) {this.name = name;}
public String getName() { return name;}
}
1.1有三种方式获取到Class的对象
第一种:
Person p = new Person();
Class class = p.getClass();
使用上面的方式即可获取到这个对象。最常用的Class方法是getName。
System.out.println(p.getClass().getName() + " : " + p.getName());
第二种:
如果已知类名,则可以通过Class.forName(className)获取到Class对象。
String className = "java.lang.String";
Class cl = Class.forName(className);
第三种:
如果T是任意的Java类型(或void关键字),T.class将代表匹配的对象。例如:
Class cl1 = Random.class;
Class cl2 = int.class;
Class cl3 = Double[].class;
注:一个Class表示的是一个类型,但是并不一定是类。例如,int就不是一个类。
1.2 可以使用 "=="
虚拟机为每个类型管理一个Class对象,所以不需要使用equal(),可以直接使用==进行两个类对象的比较。
if(p.getClass() == Person.class)
1.3 newInstance() 方法
可以使用newInstance()方法来动态创建一个类的实例。例如:
p.getClass().newInstance();
newInstance调用默认的构造器。如果没有默认构造器,即存在有参数的构造器,则会报错。所以上文中的Person类的构造方法要改成
public Person() {this.name = "a";}
如果需要使用有参数的构造器,则可以使用Constructor类中的newInstance()方法,见下文。
1.4 forName 与 newInstance() 配合
forName与newInstance()配合可以根据字符串创建对象;
String s = "java.util.Random";
Object o = Class.forName(s).newInstance;
2. 分析类
2.1 Field、Method 和 Constructor
在java.lang.reflect包中,有三个类Field、Method和Constructor分别描述类的域、方法和构造器。
通过Class类中的getFields、getMethods和getConstructors可获得类提供的public类型的域、方法和构造器,其中包括父类的public成员。
通过Class类中的getDeclareFields、getDeclareMethods和getDeclaredConsturctors可获得类中声明的全部的域、方法和构造器,包括非公有的。但是不包括父类的任何成员。
Field[] getFields();
Field[] getDeclaredFields();
Method[] getMethods();
Method[] getDeclaredMethods();
Constructor[] geConstructors();
Constructor[] getDeclaredConstructors();
Field、Method、Constructor的方法 :
String getName(); //返回描述Field、Method、Constructor的字符串
Class getType(); //返回描述Field所属类型的Class对象
int getModifiers(); //返回描述Field、Method、Constructor修饰符的整型值。再通过Modifier类中的静态方法对这个整型值进行分析。例如,Modifier.isPublic()判断是否是公有,Modifier.isFinal()是否是final。
Class[ ] getParameterTypes(); //返回描述Method、Constructor参数类型的数组。
Class[] getReturnType(); //返回一个Method的返回值类型。
3. 运行时获取域的值
利用反射机制可以查看运行时对象域的值,方式如下:
Class c = Person.class; // 获取Person的类型
Field f = c.getDeclaredField("name"); //使用此方法可以获取指定域的类型
Person p = new Person("a"); //创建Person对象
Object v = f.get(p); //通过Field的get方法即可获得内容
实际上这段代码有一个问题,由于name是一个私有域,f.get()会抛出IllegalAccessException异常。反射默认行为受限于Java的访问控制。要达到能访问的目的,需要调用Field、Method或Constructor对象的setAccessible方法。例如:
f.setAccessible(true); // 执行之后,就可以访问了
setAccessible 方法是 AccessibleObject 类中的一个方法, 它是 Field、 Method 和 Constructor 类的公共超类。
get方法还有一个需要解决的问题,上面代码中name域的类型是String所以可以传给Object。如果是数值类怎么办?数值类不是对象,例如,double。这时,可以使用Field类中的getDouble()方法,也可以直接调用get()方法,如果直接调用get(),则会通过自动包装,把值包装成Double。
可以读取也可以设置。f.set(obj,value)可以将obj对象的域设置成新值。
4. 调用方法
类似于上一节中通过Field的get()方法获取域的值。可以通过Method的invoke()方法调用方法。注意,由于
但是,由于同一个方法可能有不同的参数,所以获取方法需要额外的参数。
Method getMethod(String name, Class... parameterTypes)
举例如下:
public Person
{
private String name;
public Person(String name) {this.name = name;}
public String getName() { return name;}
public String setName(String name) {this.name = name;};
public static void printHello() { System.out.println("Hello")}
}
Class cl = Person.class; // 获取类型
Method m1 = cl.getMethod("getName"); // 获取getName方法
Method m2 = cl.getMethod("setName",String.class); //获取setName方法
Method m3 = cl.getMethod("printHello"); //获取printHello方法
Person p = new Person("PersonA");
String s1 = (String)m1.invoke(p); //执行getName
System.out.println("s1:" + s1);
m2.invoke(p4, "personB"); //执行setName
System.out.println("name:" + p.getName());
m3.invoke(null);//执行printHello,静态方法,参数可填null
结果:
s1:personA
name:personB
hello