1.定义: 什么是反射?
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
2.用途:
在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。当然,也不是所有的都适合反射,之前就遇到一个案例,通过反射得到的结果与预期不符。阅读源码发现,经过层层调用后在最终返回结果的地方对应用的权限进行了校验,对于没有权限的应用返回值是没有意义的缺省值,否则返回实际值起到保护用户的隐私目的。
3. 哪里用到反射机制?
-
JDBC中,利用反射动态加载了数据库驱动程序。
-
Web服务器中利用反射调用了Sevlet的服务方法。
-
Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。
-
很多框架都用到反射机制,注入属性,调用方法,如Spring框架中AOP(动态代理:代理类是动态生成的)。
-
读取配置文件等
4. 动态代理是什么?有哪些应用?
-
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决(如租房)
- 真实角色:被代理的角色(如房东)
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作。(如中介收中介费,带他们去看房子)
- 客户:访问代理对象的人!(要去租房的人)
-
动态代理:是运行时动态生成代理类。
-
动态代理的应用有 Spring AOP面向切面编程、测试框架的后端 mock、rpc,Java注解对象获取等。
5. 怎么实现动态代理?
1.基于接口的动态代理
2.基于类的动态代理
- 基于接口——JDK动态代理(原生)【我们在这里使用】
- 基于类:cglib
- java字节码实现:javasisit【多点,在JBoss】
需要了解两个类:
-
Proxy:代理,生成代理类
-
InvocationHandler:调用处理程序,处理代理实例,并返回结果
通用代理类:
package edu.xalead.demo4;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//被代理接口(组合)
private Object tar;
public void setTar(Object tar) {
this.tar = tar;
}
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),tar.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = method.invoke(tar,args);
return res;
}
}
6. 反射机制的优缺点?
-
优点:
-
可以动态执行,在运行期间根据业务功能动态执行方法、访问属性,最大限度发挥了java的灵活性(使得Java成为半动态语言)。
-
反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类
-
-
缺点:
-
对性能有影响,这类操作总是慢于直接执行java代码,使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
-
使用反射会模糊程序内内部逻辑:程序员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
-
而且有一定的安全性问题,我们可以访问到私有属性。
-
7. Java反射机制的作用
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法
8. 如何使用Java的反射?
-
通过一个全限类名创建一个对象Cls
- Class.forName(“全限类名”); 例如:com.mysql.jdbc.Driver Driver类已经被加载到 jvm中,并且完成了类的初始化工作就行了
- 类名.class; 获取Class<?> cls对象
- 对象.getClass();
- .特殊的如:Integer.TYPE
-
获取构造器对象,通过构造器new出一个对象
- Cls.getConstructor([String.class]);
- Cls.newInstance([参数]);
-
通过class对象创建一个实例对象(就相当与new类名()无参构造器)
- Cls.newInstance();//默认调用午餐构造
-
通过class对象获得一个属性对象
- Field c=Cls.getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。
- Field c=Cls.getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的声明字段
-
通过class对象获得一个方法对象
- Cls.getMethod(“方法名”,class……parameaType);(只能获取公共的)
- Cls.getDeclareMethod(“方法名”);(获取任意修饰的方法,不能执行私有)
- Cls.setAccessible(true);(让私有的方法可以执行,可以提高效率,不让jvm检查合法性)
-
让方法执行
1). Method.invoke(obj实例对象,obj可变参数);-----(是有返回值的)这是一个工具类,让一个servlet实现多个请求方法。
package edu.xalead.web.servlet.backend.Utils; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Method; public class BaseServlet extends HttpServlet { //基类servlet 进行了深层的封装 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //请求头 :Get uri?method=add Http/1.1 //取出method String method = request.getParameter("method"); //通过反射获取里面的方法 Class c = this.getClass(); try { Method m = c.getMethod(method, HttpServletRequest.class, HttpServletResponse.class); m.setAccessible(true); m.invoke(this, request, response); } catch (Exception e) { e.printStackTrace(); } } }
请求的时候根据method参数来判断调用哪个servlet方法
<c:forEach items="${list}" var="row"> · <a href="/mainServlet?method=seeArticle&id=${row[0]}">${row[1]}</a> </c:forEach>
9.最后谈谈什么叫对象序列化,什么是反序列化,实现对象序列化需要做哪些工作?
- 对象序列化,将对象中的数据编码为字节序列的过程。
- 反序列化;将对象的编码字节重新反向解码为对象的过程。
- JAVA提供了API实现了对象的序列化和反序列化的功能,使用这些API时需要遵守如下约定:
- 被序列化的对象类型需要实现序列化接口,此接口是标志接口,没有声明任何的抽象方法,JAVA编译器识别这个接口,自动的为这个类添加序列化和反序列化方法。
- 为了保持序列化过程的稳定,建议在类中添加序列化版本号。
- 不想让字段放在硬盘上就加transient
- 以下情况需要使用 Java 序列化:
- 想把的内存中的对象状态保存到一个文件中或者数据库中时候;
- 想用套接字在网络上传送对象的时候;
- 想通过RMI(远程方法调用)传输对象的时候。
欢迎访问我的个人博客交流学习:http://www.ayjup.cn