反射就是把Java类中的各个组成部分进行解剖,并映射成一个个的Java对象,拿到这些对象后可以做一些事情。
既然说反射是解剖Java类中的各个组成部分,所以说咱们得知道一个类中有哪些部分?
一个类里一般有构造函数、方法、成员变量(字段/属性)这三部分组成
Class类
实际上,我们创建的每一个类也都是对象,即类本身是java.lang.Class类的实例对象。
这个实例对象称之为类对象,也就是Class对象。
Class类本身没有构造方法,得到Class类实例的3种方式 **(1)、Class.forName("包名.类名"); **最常用,可写在配置文件里
(2)、对象.getClass();
(3)、类名.class;
把解剖出来的部分,分别用Constructor(构造)、Method(方法)、Field(属性)对象表示,而这3个类都是AccessibleObject的子类。注意:这3个类默认都只能访问public的,要想访问其他的要设置访问权限
forName("包名.类名"); | 实例化class对象 |
---|---|
getConstructors() | 得到一个类中的全部构造方法 |
getDeclaredFields() | 得到一个类的父类的全部属性 |
getFields() | 得到一个类中的全部属性 |
getMethods() | 得到一个类中的全部方法 |
getMethod(String 方法名,该方法的各个参数的Class对象) | 得到一个指定的方法 |
getInterfaces() | 得到一个类实现的全部接口 |
getName() | 得到一个类完整的“包名.类名”的名称 |
getPackage() | 得到一个类的包,返回类型是 Package |
getSuperclass | 得到一个类的父类 |
newInstance() | 根据Class定义的类实例化对象 |
isArray() | 判断此 Class是否为一个数组 |
注意:newInstance()只能实例化具有无参构造方法的类
注意:getConstructor()只能反射public的构造函数
getDeclaredConstructor()可以读取任何权限的构造函数,
如果是私有的构造方法就要设置一下暴力反射才可以 setAccessible(true);//暴力反射
Constructor构造对象
getModifiers() | 得到构造方法的修饰符 |
---|---|
getName() | 得到构造方法的名称 |
getParameterTypes() | 得到构造方法中参数的信息 |
toString() | 返回构造方法的信息 |
newInstance(该构造方法的各种参数) | 返回实例 |
class shit{
int age;
String str;
public shit(int age,String str)
{
this.age=age;
this.str=str;
}
public String toString()
{
return str+age;
}
}
public class test {
public static void main(String[] args) throws Exception {
Class c=shit.class;
Constructor[] con=c.getConstructors();
shit s=(shit) con[0].newInstance(123,"ui");
System.out.print(s);
}
}
这是用有多个参数的构造方法来实例化对象的 例子
Method
getModifiers() | 得到方法的修饰符 |
---|---|
getName() | 得到方法的名称 |
getParameterTypes() | 得到方法中参数的信息 |
getReturnTypes() | 得到返回类型 |
toString() | 返回方法的信息 |
getExceptionTypes() | 返回该方法可能抛出的异常 |
invoke(该类的对象,该方法的参数.......) | 调用该方法 |
Field
get(Object) | 得到一个对象中属性的具体内容 |
---|---|
set(Object指定对象,Object 值) | 设置指定对象中属性 的内容 |
getModifiers() | 得到属性的修饰符 |
getName() | 返回该属性的名称 |
isAccessible() | 判断是否可以被访问 |
setAccessible(boolean 是/否) | 设置是否可被访问 |
setAccessible(AccessibleObject[],boolean) | 设置一组属性是否可被外部访问 |
toString() | 返回此Field的信息 |
Array
通过反射操作数组,一下都是静态方法
get(数组,int index) | 返会指定位置的元素 |
---|---|
getLength() | 返回数组长度 |
set(数组,int index,值) | 设置指定位置的值 |
newInstance(数组的Class对象,int 长度) | 根据已有的数组类型开辟一个新的数组对象 |
得到数组Class对象的 方法
数组对象.getClass().getComponentType()
ClassLoader类加载器
以下是系统的3个类加载器,加载顺序依次降低
Bootstrap CLassloder | 不是类,由c++编写 |
---|---|
Extention ClassLoader(ExtClassLoader) | 真实的类 |
AppClassLoader | 真实的类 |
每一个类都有父加载器,除了Bootstrap CLassloder,这是父加载器示意图
注意的是父加载器 不是 父类,这是各个加载器的继承关系,Bootstrap CLassloder不是java中的类,它本身是java虚拟机的一部分(int.class与String.class就是她加载的)
双亲委托
一个类加载器查找class和resource时,是通过“委托模式”进行的。
它首先判断这个class是不是已经加载成功,如果当前的类加载器没有查询到这个class对
象已经加载就请求父加载器进行操作,然后以此类推。直到Bootstrap ClassLoader。
如果Bootstrap ClassLoader也没有加载过此class实例,那么它就会从它指定的路径中去查找,如果查找成功则返回**,如果没有查找成功则交给子类加载器去查找。以此类推,总的来说是一个递推的过程
这种机制就叫做双亲委托。整个流程可以如下图所示:
系统类库使用系统的类加载器,非系统的类才能使用自定义的加载器
自定义ClassLoader
- 编写一个类继承自ClassLoader抽象类。
- 复写它的findClass()方法。
- 在findClass()方法中调用defineClass()。
defineClass(),它能将class二进制内容转换成Class对象,
defineClass(类名,byte数组,int 起始,int 结束) 传入一个装有class文件内容的字节数组转换为Class对象
Class的getClassLoader()方法得到类加载器
Classloder的loadClass(String path)方法传入地址返回Class对象