其他分享
首页 > 其他分享> > 注解与反射

注解与反射

作者:互联网

一、 注解

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
}

常用注解

二、 反射

反射就是根据他的Class文件来获取他的属性 方法

我们知道,每一个类加载完成后会在方法区生成一个Class类型的对象,辅助我们访问这个的方法、构造器、字段等。这个对象是Class的子类,每个类【有且仅有】一个Class类,也叫类对象。

image-20220830121041028

让我们先来看看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