本帖最后由 小江哥 于 2017-11-21 16:00 编辑
1.在研究反射之前,我们先看看类加载器的相关内容,他是反射的基石.
1.1什么是类加载器? 类加载器是负责将.class文件加载到内在中,并为之生成对应的Class对象,也就是字节码文件对象。 1.2类加载器有哪些?(我们来看代码) [Java] 纯文本查看 复制代码 /*
* 演示:类加载的种类:
* 获取类的类加载器:
* ClassLoader getClassLoader()返回该类的类加载器。
* 获取类加载器的父亲:
* loader.getParent()返回委托的父类加载器
*/
public class ClassLoaderDemo01 {
public static void main(String[] args) {
// 获取字节码:
ClassLoader loader = ClassLoaderDemo01.class.getClassLoader();
System.out.println(loader);// sun.misc.Launcher$AppClassLoader@f52d950
// 获取父类加载器:
ClassLoader parent = loader.getParent();
System.out.println(parent);// sun.misc.Launcher$ExtClassLoader@f52d950
// 获取爷爷加载器
ClassLoader grandpa = parent.getParent();
System.out.println(grandpa);// null ,因为BootStrap是顶级类加载器,不是用Java写的。看不到。
ClassLoader loader2 = System.class.getClassLoader();
System.out.println(loader2);// null
}
} 有代码运行结果,我们知道了,一共有三个类加载器.
1.3什么是全盘委托机制
我们自己定义了一个类:
而系统的rt.jar中也有一个一模一样的类:
我们编译:发现没有任何问题,调用的是当前自定义类。
结果,运行报错:
我们在编译的时候,没有加载类,此时编译器就认为我们当前用的是自定义类。当我们正真运行的时候,就会调用类加载器,根据全盘负责委托机制,委托父类去加载,最终BootStrap在自己的底盘找到了自己的MimeType所以,加载的是系统的类。不是我们自定义的,运行的时候,肯定没有show方法。所以报错!
1.4.自定义类加载器
我们写了自定义类加载器:加载我们的类
结果运行,发现又报错了:
等号的左侧:是直接引用MimeType类,所以就会全盘负责委托机制,用APPClassLoader加载,再去找爷爷:于是加载的就是系统的MimeType 等号的右侧,我们自己写的类加载器,自己加载自己的类,肯定是我们定义的MimeType,内存中就出现了两份MimeType。肯定不是一个类。报类型转换异常就很正常了!!注意:两个不同的类加载器,如果加载了同一个class文件,也会在内存中生成两个字节码,这样就会出问题!!
1.5类加载器的注意事项:
2.反射
反射的基石: Class对象Class中包含了类中的所有信息:类名称、类修饰符、类所在包 ;类中的字段: Field ; 类中的构造函数:Constructor ;类中的成员方法:Method
反射:就是通过Class对象获取Class中的所有信息,比如Field、Constructor、Method。获取之后有什么用?怎么用?这是反射要学习的要点。
2.1获取Class对象的方式[JavaFX] 纯文本查看 复制代码 /*
* 演示:三种获取Class的方式:
* 方式1:类名.class
* 方式2:对象.getClass()
* 方式3:Class.forName(String classname)必须传类的全名!!
*/
public class ReflectDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1:类名.class
Class c1 = String.class;
System.out.println(c1.getSimpleName());
System.out.println(c1.getName());
// 方式2:对象.getClass()
Class c2 = "".getClass();
System.out.println(c2.getSimpleName());
System.out.println(c2.getName());
// 方式3:Class.forName(String classname)
Class c3 = Class.forName("java.lang.String");
System.out.println(c3.getSimpleName());
System.out.println(c3.getName());
System.out.println(c1 == c2);// true
System.out.println(c1 == c3);// true
System.out.println(int.class);
System.out.println(void.class);
}
}
首先我们先定义一个学生类[Java] 纯文本查看 复制代码 package cn.itcast.reflect;
@SuppressWarnings("all")
public class Student {
private String name;
public int age;
private String addr;
public Student() {
super();
}
public Student(String name, int age, String addr) {
super();
this.name = name;
this.age = age;
this.addr = addr;
}
private static void show(){
System.out.println("show ... run");
}
public void show(String desc){
System.out.println(name + " :" + desc);
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", addr=" + addr + "]";
}
} 2.2反射获取构造函数 [Java] 纯文本查看 复制代码 /*
* 演示:反射获取构造函数:
* Constructor[] getConstructors() 获取所有公共的构造函数
* Constructor[] getDeclaredConstructors() 获取所有声明的构造函数
* Constructor getConstructor(Class ... parameterTypes)获取指定的公共构造函数
* Constructor getDeclaredConstructor(Class ... parameterTypes) 获取指定的声明的构造函数
* 创建对象:
* T newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例
*
* 如果一个类有公共的空参构造,此时,你就可以直接用字节码文件对象.newInstance()来创建对象
*/
public class ReflectDemo02 {
public static void main(String[] args) throws Exception {
Class clazz = Student.class;
// Constructor[] getConstructors() 获取所有公共的构造函数
// Constructor[] cons = clazz.getConstructors();
// for (Constructor con : cons) {
// System.out.println(con);// public cn.itcast.reflect.Student()
// }
// Constructor[] getDeclaredConstructors() 获取所有声明的构造函数
// Constructor[] cons = clazz.getDeclaredConstructors();
// for (Constructor con : cons) {
// System.out.println(con);
// }
// Constructor getConstructor(Class ... parameterTypes)获取指定的公共构造函数
Constructor con = clazz.getConstructor();
System.out.println(con);// public cn.itcast.reflect.Student()
// Constructor getDeclaredConstructor(Class ... parameterTypes)
// 获取指定的声明的构造函数
// 获取私有的private Student(String name, int age, String addr)
Constructor con2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
System.out.println(con2);
// T newInstance(Object... initargs) 使用此 Constructor
// 对象表示的构造方法来创建该构造方法的声明类的新实例,
// 空参构造创建对象
Student s1 = (Student) con.newInstance();
System.out.println(s1);
// 有参构造
// 暴力访问:
con2.setAccessible(true);
Student s2 = (Student) con2.newInstance("小江哥", 21, "杭州");
System.out.println(s2);
// 通过字节码文件对象,直接生产本类对象
Object obj = clazz.newInstance();
System.out.println(obj);
}
}
2.3反射获取成员变量
[Java] 纯文本查看 复制代码 /*
* 演示:反射获取成员变量:
* Field[] getFields()获取所有公共字段
* Field[] getDeclaredFields() 获取所有声明字段
* Field getField(String name) 获取指定的公共字段
* Field getDeclaredField(String name) 获取指定任意字段
* 获取字段值或设置字段值
* set(Object obj, Object value)将obj对象上此 Field 表示的字段设置为指定的值
* Object get(Object obj)获取指定对象上当前字段的值
*/
public class ReflectDemo03 {
public static void main(String[] args) throws Exception {
// 我们要反射的对象
Student s = new Student("小江哥", 21, "杭州");
// 获取字节码
Class clazz = s.getClass();
// Field[] getFields()获取所有公共字段
// Field[] fields = clazz.getFields();
// for (Field f : fields) {
// System.out.println(f);// public int cn.itcast.reflect.Student.age
// }
// Field[] getDeclaredFields() 获取所有声明字段
// Field[] fields = clazz.getDeclaredFields();
// for (Field f : fields) {
// System.out.println(f);
// }
// Field getField(String name) 获取指定的公共字段
Field f1 = clazz.getField("age");
System.out.println(f1);// public int cn.itcast.reflect.Student.age
// Field getDeclaredField(String name) 获取指定任意字段
Field f2 = clazz.getDeclaredField("addr");
System.out.println(f2);// private java.lang.String cn.itcast.reflect.Student.addr
// 获取字段值:
int age = (int) f1.get(s);
System.out.println(age);// 21
// 修改字段值
f2.setAccessible(true);
f2.set(s, "北京");
System.out.println(s);// Student [name=小江哥, age=21, addr=北京]
}
}
2.4反射获取成员方法
[Java] 纯文本查看 复制代码 /*
* 演示:反射获取成员方法:
* Method[] getMethods() 获取所有公共成员方法,包含继承自父类的
* Method[] getDeclaredMethods() 获取所有声明的方法,只要自己声明的方法。
* Method getMethod(String methodName, Class ... parameterTypes) 获取指定的公共方法
* Method getDeclaredMethod(String methodName, Class ... parameterTypes) 获取指定的声明方法
* 调用方法
* Object invoke(Object obj, Object... args) 调用指定对象obj的当前method表示的方法
*/
public class ReflectDemo04 {
public static void main(String[] args) throws Exception {
Student s = new Student("小江哥", 21, "杭州");
Class clazz = s.getClass();
// Method[] getMethods() 获取所有公共成员方法
// Method[] methods = clazz.getMethods();
// for (Method m : methods) {
// System.out.println(m);
// }
// Method[] getDeclaredMethods() 获取所有声明的方法
// Method[] methods = clazz.getDeclaredMethods();
// for (Method m : methods) {
// System.out.println(m);
// }
// Method getMethod(String methodName, Class ... parameterTypes) 获取指定的公共方法
// public void show(String desc)
Method m = clazz.getMethod("show", String.class);
System.out.println(m);// public void cn.itcast.reflect.Student.show(java.lang.String)
// Method getDeclaredMethod(String methodName, Class ... parameterTypes) 获取指定的声明方法
Method m2 = clazz.getDeclaredMethod("show");
System.out.println(m2);// private static void cn.itcast.reflect.Student.show()
// 举例:我们调用show(String desc)
// 非反射调用:
s.show("一个技术宅!");
// 反射调用
m.invoke(s, "一个胸怀宽广的人!");
// Object invoke(Object obj, Object... args) 调用指定对象obj的当前method表示的方法
// 演示:无参静态方法 private static void show()
m2.setAccessible(true);
m2.invoke(null);
// 带返回值方法
Method m3 = clazz.getMethod("toString");
String str = (String) m3.invoke(s);
System.out.println(str);
}
}
2.5实战用反射模拟BeanUtils的populate方法 [Java] 纯文本查看 复制代码 /*
* 演示:用反射模拟一个BeanUtils的populate方法
*/
public class ReflectDemo05 {
public static void main(String[] args) throws Exception {
Student s = new Student();
Map<String,Object> m = new HashMap<>();
m.put("name", "小江哥");
m.put("age", 21);
m.put("addr", "杭州");
myPopulate(s, m);
System.out.println(s);// Student [name=小江哥, age=21, addr=杭州]
}
/*
* 定义一个功能:实现把一个Map中的属性,设置到一个对象中
*/
public static void myPopulate(Object o, Map<String,Object> m) throws Exception{
// 获取对象的字节码文件对象
Class<?> clazz = o.getClass();
// 反射获取所有成员变量数组
Field[] fields = clazz.getDeclaredFields();
// 循环遍历,取出每个成员
for (Field f : fields) {
// 获取成员的名字
String name = f.getName();
// 从map中根据键找值
Object value = m.get(name);
if( value != null){
// 把这个值设置给当前字段
f.setAccessible(true);
f.set(o, value);
}
}
}
}
最后相信大家对反射和类加载机制有了一定的了解,我们下次再见.
|