注解与反射
作者:互联网
一、 注解
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种机制,是给程序看的。Java 语言中的类、方法、变量、参数和包等都可以被标注。
以Override FunctionalInterface为例:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
可以很清楚的发现他们的共同点 元注解与@Interface
所以我们也可以定义一个
public @interface MyUser {
String name() default "hangman"; //指定默认值
int age();
}
public class User {
private String name;
private int age;
@MyUser(age=18)
public User(String name, int age) {
this.name = name;
this.age = age;
}
不妨编译一下,查看一下我们的字节码文件
@MyUser(age=18)
public static void main(String[] args) {
System.out.println(1);
}
输入指令 javap v .\MyUser.class
SHA-256 checksum fb9e3367219f625774cbf93caa409bc97f5f64d829d49b8ecca33be2b28adfa6
Compiled from "MyUser.java"
**********************
public interface test.MyUser extends java.lang.annotation.Annotation
minor version: 0
major version: 52
flags: (0x2601) ACC_PUBLIC,//public 公开
ACC_INTERFACE,//接口
ACC_ABSTRACT,//抽象类
ACC_ANNOTATION//注解
*********************
this_class: #1 // test/MyUser
super_class: #2 // java/lang/Object
interfaces: 1, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Class #12 // test/MyUser
#2 = Class #13 // java/lang/Object
#3 = Class #14 // java/lang/annotation/Annotation
#4 = Utf8 name
#5 = Utf8 ()Ljava/lang/String;
#6 = Utf8 AnnotationDefault
#7 = Utf8 hangman
#8 = Utf8 age
#9 = Utf8 ()I
#10 = Utf8 SourceFile
#11 = Utf8 MyUser.java
#12 = Utf8 test/MyUser
#13 = Utf8 java/lang/Object
#14 = Utf8 java/lang/annotation/Annotation
*****************
{
public abstract java.lang.String name();
descriptor: ()Ljava/lang/String;
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
AnnotationDefault:
default_value: s#7
"hangman"
public abstract int age();
descriptor: ()I
flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
}
SourceFile: "MyUser.java"
可以看到@Interfac本质上也是个接口,并且他还会继承Annotation类
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();//返回此注解的注解类型,可以看到也是个接口
元注解
回到我们的函数式接口,让我们来看下这些用在注解上的注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
@Documented
@Documented 是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
@Target
@Target 注解用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什么地方,类?构造方法?成员变量?。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
@Target 注解有一个成员变量(value)用来设置适用目标,value 是 java.lang.annotation.ElementType 枚举类型的数组,下表为 ElementType 常用的枚举常量。
名称 | 说明 |
---|---|
PACKAGE | 用于包 |
FIELD | 用于成员变量(包括枚举常量) |
TYPE | 用于类、接口(包括注解类型)或 enum 声明 |
CONSTRUCTOR | 用于构造方法 |
METHOD | 用于方法 |
LOCAL_VARIABLE | 用于局部变量声明 |
PARAMETER | 用于类型参数 |
TYPE_PARAMETER | 类型参数声明 |
TYPE_USE | 用于使用类型 |
ANNOTATION_TYPE | 用于注解类型声明 |
@Retention
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
RetentionPolicy 是 Enum 枚举类型,它用来指定 Annotation 的策略。通俗点说,就是不同 RetentionPolicy 类型的 Annotation 的作用域不同。
public enum RetentionPolicy {
/**
* 编译完成后将被编译器丢弃,逼入@Override,它修饰一个方法的时候,就意味着该方法覆盖父类的方法;并且在编译期间会进行 * 语法检查!编译器处理完后,"@Override" 就没有任何作用了。
*/
SOURCE,
/**
* 释将由编译器记录在类文件中,但不需要在运行时由 VM 保留。说人话就是存到对应的class文件里边,但不会加载到JVM中,
* 在编译时进行一些预处理,这是默认的。
*/
CLASS,
/**
* 注释将由编译器记录在类文件中,并在运行时由 VM 保留,因此可以反射性地读取它们。
* 简单说运行的时候jvm会读取到,起作用,动态获取注解信息。
*/
RUNTIME
}
常用注解
- @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
- @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
- @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口,以配合lambal表达式。
- @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
- @Inherited - 加有该注解的注解会被子类继承,注意,仅针对类,成员属性、方法并不受此注释的影响。
二、 反射
反射就是根据他的Class文件来获取他的属性 方法
我们知道,每一个类加载完成后会在方法区生成一个Class类型的对象,辅助我们访问这个的方法、构造器、字段等。这个对象是Class的子类,每个类【有且仅有】一个Class类,也叫类对象。
让我们先来看看Class类
public final class Class<T> {
// 获得他实现的接口
public Class<?>[] getInterfaces() {
ReflectionData<T> rd = reflectionData();
if (rd == null) {
// no cloning required
return getInterfaces0();
} else {
Class<?>[] interfaces = rd.interfaces;
if (interfaces == null) {
interfaces = getInterfaces0();
rd.interfaces = interfaces;
}
// defensively copy before handing over to user code
return interfaces.clone();
}
}
private native Class<?>[] getInterfaces0();
// 获得方法
@CallerSensitive
public Method[] getMethods() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyMethods(privateGetPublicMethods());
}
// 获得他的构造器
@CallerSensitive
public Constructor<?>[] getConstructors() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyConstructors(privateGetDeclaredConstructors(true));
}
// 获得他的属性
@CallerSensitive
public Field getField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Field field = getField0(name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
}
1. 获取类对象的方法
1、使用类
Class clazz = Dog.class;
2、使用全类名
Class aClass = Class.forName("com.ydl.Dog");
3、使用对象
Dog dog = new Dog();
Class clazz = dog.getClass();
2. 对类对象的操作
//获取类名字
String name = clazz.getName();
//获取类加载器
ClassLoader classLoader = clazz.getClassLoader();
//获取资源
URL resource = clazz.getResource("");
//得到父类
Class superclass = clazz.getSuperclass();
//判断一个类是不是接口,数组等等
boolean array = clazz.isArray();
boolean anInterface = clazz.isInterface();
//重点,使用class对象实例化一个对象
Object instance = clazz.newInstance();
3.对成员变量的操作
先看看怎么获取
//获取字段,只能获取公共的字段(public)
Field name = clazz.getField("type");
Field[] fields = clazz.getFields();
//能获取所有的字段包括private
Field color = clazz.getDeclaredField("color");
Field[] fields = clazz.getDeclaredFields();
System.out.println(color.getType());
再看看怎么修改
public class User {
public String name;
private int age;
User user = new User("zangsna",18);
Class userClass = user.getClass();
Field names = userClass.getField("name");
//按理来说private修饰的无法被访问,但反射有这个权限
Field age = userClass.getDeclaredField("age");
names.set(user, "lisi");
System.out.println(names.get(user));
//暴力修改
age.setAccessible(true);
age.set(user, 1000);
System.out.println(names.get(user));
4. 对方法的操作
public void eat(){
System.out.println(name+"正在食用");
}
private void eat(String aname){
System.out.println("神秘人"+aname+"正在食用");
}
Method eat1 = userClass.getDeclaredMethod("eat", String.class);
Method eat2 = userClass.getDeclaredMethod("eat");
eat1.setAccessible(true);
eat1.invoke(user,"的黑衣人");
eat2.invoke(user);
5. 对构造器的操作
获取并构建对象
Constructor[] constructors = clazz.getConstructors();
Constructor constructor = clazz.getConstructor();
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
Constructor declaredConstructor = clazz.getDeclaredConstructor();
Object obj = constructor.newInstance();
6.对注解的操作
//元注解 要加上runtime
//类上
Annotation annotation = clazz.getAnnotation(Bean.class);
Annotation[] annotations = clazz.getAnnotations();
//字段上
Annotation annotation = field.getAnnotation(Bean.class);
Annotation[] annotations = field.getAnnotations();
//方法上
Annotation annotation = method.getAnnotation(Bean.class);
Annotation[] annotations = method.getAnnotations();
标签:反射,name,clazz,public,Annotation,注解,Class 来源: https://www.cnblogs.com/yfmw/p/16640156.html