Java 高级特性之注解+反射
一、注解
1.什么是 注解
Annotation 是从JDK5.0开始引入的新技术
Annotation的作用:
- 注解不是程序的本身,可以对程序作出解析,这点和注释没什么区别
- 可以被其他程序读取,这一特性特别作用,因为我们通过反射的技术获取到注解里的相关信息,然后进行相关操作,如我们常常自定义的校验规则,api接口的权限值等等都可以通过注解实现。
annotation 的格式: @注解名 ,如:@SuppressWarnings(value=”unchecked”);注解的定义:@interface 注解名{}
Annotation使用的地方: 可以使用在 PACKAGE ,class.method,field等上面,相当于给他们添加了二外的辅助信息,我们可以通过 反射机制 对这些元数据的访问。
2.内置注解
在Java中有许多已经内置好的注解供我们使用,如@Override,@Deprecated,@SuppressWainings等等
3.元注解
元注解的作用就是负责注解其他注解,也就是对其他一般的注解的一种说明。Java提供4个元注解供使用 。
@Target: 用于描述注解的使用范围(即被描述的注解用在什么地方)。
类型 | 描述 |
TYPE | 作用范围:类 |
FIELD | 作用范围:变量字段 |
METHOD | 作用范围:方法 |
PARAMETER | 作用范围:参数 |
Constructor | 作用范围: 构造器 |
LOCAL_VARIABLE | 作用范围:本地变量 |
ANNOTATION_ Type | 作用范围:注解 |
PACKAGE | 作用范围:包 |
TYPE_PARAMETER | 作用范围:任何声明类型的地方 |
TYPE_USE | 作用范围:任意使用类型的地方 |
@Retention: 表示需要在什么级别保存该注解信息,用于描述注解的 生命周期 (SOURCE<CLASS<RUNTIME)
类型 | 描述 |
SOURCE | 生命周期只保留在源码 |
CLASS | 生命周期保留到.class编译阶段 |
RUNTIME | 生命周期保留到 jvm 运行阶段 |
@Document: 说明该注解将被包换在javadoc中
@Inherited: 说明子类可以继承父类中的注解
4.自定义注解
使用@Interface自定义注解时,自动继承了 java.lang .annotation接口
- @Interface用来声明一个注解,格式:public @ interface 注解名 {定义内容}
- 其中的每个方法实际上是声明了一个配置参数,方法的名称就是参数的名称
- 返回值类型就是参数的类型(返回值只能是基本类型、Class、 String 、 enum )
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0来作为默认值
package com.xiaoxian.missyou.annotion;
import java.lang.annotation.*;
@MyAnnotion(num = 0,friends = {"123","456"})
public class AnnotionTest {
}
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface MyAnnotion{
String name() default "";
int num();
String[] friends() default {"123","456"};
}
二、反射
1.什么是反射
Reflection (反射) 是Java被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类得 内部信息 ,并能直接操作任意对象的内部属性及方法。
类加载完后,在堆内存的方法区中就产生一个 Class类型的对象 (一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。
正常方式 :引入包名 —> new实例化 —-> 取得实例化对象
反射方式 :实例化对象 —–> getClass()方法 ——> 得到完整的包名
2.反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取 泛型 信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
3.反射的优缺点
优点: 可以实现动态创建对象和编译,体现出很大的灵活性。
缺点: 对性能有影响,这类操作总是慢于直接执行相同的操作。通过invoke()调用方法的时候,可以先使用setAccessible(true)方法,取消java语言访问检查,可以提高一点点性能。
4.通过反射获取class对象
package com.xiaoxian.missyou.inject;
import lombok.Data;
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> c1 = Class.forName("com.xiaoxian.missyou.inject.Student");
Class<?> c2 = Class.forName("com.xiaoxian.missyou.inject.Student");
//获取的classd对象都是同一个对象,因为只有一个class对象
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
Person person = new Student();
//方式一:通过对象获取class对象
Class c3 = person.getClass();
System.out.println(c3.hashCode());
//方式二:forname()获取class对象
Class c4 = Class.forName("com.xiaoxian.missyou.inject.Student");
System.out.println(c4.hashCode());
//方式三:通过类名.class获取class对象
Class c5 = Student.class;
System.out.println(c5.hashCode());
//方式四:基本类型的包装类可以通过Type属性获取class对象
Class c6 = Integer.TYPE;
System.out.println(c6.hashCode());
//获取父类的class对象
Class c7 = c3.getSuperclass();
System.out.println(c7.hashCode());
}
}
@Data
class Person {
private String name;
}
@Data
class Student extends Person{
private Integer age;
}
5.哪些类型可以有Class对象
- class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface: 接口
- []: 数组
- enum: 枚举
- annotation: 注解@interface
- primitive type: 基本数据类型
- void
package com.xiaoxian.missyou.inject;
import java.lang.annotation.ElementType;
import java.util.Map;
public class Test2 {
public static void main(String[] args) {
Class c1 = Object.class;//类
Class c2 = Map.class;//接口
Class c3 = String[].class;//一维数组
Class c4 = String[][].class;// 二维数组
Class c5 = Override.class;//注解
Class c6 = ElementType.class;//枚举
Class c7 = Integer.class;//基本数据类型
Class c8 = void.class;//void
Class c9 = Class.class;//class
}
}
6.获取运行时类的完整结构
package com.xiaoxian.missyou.inject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test3 {
public static void main(String[] args) throws NoSuchField Exception , NoSuchMethodException {
Class c1 = Student.class;
//获取类的名字
System.out.println(c1.getName());//获取 包名+类名
System.out.println(c1.getSimpleName());//获取类名
//获取类的属性
System.out.println("================================");
Field[] fields = c1.getFields();//只能获取public的属性
for (Field field : fields) {
System.out.println(field);
}
System.out.println("================================");
Field[] declaredFields = c1.getDeclaredFields();//获取全部属性
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
//获取指定属性
Field age = c1.getDeclaredField("age");
System.out.println(age);
//获取类的方法
System.out.println("================================");
Method[] methods = c1.getMethods();//获取本类+父类的全部public方法
for (Method method : methods) {
System.out.println(method);
}
Method[] declaredMethods = c1.getDeclaredMethods();//获取本类所有方法
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
//获取指定的方法
Method getAge = c1.getMethod("getAge", null);
Method setAge = c1.getMethod("setAge", Integer .class);
System.out.println(getAge);
System.out.println(setAge);
//获取 构造方法
System.out.println("================================");
Constructor[] constructors = c1.getConstructors();//获取public的构造方法
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
Constructor[] declaredConstructors = c1.getDeclaredConstructors();//获取全部的构造方法
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
Constructor declaredConstructor = c1.getDeclaredConstructor();
System.out.println("指定构造器:" + declaredConstructor);
}
}
7.动态创建对象执行方法
package com.xiaoxian.missyou.inject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test4 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class c1 = Student.class;
//方式一:本质调用了类的无参构造函数,并且要求该无参构造函数为public
Student student = (Student)c1.newInstance();
System.out.println(student);
//方式二:通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(Integer.class);
Student student2 = (Student) constructor.newInstance(18);
System.out.println(student2);
//通过反射调用普通方法
Method setAge = c1.getDeclaredMethod("setAge", Integer.class);
setAge.invoke(student, 19);
System.out.println(student.getAge());
//通过反射操作属性
System.out.println("==========================================");
Field age = c1.getDeclaredField("age");
//不能直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法的setAccessible(true)
age.setAccessible(true);
age.set(student, 20);
System.out.println(student.getAge());
}
}
注意: Object invoke(Object obj,Object … args)
- Object对应的原方法的返回值,若原方法无返回值,此时返回null
- 若原方法为 静态方法 ,此时 形参 Object obj可为null
- 若原方法形参列表为空,则Object[] args为null
- 若原方法声明为private,则需要在调用invoke()方法前,显式调用setAccessible(true)方法
8.通过反射获取泛型
为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WidcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
package com.xiaoxian.missyou.inject;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
public class Test5 {
public void test01(Map<String, Student> map, List<Student> list) {
System.out.println("test01");
}
public Map<String, Student> test02() {
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Class c1 = Test5.class;
Method test01 = c1.getDeclaredMethod("test01", Map.class, List.class);
Type[] genericParameterTypes = test01.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println("===" + genericParameterType);
if (genericParameterType instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
System.out.println("=======================================");
Method test02 = c1.getDeclaredMethod("test02");
Type genericReturnType = test02.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
9.通过反射操作注解
package com.xiaoxian.missyou.inject;
import lombok.Data;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class Test6 {
public static void main(String[] args) {
Class c1 = User.class;
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获取指定的注解的值
MyTable myTable = (MyTable)c1.getAnnotation(MyTable.class);
System.out.println(myTable.value());
//获取类上字段的注解
Field[] declaredFields = c1.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("=======================");
MyField myField = (MyField) declaredField.getAnnotation(MyField.class);
System.out.println(myField.columnName());
System.out.println(myField.type());
System.out.println(myField.length());
}
}
}
@Data
@MyTable("db_user")
class User{
@MyField(columnName = "db_user_id", type = "Long", length = 10)
private Long id;
@MyField(columnName = "db_user_name", type = "String", length = 128)
private String name;
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTable {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyField {
String columnName();
String type();
int length();
}
如果对您有一点点的帮助,请关注+转发哟,您的肯定是我继续编写文章的动力。由于本人工作比较忙碌,要学习后,再编写笔记,所以一周大概更新一次,创作不易,请大家多多支持,想要markdown版笔记,可以关注+私聊获取哟