反射
反射
类加载的区别
我们来看以下这张图,
图中的RTTI方式,我们编写完java代码就进行编译,即在编译器我们就生成了class文件,这个过程就是我们常规的对一个对象的创建过程,在运行期我们再将其加载到内存中,进行运作:Animal animal = new Dog();即我们在编译器的时候就已经确定了子类对象到底具体是什么了
图中我们的反射在编译器没有确定什么东西,在运行期我们就可以动态的确定一个Animal对象是一个Dog还是Cat
JVM类加载流程和内存结构
我们编写的java文件在类编译器中会编译为class文件,我们下图中cafe babe这份文件(包含类的所有信息)。然后我们的ClassLoader将文件load到内存中,经过加载、验证、准备、解析、初始化这些阶段后,加载进内存之后我们就会对这个文件进行一个内存的管理
Class文件包含的内容
生成对象的步骤
横线以上其实就是我们常用到的生成对象(编译期),横线以下就是我们用到的反射方式进行创建对象(运行期),二者的共同点都是从构造函数进行创建对象。
获取Class类实例的三种方式
同一个对象的class对象只有一个,所以无论通过哪种方式创建,他们的对象都是同一个
@Test
public void test() throws Throwable{
// 方式一 类.class
Class personClazz = Person.class;
// 方式二 实例.getClass()
Person person = new Person();
Class personClazz1 = person.getClass();
// 方式三 Class.forName("类的全路径")
Class personClazz2 = Class.forName("com.reflect.Person");
System.out.println(personClazz == personClazz1);
System.out.println(personClazz == personClazz2);
}
通过反射创建对象
/**
* // 无参数
* <bean id="person" class="com.muse.reflect.Person" />
*
* // 有参数
* <bean id="person" class="com.muse.reflect.Person" >
* <constructor-arg index="0" type="java.lang.String" value="muse"/>
* </bean>
*
*/
@Test
public void test2() throws Throwable{
/** 首先:获得Person的字节码 */
Class personClazz = Class.forName("com.reflect.Person");
/** 其次:通过Class对象,创建构造方法对象 */
Constructor constructor1 = personClazz.getConstructor(String.class, Integer.class, Byte.class,Boolean.class); // 初始化有参构造方法对象
Constructor constructor = personClazz.getConstructor(); // 初始化无参构造方法
/** 最后:通过构造方法创建对象 */
// 调用无参数构造方法创建Person对象
Person person = (Person) constructor.newInstance();
person.setName("ironMan");
System.out.println("person=" + person);
// 调用有参数构造方法创建Person对象
Person person1 = (Person) constructor1.newInstance("ironMan", 10, (byte) 1, true);
System.out.println("person1=" + person1);
}
反射的使用
获得属性
/**
* public属性的
*/
@Test
public void test3() throws Throwable{
// 第一步:获得Class
Class personClazz = Person.class;
// 第二步:获得构造方法
Constructor<Person> constructor = personClazz.getConstructor();
Person person = constructor.newInstance();
// 第三步:通过Class对象,获得Field对象
Field nameField = personClazz.getField("name");
// 第四步:操作Field,获得属性值
String name = String.valueOf(nameField.get(person));
System.out.println(name);
}
获得私有属性
没有sexField.setAccessible(true);这句代码的话,会提示无权访问private属性
/**
* private
*/
@Test
public void test4() throws Throwable{
// 第一步:获得Class
Class personClazz = Person.class;
// 第二步:获得构造方法
Constructor<Person> constructor = personClazz.getConstructor();
Person person = constructor.newInstance();
// 第三步:通过Class对象,获得Field对象
Field sexField = personClazz.getDeclaredField("sex");
sexField.setAccessible(true);
// 第四步:操作Field,获得属性值
System.out.println(sexField.get(person));
}
获得public属性值
通过反射获得类的public属性值
/**
* getField 只能获取public的,包括从父类继承来的字段。
* getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true))
*/
@Test
public void getPublicField() throws Throwable {
/** 首先:获得Person的字节码 */
Class personClazz = Person.class;
/** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
Person person = (Person) personClazz.getConstructor().newInstance();
/** 第三:通过Class对象,获得Field对象 */
Field nameField = personClazz.getField("name");
// Field nameField = personClazz.getDeclaredField("name"); 使用getDeclaredField也是ok的。
/**
* Field.toString();
* --------------------------------------------------------------------
* public String toString() {
* int mod = getModifiers();
* return (((mod == 0) ? "" : (Modifier.toString(mod) + " "))
* + getType().getTypeName() + " "
* + getDeclaringClass().getTypeName() + "."
* + getName());
* }
*
* 【Field的重要方法】
* System.out.println("获取字段的类型:" + nameField.getType());
* System.out.println("获取字段的名字:" + nameField.getName());
* System.out.println("获取字段的访问修饰符:" + Modifier.toString(nameField.getModifiers()));
* System.out.println("获取字段所在类的全路径:" + nameField.getDeclaringClass().getName());
*/
System.out.println(nameField); // protected java.lang.Integer com.Person.name
/** 最后:获取字段的类型 */
String name = String.valueOf(nameField.get(person));
System.out.println(name);
}
获得private属性
/**
* 获得private属性
* @throws Throwable
*/
@Test
public void getPrivateField() throws Throwable {
/** 首先:获得Person的字节码 */
Class personClazz = Person.class;
/** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
Person person = (Person) personClazz.getConstructor().newInstance();
/** 第三:通过Class对象,获得Field对象 */
// Field sexField = personClazz.getField("sex"); 不能使用getField,否则报错:java.lang.NoSuchFieldException: sex
Field sexField = personClazz.getDeclaredField("sex");
sexField.setAccessible(true); // 必须设置为true
/** 最后:获取字段的类型 */
Byte sex = (Byte) sexField.get(person);
System.out.println(sex);
}
获得protected属性
/**
* 获得protected属性
*
* @throws Throwable
*/
@Test
public void getProtectedField() throws Throwable {
/** 首先:获得Person的字节码 */
Class personClazz = Person.class;
/** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
Person person = (Person) personClazz.getConstructor().newInstance();
/** 第三:通过Class对象,获得Field对象 */
Field ageField = personClazz.getDeclaredField("age");
ageField.setAccessible(true); // 必须设置为true,如果不设置,则报错:java.lang.IllegalAccessException: Class ReflectionTest can not access a member of class com.muse.Person with modifiers "protected"
/** 最后:获取字段的类型 */
Integer age = (Integer) ageField.get(person);
System.out.println(age);
}
获得default属性
/**
* 获得default属性
*
*/
@Test
public void getDefaultField() throws Throwable {
/** 首先:获得Person的字节码 */
Class personClazz = Person.class;
/** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
Person person = (Person) personClazz.getConstructor().newInstance();
/** 第三:通过Class对象,获得Field对象 */
Field isMarriageField = personClazz.getDeclaredField("isMarriage");
isMarriageField.setAccessible(true); // 必须设置为true,如果不设置,则报错:java.lang.IllegalAccessException: Class ReflectionTest can not access a member of class com.muse.Person with modifiers ""
/** 最后:获取字段的类型 */
Boolean isMarriage = (Boolean) isMarriageField.get(person);
System.out.println(isMarriage);
}
反射的应用
采用反射机制来实现一个工具BeanUtils,可以将一个对象属性相同的值赋值给另一个对象
public static void convertor(Object originObj, Object targetObj) throws Throwable{
// 第一步,获得class对象
Class orginClazz = originObj.getClass();
Class targetClazz = targetObj.getClass();
// 第二步,获得Field
Field[] orginFields = orginClazz.getDeclaredFields();
Field[] targetFields = targetClazz.getDeclaredFields();
// 第三步:赋值
for (Field originField : orginFields) {
for (Field targetField : targetFields) {
if (originField.getName().equals(targetField.getName())) {
originField.setAccessible(true);
targetField.setAccessible(true);
targetField.set(targetObj, originField.get(originObj));
}
}
}
}