什么是反射机制
反射就是在运行时,动态获取类的接口,成员,方法和构造器等信息。根据这些动态获取到的信息来创建对象、访问/修改成员、调用方法等。
获取Class对象
当我们编写的类生成的字节码文件中的二进制字节流被类加载器加载到内存当中时,会在方法区产生一个Class对象,作为访问这些类信息的入口。
假如我们编写一个Person类,获取Class对象一般有3种方式:
- 通过类名本身:
Class clazz = Person.class;
- 通过实例变量的方式:
Class clazz = new Person().getClass();
- 通过Class的静态方法Class.forName(String classname)(需加上try…catch,可能会抛ClassNotFoundException异常)。
Class clazz = Class.forName("com.wgs.reflecttest.Person");
forName方法会调用Native方法forName0(),在JVM中会调用findClassFromClassLoader()去加载Person类,触发Person类的构造器初始化。
在上述创建了类对象以后,Class有一个方法可以创建类对象的实例:
public T newInstance() throws InstantiationException, IllegalAccessException
注:它会调用类的默认构造方法(即无参public构造方法),如果类没有该构造方法,会抛出异常InstantiationException。
如:
Person p = (Person )clazz.newInstance();
需要该类提供无参构造器。
获取Field字段信息
Class有4个获取字段信息的方法,包括静态变量和实例变量:
//返回所有的public字段,包括其父类的,如果没有字段,返回空数组
public Field[] getFields()
//返回本类声明的所有字段,包括非public的,但不包括父类的
public Field[] getDeclaredFields()
//返回本类或父类中指定名称的public字段,找不到抛出异常NoSuchFieldException
public Field getField(String name)
//返回本类中声明的指定名称的字段,找不到抛出异常NoSuchFieldException
public Field getDeclaredField(String name)
当然还可以获取Filed的权限修饰符,类型及属性名等信息。
获取Method类方法信息
类中定义的静态和实例方法都被称为方法,用类Method表示,Class有四个获取方法信息的方法:
//返回本类和父类所有public方法(包括Object的7个方法)
public Method[] getMethods()
//只返回本类声明的所有方法
public Method[] getDeclaredMethods()
//返回本类或父类中指定名称和参数类型的public方法,找不到抛出异常NoSuchMethodException
public Method getMethod(String name, Class<?>... parameterTypes)
//返回本类中声明的指定名称和参数类型的方法,找不到抛出异常NoSuchMethodException
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
Method有个invoke()方法,可以调用对象的方,这个invoke()也是很常用的方法,动态代理就用到了方法:
Method method = XXX.class.getDeclaredMethod(xx,xx);
method.invoke(target,params)
获取构造器信息,并创建运行时的类的对象
//获取所有的public构造方法,返回值可能为长度为0的空数组
public Constructor<?>[] getConstructors()
//获取所有的构造方法,包括非public的
public Constructor<?>[] getDeclaredConstructors()
//获取指定参数类型的public构造方法,没找到抛出异常NoSuchMethodException
public Constructor<T> getConstructor(Class<?>... parameterTypes)
//获取指定参数类型的构造方法,包括非public的,没找到抛出异常NoSuchMethodException
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
如:
//获取构造器信息
Constructor constructor = clazz.getConstructor(String.class, int.class);
//创建运行时的类的对象
constructor.newInstance("Tom", 10);
应用一:使用反射机实现简单的设值注入过程
class B{
String name = "";
Integer age = 0;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
public class InjectDemo {
public void inject() throws Exception{
//反射获取B类的Class对象,作为在方法区访问类信息的入口
Class bClazz = B.class;
//Class bClazz = Class.forName("com.wgs.反射.B");
//创建B类实例
B bObj = (B) bClazz.newInstance();
/** 反射注入的过程 **/
//获取name的setter方法,利用反射注入值
Method setNameMethod = bClazz.getMethod("setName", String.class);
setNameMethod.invoke(bObj, "wgs");
//获取age的setter方法,利用反射注入值
Method setAgeMethod = bClazz.getMethod("setAge", Integer.class);
setAgeMethod.invoke(bObj, 25);
//打印,验证是否注入成功
System.out.println(bObj.getName() +","+ bObj.getAge());
}
public static void main(String[] args) throws Exception {
new InjectDemo().inject();
}
}
输出:
wgs,25
从上可以看出,利用反射可以获取一个类的实例以及对应属性的set方法,设值注入即利用反射调用set方法进行赋值的一个过程。
method.invoke(obj, params);
obj是类的实例,method是属性对应的set方法,而params即为要设置的值。
依赖注入的过程也是类似。
应用二:使用反射机制+注解实现Spring依赖注入的过程
1)写一个属于自己的注解:
@Documented
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WGSAnnotation {
public String nation() default "";
}
2)写一个接口,以及两个不同的实现类。后期注入不同实现类
interface IUser {
public void login();
}
class ChineseUserImpl implements IUser{
@Override
public void login() {
System.out.println("中文用户登录!");
}
}
class EnglishUserImpl implements IUser{
@Override
public void login() {
System.out.println("English User Login!");
}
}
@WGSAnnotation
public class UserService {
private IUser userDao;
//通过注解+实现实现依赖注入的过程:获取注解的值,通过反射调用set方法,设值注入
@WGSAnnotation(nation = "EnglishUserImpl")
public void setUserDao(IUser userDao) {
this.userDao = userDao;
}
public IUser getUserDao() {
return userDao;
}
public void userLogin(){
userDao.login();
}
}
3)通过反射+注解实现注入过程:
/**
* 模拟Spring中的容器
*/
public class BeanContainer{
public static UserService getBean() throws Exception{
Class clazz = UserService.class;
UserService user = clazz.newInstance();
Class annotationClass = WGSAnnotation.class;
if(clazz.isAnnotationPresent(annotationClass)){
Method[] methods = clazz.getDeclaredMethods();
for(Method m : methods){
//方法上有注解,就获取其方法并调用set值,将注解中的值注入进去
if(m.isAnnotationPresent(annotationClass)){
WGSAnnotation anno = m.getAnnotation(annotationClass);
System.out.println("方法" + m.getName() +"开始注入过程,注入值:" + anno.nation());
//获取注解实例,注入
IUser userImpl = (IUser) Class.forName("com.wgs.反射." + anno.nation()).newInstance();
m.invoke(user, userImpl);// == user.setUserDao(userImpl);
}
}
}else{
System.out.println("类上无注解!");
}
return user;
}
}
4)测试
public static void main(String[] args) throws Exception {
UserService user = new BeanContainer().getBean();
user.userLogin();
}
输出:
方法setUserDao开始注入过程,注入值:EnglishUserImpl
English User Login!
应用三:使用反射机制实现简单的序列化和反序列化
package com.wgs.反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class SimpleSerializeUtil {
//反序列化:由String(Student [name="张三", age="11", score="22"]) >>> Object(Student类)
public static Object toObject(String str){
try {
String[] strArr = str.split("\n");
if(strArr.length < 1){
throw new IllegalArgumentException();
}
Class<?> clazz = Class.forName(strArr[0]);
Object obj = clazz.newInstance();
for(int i = 1; i < strArr.length; i++){
String[] fieldArr = strArr[i].split("=");
if(fieldArr.length != 2){
throw new IllegalArgumentException();
}
Field field = clazz.getDeclaredField(fieldArr[0]);
String fieldValue = fieldArr[1];
if(!field.isAccessible()){
field.setAccessible(true);
}
setField(obj, field, fieldValue );
}
return obj;
} catch (Exception e) {
throw new RuntimeException();
}
}
private static void setField(Object obj, Field field, String fieldValue) throws Exception {
//若是基本类型,就将值转为基本类型,赋值
//否则,调用类的构造器赋值
Class<?> fieldType = field.getType();
if(fieldType == char.class){
field.setChar(obj, fieldValue.charAt(0));
}else if(fieldType == byte.class){
field.setByte(obj, Byte.parseByte(fieldValue));
}else if(fieldType == short.class){
field.setShort(obj, Short.parseShort(fieldValue));
}else if(fieldType == int.class){
field.setInt(obj, Integer.parseInt(fieldValue));
}else if(fieldType == long.class){
field.setLong(obj, Long.parseLong(fieldValue));
}else if(fieldType == float.class){
field.setFloat(obj, Float.parseFloat(fieldValue));
}else if(fieldType == double.class){
field.setDouble(obj, Double.parseDouble(fieldValue));
}else if(fieldType == boolean.class){
field.setBoolean(obj, Boolean.parseBoolean(fieldValue));
}else if(fieldType == String.class){
field.set(obj, fieldValue);
}else {
//获取构造器
Constructor<?> con = fieldType.getDeclaredConstructor(String.class);
//利用构造器赋值
con.newInstance("fieldValue");
field.set(obj, con);
}
}
//序列化:将Object(Student实例对象) 转化为 String(格式:Student [name="张三", age="11", score="22"])
public static String toString(Object obj){
try {
Class<?> clazz = obj.getClass();
StringBuilder sb = new StringBuilder();
//封装类名
sb.append(clazz.getName() + "\n");
for (Field field : clazz.getDeclaredFields()){
if(!field.isAccessible()){
//字段不可访问的时候,改为可见
field.setAccessible(true);
}
sb.append(field.getName()+ "=" + field.get(obj).toString() + "\n");
}
return sb.toString();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
测试:
package com.wgs.反射;
class Student{
private String name;
private int age;
public double score;
public Student(String name, int age, double score) {
super();
this.name = name;
this.age = age;
this.score = score;
}
public Student() {
super();
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", score=" + score + "]";
}
}
public class SimpleSerializeTest {
public static void main(String[] args) {
Student stu1 = new Student("詹姆斯", 33, 99.99d);
String str = SimpleSerializeUtil.toString(stu1);
System.out.println(str);
Student stu2 = (Student) SimpleSerializeUtil.toObject(str);
System.out.println(stu2);
}
}
输出:
序列化:com.wgs.反射.Student
name=詹姆斯
age=33
score=99.99f反序列化:Student [name=詹姆斯, age=33, score=99.99]
---
参考:http://www.cnblogs.com/swiftma/p/6804342.html