1、注解(Annotation)
1.1、何为注解?
注解(Annotation)是从JDK5.0开始引入的技术,以@注解名
在代码中存在,例如:
- @Override:限定重写父类方法,该注解只能用于方法
- @Deprecated:用于表示所修饰的元素(类,方法)已过时
- @SuppressWarnings:抑制编译器警告(镇压警告)
Annotation可以像修饰符一样被使用,可用于修饰包、类、构造器、成员变量、参数、局部变量的声明,还可添加一些参数值
1.2、注解与注释
注解也可以看做是一种注释,通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。但是,注解,不同于单行注释和多行注释
- 对于单行注释和多行注释是给程序员看的
- 而注解是可以被编译器或其他程序读取的。程序还可以根据注解的不同,做出相应的处理
10.3、注解的重要性
在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等
注解是大势所趋,框架 = 注解 + 反射 + 设计模式
10.4、自定义注解
public @interface MyAnnotation {
String value() default "hello world";
}
特殊属性
- value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写
- 但是如果有多个属性,且多个属性没有默认值,那么value名称是不能省略的
10.5、元注解
元注解:对现有的注解进行解释说明的注解
- Target:约束自定义注解只能在哪些地方使用
- Retention:申明注解的生命周期
- Documented:表示所修饰的注解在被javadoc解析时,保留下来
- Inherited:被它修饰的Annotation将具备继承性
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface MyAnnotation {
String value() default "hello world";
}
@Target中可使用的值定义在ElementType枚举类中,常用值如下:
- TYPE:类,接口
- FIELD:成员变量
- METHOD:成员方法
- PARAMETER:方法参数
- CONSTRUCTOR:构造器
- LOCAL_VARIABLE:局部变量
@Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下:
- SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在
- CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值
- RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
2、反射(reflection)
2.1、何为反射?
反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分
在运行时,可以直接得到这个类的构造器对象:Constructor
在运行时,可以直接得到这个类的成员变量对象:Field
在运行时,可以直接得到这个类的成员方法对象:Method
这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制
反射的关键:反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分
反射的作用:
- 获取一个类里面所有的信息,获取到了之后,再执行其他的业务逻辑
- 结合配置文件,动态的创建对象并调用其方法
2.2、获取class对象三种方式
public static void main(String[] args) throws ClassNotFoundException {
// 全类名 = 包名 + 类名
// 第一种方式:最为常用
Class clazz1 = Class.forName("com.zhang.Student");
System.out.println(clazz1);
// 第二种方式:一般当做参数传递
Class<Student> clazz2 = Student.class;
// 第三种方式:必须有了该类之后,才可获取使用
Student stu = new Student();
Class<? extends Student> clazz3 = stu.getClass();
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz2 == clazz3); // true
}
2.3、反射获取构造方法
获取构造器的方法
方法 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 返回所有构造器对象的数组(只能拿public的) |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造器对象的数组,存在就能拿到 |
Constructor<T> getConstructor(Class<?>… parameterTypes) | 返回单个构造器对象(只能拿public的) |
Constructor<T> getDeclaredConstructor(Class<?>… parameterTypes) | 返回单个构造器对象,存在就能拿到 |
Constructor类中用于创建对象的方法
符号 | 说明 |
---|---|
T newInstance(Object… initargs) | 根据指定的构造器创建对象 |
public void setAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 |
public static void main(String[] args) throws Exception {
// 获取字节码文件的对象
Class<?> clazz = Class.forName("com.zhang.Student");
// 获取所有公共的构造方法
/* Constructor<?>[] cons = clazz.getConstructors();
for (Constructor<?> con : cons) {
System.out.println(con);
} */
// 获取所有的构造方法(包括私有)
/* Constructor<?>[] dc = clazz.getDeclaredConstructors();
for (Constructor<?> d : dc) {
System.out.println(d);
} */
// 获取空参构造
Constructor con1 = clazz.getDeclaredConstructor();
System.out.println(con1);
// 获取一个参数的构造(参数类型是String的)
Constructor<?> con2 = clazz.getDeclaredConstructor(String.class);
System.out.println(con2);
// 获取二个参数的构造(参数类型是String、int的)
Constructor<?> con3 = clazz.getConstructor(String.class, int.class);
System.out.println(con3);
// 临时取消权限校验(暴力反射)
con2.setAccessible(true);
Student stu = (Student) con2.newInstance("小昭");
System.out.println(stu);
}
2.4、反射获取成员变量
public static void main(String[] args) throws Exception {
// 获取字节码文件的对象
Class<?> clazz = Class.forName("com.zhang.Student");
// 获取所有公共的成员变量
/* Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
} */
// 获取所有成员变量(包括私有)
/* Field[] dfs = clazz.getDeclaredFields();
for (Field field : dfs) {
System.out.println(field);
} */
// 获取指定的成员变量(公共的)
Field address = clazz.getField("address");
System.out.println(address);
// 获取指定的成员变量(公共私有)
Field name = clazz.getDeclaredField("name");
System.out.println(name.getName());
// 获取成员变量的类型
System.out.println(name.getType());
Student s1 = new Student("小黑", 22, "河南-郑州");
// 暴力反射
name.setAccessible(true);
// 获取成员变量的值
String value = (String) name.get(s1);
System.out.println(value);
// 修改对象中的值
name.set(s1, "小白");
System.out.println(s1);
}
2.5、反射获取成员方法
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.zhang.Student");
// 获取所有公共的成员方法(包括父类)
// Method[] methods = clazz.getMethods();
// for (Method method : methods) {
// System.out.println(method);
// }
// 获取所有成员方法(包括本类私有,不包括父类)
// Method[] dm = clazz.getDeclaredMethods();
// for (Method method : dm) {
// System.out.println(method);
// }
// 获取指定成员方法(公共的)
Method getName = clazz.getMethod("getName");
System.out.println(getName);
// 获取指定成员方法(公共私有)
Method hello = clazz.getDeclaredMethod("sayHello", String.class);
System.out.println(hello);
// 获取方法名
System.out.println(hello.getName()); // sayHello
// 获取方法参数个数
System.out.println(hello.getParameterCount()); // 1
// 获取方法抛出的异常
Class<?>[] ets = hello.getExceptionTypes();
for (Class<?> ex : ets) {
System.out.println(ex);
}
// 调用成员方法
// 暴力反射
hello.setAccessible(true);
Student s1 = new Student("晓琳", 22, "北京-海淀");
String res = (String) hello.invoke(s1, s1.getName());
System.out.println(res);
// 获取方法的返回值
System.out.println(hello.getReturnType()); // java.lang.String
}