java反射的使用
Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性
灵活使用反射能让我们代码更加灵活,这里比如JDBC原生代码注册驱动,hibernate 的实体类,Spring 的 AOP等等都有反射的实现。但是凡事都有两面性,反射也会消耗系统的性能,增加复杂性等
Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。)语言的一个关键性质。
反射能做什么
我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!
得到 Class 的三种方式
1 package com.my.reflex;
2 public class Person {
3 //私有属性
4 private String name = "Tom";
5 //公有属性
6 public int age = 18;
7 //构造方法
8 public Person() {
9 }
10 //私有方法
11 private void say(){
12 System.out.println("private say()...");
13 }
14 //公有方法
15 public void work(){
16 System.out.println("public work()...");
17 }
18 }
1 //1、通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
2 // 类型的对象,而我不知道你具体是什么类,用这种方法
3 Person p1 = new Person();
4 Class c1 = p1.getClass();
5
6 //2、直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
7 // 这说明任何一个类都有一个隐含的静态成员变量 class
8 Class c2 = Person.class;
9
10 //3、通过 Class 对象的 forName() 静态方法来获取,用的最多,
11 // 但可能抛出 ClassNotFoundException 异常
12 Class c3 = Class.forName("com.my.reflex.Person");
需要注意的是:一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的 c1,c2,c3进行 equals 比较,发现都是true
public static void main(String[] args) {
Student student=new Student();
System.out.println(student.getClass().getName());
}
try {
Class<?> c=Class.forName("chap07.sec01.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 | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
s.setName("王小二");
s.setAge(28);
System.out.println(s);
通过得到Class对象
在这种情况下,我们可以通过Class类来获取Student类里的构造方法,再调用带参数构造方法即可
包括 属性 方法 构造方法 等等。
Class<?> c=null;
try {
c=Class.forName("chap07.sec01.Student");
System.out.println(c.getName());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
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);
②、通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等
查阅 API 可以看到 Class 有很多方法:
getName():获得类的完整名字。
getFields():获得类的public类型的属性。
getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
getMethods():获得类的public类型的方法。
getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
getConstructors():获得类的public类型的构造方法。
getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
1 //获得类完整的名字
2 String className = c2.getName();
3 System.out.println(className);//输出com.my.reflex.Person
4
5 //获得类的public类型的属性。
6 Field[] fields = c2.getFields();
7 for(Field field : fields){
8 System.out.println(field.getName());//age
9 }
10
11 //获得类的所有属性。包括私有的
12 Field [] allFields = c2.getDeclaredFields();
13 for(Field field : allFields){
14 System.out.println(field.getName());//name age
15 }
16
17 //获得类的public类型的方法。这里包括 Object 类的一些方法
18 Method [] methods = c2.getMethods();
19 for(Method method : methods){
20 System.out.println(method.getName());//work waid equls toString hashCode等
21 }
22
23 //获得类的所有方法。
24 Method [] allMethods = c2.getDeclaredMethods();
25 for(Method method : allMethods){
26 System.out.println(method.getName());//work say
27 }
28
29 //获得指定的属性
30 Field f1 = c2.getField("age");
31 System.out.println(f1);
32 //获得指定的私有属性
33 Field f2 = c2.getDeclaredField("name");
34 //启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
35 f2.setAccessible(true);
36 System.out.println(f2);
37
38 //创建这个类的一个对象
39 Object p2 = c2.newInstance();
40 //将 p2 对象的 f2 属性赋值为 Bob,f2 属性即为 私有属性 name
41 f2.set(p2,"Bob");
42 //使用反射机制可以打破封装性,导致了java对象的属性不安全。
43 System.out.println(f2.get(p2)); //Bob
44
45 //获取构造方法
46 Constructor [] constructors = c2.getConstructors();
47 for(Constructor constructor : constructors){
48 System.out.println(constructor.toString());//public com.my.reflex.Person()
49 }
根据反射调用方法操作属性
通过反射调用方法,主要通过invoke方法
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();
}
}
根据反射获得属性
Field fs[]=c.getDeclaredFields();
for(Field f:fs){
System.out.println("属性:"+f);
}
操作私有属性
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();
}
根据反射获取父类属性
父类 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();
}
}
}