Java基础之注解和反射

注解

注解就是作用于类,方法以及字段上的特殊标记,这些标记可以在编译、类加载、运行时被读取,从而做相对应的处理。注解跟注释很像,区别是注释是给人看的,而注解是给程序看的,它可以被编译器读取。
注解格式为

元注解
public @interface 注解名称{
    数据类型 参数名();   //参数名默认为value
}

元注解及参数

元注解就是修饰注解的注解

元注解之一@Target

由源代码可以知道,@Target注解用来指定注解的作用域(如方法、类或字段),其中 ElementType 是枚举类型,也代表可能的取值范围。要注意的是value()不是方法,而是参数,value为参数名。
在这里插入图片描述
ElementType的值可以是:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    //类、接口(包括注释类型)或枚举声明
    TYPE,

    /** Field declaration (includes enum constants) */
    //字段声明(包括枚举常量)
    FIELD,

    /** Method declaration */
    //方法声明
    METHOD,

    /** Formal parameter declaration */
    //形式参数声明
    PARAMETER,

    /** Constructor declaration */
    //构造方法声明
    CONSTRUCTOR,

    /** Local variable declaration */
    //局部变量声明
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    //注解类型声明
    ANNOTATION_TYPE,

    /** Package declaration */
    //包声明
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
     //类型参数声明,起源于1.8
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
     //类型的使用,起源于1.8
    TYPE_USE
}

元注解之二@Retention

在这里插入图片描述
@Retention用来指定注解的生命周期,它有三个值,对应 RetentionPolicy 中的三个枚举值,分别是:源码级别(source),类文件级别(class)或者运行时级别(runtime)
在这里插入图片描述

  • SOURCE:作用于源码中
  • CLASS:作用于.class 文件中,但会被虚拟机丢弃(该类型的注解信息会保留在源码里和 class 文件里,在执行的时候,不会加载到虚拟机中)
    当注解未定义 Retention 值时,默认值是 CLASS,如 Java 内置注解,@Override、@Deprecated、@SuppressWarnning 等
  • RUNTIME:在源码,.class,运行时均可用,因此可以通过反射机制读取注解的信息(源码、.class 文件和执行的时候都有注解的信息),如 SpringMVC 中的 @Controller、@Service、@Mapping 等。此外,我们自定义的注解也大多在这个级别。

元注解之三Documented

执行 javaDoc 的时候,标记这些注解是否包含在生成的用户文档中。

元注解之四Inherited

标记这个注解具有继承性

自定义注解

自定义注解其实很简单,但要用到这个注解,讲这个注解使用起来就可以通过后面的反射机制来实现。
这是我自己定义的一个注解:关于使用可以继续往后看。

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String name() default "张三";
    String id() default "1001";
    String desc() default "";
}

反射

Java是一门静态语言,但也有说可以称为准动态语言。这个说法其中就可也体现在Java的反射机制上。

实体类(以商品类为例):

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.base.annotationTest.MyAnnotation;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.time.LocalDateTime;

@Data
@TableName("product")
@MyAnnotation(name = "商品实体类",id = "1000",desc = "商品实体类")
public class Product {

    @MyAnnotation(name = "id",id = "1001",desc = "主键")
    @TableId(type = IdType.INPUT)
    private Long id;
    @MyAnnotation(name = "pro_name",id = "1002",desc = "商品名称")
    private String proName;
    @MyAnnotation(name = "pro_type",id = "1003",desc = "商品类型")
    private String proType;
    @MyAnnotation(name = "pro_price",id = "1004",desc = "价格")
    private float proPrice;
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @MyAnnotation(name = "date_of_manufacture",id = "1005",desc = "生产日期")
    private LocalDateTime dateOfManufacture;
    @MyAnnotation(name = "manufacturer",id = "1006",desc = "厂商")
    private String manufacturer;
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @MyAnnotation(name = "create_time",id = "1007",desc = "创建时间")
    private LocalDateTime createTime;
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @MyAnnotation(name = "last_modify_time",id = "1008",desc = "最后修改时间")
    private LocalDateTime lastModifyTime;
    @MyAnnotation(name = "status",id = "1009",desc = "是否上架")
    private boolean status;

}

通过反射获取类对象

如下代码所示的三种方法。

  • 通过对象的实例的getClass()方法来获取类对象;
  • 通过类名的.class直接获取类对象;
  • 通过全限类名调用Class.forName()方法。
import com.base.annotationTest.MyAnnotation;
import com.base.reflect.pojo.Product;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

/**
 * 通过反射获取类对象的三种方式
 */
public class Test02 {

    public static void main(String[] args) throws ClassNotFoundException {
        //1,通过对象的实例的getClass()方法来获取类对象
        Product product = new Product();
        Class c1 = product.getClass();
        //2,通过类名的.class直接获取类对象
        Class c2 = Product.class;
        //3,通过全限类名调用Class.forName方法创建
        Class c3 = Class.forName("com.base.reflect.pojo.Product");

        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());

    }

}

由结果可知,三种方法创建的对象都为同一个对象。
在这里插入图片描述

类对象方法

通过反射创建的类对象可以调用方法来获取类的字段,方法,注解等信息。
在这里插入图片描述
举几个例子:
1,getName()与getSimpleName()

//获取类对象全限类名(包名+类名)
System.out.println(c1.getName());
//获取类对象名称
System.out.println(c1.getSimpleName());

在这里插入图片描述
2,getClassLoader()
获取类加载器。

ClassLoader classLoader = c1.getClassLoader();
ClassLoader parent = classLoader.getParent();
ClassLoader parent1 = parent.getParent();
System.out.println(classLoader);
System.out.println(parent);
System.out.println(parent1);

自定义的类加载器都是委托给AppClassLoader,而AppClassLoader委托给ExtClassLoader,ExtClassLoader委托给根加载器。这就是ClassLoader的委托链。可以看到类加载器的层次结构如下:
在这里插入图片描述

ClassLoader的双亲委派机制是这样的:

  • 当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
  • 当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
  • 如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
  • 若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。

3,getAnnotation();
该方法用来获取类,方法获取字段上的注解,以上面的商品类和之前的自定义注解MyAnnotation为例:

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

/**
 * 获取自定义注解,并使用
 */
public class Test01 {

    public static void main(String[] args) throws ClassNotFoundException {
        Class clazz = Class.forName("com.base.reflect.pojo.Product");
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        MyAnnotation annotation = (MyAnnotation) clazz.getAnnotation(MyAnnotation.class);
        System.out.println(annotation.name());

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            MyAnnotation myAnnotation = field.getAnnotation(MyAnnotation.class);
            System.out.println(myAnnotation.id()+"==="+myAnnotation.name()+"==="+myAnnotation.desc());
        }
    }

}

结果如下图所示:
在这里插入图片描述
可以通过反射机制来获取注解里面参数的值,自然就可以对这些值进行其他操作了。

除了以上这些类方法还有其他的例如:getMethod(),getFiled()等,在Spring的框架中被大量用到。Spring的AOP原理就是动态代理+反射来实现的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值