反射:框架设计的灵魂
框架:半成品软件,可以在框架的基础上进行软件开发,简化编码。
反射:将类的各个组成部分封装为其他对象,这就是反射机制。
好处:
1.可以在程序运行过程中,操作这些对象。
2.可以解耦,提高程序的可拓展性。
java代码在计算机中的三个阶段:
在学习反射之前,先让我们回顾下创建对象的四种方式:
创建对象的四种方式
1.使用new创建对象
2.使用反射机制创建对象
3.使用clone创建对象
4.使用序列化机制
我们接下来就要学习如何通过反射
获取class对象的方式:
1.Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
2.类名.class:通过类名的属性class获取
3.对象.getClass(); getClass()方法在Object类中定义着。
/**
* @author Bmo
* @date 2019/8/15 16:35
*/
public class GetClassDemo {
public static void main(String[] args) throws ClassNotFoundException {
//1.Class.forName("全类名")
Class cls1 = Class.forName("demo01.entity.Student");
System.out.println(cls1);
//2.类名.class
Class cls2 = Student.class;
System.out.println(cls2);
//3.对象.getClass()
Student s = new Student();
Class cls3 = s.getClass();
System.out.println(cls3);
// == 比较三个对象
//true
System.out.println(cls1 == cls2);
//true
System.out.println(cls1 == cls3);
//结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
}
}
通过比较我们获取的三个Class对象我们得出结论:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
Class对象功能:
获取功能:
1.获取成员变量们:
Field[] getFields()
Field getField(String name)
Field getDeclaredField(String name)
Field[] getDeclaredFields()
2.获取构造方法们:
Constructor<?>[] getConstructors()
Constructor getConstructor(Class<?>… parameterTypes)
Constructor getDeclaredConstructor(Class<?>… parameterTypes)
Constructor<?>[] getDeclaredConstructors()
3.获取成员方法们:
Method getMethod(String name, Class<?>… parameterTypes)
Method[] getMethods()
Method getDeclaredMethod(String name, Class<?>… parameterTypes)
Method[] getDeclaredMethods()
4.获取类名:
String getName()
一个熟悉以上知识点的小Demo:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author Bmo
* @date 2019/8/15 11:15
*/
public class Test {
public static void main(String[] args) {
String classPath = "demo02.Student";
printProperty(classPath);
}
public static Object createInstance(String classPath) {
try {
Class<?> clazz = Class.forName(classPath);
return clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
public static void printProperty(String classPath) {
try {
Class<?> clazz = Class.forName(classPath);
// 获取这个字节码所有的public字段(含父类) 目前没有 不会打印
Field[] fields = clazz.getFields();
//获取当前clazz的所有字段
Field[] myfields = clazz.getDeclaredFields();
for (Field field : myfields) {
System.out.println(field.getName());
}
//带declared是不含父类的
//获取所有public方法(含父类)
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
//动态获取当前类的类名
System.out.println(clazz.getName());
Constructor<?>[] constructors = clazz.getConstructors();
System.out.println(constructors.length);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Field : 成员变量
操作:
- 设置值 void set(Object obj, Object value)
- 获取值 get(Object obj)
- 忽略访问权限修饰符的安全检查 setAccessible(true):暴力反射
Constructor:构造方法
创建对象:
T newInstance(Object... initargs)
如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
Method : 方法对象
执行方法:
Object invoke(Object obj, Object... args)
获取方法名称:
String getName:获取方法名
案例时间:
学习完相关的方法以后,是不是迫不及待跃跃欲试想要写一个案例来验证下自己的学习成果了呢?别急,这里正好有一个。
案例需求:
写一个"框架",可以帮我们创建任意类的对象,并且执行其中任意的方法。
需求分析:
实现:
- 1.配置文件
- 2.反射
步骤:
- 1.将需要创建的对象全类名和需要执行的方法定义在配置文件中
- 2.在程序中加载读取配置文件
- 3.使用反射技术来加载类文件进内存
- 4.创建对象
- 5.执行方法
Student.java
package reflect;
/**
* @author Bmo
* @date 2019/8/15 17:15
*/
public class Student {
public void sleep() {
System.out.println("sleep...");
}
public void eat() {
System.out.println("eat....");
}
public void eat(String food) {
System.out.println("eat...." + food);
}
}
pro.properties
className=reflect.Student
methodName=sleep
ReflectTest
package reflect;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* @author Bmo
* @date 2019/8/15 17:17
* 框架类
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
//可以创建任意类的对象,可以执行任意方法
//1.加载配置文件
//1.1创建Propertie对象
Properties pro = new Properties();
//1.2加载配置文件,转换为一个集合
//1.2.1获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
pro.load(is);
//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加载该类进内存
Class cls = Class.forName(className);
//4.创建对象
Object obj = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(obj);
}
}