Java Reflection
Java被视为动态或准动态语言的一个关键性质
- Introduction
- Usage
- JavaBean及内省
- 内省(Introspector)
- 实际应用场景👀
Introduction
Reflection机制允许程序在运行时透过 Reflection APIs 取得任何一个已知名称的 class 的内部信息,包括 modifiers、superclass、interfaces,也包括 fields 和 methods 的所有信息,并可于运行时改变 fields 内容或调用 methods。换句话说,Java 程序可以加载一个运行时才得知名称的 class,获悉其完整构造但不包括methods定义,并生成其对象实体、或对其 fields 设值、或唤起其methods。这种“看透class”的能力被称为内省。Reflection和introspection是常被并提的两个术语。
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflet包中:
- Class类:代表一个类
- field类:代表类的成员变量(成员变量也被称为类的属性)
- Method类:代表类的方法
- Constructor类:代表类的构造方法
- Array类:提供了动态创建数组,以及访问数组的元素的静态方法
Usage
Java中每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。
Class对象可以获得该类里的方法(由Method对象表示)、构造器(由Constructor对象表示)、成员变量(由Field)对象表示,这三个类都位于java.lang.reflect包下并实现了java.lang.reflect.Member接口。程序可以通过Method对象来执行对应的方法,通过Constructor对象来调用对应的构造器创建实例,能通过Field对象直接访问并修改对象的成员变量值。
在Java程序中获得Class对象通常有如下三种方式:
//使用Class类的forName(String clazzName)静态方法
//该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须包括完整包名)。
Class clazz=Class.forName(“demo.Person”);
//调用某个类的class属性来获取该类对应的Class对象
//Person.class将会返回Person类对应的Class对象
Class clazz=Person.class;
//调用某个对象的getClass()方法。
//该方法是java.lang.Object类中的一个方法,所以所有的Java对象都可以调用该方法,该方法将会返回该对象所属类对应的Class对象。
Person person=new Person();
Class clazz=person.getClass();
通过反射来生成实例对象有如下两种方式:
//使用Class对象的newInstance()方法来创建该Class对应类的实例。、
//这种方式要求该Class对象的对应类有默认构造器,而执行newInstance()时实际上是利用默认构造器来创建该类的实例
Class clazz=Person.class;
Object obj=clazz.newInstance();
//先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。
//通过这种方式可以选择使用指定的构造器来创建实例
Class clazz=Person.class;
Constructor constructor=clazz.getConstructor(String.class);
Object obj=constructor.newInstance();
参考示例:http://blog.csdn.net/ljphhj/article/details/12858767
package com.iamwyj;
import java.lang.reflect.*;
/*
1.工厂模式:Factory类中用反射的话,添加了一个新的类之后,就不需要再修改工厂类Factory了
2.数据库JDBC中通过Class.forName(Driver).来获得数据库连接驱动
3.分析类文件:毕竟能得到类中的方法等等
4.访问一些不能访问的变量或属性:破解别人代码
*/
class Person {
private int age;
private String name;
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
interface ActionInterface {
public void walk(int m);
}
class SuperMan extends Person implements ActionInterface {
private boolean BlueBriefs;
public void fly() {
System.out.println("超人会飞耶~~");
}
public boolean isBlueBriefs() {
return BlueBriefs;
}
public void setBlueBriefs(boolean blueBriefs) {
BlueBriefs = blueBriefs;
}
@Override
public void walk(int m) {
// TODO Auto-generated method stub
System.out.println("超人会走耶~~走了" + m + "米就走不动了!");
}
}
public class ReflectionTest {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchFieldException, NoSuchMethodException {
System.out.println("1.通过Java反射机制得到类的包名和类名");
Person person = new Person();
System.out.println("包名为:" + person.getClass().getPackage().getName());
System.out.println("完整类名:" + person.getClass().getName());
System.out.println("2.验证所有的类都是Class类的实例对象");
// 定义两个类型都未知的Class,设置初值为null,看看如何给它们赋值成Person类
Class<?> class1 = null;
Class<?> class2 = null;
// 写法1,可能抛出ClassNotFoundException[多用这个写法]
class1 = Class.forName("com.iamwyj.Person");
System.out.println("包名为:" + class1.getClass().getPackage().getName());
System.out.println("完整类名:" + class1.getClass().getName());
// 写法2
class2 = Person.class;
System.out.println("包名为:" + class2.getClass().getPackage().getName());
System.out.println("完整类名:" + class2.getClass().getName());
System.out.println("3.通过Java反射机制,用Class创建类对象[这也就是反射存在的意义所在],无参构造");
Class<?> class3 = null;
class3 = Class.forName("com.iamwyj.Person");
//由于这里不能带参数,所以你要实例化的这个类Person,一定要有无参构造函数
Person person3 = (Person) class3.newInstance();
person3.setAge(20);
person3.setName("wyj");
System.out.println(person3.getName() + " : " + person3.getAge());
System.out.println("4.通过Java反射机制得到一个类的构造函数并实现构造带参实例对象");
Class<?> class4 = null;
Person person4 = null;
Person person5 = null;
class4 = Class.forName("com.iamwyj.Person");
//得到一系列构造函数集合
Constructor<?>[] constructors = class4.getConstructors();
person4 = (Person) constructors[0].newInstance();
person4.setAge(30);
person4.setName("xm");
System.out.println(person4.getName() + " : " + person4.getAge());
person5 = (Person) constructors[1].newInstance(32, "lk");
System.out.println(person5.getName() + " : " + person5.getAge());
System.out.println("5.通过Java反射机制操作成员变量set和get");
Class<?> class6 = null;
class6 = Class.forName("com.iamwyj.Person");
Object obj = class6.newInstance();
Field personNameField = class6.getDeclaredField("name");
personNameField.setAccessible(true);
personNameField.set(obj, "胖虎");
System.out.println("修改属性之后得到属性变量的值:" + personNameField.get(obj));
System.out.println("6.通过Java反射机制得到类的一些属性:继承的接口,父类,函数信息,成员信息,类型等");
Class<?> class7 = null;
class7 = Class.forName("com.iamwyj.SuperMan");
//取得父类名称
Class<?> superClass = class7.getSuperclass();
System.out.println("SuperMan类的父类名: " + superClass.getName());
Field[] fields = class7.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
System.out.println("类中的成员: " + fields[i]);
}
//取得类方法
Method[] methods = class7.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
System.out.println("取得SuperMan类的方法:");
System.out.println("函数名:" + methods[i].getName());
System.out.println("函数返回类型:" + methods[i].getReturnType());
System.out.println("函数访问修饰符:" + Modifier.toString(methods[i].getModifiers()));
System.out.println("函数代码写法: " + methods[i]);
}
//取得类实现的接口,因为接口类也属于Class,所以得到接口中的方法也是一样的方法得到哈
Class<?> interfaces[] = class7.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
System.out.println("实现的接口类名: " + interfaces[i].getName());
}
System.out.println("7.通过Java反射机制调用类中方法");
Class<?> class8 = null;
class8 = Class.forName("com.iamwyj.SuperMan");
System.out.println("调用无参方法fly():");
Method method = class8.getMethod("fly");
method.invoke(class8.newInstance());
System.out.println("调用有参方法walk(int m):");
method = class8.getMethod("walk", int.class);
method.invoke(class8.newInstance(), 100);
System.out.println("8.通过Java反射机制获得类加载器");
Class<?> class9 = null;
class9 = Class.forName("com.iamwyj.SuperMan");
String nameString = class9.getClassLoader().getClass().getName();
System.out.println("类加载器类名: " + nameString);
}
}
/*output:
1.通过Java反射机制得到类的包名和类名
包名为:com.iamwyj
完整类名:com.iamwyj.Person
2.验证所有的类都是Class类的实例对象
包名为:java.lang
完整类名:java.lang.Class
包名为:java.lang
完整类名:java.lang.Class
3.通过Java反射机制,用Class创建类对象[这也就是反射存在的意义所在],无参构造
wyj : 20
4.通过Java反射机制得到一个类的构造函数并实现构造带参实例对象
xm : 30
lk : 32
5.通过Java反射机制操作成员变量set和get
修改属性之后得到属性变量的值:胖虎
6.通过Java反射机制得到类的一些属性:继承的接口,父类,函数信息,成员信息,类型等
SuperMan类的父类名: com.iamwyj.Person
类中的成员: private boolean com.iamwyj.SuperMan.BlueBriefs
取得SuperMan类的方法:
函数名:fly
函数返回类型:void
函数访问修饰符:public
函数代码写法: public void com.iamwyj.SuperMan.fly()
取得SuperMan类的方法:
函数名:walk
函数返回类型:void
函数访问修饰符:public
函数代码写法: public void com.iamwyj.SuperMan.walk(int)
取得SuperMan类的方法:
函数名:isBlueBriefs
函数返回类型:boolean
函数访问修饰符:public
函数代码写法: public boolean com.iamwyj.SuperMan.isBlueBriefs()
取得SuperMan类的方法:
函数名:setBlueBriefs
函数返回类型:void
函数访问修饰符:public
函数代码写法: public void com.iamwyj.SuperMan.setBlueBriefs(boolean)
实现的接口类名: com.iamwyj.ActionInterface
7.通过Java反射机制调用类中方法
调用无参方法fly():
超人会飞耶~~
调用有参方法walk(int m):
超人会走耶~~走了100米就走不动了!
8.通过Java反射机制获得类加载器
类加载器类名: sun.misc.Launcher$AppClassLoader*/
JavaBean及内省
遵循特定写法的Java类。Introspection means the ability of the program to examine itselt.
具有以下特点:
- 有无参构造函数 ==> 使用内省可以帮你创建对象.
- 所有作为属性保存的成员变量私有化 ==> java不能脱离基本特性==> 封装性
- 属性有对应get/set方法 ==> 1.满足了封装性;2.符合命名规则的话,内省就能分辩出哪些是操作属性的方法
JavaBean在J2EE开发中,通常用于封装数据,对于遵循以上写法的JavaBean组件,其它程序可以通过反射技术实例化JavaBean对象,并且通过反射那些遵守命名规范的方法,从而获知JavaBean的属性,进而调用其属性保存数据。
符合上面的格式,对我们开发来说有什么好处? 可以应用JAVA的内省机制,内省就是操作我们javabean提供的一套api
内省(Introspector)
访问JavaBean属性的两种方式:
- 直接调用bean的setXXX或getXXX方法。
- 通过内省技术访问(java.beans包提供了内省的API)
原理:
- 内省技术基于反射技术
- 通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法
例如: 利用内省原理设置u的名字为tom,密码为1234
package bean;
public class User {
private String name;
private String password;
public User(String name, String password) {
super();
this.name = name;
this.password = password;
}
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "name:" + this.name + " " + "password:" + password;
}
}
package bean;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
//内省就是操作我们javabean提供的一套api
User u = new User();
try {
// 1.获得javaBean的描述信息
BeanInfo info = Introspector.getBeanInfo(User.class);
// 2.获得User中的属性信息
PropertyDescriptor[] pds = info.getPropertyDescriptors();
// 3.遍历属性信息
for (PropertyDescriptor pd : pds) {
Method writer = pd.getWriteMethod();
//判断当前遍历的属性是否是name属性
if (pd.getName().equals("name")) {
//如果是name属性,获得操作name属性的写入方法(setName)
writer.invoke(u, "tom");
} else if (pd.getName().equals("password")) {
//如果是password属性,获得操作password属性的写入方法(setPassword)
writer.invoke(u, "1234");
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(u);
}
}
// output: name:tom password:1234
实际应用场景👀
package bean;
import java.util.Date;
public class User {
private String name;
private String password;
private int age;
private Date birthday;
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public User() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [name=" + name + ", password=" + password + ", age=" + age
+ ", birthday=" + birthday + "]";
}
}
应用一:
import bean.User;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.Date;
public class Test {
private static void copyProperties(User u1, User u2) {
try {
// 1.获得u1中的属性描述
// 2.遍历这些属性描述
for (PropertyDescriptor pd : Introspector.getBeanInfo(u1.getClass()).getPropertyDescriptors()) {
String propName = pd.getName();
if (!propName.equals("class")) {
// 3.查看u2是否具有这些属性
PropertyDescriptor pd2 = new PropertyDescriptor(propName, u2.getClass());
// 有该属性:获得该属性值,查看是否是null
if (pd2 != null) {
Object obj = pd.getReadMethod().invoke(u1);
if (obj != null) {
//不是null 拷贝到u2
pd2.getWriteMethod().invoke(u2, obj);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
User u1 = new User();
u1.setName("tom");
u1.setPassword("1234");
u1.setAge(18);
u1.setBirthday(new Date());
User u2 = new User();
copyProperties(u1, u2);// struts1
System.out.println(u2);
}
}
//output: User [name=tom, password=1234, age=18, birthday=Thu Jun 28 15:42:40 CST 2018]
应用二: BeanUtils工具类的populate(Object bean, Map properties)方法 可以把表单提交的参数自动封装到Bean中,并且可以自动进行类型转换,转换范围是 8个基本数据类型,我们也可以注册一个转换器,让BeanUtils可以转换其他类型
自定义转换器:
package tool;
import org.apache.commons.beanutils.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class MyConverter implements Converter {
//1990-01-01 ==> date
public Object convert(Class arg0, Object arg1) {
String birthdayStr = arg1.toString();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
return format.parse(birthdayStr);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
}
表单提交数据:
<body>
<form action="/Review/my" method="post">
用户名:<input type="text" name="name"/><br>
密码:<input type="password" name="password"/><br>
年龄:<input type="text" name="age"/><br>
生日:<input type="text" name="birthday"/><br>
<input type="submit"/><br>
</form>
</body>
业务处理:
package servlet;
import bean.User;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import tool.MyConverter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.Map;
public class MyServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
User u = new User();
try {
//注册一个转换器,告诉BeanUtils如何进行转换
ConvertUtils.register(new MyConverter(), Date.class);
BeanUtils.populate(u, request.getParameterMap());
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(u);
}
// populate底层方法使用内省封装参数
private void populate(Map<String, String[]> map, User u) {
try {
Map<String, String[]> params = map;
BeanInfo info = Introspector.getBeanInfo(User.class);
PropertyDescriptor[] pds = info.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
//System.out.println(pd.getName());
String[] param = params.get(pd.getName());
if (param != null && param.length > 0) {
pd.getWriteMethod().invoke(u, param[0]);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}