反射

JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

框架:半成品软件,可以在框架的基础上进行开发,而框架本身不能运行

反射:反射是框架的灵魂。将类的各个组成部分封装为其他对象

反射的好处:可以在程序运行的过程中去操作对象、字节码文件,不需要重新编译。提高程序拓展性、复用性,解耦。

程序在计算机中的三个阶段

反射 - 图1

Class

Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。

  • 获得类的相关的方法 | 方法 | 用途 | | —- | —- | | asSubclass(Class clazz) | 把传递的类的对象转换成代表其子类的对象 | | getClassLoader() | 获得类的加载器 | | getClasses() | 返回一个数组,数组中包含该类中所有公共类和接口类的对象 | | getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象 | | forName(String className) | 根据类名返回类的对象 | | getName() | 获得类的完整路径名字 | | newInstance() | 创建类的实例 | | getPackage() | 获得类的包 | | getSimpleName() | 获得类的名字 | | getSuperclass() | 获得当前类继承的父类的名字 | | getInterfaces() | 获得当前类实现的类或是接口 | | .class | 获取当前对象的类 |
  • 获得类中字段相关的方法 | 方法 | 用途 | | —- | —- | | getField(String name) | 获得某个公有的字段对象 | | getFields() | 获得所有公有的字段对象 | | getDeclaredField(String name) | 获得某个字段对象 | | getDeclaredFields() | 获得所有字段对象 |
  • 获取类中构造器相关的方法 | 方法 | 用途 | | —- | —- | | getConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 | | getConstructors() | 获得该类的所有公有构造方法 | | getDeclaredConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法 | | getDeclaredConstructors() | 获得该类所有构造方法 |
  • 获得类中方法相关的方法 | 方法 | 用途 | | —- | —- | | getMethod(String nane,Class…<?> parameterTypes) | 获得该类某个公有的方法 | | gstMethods() | 获得该类所有公有的方法 | | getDeclaredMethod(String nane,Class…<?> parameterTypes) | 获得该类某个方法 | | getDeclaredMethods() | 获得该类所有方法 |
  • 类中其他重要的方法 | 方法 | 用途 | | —- | —- | | isAnnotation() | 如果是注解类型则返回true | | isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定类型注解类型则返回true | | isArray() | 如果是一个数组类则返回true | | isEnum() | 如果是枚举类则返回true | | isInstance(Object obj) | 如果obj是该类的实例则返回true | | isInstance() | 如果是接口类则返回true |

Field

Field代表类的成员变量。成员变量(字段)和成员属性是两个概念。比如:当一个User类中有一个name变量,那么这个时候我们就说它有name这个字段。但是如果没有getName和setName这两个方法,那么这个类就没有name属性。反之,如果这个类拥有getAge和 setAge这两个方法,不管有没有age字段,我们都认为它有age这个属性

方法 用途
get(Object obj) 获得obj中对应的属性值
set(Object obj,Object value) 设置obj中对应属性值
SetAccessible 暴力反射,忽略访问权限修饰符

Method

Method代表类的方法(不包括构造方法)。

方法 用途
invoke(Object obj,Object… args) 传递object对象及参数调用该对象对应的方法
getName 获取方法名
SetAccessible 暴力反射,忽略访问权限修饰符

Invoke方法的用处:SpringAOP在切面方法执行的前后进行某些操作,就是使用的invoke方法。

Constructor(用的比较少)

Constructor代表类的构造方法。

方法 用途
newInstance(Object… initargs) 根据传递的参数创建类的对象

Constructor 类在实际开发中使用极少,几乎不会使用Constructor。因为:Constructor违背了Java的一些思想,比如:私有构造不让用户去new对象;单例模式保证全局只有一个该类的实例。而Constructor则可以破坏这个规则

案例:写一个小工具,可以帮我们创建任意类的对象,并且执行其中任意方法案例:通过反射,验证运行中泛型会被擦除

  1. List<User> userList = new Arraylist<>();
  2. userList.add(new User(name:"张三", age:23, idNumber:"233"));
  3. userList.add(new User(name:"李四", age:24, idNumber:"666"));
  4. Class<? extends List> listClass = userlist.getClass();
  5. Method addMethod = listClass.getDeclaredMethod(name:"add", Object.class);
  6. addMethod.invoke(userList,123);
  7. addMethod.invoke(userList,"哈哈哈");
  8. System.out.print1n(userList);

注解

注释:用文字描述程序,给程序员看的。

定义:用来说明程序的一个标识,给计算机看的。注解也叫元数据,是一种代码级别的说明,它是jdk1.5之后引入的一个特性,是一种特殊的接口,可以使用在字段、类、方法、包、参数等上面。
注意:注解本身并没有任何功能,仅仅起到一个标识性的作用。我们是通过反射去获取到注解,再根据是否有这个注解、注解中的一些属性去判断,执行哪种业务逻辑。

作用分类:
编写文档:通过代码里的注解标识去生成API文档(Swagger)

  1. 代码分析:通过注解去对代码进行逻辑上的分析(通过反射去操作业务)
  2. 编译检查:通过注解让编译器实现基本的编译检查(OverrideSuppressWarming)<br />JDK中预定义的一些注解
  3. Overide:检测该注解标识的方法是否是继承自父类(接口)
  4. Deprecated:标识该内容已过时,后续的版本可能会进行移除。<br /> SuppressWarmings;压制警告,指定警告级别,这个级别下的警告全部不显示

自定义注解

在实际开发中,可能存在一些代码极其复杂或者复用性很低的业务逻辑,比如:导出Excel、缓存、将返回值转JSON、事务等等等等,这个时候就可以使用自定义注解

格式

  1. 元注解
  2. public @interface 注解名称{
  3. 属性列表;
  4. }

本质:本质就是个接口,该接口继承Annotation接口

属性

本质:接口中的抽象方法
属性只能是以下几种数据类型:

  1. 基本数据类型
  2. String
  3. 枚举
  4. 注解
  5. 以上类型的数组

如果定义了属性,在使用的时候需要给属性赋值。如果有默认值则可以不赋值
如果只有一个属性需要赋值,并且这个属性叫value,那么就可以省略属性名
数组赋值时,值用{}包裹,如果数组中只有一个值,那么大括号可以省略。

元注解

用于描述注解的注解
@Target:描述该注解作用范围
ElementType取值:

  1. Type:作用于类<br /> METHOD:作用于方法<br /> FIELD:作用于字段<br />@Retention:描述注解被保留的阶段<br /> RetentionPolicy.RUNTIME:当前描述的注解,会保留到class字节码文件中,并被vm读取到<br />@Documented:描述注解是否被抽取到api文档中<br />@Inherited:描述注解是否可以被继承

总结:

反射:就是在程序运行中对Class、对象等进行一些列的操作注解:注解就是个标识,相当于是给程序看的注释。注解本身不存在功能,需要通过反射去进行某些判断,根据判断结果去执行对应的逻辑,这个过程就是给注解赋予功能的过程。

反射和自定义注解,在我们刚入职的时候,是用不到的,因为刚入职那段时间一般只会写crud。但是,反射和自定义注解是框架的基础,写框架、看源码则是架构师的基础。