一、反射的概念
官方的话语描述:反射是Java中的一种机制,通过反射可以动态的实例化对象、读写属性、调用方法。
咱的理解是:通过反射,你可以在不知道要面对什么类的时候,更好的写代码,走流程,动态应该就是这个意思,他不是个死的,他可以随着你请求的类来获取class对象,反向获取到这个对象的所有信息,咱感觉反射像是能把一个未知类的属性方法一个一个剖析出来进行使用。
二、为什么需要使用反射
咱来举栗子,假设我们在web.xml中配置了一个stuServlet,那么我们在浏览器请求时,Tomcat是如何找到stuservlet这个业务处理类的?
<servlet>
<servlet-name>stuServlet</servlet-name>
<servlet-class>com.zking.zengjing.StuServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>stuServlet</servlet-name>
<url-pattern>/stu</url-pattern>
</servlet-mapping>
1.首先是xml建模通过url-pattern找到com.zking.zengjing.StuServlet;
2.Tomcat根据servlet-class实例化未知的servlet,来帮助开发人员实现对应的网络请求;
反射使得我们的代码更具灵活性。在上面类似的情况下反射的作用就很大啦!
三、反射的作用
我们还得知道:一切反射的相关代码都从获取类对象开始。咱反向理解理解,就是得先知道要面对的是什么类,他带着什么成员,所以反射的一切相关代码都从获取类对象开始。
反射可以动态的实例化对象,读写属性,调用方法。
获取类对象:
1.Class.forName(完整类名) 常用
2.类名.class
3.对象.getClass()
咱们来看一个实体类:
package com.zengjing.reflect;
public class Student {
private String sid;
private String sname;
public Integer age;
static{
System.out.println("加载进jvm中!");
}
public Student() {
super();
System.out.println("调用无参构造方法创建了一个学生对象");
}
public Student(String sid) {
super();
this.sid = sid;
System.out.println("调用带一个参数的构造方法创建了一个学生对象");
}
public Student(String sid, String sname) {
super();
this.sid = sid;
this.sname = sname;
System.out.println("调用带二个参数的构造方法创建了一个学生对象");
}
public Student(String sid, String sname,Integer age) {
super();
this.sid = sid;
this.sname = sname;
this.age=age;
System.out.println("调用带三个参数的构造方法创建了一个学生对象");
}
@SuppressWarnings("unused")
private Student(Integer age) {
System.out.println("调用Student类私有的构造方法创建一个学生对象");
this.age = age;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public void hello() {
System.out.println("你好!我是" + this.sname);
}
public void hello(String name) {
System.out.println(name + "你好!我是" + this.sname);
}
@SuppressWarnings("unused")
private Integer add(Integer a, Integer b) {
return new Integer(a.intValue() + b.intValue());
}
}
假设我们获取他的class对象来进行实例化,读写属性,调用方法,那么我们可以
通过反射实例化对象:
1、无参公有的实例化
2、有一个参数的公有构造器实例化
3、有两个参数的公有构造器实例化
4、私有的构造器实例化 重点
通过反射读写属性:
1.公有的
2.私有的
通过反射调用方法:
1.无参的公有的
2.有参的公有的
3.私有的方法调用
3.1反射实例化对象
获取构造器:getConstructor;
实例化该对象:newInstance(),可带参可不带参,不带参可直接使用这个方法实例化对象,带参数得先获取对应的构造器再进行实例化,如下案例;
package com.zengjing.reflect;
import java.lang.reflect.Constructor;
/**
* 反射实例化对象
* 1.无参共有的实例化
* 2.有一个参数的公有构造器实例化
* 3.有两个参数的公有构造器实例化
* 4.私有的构造器实例化
*
* @author jiang
*
* date 下午4:21:39
*/
public class Demo2 {
public static void main(String[] args) throws Exception {
//oop实例化
Student stu=new Student();
Class<? extends Student> clz = stu.getClass();
// 1.无参共有的实例化
//反射实例化 clz.newInstance();
Student stu2=(Student) clz.newInstance();
System.out.println(stu2);
//2.有一个参数的公有构造器实例化
Constructor<? extends Student> c = clz.getConstructor(String.class);
System.out.println("--------------------------------");
Student stu3 = c.newInstance("s001");
System.out.println(stu3);
//3.有两个参数的公有构造器实例化
Constructor<? extends Student> c2 = clz.getConstructor(String.class,String.class);
System.out.println("--------------------------------");
Student stu4 = c2.newInstance("s002","zhangsanbaobao");
System.out.println(stu4);
//4.私有的构造器实例化
Constructor<? extends Student> c4 = clz.getDeclaredConstructor(Integer.class);
System.out.println("-------------------------------");
c4.setAccessible(true);
Student stu6=c4.newInstance(11);
System.out.println(stu6);
}
}
结果图:
3.2通过反射读写属性
1.获取单个属性值:getDeclaredField(“属性名”)
2.一次性获取该类的属性值:getDeclaredFields();
如下案例:
package com.zengjing.reflect;
import java.lang.reflect.Field;
/**
* 读写属性
*
* 公有的 私有的读写属性
* BaseDao 增删改
* @author jiang
*
* date 上午12:40:56
*/
public class Demo4 {
public static void main(String[] args) throws Exception {
Student stu=new Student("s001","zhangsan");
stu.age=22;
Class<? extends Student> clz = stu.getClass();
System.out.println(stu.getSname());
Field f = clz.getDeclaredField("sname");
f.setAccessible(true);
System.out.println(f.get(stu));
System.out.println("----------------------------------");
//一次性获取属性的常用方法(一次性获取该类的属性值)
Field[] fs = clz.getDeclaredFields();
for (Field ff : fs) {
ff.setAccessible(true);
System.out.println(ff.get(stu));
}
System.out.println("-==========================");
//想通过反射改变sname对应的值
f.set(stu, "lisi");
System.out.println(stu.getSname());
}
}
结果图:
3.3通过反射调用方法
获取公有的方法:getMethod(“方法名”,可带参可不带参);
获取该类所有的的方法:getDeclaredMethod(“方法名”,要带的参数类型.class);
1.动态调用的方法 如果该方法是void类型的返回值 ,那么被invoke,此时的值为null;
2.动态调用的方法,如果该方法是非void类型的返回值,那么被invoke后,就是该被调用方法的结果;
如下案例:
package com.zengjing.reflect;
import java.lang.reflect.Method;
/**
* 调用方法
* 1.无参公有的方法调用
* 2.有一个参数的方法调用
* 3.私有的构造器实例化
* 自定义MVC增强子控制器
* @author jiang
*
* date 上午12:30:22
*/
public class Demo3 {
public static void main(String[] args) throws Exception {
Student stu=new Student();
Class<? extends Student> clz=stu.getClass();
Method m = clz.getMethod("hello");
Object invoke = m.invoke(stu);
//动态调用的方法 如果该方法是void类型的返回值 ,那么被invoke,此时的值为null
System.out.println(invoke);
System.out.println("------------------------");
Method m2 = clz.getMethod("hello", String.class);
Object invoke2 = m2.invoke(stu, "zhangsan");
System.out.println(invoke2);
Method m3 = clz.getDeclaredMethod("add", Integer.class,Integer.class);
m3.setAccessible(true);
Object invoke3 = m3.invoke(stu, 22,11);
//动态调用的方法,如果该方法是非void类型的返回值,那么被invoke后,就是该被调用方法的结果
System.out.println(invoke3);
}
}
结果图:
3.4注意事项
1.读写属性和调用方法时,如果其修饰符为私有的,我们需要开放权限xx.setAccessible(true);,不然会报java.lang.IllegalAccessException的错,如下在调用方法时我把xx.setAccessible(true)注释掉,就会报错;
2.调用方法时,如果调用的时候参数写错会出现java.lang.NoSuchMethodException没有匹配的方法异常。由上面的student实体类中得知,add方法是计算两个integer类型的和,如果我只写了一个integer,便会报错;
今日总结
反射其实看官方解释还比较抽象,可以试着参考案例学习哦,今日反射总结到此为止啦,大家有问题多多指教哦,拜拜~