能够分析类能力的程序称为 反射 。对于给定的Java类名,可以通过反射获取类的信息、将类的各成分映射出相应的Java类。
在程序运行期间,Java运行时系统始终对所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。可以通过专门的Java类访问这些信息。保存这些信息的类被称为 Class 。
创建Class类对象的三种方法:
Object中的 getClass
方法将返回一个Class类型的实例。
class Person { //... } Person p = new Person(); Class c = p.getClass();
可以通过静态方法 forName
获得类名对应的Class对象。
String className = "java.util.Date"; Class c = Class.forName(className);
如果需要在运行过程中需要改变类名,就可以使用这种方法。当然,字符串className必须是一种类名或接口(包括包名),否则会出现异常。
Class c1 = Date.class; Class c2 = int.class; Class c3 = Double[].class;
T表示任意的Java类型。通过T.class获取匹配的类对象。
虚拟机为每个类型管理一个Class对象。因此可以用==运算符来实现两个类对象的比较
if (p.getClass() == Person.class) {...}
通过 newInstance
方法可以创建一个类的实例。
Person person = p.getClass().newInstance();
创建了一个与p具有相同类类型的实例。 newInstance调用默认的构造器 初始化创建的对象。如果这个类没有默认的构造器,就会抛出异常。
package com.xiaoxiaoyihan.reflection; class Person { //... } class Student extends Person{ } public class ReflectionDemo1 { public static void main(String[] args) { Student s = new Student(); Class cl = s.getClass(); Class superCl = cl.getSuperclass(); System.out.println("获取类名:" + cl.getName()); System.out.println("获取父类名:" + superCl.getName()); } }
【运行结果】:
获取类名:com.xiaoxiaoyihan.reflection.Student
获取父类名:com.xiaoxiaoyihan.reflection.Person
说明:如果类在一个包中,包的名字也作为类名的一部分。
首先看一个例子:
class Person { private String name = "萧萧弈寒"; // 省略setter和getter } class Animal { private String name = "paqi"; // 省略setter和getter } Person p; Animal a; Class cPerson = p.getClass(); Class cAnimal = a.getClass(); // cPerson.getName()获取的是类名、p.getName()是Person实例的name属性值 System.out.println(cPerson.getName() + "<--->" + p.getName()); System.out.println(cAnimal.getName() + "<--->" + p.getName());
【运行结果】:
com.xxyh.reflec.Person<--->萧萧弈寒
com.xxyh.reflec.Animal<--->paqi
由此说明,一个Person对象p表示一个特定人的属性,一个Animal对象a表示一个特定动物的属性,一个Class对象表示一个特定类(Person或Animal)的属性。从这点看, Class的实例是一种对象 。
Class类中的 getConstructors
方法将返回 公共 构造器数组。Class类中的getDeclaredConstructors将返回类中 声明的 构造器数组。
package com.xiaoxiaoyihan.reflection; import java.lang.reflect.Constructor; class Person { private String name; private int age; private Person() {} protected Person(String name) { this.name = name; } public Person(String name, int age) { this.name = name; this.age = age; } } public class ReflectionDemo1 { public static void main(String[] args) throws ClassNotFoundException { Class cl = Class.forName("com.xiaoxiaoyihan.reflection.Person"); // 直接抛出异常以简化代码 Constructor[] constructors = cl.getDeclaredConstructors(); //Constructor[] constructors = cl.getConstructors(); for (Constructor c :constructors) { System.out.println(c); } } }
【运行结果】:
private com.xiaoxiaoyihan.reflection.Person()
protected com.xiaoxiaoyihan.reflection.Person(java.lang.String)
public com.xiaoxiaoyihan.reflection.Person(java.lang.String,int)
// public com.xiaoxiaoyihan.reflection.Person(java.lang.String,int)// 本部分为运行getConstructors方法输出结果
对结果加以分析,会发现通过 System.out.println(c);
直接打印的构造函数是由几部分组成的,其中包括了 修饰符 (public/protected/private)、 类名 以及 构造函数的参数 ,那么,这些部分是如何获取的呢?
在 Modifier
类中包含静态方法 getModifiers
方法,它返回一个整数i,用不同的数值表示public、static、final这样的修饰符。Modifier中的静态方法 toString(i)
返回对应的修饰符。 Constructor
类中包含由静态方法 getParameterTypes
,它返回一个描述参数类型的 Class对象数组 。
package com.xiaoxiaoyihan.reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; class Person { private String name; private int age; private Person() {} protected Person(String name) { this.name = name; } public Person(String name, int age) { this.name = name; this.age = age; } } public class ReflectionDemo1 { public static void main(String[] args) throws ClassNotFoundException { Class cl = Person.class; Constructor[] constructors = cl.getDeclaredConstructors(); for (Constructor c :constructors) { // 获取包名和类名 String name = c.getName(); // 获取修饰符 String modifiers = Modifier.toString(c.getModifiers()); if (modifiers.length() > 0) { //如果有修饰符 System.out.print(modifiers + " "); } System.out.print(name + "("); // 获取构造函数的参数类型 Class[] paramTypes = c.getParameterTypes(); for (int i = 0; i < paramTypes.length; i++) { if (i > 0) { // 如果不止一个参数,使用","将参数类型分割 System.out.print(","); } System.out.print(paramTypes[i].getName()); } System.out.println(");"); } } }
【运行结果】:
private com.xiaoxiaoyihan.reflection.Person();
protected com.xiaoxiaoyihan.reflection.Person(java.lang.String);
public com.xiaoxiaoyihan.reflection.Person(java.lang.String,int);
Class类中的 getMethods
将返回方法的数组,其中 包括本类的所有方法、从接口实现的方法、父类的公共(public)方法、父类的父类的公共方法……一直延伸到Object的公共方法 。 getDeclaredMethods
方法将返回 本类声明的方法、从接口实现的方法 。
package com.xiaoxiaoyihan.reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Method; class Person/* extends Object */ { private void privateMethodPerson() { //... } protected void protectedMethodPerson() { // ... } public void publicMethodPerson() { //... } } interface Smoke { void smoking(); } class Student extends Person implements Smoke{ @Override public void smoking() { // ... } private void privateMethodStudent() { //... } protected void protectedMethodStudent() { // ... } public void publicMethodStudent() { //... } } public class ReflectionDemo1 { public static void main(String[] args) { Student s = new Student(); Class cl = s.getClass(); Method[] methods = cl.getDeclaredMethods(); // Method[] methods = cl.getMethods(); for (Method m : methods) { System.out.println(m); } } }
【运行结果】:
public void com.xiaoxiaoyihan.reflection.Student.smoking()
protected void com.xiaoxiaoyihan.reflection.Student.protectedMethodStudent()
private void com.xiaoxiaoyihan.reflection.Student.privateMethodStudent()
public void com.xiaoxiaoyihan.reflection.Student.publicMethodStudent()
上面的例子故意给出一种继承结构,是为了查看 getMethods
的结果,在此就不给出结果了。注意Person默认继承了Object类。顺便说一句,笔者(非科班自学菜鸟)曾在面试的时候被问到Object中有哪些方法,估计他是想问有哪些常用方法吧,我没答全~2333,不知道读者您能一眼看出结果吗??。
类似地,我们看到上面的 System.out.println(m);
打印出方法由 修饰符 、 返回值 、 类名 、 方法名 以及 参数 组成。那么这些部分是如果获取的呢? 与Construction类相似,Method类中也提供了静态方法 getParamTypes
,该方法返回描述参数类型的Class对象数组。此外,Method还提供了 getReturnType
方法,用于获取返回类型的Class对象。
package com.xiaoxiaoyihan.reflection; import java.lang.reflect.Method; import java.lang.reflect.Modifier; class Person { private String name = "萧萧弈寒"; public String getName() { return name; } public void setName(String name) { this.name = name; } public static void speak() { // ... } public final void eat() { // ... } } public class ReflectionDemo1 { public static void main(String[] args) { Class cl = Person.class; Method[] methods = cl.getDeclaredMethods(); for (Method m : methods) { // 返回类型的Class对象 Class retType = m.getReturnType(); // String retTypeName = retType.getName(); // 获取方法名 String name = m.getName(); String modifiers = Modifier.toString(m.getModifiers()); if (modifiers.length() > 0) // 如果有修饰符 System.out.print(modifiers + " "); // 返回值名 System.out.print(retType.getName() + " "); System.out.print(name + "("); Class[] paramTypes = m.getParameterTypes(); for (int i = 0; i < paramTypes.length; i++) { if (i > 0) { // 如果不止一个参数,用","分割 System.out.print(paramTypes[i].getName()); } } System.out.println(");"); } } }
【运行结果】:
public java.lang.String getName();
public void setName();
public static void speak();
public final void eat();
Class类中的 getDeclaredFields
方法返回类中 声明的 域数组,getFields方法返回类中的 公有 域、接口中的域所组成的Field对象数组。
package com.xiaoxiaoyihan.reflection; import java.lang.reflect.Field; import java.util.Date; class Person { private String name; protected int age; public Date birthday; } class Student extends Person implements Smoke{ private float score; } interface Smoke { String brand = "大中华"; } public class ReflectionDemo1 { public static void main(String[] args) { Class cl = Student.class; Field[] fields = cl.getFields(); for (Field f : fields) { System.out.println(f); } } }
【运行结果】:
public static final java.lang.String com.xiaoxiaoyihan.reflection.Smoke.brand
public java.util.Date com.xiaoxiaoyihan.reflection.Person.birthday
结果显示了字段由 修饰符 、 类型 、 类名 、 字段名 构成。这里需要引入Field类中的 getType 方法,它返回 字段声明类型的Class对象 。下面同样作出解析:
package com.xiaoxiaoyihan.reflection; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Date; class Person { private String name; protected int age; public Date birthday; } public class ReflectionDemo1 { public static void main(String[] args) { Class cl = Person.class; Field[] fields = cl.getDeclaredFields(); for (Field f : fields) { // 属性类型的Class对象 Class type = f.getType(); // 属性类型名 String name = type.getName(); System.out.print(" "); // 修饰符 String modifiers = Modifier.toString(f.getModifiers()); if (modifiers.length() > 0) { // 如果有修饰符 System.out.print(modifiers + " "); } System.out.println(type.getName() + " " + name + ";"); } } }
【运行结果】:
private java.lang.String java.lang.String;
protected int int;
public java.util.Date java.util.Date;
本人非科班菜鸟一枚,不愿去培训,于是开始了苦逼地自学之路,好不容易找到一份能养活自己的工作~orz。在工作中后知后觉,越来越觉得“基础不牢,地动山摇”、及时总结的重要性。于是开始巩固基础知识,深知水平有限,望大神指教,拜谢!