反射

JAVA反射机制是在运行状态中,获取任意一个类的结构 , 创建对象 , 得到方法,执行方法 , 属性。

这种在运行状态动态获取信息以及动态调用对象方法的功能被称为java语言的反射机制。

 

使用场景

当我们知道要使用的类是什么的时候可以直接new出来,但是不知道的时候呢?这时候就要用到反射了。

反射,即在运行时才知道要使用的类是什么,并且要获取到类的结构、属性,调用类的方法。

例如:

类加载器:运行时需要使用某个类就加载某个类

此处简单介绍一下类加载器

类加载器

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

双亲委派模型:类加载的方式

如果一个类加载器收到了一个类加载请求,它不会自己去尝试加载这个类,而是把这个请求转交给父类加载器去完成。每一个层次的类加载器都是如此。因此所有的类加载请求都应该传递到最顶层的启动类加载器中,只有到父类加载器反馈自己无法完成这个加载请求(在它的搜索范围没有找到这个类) 时,子类加载器才会尝试自己去加载。委派的好处就是避免有些类被重复加载。

 

使用

使用反射获取对象的步骤:

1、获取类的 Class 对象实例

  要想了解一个类,必须先要获取到该类的字节码文件对象.

  在Java中,每一个字节码文件,被加载到内存后,都存在一个对应的Class类型的对象。(一个class文件 在内存中不会存在两个类对象 )

  获取Class对象:

    • 如果在编写代码时, 知道类的名称, 且类已经存在, 可以通过 包名.类名.class 得到一个类的类对象
    • 如果拥有类的对象, 可以通过 Class 对象.getClass() 得到一个类的类对象
    • 如果在编写代码时, 知道类的名称 , 可以通过 Class.forName(包名+类名): 得到一个类的 类对象

    上述的三种方式, 在调用时, 如果类在内存中不存在, 则会加载到内存 ! 如果类已经在内存中存在, 不会重复加载, 而是重复利用 !

  特殊的类对象

    基本数据类型的类对象:

    • 基本数据类型.clss
    • 包装类.type

    基本数据类型包装类对象:

    • 包装类.class

2、根据 Class 对象实例获取 Constructor 对象

  通过指定的参数类型, 获取指定的单个构造方法:getConstructor(参数类型的class对象数组)

  :     构造方法: Book(String name,int price)

     得到这个构造方法的代码: Constructor c = b.getClass().getConstructor(String.class,int.class);

  获取构造方法数:    getConstructors();

  获取所有权限的单个构造方法:    getDeclaredConstructor(参数类型的class对象数组)

  获取所有权限的构造方法数组:    getDeclaredConstructors();

3、使用 Constructor 对象的 newInstance 方法获取反射类对象

newInstance(Object... para) 
// 参数: 是一个Object类型可变参数, 传递的参数顺序 必须匹配构造方法中形式参数列表的顺序
setAccessible(boolean flag)
// 如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的方法)

4、通过Class对象获取类的一个Method,并执行

//根据参数列表的类型和方法名, 得到一个方法(public修饰的)
getMethod(String methodName , class.. clss)

// 得到一个类的所有方法 (public修饰的)
getMethods();

// 根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认)
getDeclaredMethod(String methodName , class.. clss)

//得到一个类的所有方法 (除继承以外所有的:包含私有, 共有, 保护, 默认)
getDeclaredMethods();


// 执行:   
//参数1. 要调用方法的对象   参数2. 要传递的参数列表
invoke(Object o,Object... para) 

// 获取方法的方法名称
getName()

// 如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的方法)
setAccessible(boolean flag)

5、通过Class对象获取类的属性(Field)

// 根据属性的名称, 获取一个属性对象 (所有属性)
getDeclaredField(String filedName)

// 获取所有属性
getDeclaredFields()

// 根据属性的名称, 获取一个属性对象 (public属性)
getField(String filedName)

//获取所有属性 (public)
getFields()


// Field 方法
// 参数: 要获取属性的对象
// 获取指定对象的此属性值
get(Object o );

// 参数1. 要设置属性值的 对象
// 参数2. 要设置的值
// 设置指定对象的属性的值
set(Object o , Object value);

// 获取属性的名称
getName()

// 如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的属性)
setAccessible(boolean flag)

6、获取注解信息

// 获取类/属性/方法的全部注解对象 
Annotation[] annotations1 = Class/Field/Method.getAnnotations();
for (Annotation annotation : annotations1) {
    System.out.println(annotation);
}

