JAVA反射机制是在运行状态中,获取任意一个类的结构,创建对象 , 得到方法,执行方法,属性;这种在运行状态动态获取信息以及动态调用对象方法的功能被称为java语言的反射机制。
jvm的类加载器
Java类加载器(Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分,负责动态加载Java类到Java虚拟机的内存空间中。
三种默认的加载器
java默认有三种类加载器,BootstrapClassLoader、ExtensionClassLoader、App ClassLoader。
BootstrapClassLoader(引导启动类加载器)
嵌在JVM内核中的加载器,该加载器是用C++语言写的,主要负载加载JAVA_HOME/lib下的类库,引导启动类加载器无法被应用程序直接使用。
ExtensionClassLoader(扩展类加载器)
ExtensionClassLoader是用JAVA编写,且它的父类加载器是Bootstrap。是由sun.misc.Launcher$ExtClassLoader实现的,主要加载JAVA_HOME/lib/ext目录中的类
库。它的父加载器是BootstrapClassLoader
App ClassLoader(应用类加载器)
App ClassLoader是应用程序类加载器,负责加载应用程序classpath目录下的所有jar和class文件。它的父加载器为Ext ClassLoader
双亲委派模型
类通常是按需加载,即第一次使用该类时才加载。由于有了类加载器,Java运行时系统不需要知道文件与文件系统。
双亲委派模型:如果一个类加载器收到了一个类加载请求,它不会自己去尝试加载这个类,而是把这个请求转交给父类加载器去完成。每一个层次的类加载器都是如此。因此所有的类加载请求都应该传递到最顶层的启动类加载器中,只有到父类加载器反馈自己无法完成这个加载请求(在它的搜索范围没有找到这个类)时,子类加载器才会尝试自己去加载。委派的好处就是避免有些类被重复加载。
动态加载类对象
在静态的面向对象的java编程中,我们都是将类视为"一个类型",用"这个类型"去创建"这个类型"的对象。因此如果我们将每个类视为某种类型的对象(例如:Class),就能够在编程过程中动态获取一个类并根据这个类创建一个对象。在Java中,每一个字节码文件,被加载到内存后,都存在一个对应的Class类型的对象。
三种方式得到Class
- 如果在编写代码时, 知道类的名称, 且类已经存在, 可以通过
包名.类名.class
得到一个类的 类对象- 如果拥有类的对象, 可以通过
Class 对象.getClass()
得到一个类的 类对象- 如果在编写代码时, 知道类的名称 , 可以通过
Class.forName(包名+类名)
: 得到一个类的 类对象
上述的三种方式, 在调用时, 如果类在内存中不存在, 则会加载到内存存。如果类已经在内存中存在, 不会重复加载, 而是重复利用。
其中Class.forName(包名+类名)
允许我们尝试获取一个仅知道名字的类,当要加载的类在编写这段代码不存在的时候,Class.forName
也可以对其进行加载,非常适用于各种框架的编程。
特殊的类对象
基本数据类型的类对象:
基本数据类型.clss
包装类.type
基本数据类型包装类对象:
包装类.class
方法/属性/注解
构造方法 Constructor
在上文中获取到Class的对象之后,我们可以获取它的构造器。类的构造方法是不能有返回值的,但是我们可以视为其返回值为要创建的对象本身(事实上还真是这样)
获取构造方法
已知Class对象(以Person类为例),构造方法的获取方式如下:
- 通过指定的参数类型, 获取指定的单个构造方法
getConstructor(参数类型的class对象数组)
例如: 构造方法如下:Person(String name,int age)
得到这个构造方法的代码如下:Constructor c = p.getClass().getConstructor(String.class,int.class);
- 获取构造方法数组
getConstructors()
- 获取所有权限的单个构造方法
getDeclaredConstructor(参数类型的class对象数组)
- 获取所有权限的构造方法数组
getDeclaredConstructors()
返回的是一个Constructor
类型的对象。
上文中带有Declared的方法也可以获取到不能通过当前权限获取的构造方法(例如private),但是默认状态下是不能运行的。Constructor
对象可以通过调用setAccessible(true)
方法忽略权限检查。这里就打破了类的封装性。本部分中所有的方法都可以这样子忽略权限检查。
利用构造器创建对象
newInstance(Object... para)
调用这个构造方法, 把对应的对象创建出来
参数: 是一个Object类型可变参数, 传递的参数顺序必须匹配构造方法中形式参数列表的顺序。
方法 Method
getMethod(String methodName , class.. clss)
根据参数列表的类型和方法名, 得到一个方法(public修饰的)getMethods()
得到一个类的所有方法 (public修饰的)getDeclaredMethod(String methodName , class.. clss)
根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认)getDeclaredMethods()
得到一个类的所有方法 (除继承以外所有的:包含私有, 共有, 保护, 默认)
返回的是一个Method
类型的对象。Method有如下方法:
invoke(Object o,Object... para)
调用方法
参数1. 要调用方法的对象
参数2. 要传递的参数列表
getName()
获取方法的方法名称
属性 Field
getDeclaredField(String filedName)
根据属性的名称, 获取一个属性对象 (所有属性)getDeclaredFields()
获取所有属性getField(String filedName)
根据属性的名称, 获取一个属性对象 (public属性)getFields()
获取所有属性 (public)
获取到的是Field
类型的属性对象。常用方法:
get(Object o)
参数: 要获取属性的对象 获取指定对象的此属性值set(Object o, Object value)
参数1. 要设置属性值的 对象
参数2. 要设置的值
设置指定对象的属性的值getName()
获取属性的名称
注解 Annotation
获取全部的注解对象,可用
Annotation[] annotations01 = Class/Field/Method.getAnnotations()
根据类型获取类/属性/方法的注解对象,可用
注解类型 对象名 = (注解类型) c.getAnnotation(注解类型.class)
内省
基于反射 , java所提供的一套应用到JavaBean的API
一个定义在包中的类
拥有无参构造器
所有属性私有
所有属性提供get/set方法
实现了序列化接口
这种类, 我们称其为bean类
Java提供了一套java.beans包的api , 对于反射的操作, 进行了封装。
Introspector
BeanInfo getBeanInfo(Class cls)
通过传入的类信息, 得到这个Bean类的封装对象
BeanInfo
MethodDescriptor[] getPropertyDescriptors()
获取bean类的 get/set方法 数组
MethodDescriptor
Method getReadMethod()
获取一个get方法
Method getWriteMethod()
获取一个set方法
有可能返回null