一、什么是反射?
反射java语言中的一种机制,通过这种机制可以动态的实例化对象、读写属性、调用方法,在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。
想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。
一切反射相关的代码都从获得类(java.lang.Class)对象开始
二,反射有什么用
1,在运行时判断任意一个对象所属的类;
2,在运行时构造任意一个类的对象;
3,在运行时判断任意一个类所具有的成员变量和方法;
4,在运行时调用任意一个对象的方法;
5,生成动态代理。
获取字节码文件对象的三种方式:
1、Class clazz1 = Class.forName(“全限定类名”); //通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。
2、Class clazz2 = Person.class; // 当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己, 该类处于字节码阶段。
3、Class clazz3 = p.getClass(); //通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段
我们通过以下案例来理解反射:
案例代码如下:
package com.dengrenli.reflect;
public class Student {
private String sid;
private String sname;
public Integer age;
public static final int i = 37;
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("调用带二个参数的构造方法创建了一个学生对象");
}
@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());
}
@Override
public String toString() {
return "Student [sid=" + sid + ", sname=" + sname + ", age=" + age + "]";
}
}
获取类对象
1.Class.forName(“类的全路径名”);
Class cla = Class.forName(“com.caoguangli.reflect.Student”);
2.类名.class 做通用的查询
Class cla2 = Student.class;
System.out.println(cla2);
3.类(Class类类的类对象)实例 .getClass()
Student s = new Student();
Class cla3 = s.getClass();
System.out.println(cla3);
代码演示
package com.dengrenli.reflect;
/**
* 如何获取类对象(三种方式)
* 1.Class.forName("类的全路径名"); jdbc、自定义MVC框架
* 2.类名.class 做通用的查询
* 3.类(Class类类的类对象)实例 .getClass() 做通用的增删改
*
* @author machenike
*
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
// 1.Class.forName("类的全路径名");
Class cla = Class.forName("com.dengrenli.reflect.Student");
System.out.println(cla);
System.out.println("-------------------");
// 2.类名.class 做通用的查询
Class cla2 = Student.class;
System.out.println(cla2);
System.out.println("-------------------");
// 3.类(Class类类的类对象)实例 .getClass() 做通用的增删改
Student s = new Student();
Class cla3 = s.getClass();
System.out.println(cla3);
/**
* 为什么jdbc连接要使用class.forName("com.jdbc.mysql.Driver");
* 无论是mysql的驱动Driver还是Oracle的驱动......;它必然需要实现jdbc的一个驱动接口;
* java.sql.Driver d = Class.forName("com.jdbc.mysql.Driver")
*
*
* web.xml
* <servlet>
* <servlet-name>Xxx</servlet-name>
* <servlet-class>com.caoguangli.XxxServlet</servlet-class>
* </servlet>
* .....
*
*
* com.caoguangli.XxxServlet extends httpServlet
* Class cla = Class.forName("com.caoguangli.XxxServlet");
* httpServlet httpServlet = cla.newInstanse;
*
* XxxServlet 实例
* new XxxServlet();
*
*/
}
}
运行结果:
如图所示
反射实例化
1.能够实例化未知的类
2.能够通过私有构造器创建实例
package com.dengrenli.reflect;
import java.lang.reflect.Constructor;
/**
* 反射实例化
* 1.能够实例化未知的类
* 2.能够通过私有构造器创建实例
* @author machenike
*
*/
public class Demo2 {
public static void main(String[] args) throws Exception{
Class cla = Student.class;
// 反射调用无参构造方法创建了一个学生对象
Student s = (Student)cla.newInstance();
// 要拿到构造器类
Constructor c = cla.getConstructor(String.class);
// 通过构造器实例对象
Student s1 = (Student)c.newInstance("s001");
// 调用带二个参数的构造方法创建了一个学生对象
Constructor c2 = cla.getConstructor(String.class,String.class);
// 通过构造器实例对象
Student s2 = (Student)c2.newInstance("s001","hh");
// 调用Student类私有的构造方法创建一个学生对象
// java.lang.NoSuchMethodException(getConstructor只能找到public修饰的构造器;getDeclaredConstructor可以找到任何修饰的构造器)
// Constructor c3 = cla.getConstructor(Integer.class);
// Exception in thread "main" java.lang.IllegalAccessException: Class com.caoguangli.reflect.Demo2 can not access a member of class com.caoguangli.reflect.Student with modifiers "private"
Constructor c3 = cla.getDeclaredConstructor(Integer.class);
c3.setAccessible(true);
// 通过构造器实例对象
Student s3 = (Student)c3.newInstance(12);
}
}
运行结果:
反射调用方法
package com.dengrenli.reflect;
import java.lang.reflect.Method;
/**
* 反射调用方法
* AddOrderServlet
* delOrderServlet
* ...
*
* -->OrderServlet
* jsp--> methodName
* dopost
* String methodName = req.getParameter("methodName");
* if("add".equals(methodName)){
*
* }else if("del".equals(methodName)){
*
* }
*
* -->
* 上面的代码全部都不需要
* del(req,resp);
* add(req,resp);
*
* @author machenike
*
*/
public class Demo3 {
public static void main(String[] args) throws Exception {
Student s = new Student();
s.hello();
Class cla = s.getClass();
Method m = cla.getDeclaredMethod("hello");
m.invoke(s);
Method m1 = cla.getDeclaredMethod("hello",String.class);
System.out.println(m1.invoke(s, "哈哈"));
Method m2 = cla.getDeclaredMethod("add",Integer.class,Integer.class);
m2.setAccessible(true);
// invoke:如果反射动态调用的方法是被void所修饰,那么返回的就是null
// 如果反射动态调用的方法是不被void所修饰,那么返回的就是被调用的方法的返回值
Object invoke = m2.invoke(s, 20,6);
System.out.println(invoke);
}
}
运行结果:
反射属性赋值取值
package com.dengrenli.reflect;
import java.lang.reflect.Field;
/**
* 反射属性赋值取值
* jsp
* --->uname,sex,age,pwd,phon,address......
* servlet
* String uname = req.getParameter("uname");
* .....
*
* User u = new User();
* u.setName(uname);
* .....
*
* -->
* 反射能够将jsp传递过来的参数直接封装到实体类中
* @author machenike
*
*/
public class Demo4 {
public static void main(String[] args) throws Exception {
Student s = new Student();
// 面向对象赋值
s.setSid("s002");
s.age = 22;
System.out.println(s);
System.out.println("-------------------");
// 反射赋值
// 写
Class cla = s.getClass();
Field field = cla.getDeclaredField("sid");
field.setAccessible(true);
field.set(s, "s002");
System.out.println(s);
System.out.println("-------------------");
// 读
System.out.println(field.get(s));
//读取到属性的类型
System.out.println(field.getType());
Field[] fields = cla.getDeclaredFields();
for (Field field2 : fields) {
field2.setAccessible(true);
System.out.println(field2.getName() + " :" + field2.get(s)+" "+field2.getType());
}
}
}
运行结果:
getModifiers()方法的运用
JAVA 反射机制中,Field的getModifiers()方法返回int类型值表示该字段的修饰符。
其中,该修饰符是java.lang.reflect.Modifier的静态属性。
对应表如下:
PUBLIC: 1
PRIVATE: 2
PROTECTED: 4
STATIC: 8
FINAL: 16
SYNCHRONIZED: 32
VOLATILE: 64
TRANSIENT: 128
NATIVE: 256
INTERFACE: 512
ABSTRACT: 1024
STRICT: 2048
我们依然可以通过以上的案例来理解:
代码如下:
package com.dengrenli.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Demo5 {
//获得类及其属性的修饰符
/**
* JAVA 反射机制中,Field的getModifiers()方法返回int类型值表示该字段的修饰符。
其中,该修饰符是java.lang.reflect.Modifier的静态属性。
对应表如下:
PUBLIC: 1
PRIVATE: 2
PROTECTED: 4
STATIC: 8
FINAL: 16
SYNCHRONIZED: 32
VOLATILE: 64
TRANSIENT: 128
NATIVE: 256
INTERFACE: 512
ABSTRACT: 1024
STRICT: 2048
*
*
*/
public static void main(String[] args) {
//获得类的修饰符
Student stu =new Student();
Class uiClass = stu.getClass();
int m = uiClass.getModifiers();
//可以用Modifier.toString(m)来把对应的修饰符得到,然后不用这个方法就只能在对应表上一个一个去对照
System.out.println("获得这个类的修饰符:"+m+"\n");
System.out.println("获得这个类的修饰符:"+Modifier.toString(m)+"\n");
//获得属性的修饰符
Class tc = stu.getClass();
Field[] fl = tc.getDeclaredFields();
for(int i=0;i<fl.length;i++)
{
//例如:得到的结果是25。可以看到对应表上那几个的和是正好等于25的,所以我们可以看出修饰符为public(1) static(8) final(16)
System.out.println("第"+(i+1)+"个属性的修饰符为:"+fl[i].getModifiers());
System.out.println("第"+(i+1)+"个属性的修饰符为:"+Modifier.toString(fl[i].getModifiers()));
}
}
}
结果如图:
谢谢大家,多多指教!