反射

173 阅读2分钟

什么是反射

大多情况下我们通过new来对一个类进行实例化,然后操作这个类对象。这种情况下代码在未运行时就已确定要运行的类。

Employee e = new Employee();
e.setAge(18);

而反射是指一开始不知道我们要初始化的类是什么,只有在运行时才知道要操作的类是什么,并可以在运行时获取类的完整构造及对应方法。

Class clz = Class.forName("Ano.Employee");   
Method method = clz.getMethod("setAge", int.class);
Constructor constructor = clz.getConstructor(); //获取反射类对象
Object object = constructor.newInstance();
method.invoke(object, 18);   //通过invoke 方法调用来调用对象的方法

优点

(1)通过运行期间的类型判断来动态加载类,可以提高代码灵活度;

(2)此外对于任意一个类,都能知道其所有属性和方法,对于任意一个对象,都可以调用其任意方法。

缺点

(1)使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码性能比直接的 java 代码要慢很多。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用;

(2)动态操作/改变类的属性增加了类的安全隐患。

应用场景

(1)使用 JDBC 连接数据库时通过 Class.forName()通过反射加载数据库的驱动程序;

(2)Spring 框架的 IOC创建对象以及 AOP功能都和反射有联系;


反射步骤

(1)获取反射中的Class对象

在反射中,要想获取一个类或调用其方法,首先要获取类的Class对象。

获取Class类对象有三种方法:

Class clz = Class.forName("Ano.Employee");  //(1)当知道类的全路径名时
Class clz = Employee.Class; //(2)
Employee e = new Employee();        
Class clz = e.getClass();          //(3)

(2)通过反射创建类对象

创建类对象主要有两种方式:

Class clz = Employee.Class;
Employee e = (Employee)clz.newInstance();
Class clz = Employee.class;
Constructor constructor = clz.getConstructor();
Employee e = (Employee)constructor.newInstance("C2y", 31);

第一种方式只能使用默认的无参构造方法,另一种则可以指定某个构造方法。

(3)通过反射查看类属性、方法、构造器

我们可以通过 Class 对象的 getFields() 方法 获取 Class 类的属性,不过无法获取私有属性。

        Class clz = Employee.class;
        Field[] fields = clz.getFields();
        for(Field field : fields) {
            System.out.println(field.getName());
        }  

Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性:

Field[] fields = clz.getDeclaredFields();

与获取属性类似,如果想获取类的私有方法,私有构造器,则必须使用带有declared 前缀的方法名。

        Class clz = Employee.class;
        Method[] methods = clz.getDeclaredMethods();
        for(Method method : methods) {
            System.out.println(method.getName());
        }

调用私有方法

        Class clz = Employee.class;
        Employee e = (Employee)clz.newInstance();

        Method privateMethod  = clz.getDeclaredMethod("selfIntroduction", 
                                            String.class, int.class);
        //当调用private方法时,我们需要取消安全检查
        privateMethod.setAccessible(true);      
        privateMethod.invoke(e, "c2y", 18);

修改指定属性

        Class clz = Employee.class;
        Employee e = (Employee)clz.newInstance();
        Field field = clz.getDeclaredField("name");
        field.setAccessible(true);
        field.set(e, "c2y");