说到反射,第一次用是在收费系统中使用抽象工厂+反射+配置文件中,对这段代码记忆犹新:
Imports System.Reflection
Public Class DFactory
Dim strDB As String = System.Configuration.ConfigurationSettings.AppSettings("DBString")
Function CreateUserInfo() As IUserInfo
Return CType(Assembly.Load("DAL").CreateInstance("DAL.DalUserInfo" & strDB), IUserInfo)
End Function
End Class
用到的核心内容就是上面:Assembly.Load("DAL").CreateInstance("DAL.DalUserInfo" & strDB),当时仅限于使用,并没有深入学习,学JAVA的过程中,再次遇到索性就再学习一下,先来说说什么是反射。
反射
反射技术出现在.NET和JAVA之前,是指程序可以访问、检测和修改它本身状态或行为的一种能力,对应到JAVA和,也就是指的JAVA程序对自身进行检查,并能直接操作程序的内部属性和方法。
再说直白一点,就是JAVA提供了一系列API,可以在程序运行期间,得到class中内部信息,如包名、父类信息、接口信息、函数、属性等,根据这一特性,可以实现在执行期根据class文件动态生成实例等功能。
根据class生成instance,需要class名,这个通过.NET和JAVA中的使用即可得知:
JAVA中:Class.forName("com.tgb.person").newInstance();
.NET中:Assembly.Load("DAL").CreateInstance("DAL.DalUserInfo" & strDB);
他们的作用是相同的:根据包名+类名,生成类的实例,省略了new的过程,从而实现动态、延迟加载的效果。它允许程序在运行时加载、探知、使用编译期间完全未知的classes。
JAVA中的反射
上面笼统的说了一下反射的作用,现在以JAVA为例,说明一下实现反射的APIs::Field、Constructor、Method、Class、Object:
- Field类:提供有关类或接口的属性的信息,以及对它的动态访问权限。
- Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限。
- Method类:提供关于类或接口上单独某个方法的信息。
- Class类:类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
- Object类:每个类都使用 Object 作为父类。
简单实例
我们以Person为例。
package reflection;
public class Person {
private String name;
private String sex;
private int age;
private void grow()
{
System.out.println("im growing");
}
public void eat()
{
System.out.println("im eating");
}
public void sleep()
{
System.out.println("im sleeping");
}
}
测试类
package reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException {
/* 也可以使用:
person one=(Person)Class.forName("reflction.Person").newInstance();
生成类的实例*/
Person one=new Person();
//得到对象的类信息
Class <?> classes=one.getClass();
//获取Person类的所有方法
Method m[] = classes.getDeclaredMethods();
System.out.println("reflection.Person的方法:");
for (int i = 0; i < m.length; i++)
{
System.out.println(m[i].toString());
}
System.out.println("<==================>");
//获取Person类的所有方法
Field f[] = classes.getDeclaredFields();
System.out.println("reflection.Person的属性:");
for (int i = 0; i < f.length; i++)
{
System.out.println(f[i].toString());
}
System.out.println("<==================>");
//获取Person类的构造函数
Constructor<?> c[] = classes.getConstructors();
System.out.println("reflection.Person的构造函数:");
for (int i = 0; i < c.length; i++)
{
System.out.println(c[i].toString());
}
System.out.println("<==================>");
//获取Person类的接口
Class<?> in[] = classes.getInterfaces();
System.out.println("reflection.Person的接口:");
for (int i = 0; i < c.length; i++)
{
System.out.println(in[i].toString());
}
System.out.println("<==================>");
}
}
测试结果
reflection.Person的方法:
public void reflection.Person.sleep()
private void reflection.Person.grow()
public void reflection.Person.eat()
<==================>
reflection.Person的属性:
private java.lang.String reflection.Person.name
private java.lang.String reflection.Person.sex
private int reflection.Person.age
<==================>
reflection.Person的构造函数:
public reflection.Person()
<==================>
reflection.Person的接口:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at reflection.Test.main(Test.java:48)
分析
demo比较简单,查找到一个类后,生成实例,再进一步使用反射获取到类的信息。以上有两点需要注意:即使是类的私有属性,也反射出来了;因为Person类并没有实现任何接口,故抛出异常。
原理解释
理解有限,下面的图算是抛砖引玉:
上图是从加载到实例化出对象的过程概览,我们说的反射,实际上是方法区的Person.class和堆中person对象之间的关系。
正是因为这种相互的引用关系,使得class和对象之间可以相互得到,从而实现灵活、延迟、动态的效果,也给了开发人员更多的操作方式和想象空间。
总结
反射说到这里先到此为止,如果有兴趣可以深入学习一下classloader相关内容,再学习反射就容易许多。那么,反射具体有哪些应用的场景,详见下篇博客。