注解与注释有异曲同工之妙,注释便于操作者理解代码,而注解是在此之上给Java代码加标识,同时传输参数,这样的设定就可以让其他的类通过反射机制获取到该类的注解及其信息,并对该类进行某些操作。
这便是框架的实现概述,换句话说框架的核心就是注解,框架通过注解对某块代码实现框架的附加功能。
注解可以分为元注解和自定义注解(JDK或框架自带注解和纯自定义注解)
元注解是用于描述自定义注解的注解。
自定义注解是用于描述其他代码的注解。
关键字 | 含义 |
---|---|
@Target | 描述该注解应用的范围 |
@Retention | 描述该注解生效周期 |
@Documented | 在JavaDoc文档中能否显示该注解 |
@Inherited | 子类是否可以继承超类的注解 |
@Target使用范围
关键字 | 范围 |
---|---|
TYPE | 类、接口(包括注解类型)或枚举声明 |
FIELD | 字段声明(包括枚举常量) |
METHOD | 方法声明 |
PARAMETER | 形参声明 |
CONSTRUCTOR | 构造函数声明 |
LOCAL_VARIABLE | 局部变量声明 |
ANNOTATION_TYPE | 注解类型声明 |
PACKAGE | 包装声明 |
TYPE_PARAMETER | 类型参数声明 |
TYPE_USE | 类型的使用 |
MODULE | 模块声明 |
@Retention使用范围
关键字 | 范围 |
---|---|
SOURCE | 编译器将丢弃注解 |
CLASS | 注解将由编译器记录在类文件中,但在运行时不需要由 VM 保留。 这是默认行为 |
RUNTIME | 注解将被编译器记录在类文件中,并在运行时由 VM 保留,因此它们可以被反射读取 |
创建自定义注解格式
权限 @interface 注解名 { 参数类型 参数名() default 默认值 }
自定义注解实例
@Target(value = ElementType.CONSTRUCTOR) @Retention(value = RetentionPolicy.RUNTIME) @Documented @Inherited public @interface FieldParameter { int id() default 0; String name() default " "; int age() default 0; }
该部分涉及一些JVM底层原理,若只是想知道反射如何使用则不必观看此位置。
Java面向对象的核心就是类,而类的加载关联着反射机制。所以为更好理解反射机制,应该先把类的底层原理有大概的理解。在Java体系中类生成两种文件:.java和.class,.java为源码文件给开发者使用,.class为字节码文件给JVM使用,也就是说.java和.class文件是一对一的关系。
Java使用内存的三个区域
将class字节码文件加载到内存中,并将这些数据转换成方法区中的运行时数据(静态变量、静态代码块、常量池等),在堆中生成一个Class类对象代表这个类(反射原理),作为方法区类数据的访问入口,某类的Class对象只可以有一个。
将Java类的二进制代码合并到JVM的运行状态之中。
初始化阶段是执行类构造器()方法的过程。类构造器()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的。
初始化顺序
先父后子、先静后非静、先变量、代码块后构造。
初始化引用方式
主动引用(一定会初始化)
被动引用
标准的Java SE类加载器可以按要求查找类,一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过,JVM垃圾收集器可以回收这些Class对象。
引导类加载器(bootstrap class loader)
扩展类加载器(extensions class loader)
应用程序类加载器(application class loader)
自定义类加载器
java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个Java类,即java.lang.Class类的一个实例。ClassLoader还负责加载 Java 应用所需的资源,如图像文件和配置文件等。
常用方法
方法名 | 含义 |
---|---|
getParent() | 返回该类加载器的父类加载器 |
loadClass(String name) | 加载名称为 name的类,返回的结果是java.lang.Class类的实例 |
findClass(String name) | 查找名称为 name的类,返回的结果是java.lang.Class类的实例 |
findLoadedClass(String name) | 查找名称为 name的已经被加载过的类,返回的结果是 java.lang.Class类的实例 |
defineClass(String name, byte[] b, int off, int len) | 把字节数组 b中的内容转换成 Java 类,返回的结果是java.lang.Class类的实例。这个方法被声明为 final的 |
resolveClass(Class<?> c) | 链接指定的 Java 类。 |
Java被称为静态语言,因为在运行前所有数据都被固定下来。但由于反射的引入,Java被赋予了一定的动态性,利用反射机制可以在运行期获取类的所有信息并可以直接操作其内部属性和方法。
反射的核心是Class对象,就是先前介绍的每个类对应的唯一一个Class对象,作为调用数据的接口。
普通流程与反射流程对比
获得Class对象方法
通过Class对象可以反射的东西
类型 | 方法 |
---|---|
Field字段 | getField、getFields、getDeclaredField、getDeclaredFields |
Method方法 | getMethod、getMethods、getDeclaredMethod、getDeclaredMethods |
Constructor构造器 | getConstructor、getConstructors、getDeclaredConstructor、getDeclaredConstructors |
Superclass父类Class对象 | getSuperclass |
Interface实现接口 | getInterfaces |
Annotation注解 | getAnnotation、getAnnotations、getDeclaredAnnotation、getDeclaredAnnotations、getAnnotationsByType |
获取这些类型的方法中含有Declared表示获取全部(忽视权限),没有表示仅获取Public权限的。
//元注解描述 @Target(ElementType.CONSTRUCTOR) @Retention(RetentionPolicy.RUNTIME) @interface ConstructorAnnotation { //参数 String value() default "I am ConstructorAnnotation"; } //元注解描述 @Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @interface FieldMethodParameter { //参数 String describe(); Class type() default void.class; } public class Record { //字段注解并传递参数 @FieldMethodParameter(describe = "成员变量id", type = int.class) int id; @FieldMethodParameter(describe = "成员变量name", type = String.class) String name; //无参构造器加注解 @ConstructorAnnotation public Record() { } //有参构造器加注解传参数 @ConstructorAnnotation(value = "有参构造器") public Record( @FieldMethodParameter(describe = "接收参数id", type = int.class) int id, @FieldMethodParameter(describe = "接收参数name", type = String.class) String name ) { this.id = id; this.name = name; } //方法注解并传递参数,下方都一样 @FieldMethodParameter(describe = "获取成员变量id", type = int.class) public int getId() { return id; } @FieldMethodParameter(describe = "设置成员变量id") public void setId(@FieldMethodParameter(describe = "接收参数id", type = int.class) int id) { this.id = id; } @FieldMethodParameter(describe = "获取成员变量name", type = String.class) public String getName() { return name; } @FieldMethodParameter(describe = "设置成员变量name") public void setName(@FieldMethodParameter(describe = "接收参数name", type = String.class) String name) { this.name = name; } public static void main(String[] args) { //新建实例对象 Record record = new Record(); //getClass()方法获得Record类的Class对象 Class<? extends Record> recordClass = record.getClass(); //得到所有构造器 Constructor<?>[] constructors = recordClass.getDeclaredConstructors(); //循环判断是否实现ConstructorAnnotation.class的注解 for (Constructor constructor : constructors) { if (constructor.isAnnotationPresent(ConstructorAnnotation.class)) { //判断为真,得到该类型注解并强转类型 ConstructorAnnotation annotation = (ConstructorAnnotation) constructor.getAnnotation(ConstructorAnnotation.class); System.out.println(annotation.value()); //获取接收参数的注解,得到一个二维数组,该二维数据每行存储一个接收参数和其注解 Annotation[][] parameterAnnotations = constructor.getParameterAnnotations(); for (int j = 0; j < parameterAnnotations.length; j++) { //判断该行参数是否有注解 int length = parameterAnnotations[j].length; if (length == 0) { System.out.println("该构造器接收参数无注解"); } else { for (int k = 0; k < length; k++) { //获取每个注解并输出 FieldMethodParameter pa = (FieldMethodParameter) parameterAnnotations[j][k]; System.out.println(pa.describe()); System.out.println(pa.type()); } } } } } System.out.println("--------------------------------------------------------------"); //得到所有成员变量 Field[] declaredFields = recordClass.getDeclaredFields(); for (Field field : declaredFields) { //循环判断是否实现FieldMethodParameter.class的注解 if (field.isAnnotationPresent(FieldMethodParameter.class)) { //判断为真,得到该类型注解并强转类型输出参数 FieldMethodParameter annotation = field.getAnnotation(FieldMethodParameter.class); System.out.println(annotation.describe()); System.out.println(annotation.type()); } } System.out.println("--------------------------------------------------------------"); //得到所有方法 Method[] declaredMethods = recordClass.getDeclaredMethods(); for (Method method : declaredMethods) { //循环判断是否实现FieldMethodParameter.class的注解 if (method.isAnnotationPresent(FieldMethodParameter.class)) { //判断为真,得到该类型注解并强转类型输出参数 FieldMethodParameter annotation = method.getAnnotation(FieldMethodParameter.class); System.out.println(annotation.describe()); System.out.println(annotation.type()); } //获取接收参数的注解,得到一个二维数组,该二维数据每行存储一个接收参数和其注解 Annotation[][] parameterAnnotations = method.getParameterAnnotations(); for (int j = 0; j < parameterAnnotations.length; j++) { //判断该行参数是否有注解 int length = parameterAnnotations[j].length; if (length == 0) { System.out.println("方法接收参数无注解"); System.out.println(method.getName()); } else { for (int k = 0; k < length; k++) { //获取每个注解并输出 FieldMethodParameter fieldMethodParameter = (FieldMethodParameter) parameterAnnotations[j][k]; System.out.println(fieldMethodParameter.describe()); System.out.println(fieldMethodParameter.type()); } } } } } }
通过反射创建对象、修改字段变量、执行方法
创建对象
执行方法
设置字段
@Target(value = ElementType.CONSTRUCTOR) @Retention(value = RetentionPolicy.RUNTIME) @Documented @Inherited @interface FieldParameter { int id() default 0; String name() default " "; int age() default 0; } public class Table { private int id; private String name; private int age; public Table() { } @FieldParameter(id = 001, name = "赵四", age = 18) public Table(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void helloT1() { System.out.println("Hello I Am Table1"); } private void helloT2() { System.out.println("Hello I Am Table2"); } @Override public String toString() { return "Table{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } public static void main(String[] args) throws Exception { //利用 Table.class对象的newInstance()和构造器的newInstance()方法进行创建对象 Table t1 = null; Table t2 = Table.class.newInstance(); //获取所有构造器 Constructor<?>[] declaredConstructors = Table.class.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { //判断是否实现FieldParameter.class注解 if (declaredConstructor.isAnnotationPresent(FieldParameter.class)) { FieldParameter annotation1 = (FieldParameter) declaredConstructor.getAnnotation(FieldParameter.class); //获取FieldParameter注解传递的参数进行创建对象 t1 = (Table) declaredConstructor.newInstance(annotation1.id(), annotation1.name(), annotation1.age()); } } //获取所有方法 Method[] methods = Table.class.getDeclaredMethods(); for (Method method : methods) { //找到名为helloT1和helloT2的方法 if (method.getName().equals("helloT1")) { //执行invoke(具有该方法的实例对象,接收参数) method.invoke(t1, null); } else if (method.getName().equals("helloT2")) { method.invoke(t2, null); } } //获取所有字段变量 Field[] declaredFields = Table.class.getDeclaredFields(); for (Field declaredField : declaredFields) { //判断字段类型并分类型赋值 if (declaredField.getGenericType().getTypeName().equals("int")) { //判断字段名称分别赋值 if (declaredField.getName().equals("id")) //调用set(具有该字段的实例对象, 字段值) declaredField.set(t2, 2); if (declaredField.getName().equals("age")) declaredField.set(t2, 12); } else if (declaredField.getGenericType().getTypeName().equals("java.lang.String")) declaredField.set(t2, "王五"); } System.out.println(t1); System.out.println(t2); } }
注解是学习框架的核心,它可以配合反射完成系列任务。而反射是实现Java动态的核心,可以实现运行期改变数据、调用方法等。