类对象
所有的类,都存在一个类对象,提供类的属性和方法等信息。一个ClassLoader下,一种类,只会有一个类对象存在。
获取类对象
通过Class.forName或getClass获取类对象,会导致静态初始化块的执行。多次获取类对象也只会执行一次静态初始化块。Hero.class不会执行静态初始化块。
package reflection;
import character.Hero;
public class TestReflection {
public static void main(String[] args){
String className = "character.Hero";
try {
Class hClass1 = Class.forName(className); // 会执行静态初始化块
Class hClass2 = Hero.class; // 不会执行静态初始化块
Class hClass3 = new Hero().getClass(); // 会执行静态初始化块
System.out.println(hClass1 == hClass2);
System.out.println(hClass1 == hClass3);
System.out.println(hClass2 == hClass3);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package character;
public class Hero {
private int id;
private String name;
private float hp;
private int damage;
static {
System.out.println("执行静态初始化块...");
}
}
创建类对象
package reflection;
import java.lang.reflect.Constructor;
import character.Hero;
public class TestReflection {
public static void main(String[] args){
// 1. 传统方式创建对象
Hero hero1 = new Hero();
System.out.println("new对象: " + hero1);
// 2. 反射方式创建对象
String className = "character.Hero";
try {
Class pClass = Class.forName(className);
Constructor constructor = pClass.getConstructor();
Hero hero2 = (Hero)constructor.newInstance();
System.out.println("反射对象: " + hero2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
访问属性和方法
package character;
public class Hero {
private int id;
private String name;
private float hp;
private int damage;
private String getName() {
return name;
}
private void fun(String name) {
System.out.println(name + " fun...");
}
}
package reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import character.Hero;
public class TestReflection {
public static void main(String[] args){
String className = "character.Hero";
try {
Class pClass = Class.forName(className);
Constructor constructor = pClass.getConstructor();
Hero hero = (Hero)constructor.newInstance();
// 反射获取属性name
// getField只能获取public的,包括从父类继承来的字段(方法同理)
// getDeclaredField可以获取本类所有字段,包括private的,但不能获得继承来的字段(方法同理)
Field fName = pClass.getDeclaredField("name");
fName.setAccessible(true); // 访问私有属性需要设置下
fName.set(hero, "悟空");
// 反射调用方法getName
Method mGetName = pClass.getDeclaredMethod("getName");
mGetName.setAccessible(true);
String name = (String)mGetName.invoke(hero);
System.out.println(mGetName.invoke(hero));
// 反射调用方法fun
Method mFun = pClass.getDeclaredMethod("fun", String.class);
mFun.setAccessible(true);
mFun.invoke(hero, "八戒");
} catch (Exception e) {
e.printStackTrace();
}
}
}
反射应用举例
有两个业务类及测试类,如下。每个业务类都有对应的方法。
package reflection;
public class Service1 {
public void doService1(){
System.out.println("业务方法1");
}
}
package reflection;
public class Service2 {
public void doService2(){
System.out.println("业务方法2");
}
}
- 传统方式。需要硬编码来确定做哪个业务。
public class Test {
public static void main(String[] args) throws Exception{
// new Service1().doService1();
new Service2().doService2();
}
}
- 反射方式。结合配置文件确定做哪个业务。
spring.txt
className=reflection.Service1
methodName=doService1
package reflection;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;
public class Test {
public static void main(String[] args) throws Exception{
File springConfigFile = new File("C:\\CCNU\\Java\\project\\j2se\\src\\spring.txt");
Properties springConfig = new Properties();
springConfig.load(new FileInputStream(springConfigFile));
String className = springConfig.getProperty("className");
String methodName = springConfig.getProperty("methodName");
Class clazz = Class.forName(className);
Constructor constructor = clazz.getConstructor();
Object service = constructor.newInstance();
Method m = clazz.getDeclaredMethod(methodName);
m.invoke(service);
}
}
当需要从调用第一个业务方法,切换到调用第二个业务方法的时候,不需要修改一行代码,也不需要重新编译,只需要修改配置文件spring.txt,再运行即可。
参考资料
[1] How2J
[2] 《疯狂Java讲义(第4版)》 李刚
[3] 《Java核心技术 卷I》
[4] 《Java核心技术 卷II》