反射->类加载器 获取类Class对象->构造器对象 成员方法对象 成员变量对象
类加载器
类加载的五个时机;1.创建该类的对象
2.使用类的静态成员
3.创建该类的子类对象
4.使用反射创建该类的成员
5.java 运行该类.class文件
1.1java中有三种类加载器:
1.核心类加载器: BootstrapClassLoader 根类加载器-->C语言写的,我们获取不到;
也被称为引导类加载器,负责Java核心类的加载 比如System,String等。 jre/lib/rt.jar下的类都是核心类
2.扩展类加载器:- ExtClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录
3.系统类加载器: - AppClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包(第三方jar包)和类路径。或者自定义的类
1.2双亲委派机制(全盘负责委托机制)
在类加载的过程中符合全盘委托机制:
AppClassLoader负责加载Test,然后按道理来讲Person也是由AppClassLoader加载到内存
但是App先不加载,先去找ExtClassLoader,Ext不负责加载自定义类,Ext就去找Boot
但是Boot只负责加载核心类,所以Boot也不加载,最后App看两个双亲不加载,自己就加载了Person
2反射
2.1获取类的class对象:
三种方法:(以Student类为例)
Studengt st = new Student(); 1: st.getClass 2:Student.class 3: Class.forName("Student的全路径(包括包名类名)")
Class类获取空餐的对象: Class c = Class.forName("Student的全路径(包括包名类名)")
Stuent student = c.newInstance();
2.2获取构造方法对象:
获取构造方法对象:
1. Constructor[] getConstructors()
获取所有的public修饰的构造方法
2. Constructor getConstructor(Class... parameterTypes)
根据参数类型获取构造方法对象,只能获得public修饰的构造方法。
3.根据获取出来的构造方法对象,创建对象
T newInstance(Object... initargs) 如果new无参构造,参数不用写
获取私有的构造:
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 可以获取私有的构造
Constructor<?>[] getDeclaredConstructors() 可以获取所有的构造,包括私有的
AccessibleObject中的方法
void setAccessible(boolean flag)
flag:false-->代表的是不能访问私有的
flag:true-->解除了私有的权限
2.3获取成员方法对象;
* Method getMethod(String name,Class...args);
* 根据方法名和参数类型获得对应的构造方法对象,只能获得public的
* Method getDeclaredMethod(String name,Class...args);
* 根据方法名和参数类型获得对应的构造方法对象,包括public、protected、(默认)、
private的
* Method[] getMethods();
* 获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的
* Method[] getDeclaredMethods();
* 获得类中的所有成员方法对象,返回数组,只获得本类的,包括public、protected、(默
认)、private的
常用方法
* Object invoke(Object obj, Object... args)
* 调用指定对象obj的该方法
* args:调用方法时传递的参数
* void setAccessible(true)
设置"暴力访问"——是否取消权限检查,true取消权限检查,false表示不取消
2.4获取成员变量对象;
* Field getField(String name);
* 根据成员变量名获得对应Field对象,只能获得public修饰
* Field getDeclaredField(String name);
* 根据成员变量名获得对应Field对象,包括public、protected、(默认)、private的
* Field[] getFields();
* 获得所有的成员变量对应的Field对象,只能获得public的
* Field[] getDeclaredFields();
* 获得所有的成员变量对应的Field对象,包括public、protected、(默认)、private的
常用方法
void set(Object obj, Object value)
void setInt(Object obj, int i)
void setLong(Object obj, long l)
void setBoolean(Object obj, boolean z)
void setDouble(Object obj, double d)
Object get(Object obj)
int getInt(Object obj)
long getLong(Object obj)
boolean getBoolean(Object ob)
double getDouble(Object obj)
void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。
Class getType(); 获取属性的类型,返回Class对象。
3Java中反射的常用方法
* 反射案例:
* 利用反射技术,创建对象,运行对象的方法
* 配置文件,写的是类的名字和方法名字
* IO读取文件,获取类和方法名, 反射调用
*
* 实现步骤:
* 1: 创建配置文件
* 本质就是文本,记事本,写键值对
* A: 后缀名properties
* B: 文件放在哪里?存储在src目录下,切记
*
* 项目开发完毕,交给用户使用,是编译后的class文件(idea的out目录)
如果将配置文件放在src下,idea生成的out目录中这个配置文件会显示在out的项目路径下
我们给用户class文件的时候,也得把配置文件给用户,不然项目无法读取配置文件中的信息,也就无法执行
* src 源代码,编译后产生class文件, 是同步的
* 问题:如何读取src目录下的文件?
直接new FileInputStream(直接写模块名\\src\\配置文件名)是不行的,因为这样写,而我们给的用户是
out下的资源,但是out下存放class文件的目录是没有src的,所以这样写,给了用户,用户一使用,直接就读不到这个配置文件了
解决:
使用类的加载器
* ClassLoader类定义方法
* InputStream getResourceAsStream("文件名")返回字节输入流
* 此流会自动从类目录下扫描问文件读取
*
* 2: IO读取配置文件
* 读取文件中的键值对
* 3: 键值存储在集合 Properties
*
* 4: 集合中取出键值对
* 拿到类名,和方法名
*
* 5: 反射调用
代码参考
//在src下创建一个pro.properties文件 className=cn.itcast.day15.class02.Person methodName=eat public class Test06_Properties { public static void main(String[] args) throws Exception { //获取RefectTest类的加载器 ClassLoader classLoader = RefectTest.class.getClassLoader(); //加载器获取输入流,读取pro.properties文件 InputStream inputStream = classLoader.getResourceAsStream("pro.properties"); //创建Properties集合 Properties properties = new Properties(); //调用load方法,将配置文件中的信息读取出来 properties.load(inputStream); String className = properties.getProperty("className"); String methodName = properties.getProperty("methodName"); //根据获取出来的className创建Student的class对象 Class aClass = Class.forName(className); //根据获取出来的methodName获取对应的方法对象 Method method = aClass.getMethod(methodName); //根据构造创建对象 Object o = aClass.newInstance(); //执行方法 method.invoke(o); } }