反射
反射概述
反射,框架设计的灵魂所在
我们知道,Java代码在计算机会经过三个阶段:源代码、字节码、运行
其中,在编译对象之前,我们的Java程序需要经过一个叫做Class类对象的阶段,在这个阶段中,变量被封装为Field[]
,构造方法被封装为Constructor[]
,成员方法被封装为Method[]
也就是在字节码中,我们可以看到Filed[]
、Constructor[]
、Method[]
反射基本概念
反射获取Class对象有几种方式:
1、Class.forName("全类名")
:将字节码文件加载到内存,存到Class对象
2、类名.class
:通过类名的属性class获取
3、对象.getClass()
:Object中存在这个方法,所以这个方法在任何的对象中都能使用
Class对象有如下功能:
1、获取成员变量
// 获取所有public修饰的成员变量
demoClass.getFields();
// 获取指定名称修饰的public成员变量
demoClass.getField("");
// 获取所有的成员变量,包括私有的
demoClass.getDeclaredFields();
// 获取指定的成员变量,包括私有的
demoClass.getDeclaredField("");
2、获取构造方法
// 获取public构造方法们
demoClass.getConstructors();
// 获得有指定类型参数的public构造方法
demoClass.getConstructor(String.class);
// 获得构造方法们
demoClass.getDeclaredConstructors();
// 获得指定参数的构造方法
demoClass.getDeclaredConstructor(String.class);
3、获取成员方法
// 获取全部public方法
demoClass.getMethods();
// 获取指定名称,并且参数匹配的public方法,参数可以不写,可以写多个
demoClass.getMethod("",String.class);
// 获取全部方法
demoClass.getDeclaredMethods();
// 获取指定名称,并且参数匹配的方法,参数可以不写,可以写多个
demoClass.getDeclaredMethod("",String.class);
4、获取全类名
demoClass.getName()
Field有如下功能:
1、获取和设置值
// 获取值
Object o = fields[0].get(Object o);
// 设置值
fields[0].set(Object o, Object o1);
2、忽略访问权限修饰符的安全检查
// 这种形式我们称为暴力反射
fields[0].setAccessible(true);
Constructor有如下功能:
1、创建对象
Class<DynamicLinkDemo> demoClass = DynamicLinkDemo.class;
Constructor<DynamicLinkDemo> demoConstructor = demoClass.getConstructor(String.class);
// 创建对象。使用空参构造可以不用传递参数
DynamicLinkDemo demo = demoClass.newInstance("");
Method有如下功能:
1、获取方法名称
Class<DynamicLinkDemo> demoClass = DynamicLinkDemo.class;
Method method = demoClass.getMethod("A");
// 获取名称
String name = method.getName();
2、执行方法
Class<DynamicLinkDemo> demoClass = DynamicLinkDemo.class;
DynamicLinkDemo newInstance = demoClass.getConstructor().newInstance();
Method method = demoClass.getMethod("A");
// 执行方法,给出对象和对应的方法
method.invoke(newInstance,method);
注解
注解概述
注解:Annotation:也就是说给计算机看的,使用的时候使用@注解名称
它在JDK5引入的一个新的特性,和类、接口、枚举在一个层面上。它可以声明在任意的类、方法、变量上,用来对这些元素进行说明和注释
注解可以通过反射读取到,那么这样就可以在调用方法之前或之后,进行方法的增强
注解基本概念
JDK中有一些预定义的注解:
1、@Override
:检测被该注解标注的方法是否是继承自父类接口的,当然不加也没有关系,这个注解主要是检查是否合乎规范的
2、Deprecate
:表示当前的内容已经过时
3、@SuppressWarnings
:压制警告
这个注解假如加到类上的时候,说明要压制类上的警告,方法同理 这个需要传递参数,一般传递
all
,这样全部的警告都不会出现:@SuppressWarning("all")
自定义注解
自定义注解的格式分两种:
1、元注解
2、public @interface 注解名称
先说我们一般的注解
它要使用@
来声明,声明的时候使用@interface
,注意不是大写I
注解类里面的抽象方法需要几个要求:
1、返回值有如下取值:
- 基本数据类型
- String
- 枚举
- 其他注解
- 以上几个类型的数组
2、使用的时候,需要赋值
假如我们在定义抽象方法的时候,假如使用
default
关键字给一个默认的初始化值,那么则使用注解的时候,可以不进行赋值,否则必须赋值 假如只有一个需要赋值,并且属性的名字是value,那么value可以省略,直接定义值即可 数组赋值时,需要使用{}
包裹,假如数组中只有一个值,那么{}
可以省略
光说不练假把式,上代码:
package com.howling;
public @interface AnnotationDemo {
public String name() default "Jerry";
public int age() default 22;
int[] value();
}
@AnnotationDemo(name = "Tom", age = 12, value = {23, 21})
String demo;
@AnnotationDemo(name = "Jack", value = 21)
String demo2;
@AnnotationDemo(21)
String demo3;
demo中,可以看到标准的赋值 demo2中,可以看到age因为有一个
default
,所以省略了,那么省略之后,默认的值就是22。value假如设置一个值,就不需要{}
demo3中,可以看到name也省略了,然而因为是value所以可以省略
元注解
元注解,描述注解的注解。这样说话可能有点绕,但是它确实用于确立注解的规则
1、@Target
:描述注解可以作用的位置,使用ElementType取值
- TYPE:类上
- METHOD:方法上
- FIELD:成员变量上
2、@Retention
:描述注解被保留的阶段,一般我们使用RUNTIME,这样就会保留到字节码阶段,可以被JVM读取到
3、@Documented
:描述注解是否可以被抽取到API中
4、@Inherited
:描述注解是否可以被子类继承,只要有一个父类加上这个注解,那么它的子类全部都会继承这个注解
package com.howling;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface AnnotationDemo {
public String name() default "Jerry";
public int age() default 22;
int[] value();
}
反射获取注解
使用getAnnotation(Class)
可以获取指定的注解
package com.howling.dynamiclink;
import com.howling.AnnotationDemo;
@AnnotationDemo(12)
public class DynamicLinkDemo {
public static void main(String[] args) {
Class<DynamicLinkDemo> demoClass = DynamicLinkDemo.class;
AnnotationDemo annotation = demoClass.getAnnotation(AnnotationDemo.class);
if (annotation == null) {
System.out.println("null");
} else {
System.out.println("not null");
}
}
}