1、获取 Class 对象
一个Class
对象可以表示一个类型
,可能是类类型(包括数组类型)、接口类型,或者是基本数据类型和void
。
① 使用Object
类中的getClass
方法获取对应的 Class 对象:
class Employee {}
class Manager extends Employee {}
public class Main {
public static void main(String[] args) {
Employee e = new Manager();
Class<? extends Employee> cl = e.getClass();
String name = cl.getName();
System.out.println(name); // com.company.Manager
}
}
如上所示,因为 e 引用的对象的类型是 Manager,所以获取的 Class 对象保存了 Manager 类的信息。我们使用 getName 输出 cl 保存的类型名称,得到com.company.Manager
。
需要注意,虚拟机为每一个类型管理一个唯一
的 Class 对象,我们可以利用==
运算符实现两个 Class 对象的比较:
class Employee {}
class Manager extends Employee {}
public class Main {
public static void main(String[] args) {
Manager m1 = new Manager();
Manager m2 = new Manager();
System.out.println(m1.getClass() == m2.getClass()); // true
}
}
② 使用静态方法forName
获得类名对应的 Class 对象:
Class<?> cl = Class.forName("com.company.Manager");
我们需要传入一个类名或接口名的完全限定名
,否则将抛出一个检查型异常。
③ 如果 T 是任意的 Java 类型或 void 关键字,T.class
将代表对应的 Class 对象:
Class<Integer> cl1 = Integer.class;
Class<Integer> cl2 = int.class;
Class<Void> cl3 = void.class;
如上所示,它们对应的类型名分别是 java.lang.Integer、int 和 void。
需要注意的是,使用.class
获取 Class 对象的引用时,不会自动初始化
该 Class 对象表示的类,而前两个方法会进行初始化。(类加载的步骤:加载、链接和初始化)
如下所示:
class Init1 {
static { System.out.println("Initializing Init1"); }
}
class Init2 {
static { System.out.println("Initializing Init2"); }
}
class Init3 {
static { System.out.println("Initializing Init3"); }
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Init1 init1 = new Init1();
Class<? extends Init1> cl1 = init1.getClass();
Class<?> cl2 = Class.forName("com.company.Init2");
Class<Init3> cl3 = Init3.class;
}
}
运行结果:
Initializing Init1
Initializing Init2
可以发现,输出结果中并没有Initializing Init3
,说明Init3
类并没有初始化。
2、利用反射分析类
主要使用java.lang.reflect
包下的Field
、Method
、Constructor
和Modifier
类。
示例代码:
class Employee {
private String name;
private double salary;
public Employee() {}
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public void foo(String s) {
System.out.println(name + ":" + s);
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> cl = Class.forName("com.company.Employee");
// Field
Field[] fields = cl.getDeclaredFields();
for(Field f : fields) {
System.out.println(f.getType().getName() + " " + f.getName());
}
// Constructor
Constructor<?>[] constructors = cl.getDeclaredConstructors();
for(Constructor<?> c : constructors) {
Class<?>[] types = c.getParameterTypes();
System.out.println(c.getName() + " " + Arrays.toString(types));
}
// Method
Method[] methods = cl.getDeclaredMethods();
for(Method m : methods) {
Class<?>[] types = m.getParameterTypes();
System.out.println(m.getReturnType().getName() + " " +
m.getName() + " " + Arrays.toString(types));
}
// Modifier
int modifiers = fields[0].getModifiers();
boolean aPrivate = Modifier.isPrivate(modifiers);
boolean aStatic = Modifier.isStatic(modifiers);
boolean aFinal = Modifier.isFinal(modifiers);
System.out.println(aPrivate + " " + aStatic + " " + aFinal);
}
}
运行结果:
java.lang.String name
double salary
com.company.Employee []
com.company.Employee [class java.lang.String, double]
void foo [class java.lang.String]
true false false
3、利用反射在运行时分析对象
示例代码:
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Employee e = new Employee("张三", 1800.0);
Class<? extends Employee> cl = e.getClass();
Field f1 = cl.getDeclaredField("name");
Field f2 = cl.getDeclaredField("salary");
// 将private字段设置为可访问
f1.setAccessible(true);
f2.setAccessible(true);
// 获取对象e的字段值
String name = (String)f1.get(e);
double salary = (double) f2.get(e);
System.out.println(name + " " + salary);
}
}
运行结果:
张三 1800.0
其中setAccessible
方法是AccessibleObject
类中的一个方法,它是 Field、Method 和 Constructor 类的公共超类。
4、利用反射编写泛型数组代码
java.lang.reflect
包中的Array
类允许动态地创建数组,Arrays
类中的copyOf
方法就使用了这个类,我们分析一下:
// 拷贝数组,并指定长度,可能截断或者用null填充
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
// 如果是原数组是Object数组,则创建Object数组
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
可以看到,copyOf 方法先使用了 Class 类的getComponentType
方法获取数组的元素类型,然后使用 Array 类的newInstance
方法创建数组。
5、利用反射调用任意的方法和构造器
示例代码:
public class Main {
public static void main(String[] args) throws Exception {
Employee e1 = new Employee("张三", 1800);
Class<? extends Employee> cl = e1.getClass();
// 调用方法
Method foo = cl.getMethod("foo", String.class);
foo.invoke(e1, "Hello World!");
// 调用构造器
Constructor<? extends Employee> constructor = cl.getConstructor(String.class, double.class);
Employee e2 = constructor.newInstance("李四", 20000.0);
e2.foo("Java is very good!");
}
}
运行结果:
张三:Hello World!
李四:Java is very good!
如有错误,欢迎指正。.... .- ...- . .- -. .. -.-. . -.. .- -.-- -.-.--