一、反射的理解
(1)正射
在引入发射这个概念前,需要先理解“正射”。
在一般的情况下,我们使用某个类的时候,必定是了解它是什么类,是用来做什么的,有怎样的功能。于是我们可以对这个类进行实例化,之后再使用实例化的对象对这个类进行操作。
Person person = new Person();
person.sleep("8:00");
(2)反射
上面的例子简单的介绍了什么为“正射”,以及“正射”的一般例子。
而反射则是在一开始并不知道要初始化的类是什么,自然也无法使用new关键字来创建对象了。
而当我们可以的到这个类的名称时,我们就可以使用JDK提供的反射API进行反射调用。
Class clazz = Class.forName("com.reflect.Person");
Method method = clazz.getMethod("sleep", String.class);
Constructor constructor = clazz.getConstructoer();
Object object = constructor.newInstance();
method.invoke(object, "8:00");
以上的两段代码,其结果都是一致的,但是其思路却是完全不一样:
- 第一段代码在未运行前就已经确定了要运行的类(Person);
- 第二段代码则是在整个程序运行时从某些地方(例:配置文件)获取到相应的字符串值才能知道要运行的类(cn.reflect.Person);
二、反射的应用
(1) Tomcat中的类读取创建:
<servlet>
<servlet-name>ChatServlet</servlet-name>
<servlet-class>chat.ChatServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ChatServlet</servlet-name>
<url-pattern>/servlets/chat/chat</url-pattern>
</servlet-mapping>
由上所示,当一个HTTP请求过来时,Servlet容器首先会通过映射器选择到相应的Servlet类,再通过web.xml配置文件中的类的地址实例化一个Servlet对象,然后再通过固定的方法名去调用Servlet中的方法。
三、反射中的常用函数
(1)获取反射中的class对象
在反射中,要获取一个类或调用一个类的方法,首先必须要获取到该类的对象,在Java API中,获取Class类对象有三种方法:
①:Class.forName("-类的路径名-");
Class clazz = Class.forName("com.reflect.Person");
②:利用以有类对象的getClass()方法;
Person person = new Person();
Class clazz = person.getClass();
③:对于在编译前就已知道的类,可以使用.class方法。
Class clazz = Person.class;
(2)通过反射创建类对象
通过反射创建类的对象,Java API提供了两种方式:
①:通过Class对象的newInstance()方法;
Class clazz = Class.forName("com.reflect.Person");
Person person = (Person)clazz.newInstance();
②:通过Constructor对象的newInstance()方法
Class clazz = Class.forName("com.reflect.Person");
Constructor con = clazz.getConstructor();
Person person = (Person)con.newInstance();
注意:通过Constructor对象的newInstance()方法创建类对象可以选择特定的构造函数,而通过Class对象的newInstance()方法则只能使用默认的无参构造函数。例:
Class clazz = Class.forName("com.reflect.Person");
Constructor con = clazz.getConstructor();
Person person = (Person)con.newInstance("张三");
(3)通过反射操作成员变量
①:获取所有成员getFields()&getDeclaredFields()
使用getFields()方法可以获取Class类的成员变量,但是无法获取私有属性。
Class clazz = Class.forName("com.reflect.Person");
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.print(field.getName());
}
②:获取单个成员getField()&getDeclaredField()
③:修改成员的值set(Object obj, Object value)
Class clazz = Class.forName("com.reflect.Person");
Person person = (Person)clazz.newInstance();
Field field = clazz.getField("name");
field.set(person, "张三");
当属性为private时,你无法修改它的值,此时应使用setAccessible()方法取得访问权限
Class clazz = Class.forName("com.reflect.Person");
Person person = (Person)clazz.newInstance();
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(person, "张三");
(4)通过反射操作成员方法
①:获取所有方法getMethods()&getDeclaredMethods()
②:获取单个成员getMethod()&getDeclaredMethod()
操作方法与操作变量相差不大,在获取到对应方法后使用invoke()方法即可执行。同理,遇见私有方法时,也需要使用setAccessible(true)方法获取访问权限。
Class clazz = Class.forName("com.reflect.Person");
Person person = (Person)clazz.newInstance();
Method method = clazz.getMethod("setName", String.class);
method.setAccessible(true);
method.invoke("李四");
注:通常情况下,即便是当前类,private方法或属性也是没有权限访问的,你需要设置压制权限setAccessible(true)来取得访问权限,但实际上,这已经破坏了规则,所以应该尽量少使用。
网友评论