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写到配置文件中,当需要不同的实现类时,只要改配置文件即可,而不用修改代码。