java反射的使用
Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性
灵活使用反射能让我们代码更加灵活,这里比如JDBC原生代码注册驱动,hibernate 的实体类,Spring 的 AOP等等都有反射的实现。但是凡事都有两面性,反射也会消耗系统的性能,增加复杂性等
Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。)语言的一个关键性质。
反射能做什么
我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!
反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!
Java让我们在运行时识别对象和类的信息,主要有2种方式:一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型信息;另一种是反射机制,它允许我们在运行时发现和使用类的信息。
Class对象
类加载器首先会检查这个类的Class对象是否已被加载过,如果尚未加载,默认的类加载器就会根据类名查找对应的.class文件。
想在运行时使用类型信息,必须获取对象(比如类Base对象)的Class对象的引用,使用功能Class.forName(“Base”)可以实现该目的,或者使用base.class。注意,有一点很有趣,使用功能”.class”来创建Class对象的引用时,不会自动初始化该Class对象,使用forName()会自动初始化该Class对象。为了使用类而做的准备工作一般有以下3个步骤:
- 加载:由类加载器完成,找到对应的字节码,创建一个Class对象
- 链接:验证类中的字节码,为静态域分配空间
- 初始化:如果该类有超类,则对其初始化,执行静态初始化器和静态初始化块
三、Class对象方法
反射,怎么通过一个对象或完整的包路径获取一个类,获取原生类的所有结构
Class类与java.lang.reflect类库一起对反射进行了支持,该类库包含Field、Method和Constructor类,这些类的对象由JVM在启动时创建,用以表示未知类里对应的成员。这样的话就可以使用Contructor创建新的对象,用get()和set()方法获取和修改类中与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFields()、getMethods()和getConstructors()等许多便利的方法,以返回表示字段、方法、以及构造器对象的数组,这样,对象信息可以在运行时被完全确定下来,而在编译时不需要知道关于类的任何事情。
反射机制并没有什么神奇之处,当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类。因此,那个类的.class
对于JVM来说必须是可获取的,要么在本地机器上,要么从网络获取。所以对于RTTI和反射之间的真正区别只在于:
- RTTI,编译器在编译时打开和检查.class文件
- 反射,运行时打开和检查.class文件
通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等
getName():获得类的完整名字。 getFields():获得类的public类型的属性。 getDeclaredFields():获得类的所有属性。包括private 声明的和继承类。 getMethods():获得类的public类型的方法。 getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类。 getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。 getConstructors():获得类的public类型的构造方法。 getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。 newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
得到 Class 的三种方式
1、通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
//类型的对象,而我不知道你具体是什么类,用这种方法
Person p1 = new Person();
Class c1 = p1.getClass();
2、直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高,这说明任何一个类都有一个隐含的静态成员变量 class
Class c2 = Person.class;
3、通过 Class 对象的 forName() 静态方法来获取,用的最多,但可能抛出 ClassNotFoundException 异常
Class c3 = Class.forName("com.my.reflex.Person");
需要注意的是:一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的 c1,c2,c3进行 equals 比较,发现都是true
public class Base {
static int num = 1;
static {
System.out.println("Base " + num);
}
}
public class Main {
public static void main(String[] args) {
// 不会初始化静态块
Class clazz1 = Base.class;
System.out.println("------");
// 会初始化
Class clazz2 = Class.forName("zzz.Base");
}
}
②、获取所有的构造方法,普通方法,与属性
1.getName()获取一个包路径类完整的名字,知道一个类,通过类获取对象。
Class c2 = Person.class;
String className = c2.getName();
System.out.println(className);//输出com.my.reflex.Person
import com.config.vo.Config;
private final String modelType = Config.class.getName();
public class Mgr {
private static final String module = Mgr.class.getName();
}
public static void main(String[] args) {
Student student=new Student("小王",18);
System.out.println(student.getClass().getName());
}
2.获取属性与方法
newInstance()获取实例通过实例所有属性和方法
getConstructors() 调用对象有参的构造方法
public static void main(String[] args) {
Class c=null;
try {
c=Class.forName("com.Student");
System.out.println(c.getName());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Student s=null;
try {
s=(Student) c.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Object obj=c.newInstance();
Field nameField=c.getDeclaredField("name");
nameField.setAccessible(true); // 设置可以访问 包括私有属性
nameField.set(obj, "王小二");
System.out.println("name:"+nameField.get(obj));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
s.setName("小明");
s.setAge(28);
System.out.println(s);
}
}
Student s=null;
Constructor[] cons=c.getConstructors();
try {
s=(Student) cons[0].newInstance("小明",28);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(s);
Constructor cons[]=c.getConstructors();
for(Constructor con:cons){
System.out.println("构造方法:"+con);
}
}
Method mds[]=c.getMethods();
for(Method m:mds){
System.out.println("方法:"+m);
}
Field fs[]=c.getDeclaredFields();
for(Field f:fs){
System.out.println("属性:"+f);
}
package com.my.reflex;
public class Person {
//私有属性
private String name = "Tom";
//公有属性
public int age = 18;
//构造方法
public Person() {
}
//私有方法
private void say(){
System.out.println("private say()...");
}
//公有方法
public void work(){
System.out.println("public work()...");
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
Class c2 = Person.class;
//获得类的public类型的属性。
Field[] fields = c2.getFields();
for(Field field : fields){
System.out.println(field.getName());//age
}
//获得类的所有属性。包括私有的
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
System.out.println(field.getName());//name age
}
//获得类的public类型的方法。这里包括 Object 类的一些方法
Method [] methods = c2.getMethods();
for(Method method : methods){
System.out.println(method.getName());//work waid equls toString hashCode等
}
//获得类的所有方法。
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
System.out.println(method.getName());//work say
}
//获得指定的属性
Field f1 = c2.getField("age");
System.out.println(f1);
//获得指定的私有属性
Field f2 = c2.getDeclaredField("name");
//启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
f2.setAccessible(true);
System.out.println(f2);
//创建这个类的一个对象
Object p2 = c2.newInstance();
//将 p2 对象的 f2 属性赋值为 Bob,f2 属性即为 私有属性 name
f2.set(p2,"Bob");
//使用反射机制可以打破封装性,导致了java对象的属性不安全。
System.out.println(f2.get(p2)); //Bob
//获取构造方法
Constructor [] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor.toString());//public com.my.reflex.Person()
}
将数据实体转化为指定的类对象实例
import java.lang.reflect.Constructor;
/**
* @param result 数据
* @param classType 要转成的类
* @return 转化后的类对象实例 */
@SuppressWarnings("unchecked")
public static Object to(Object result, Class classType) {
Object rtnObj = null;
// 1.获取参数接口类或自身类
Class[] interFace = result.getClass().getInterfaces();
Class[] argsCls = new Class[interFace.length + 1];
for (int i = 0; i < argsCls.length; i++) {
if (i == 0) {
argsCls[i] = result.getClass();
} else {
argsCls[i] = interFace[i - 1];
}
// 2.根据类名称进行类实例化
try {
Constructor constructor = classType.getConstructor(argsCls[i]);
rtnObj = constructor.newInstance(result);
break;
} catch (Exception e) {
continue;
}
}
return rtnObj;
}
根据反射调用方法操作属性
通过反射调用方法,主要通过invoke方法
操作私有属性的方式
try {
Object obj=c.newInstance();
Method m1=obj.getClass().getMethod("setName", String.class); // 通过反射获,取方法,与参数类型
m1.invoke(obj, "小明"); // 调用方法
Method m2=obj.getClass().getMethod("getName");
String name=(String) m2.invoke(obj);
System.out.println("name="+name);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Object obj=c.newInstance();
Field nameField=c.getDeclaredField("name");
nameField.setAccessible(true); // 设置可以访问 包括私有属性
nameField.set(obj, "小明");
System.out.println("name:"+nameField.get(obj));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
getReadMethod()方法调用类的get函数,可以通过getWriteMethod()方法来调用类的set方法。
public class Person implements Serializable {
private String name;
private int age;
// get/set方法
}
public static void main(String[] args) {
Person person = new Person("luoxn28", 23);
Class clazz = person.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String key = field.getName();
PropertyDescriptor descriptor = new PropertyDescriptor(key, clazz);
Method method = descriptor.getReadMethod();
Object value = method.invoke(person);
System.out.println(key + ":" + value);
}
}
public static void main(String[] args) {
Class c=null;
try {
c=Class.forName("chap07.sec04.Student");
System.out.println(c.getName());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Method mds[]=c.getMethods();
for(Method m:mds){
System.out.println("方法:"+m);
}
Object obj=c.newInstance();
Method m2=obj.getClass().getMethod("setName", String.class);
m2.invoke(obj, "王小二");
Method m=obj.getClass().getMethod("getName");
String name=(String) m.invoke(obj);
System.out.println("name="+name);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
exeFuncAct("beforeEmp");
private void exeFuncAct(String methodStr){
try{
List actList = this.getActList();
if(null != actList){
for(int i = 0; i < actList.size(); i++){
try{
IBean act = actList.get(i);
Class cls = Class.forName(act.get("ACT_CLASS", ""));
Method method = cls.getMethod(methodStr, IBean.class);
method.invoke(cls.newInstance(), this.busiData);
}catch(Exception e){}
}
}
}catch(Exception e){}
}
private List getDataByTpl(String tplStr){
List result = null; //objWhere, 界面参数
try{
String[] tpl = tplStr.split(",");
Class cls = Class.forName(tpl[0]); //处理类
Method mf = cls.getMethod(tpl[1], JSONObject.class); //处理方法
result = (List)mf.invoke(cls.newInstance(), objWhere); // 执行方法
}catch(Exception e){}
return result;
}
根据反射获取父类属性
父类 Parent.java
1 public class Parent {
2 public String publicField = "parent_publicField";
3 protected String protectField = "parent_protectField";
4 String defaultField = "parent_defaultField";
5 private String privateField = "parent_privateField";
6
7 }
子类 Son.java
1 public class Son extends Parent {
2 }
1 public class ReflectionTest {
2
3 @Test
4 public void testGetParentField() throws Exception{
5 Class c1 = Class.forName("com.my.model.Son");
6 //获取父类私有属性值
7 System.out.println(getFieldValue(c1.newInstance(),"privateField"));
8 }
9
10 public static Field getDeclaredField(Object obj,String fieldName) {
11 Field field = null;
12 Class c = obj.getClass();
13 for(; c != Object.class ; c = c.getSuperclass()){
14 try {
15 field = c.getDeclaredField(fieldName);
16 field.setAccessible(true);
17 return field;
18 }catch (Exception e){
19 //这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。
20 //如果这里的异常打印或者往外抛,则就不会执行c = c.getSuperclass(),最后就不会进入到父类中了
21 }
22 }
23 return null;
24 }
25 public static Object getFieldValue(Object object,String fieldName) throws Exception{
26 Field field = getDeclaredField(object,fieldName);
27
28 return field.get(object);
29 }
30 }
通过执行上述代码,我们获得了父类的私有属性值,这里要注意的是直接通过反射获取子类的对象是不能得到父类的属性值的,必须根据反射获得的子类 Class 对象在调用 getSuperclass() 方法获取父类对象,然后在通过父类对象去获取父类的属性值。
value 被 final 修饰,只能保证引用不被改变,但是 value 所指向的堆中的数组,才是真实的数据,只要能够操作堆中的数组,依旧能改变数据。而且 value 是基本类型构成,那么一定是可变的,即使被声明为 private,我们也可以通过反射来改变。
1 String str = "vae";
2 //打印原字符串
3 System.out.println(str);//vae
4 //获取String类中的value字段
5 Field fieldStr = String.class.getDeclaredField("value");
6 //因为value是private声明的,这里修改其访问权限
7 fieldStr.setAccessible(true);
8 //获取str对象上的value属性的值
9 char[] value = (char[]) fieldStr.get(str);
10 //将第一个字符修改为 V(小写改大写)
11 value[0] = 'V';
12 //打印修改之后的字符串
13 System.out.println(str);//Vae
通过反射调用执行方法
public class ServletFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String reqURL = req.getRequestURI(); // /ServletIncreased/UserServlet.do
String[] strs = reqURL.split("/");
//定义 Servlet 的全类名
String servletAllName = null;
if(strs[2] != null){
//得到 请求路径的 servlet 类名
String servletName = strs[2].substring(0, strs[2].indexOf("."));
//根据获取的 Servlet 类名,由配置文件 ServletNameConfig 里面的map 得到 全类名
servletAllName = ServletNameConfig.servletMap.get(servletName);
}
//获取请求方法名
String methodName = req.getParameter("method");
System.out.println(servletAllName+"---"+methodName);
try {
//通过反射调用执行方法
Class obj = Class.forName(servletAllName);
Method method = obj.getDeclaredMethod
(methodName, HttpServletRequest.class,HttpServletResponse.class);
method.invoke(obj.newInstance(), req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
动态代理
代理模式是为了提供额外或不同的操作,而插入的用来替代”实际”对象的对象,这些操作涉及到与”实际”对象的通信,因此代理通常充当中间人角色。Java的动态代理比代理的思想更前进了一步,它可以动态地创建并代理并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的策略。
public interface Interface {
void doSomething();
void somethingElse(String arg);
}
public class RealObject implements Interface {
public void doSomething() {
System.out.println("doSomething.");
}
public void somethingElse(String arg) {
System.out.println("somethingElse " + arg);
}
}
动态代理对象处理器
public class DynamicProxyHandler implements InvocationHandler {
private Object proxyed;
public DynamicProxyHandler(Object proxyed) {
this.proxyed = proxyed;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
System.out.println("代理工作了.");
return method.invoke(proxyed, args);
}
}
public class Main {
public static void main(String[] args) {
RealObject real = new RealObject();
Interface proxy = (Interface) Proxy.newProxyInstance(
Interface.class.getClassLoader(), new Class[] {Interface.class},
new DynamicProxyHandler(real));
proxy.doSomething();
proxy.somethingElse("hello");
}
}
//代理工作了
//doSomething
//代理工作了
//somethingElse hello