反射
1)java代码的阶段
一段java代码在程序运行期间会经历三个阶段: source-->class-->runtime
2) Class 对象
在java中用一个Class对象来表示一个java类的class阶段
Class对象封装了一个java类中定义的成员变量、成员方法、构造方法、类名、包名等
获得class对象的三种方式和区别
// 1. 根据给定的类名来获得 用于类加载
String classname = "cn.itcast.reflect.Person"; // 来自配置文件
Class clazz = Class.forName(classname); // 此对象代表Person.class
// 2. 如果拿到了对象,不知道是什么类型 用于获得对象的类型
Object obj = new Person();
Class clazz1 = obj.getClass(); // 获得对象具体的类型
// 3. 如果是明确地获得某个类的Class对象 主要用于传参
Class clazz2 = Person.class;
// 在java中所有的类型都会对应一个Class对象 int Integer
Class intClazz = int.class;
Class intarrClazz = int[].class;
Class voidClazz = void.class;
3)反射
反射就是获得一个java类的各个组成部分
// 反射类的成员方法
Class clazz = Person.class;
Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
method.invoke();
// 反射类的构造函数
Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})
con.newInstance(params...)
// 反射类的属性
Field field = clazz.getField(fieldName);
field.setAccessible(true);
field.setObject(value);
4) 反射用在哪里
到底框架是什么? 框架就是将开发中大量重复的代码集中起来写个通用的程序
框架就是用反射来实现的
框架需要现在的类调用将来写的类
框架是将来的程序员调用的,框架不能实现完整的功能,框架只是一些一些通用的代码
框架要运行一定会依赖将来写的类
现在写的类要调用将来写的类,我们先针对接口进行调用,将来的类需要实现接口,那么方法就固定了
但是将来写的类的类名我们无法获知,这时就需要调用者通过配置文件告诉框架具体的类名
1. 理解Class类
对象都是根据类创建出来的—>创建一个对象代表李四这个人—>李四(跑起来)
Person.class(描述所有和李四类似的事物的信息) à Person对象 à 李四(赵六、王五)
Class.class(描述字节码这类事物的特征) –> Class对象—> Person.class字节码(Student.class Boy.class String.class)
所有的Person对象能做的事情都用方法来描述,例如跑步用run方法描述
所有的Class对象(字节码)能做的事情用方法来描述,例如创建对象用newInstance来描述
2. 通过反射获得Class对象
三种方式获得
// 1. 根据给定的类名来获得
String methodname = "run";
String classname = "cn.itcast.reflect.Person"; // 来自配置文件
Class clazz = Class.forName(classname); // 此对象代表Person.class
Object obj = clazz.newInstance(); // 创建对象
// 2. 如果拿到了对象,不知道是什么类型
Object obj = new Person();
Class clazz1 = obj.getClass(); // 获得对象具体的类型
// 3. 如果是明确地获得某个类的Class对象
Class clazz2 = Person.class; // 主要用于传参
// 在java中所有的类型都会对应一个Class对象 int Integer
Class intClazz = int.class;
Class intarrClazz = int[].class;
Class voidClazz = void.class;
3. 反射能做什么事情
// 调用任何一个对象的任何方法
// 读取配置文件获得如下信息
String classname = "cn.itcast.reflect.Student";
String methodname = "study";
// 把Person的run方法调用
// 1.创建Person对象
Class clazz = Class.forName(classname); // 此对象代表Person.class
Object obj = clazz.newInstance(); // 创建对象
// 2.获得表示run方法的对象
Method runMethod = clazz.getMethod(methodname);
// 3.通过Person对象来调用run方法
4. 通过反射获得类的成员变量
// 获得代表某个属性的Field对象
Class clazz = Person.class;
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields) {
String name = field.getName(); // 获得属性名
Class type = field.getType(); // 获得属性的类型
System.out.println("属性名:" + name + "属性类型:" + type);
}
// 获得对象的某个指定的属性,并为该属性赋值
// 明确告诉你,获得name属性
String fieldname = "name";
Object obj = new Person();
// 1. 获得Class对象(类)
Class clazz1 = obj.getClass();
// 2. 获得指定的属性
Field nameField = clazz1.getDeclaredField(fieldname);
// 3. 为属性赋值
// 私有属性不行,因为java虚拟机会检查访问权限
// 如果一定要访问,就需要取消java访问检查
nameField.setAccessible(true);
nameField.set(obj, "zhangsan");
/*
Person p = (Person) obj;
System.out.println(p.getName());*/
Object value = nameField.get(obj); // 获得指定对象上该字段的值
System.out.println(value);
5. 通过反射获得类的构造方法
// 获得类
Class clazz = Person.class;
// 获得类的所有构造函数
Constructor[] constructors = clazz.getConstructors();
for(Constructor con : constructors) {
// 遍历参数类型
Class[] parameterTypes = con.getParameterTypes();
for(Class type : parameterTypes)
System.out.print(type.getName() + " ");
System.out.println();
}
// 获得指定的构造函数,创建对象
// 要求调用参数为 String int 的构造函数
// 1. 反射出指定的构造函数
Constructor con = clazz.getConstructor(String.class, int.class);
// 2. 调用构造函数创建对象
Object obj = con.newInstance("wangwu", 23);
System.out.println(obj); // toString
6. 通过反射获得类的成员方法
Class clazz = Person.class;
// 获得类的所有方法
Method[] methods = clazz.getDeclaredMethods();
for(Method m : methods) {
String name = m.getName(); // 方法名
System.out.print("方法名:" + name);
// 参数类型
System.out.print(", 参数类型依次为:");
Class[] types = m.getParameterTypes();
for(Class type : types)
System.out.print(type.getName() + " ");
// 返回值类型
Class returnType = m.getReturnType();
System.out.print(",返回值类型:" + returnType.getName());
System.out.println();
}
// 反射出指定的方法,调用
// 创建一个对象
Object obj = clazz.newInstance();
// 调用play方法
Method playMethod = clazz.getMethod("play", String.class);
playMethod.invoke(obj, "zhangsan");
// 调用eat方法
Method eatMethod = clazz.getMethod("eat");
eatMethod.invoke(null);
// 调用sleep方法
Method sleepMethod = clazz.getMethod("sleep", String[].class);
String[] arr = {"a","b"};
sleepMethod.invoke(obj, (Object)arr); // 符合1.4和1.5的语法
// 什么情况下用哪个?
// 1. 如果我们需要加载一个类
Class.forName("cn.itcast.reflect.Person"); // 去加载一个类
// 2. 如果我们要判断一个对象的类型
Object obj = new Person();
// 判断obj对象是不是Person类型对象
if(obj.getClass() == Person.class)
System.out.println("是一个Person对象");
else
System.out.println("不是一个Person对象");
obj = new Student();
// 把子类当做父类来用,不能调用子类的方法,此时需要强转
// 强转之前需要判断类型
// instanceof 判断对象是否实现了指定的接口(或继承了指定的类)
if(obj instanceof Person) {
Person p = (Person) obj;
p.run();
}
// 3. 如果我们在调用方法时,方法需要的参数是Class类型,
doXX(Person.class);