// 根据类型获取类/属性/方法的注解对象
注解类型 对象名 = (注解类型) c.getAnnotation(注解类型.class);

 

内省(Introspector) 

基于反射 , java所提供的(java.beans包下)一套应用到JavaBean的API,其中封装了对反射的操作。

bean类:

  •   一个定义在包中的类
  •   拥有无参构造器
  •   所有属性私有,
  •   所有属性提供get/set方法
  •   实现了序列化接口

方法

变量和类型方法描述
static String decapitalize​(String name)
获取字符串并将其转换为普通Java变量名称大小写的实用方法。
static void flushCaches()
刷新所有Introspector的内部缓存。
static void flushFromCaches​(<?> clz)
刷新Introspector的给定类的内部缓存信息。
static BeanInfo getBeanInfo​(<?> beanClass)
对Java Bean进行内省并了解其所有属性,公开方法和事件。
static BeanInfo getBeanInfo​(<?> beanClass, int flags)
对Java bean进行内省并了解其所有属性,公开方法和事件,并遵守一些控制标志。
static BeanInfo getBeanInfo​(<?> beanClass, <?> stopClass)
对Java bean进行内省,并在给定的“停止”点下了解其属性,公开方法。
static BeanInfo getBeanInfo​(<?> beanClass, <?> stopClass, int flags)
对Java Bean进行内省并了解其所有属性,公开方法和事件,低于给定的 stopClass点,受某些控制 flags
static String[] getBeanInfoSearchPath()
获取将用于查找BeanInfo类的包名称列表。
static void setBeanInfoSearchPath​(String[] path)
更改将用于查找BeanInfo类的包名称列表。 

使用内省获取Bean的封装对象BeanInfo

BeanInfo方法:

变量和类型方法描述
BeanInfo[] getAdditionalBeanInfo()
此方法使当前 BeanInfo对象能够返回其他 BeanInfo对象的任意集合, BeanInfo对象提供有关当前Bean的其他信息。
BeanDescriptor getBeanDescriptor()
返回提供有关bean的整体信息的bean描述符,例如其显示名称或其自定义程序。
int getDefaultEventIndex()
当使用此bean时,bean可能具有通常应用的默认事件。
int getDefaultPropertyIndex()
当自定义此bean时,bean可能具有通常更新的默认属性。
EventSetDescriptor[] getEventSetDescriptors()
返回bean的事件描述符,用于定义此bean触发的事件类型。
Image getIcon​(int iconKind)
返回可用于表示工具箱或工具栏中的bean的图像。
MethodDescriptor[] getMethodDescriptors()
返回bean的方法描述符,用于定义此bean支持的外部可见方法。
PropertyDescriptor[] getPropertyDescriptors()
返回bean的所有属性的描述符。 

getPropertyDescriptors(): 获取bean类的 get/set方法 数组

  Method getReadMethod(); 获取一个get方法

  Method getWriteMethod(); 获取一个set方法

  有可能返回null 注意 ,加判断

PropertyDescriptor方法:

变量和类型方法描述
PropertyEditor createPropertyEditor​(Object bean)
使用当前属性编辑器类构造属性编辑器的实例。
boolean equals​(Object obj)
将此 PropertyDescriptor与指定的对象进行比较。
<?> getPropertyEditorClass()
获取已为此属性注册的任何显式PropertyEditor类。
<?> getPropertyType()
返回属性的Java类型信息。
方法 getReadMethod()
获取应该用于读取属性值的方法。
方法 getWriteMethod()
获取应该用于写入属性值的方法。
int hashCode()
返回对象的哈希码值。
boolean isBound()
对“绑定”属性的更新将导致在更改属性时触发“PropertyChange”事件。
boolean isConstrained()
尝试更新“Constrained”属性将导致在更改属性时触发“VetoableChange”事件。
void setBound​(boolean bound)
对“绑定”属性的更新将导致在更改属性时触发“PropertyChange”事件。
void setConstrained​(boolean constrained)
尝试更新“Constrained”属性将导致在更改属性时触发“VetoableChange”事件。
void setPropertyEditorClass​(<?> propertyEditorClass)
通常使用PropertyEditorManager找到PropertyEditors。
void setReadMethod​(方法 readMethod)
设置应该用于读取属性值的方法。
void setWriteMethod​(方法 writeMethod)
设置应该用于写入属性值的方法。 
 posted on 2021-04-05 17:21  一计  阅读(78)  评论(0)    收藏  举报