1、反射的概念
反射是java语言的一个特性,它允程序在运行时(注意不是编译时期)来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取他所有的成员变量和方法并且显示出来。一个常见的例子是在JavaBean中,一些组件可以通过一个构造器来操作。这个构造器就是用的反射在动态加载的时候来获取的java中类的属性的。
2、反射相关的核心类
public class User {
private String name;
private int age;
public User(){
super();
}
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String sayHello(String name){
return "Hello,"+name;
}
// get set...
}
2.1、Class
java中获取对象字节码的方式
@Test
public void classTest() throws Exception{
User user = new User();
String className = "org.javacode.model.User";
Class<?> clazz1 = Class.forName(className);
Class<?> clazz2 = User.class;
Class<?> clazz3 = user.getClass();
System.out.println(clazz1==clazz2);
System.out.println(clazz2==clazz3);
}
输出结果都为true,可见同一个类中只有唯一一份字节码。获取Class对象有3种方式。
2.2、Constructor 构造器
@Test
public void constructor() throws Exception{
//通过constructor构造对象
Class<User> clazz = User.class;
Constructor<User> constructor = clazz.getConstructor(String.class,int.class);
User user = constructor.newInstance("zhangsan",26);
String name = user.getName();
System.out.println(name);
}
通过Constructor构造对象,除此之外还有的方法:
getConstructors()获取所有的构造器;
getDeclaredConstructor(Class<?>... parameterTypes) 获取声明给定参数的构造器
getDeclaredConstructors() 获取所有声明的构造器
newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
...
2.3、Method 方法
@Test
public void method() throws Exception{
Class<User> clazz = User.class;
Method method = clazz.getDeclaredMethod("sayHello", String.class);
Object obj = clazz.newInstance();
Object resultObj = method.invoke(obj, "Hehe");
System.out.println(resultObj.toString());
}
通过class获取Method,其中比较核心的方法是method.invoke(Object , parameters...); 也就是通过Method调用对象的某一个方法,参数是动态的,这主要是为了区别方法的重载(Overloading) 。
2.4、Field 属性
@Test
public void field() throws Exception{
Class<User> clazz = User.class;
Field[] fields = clazz.getDeclaredFields() ;
for (Field field : fields) {
System.out.println(field.getName());
}
}
通过getDeclaredFields() 可以获取对象中所有的属性。在Constructor,Method,Field中都有getXXX和getDeclaredXXX的方法,他们的区别主要是访问权限的问题,getXXX 是不能获取private的属性,构造器以及方法的。
3、反射和工厂模式的结合
public interface BaseCache {
public void put(String key,Object value);
}
public class LocalCache implements BaseCache{
public void put(String key, Object value) {
System.out.println("local...");
}
}
public class RedisCache implements BaseCache {
public void put(String key, Object value) {
System.out.println("redis...");
}
}
public class CacheFactory {
public static BaseCache getInstance(String className) {
try {
Class<?> clazz = Class.forName(className);
BaseCache cache = (BaseCache) clazz.newInstance();
return cache;
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}
通过指定类名我们可以通过反射到具体的实现类,最起码在这里是可以减少好几个if else 的,那么的更好一点做法可以将className写到配置文件中,当需要不同的实现类时,只要改配置文件即可,而不用修改代码。