java基础之注解

1.什么是注解?

Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响代码的执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。通俗的将:可以完全将注解当做生活中我们对人或物贴的标签。

2.注解的原理

注解本质是一个继承了 Annotation 的特殊接口,其具体实现类是 Java 运行时生成的动态代理类。而我们通过反射获取注解时,返回的是 Java 运行时生成的动态代理对象 $Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用 AnnotationInvocationHandler 的 invoke 方法。该方法会从 memberValues 这个 Map 中索引出对应的值。而 memberValues 的来源是 Java 常量池。
——摘自《注解Annotation实现原理与自定义注解例子》

3.注解的作用 

  • 编译检查:Annotation 具有"让编译器进行编译检查的作用"。

    例如,@SuppressWarnings, @Deprecated 和 @Override 都具有编译检查作用。

  • 在反射中使用 Annotation:JavaAnnotatedElement接口中有getAnnotation()等获取注解的方法。
    MethodFieldClassPackage等类均实现了这个接口,因此均有获取注解的能力。

  • 根据 Annotation 生成帮助文档:通过给 Annotation 注解加上 @Documented 标签,能使该 Annotation 标签出现在 javadoc 中。

  • 能够帮忙查看代码:通过 @Override, @Deprecated 等,我们能很方便的了解程序的大致结构。

4.元注解

元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。或者可以理解为:元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的

元注解所在包

java.lang.annotation

五大元注解 

@Documented

Documented:“已记录”的意思,当@Documented应用到一个注解上时,表示要将注解信息添加在 Java 文档,即 Javadoc 中;在@Documented缺省的情况下表示注解信息是不出现在 javadoc 中的。

源码如下: 

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

@Inherited

Inherited:“遗传,继承”的意思,@Inherited 定义了一个注解与子类的关系;如果一个超类被带有 @Inherited 的注解修饰,那么对于该超类,它的子类如果没有被任何注解应用的话,那么这个子类就继承了超类的注解。

例如:

注解Test被@Inherited修饰

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test{}


超类:

@Test
public class A {}


子类:

public class B extends A {}

说明:
注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。

 

源码如下: 

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

@Repeatable

Repeatable:“可重复的”的意思,当@Repeatable修饰一个注解时,表明该注解可以重复应用在同一个类上,即该注解可以取多个值;如:一个人既是程序员,又是产品经理,同时也是画家。

例如:

//注解1:
@interface Persons {
    Person[] value();
}

//注解2:
@Repeatable(Persons.class)
@interface Person {
    String role default "";
}



//有使用@Repeatable()时的使用
@Person(role="programmer")
@Person(role="PM")
@Person(role="artist")
public class SuperMan {
    
}

//没使用@Repeatable()时的使用,即注解2 @Person去掉@Repeatable元注解
@Persons({
    @Person(role="programmer"),
    @Person(role="PM"),
    @Person(role="artist")})
public class SuperMan { 

}

 

源码如下: 

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    /**
     * Indicates the <em>containing annotation type</em> for the
     * repeatable annotation type.
     * @return the containing annotation type
     */
    Class<? extends Annotation> value();
}

注意:按照规定,如果使前面的 Persons 里面可以重复调用某个注解,则 Persons 必须有一个 value 的属性,且属性类型必须为被 @Repeatable 注解的 Person。

@Retention

Retention:“保留”的意思,当 @Retention 应用到一个注解上时,用来定义该注解的生命周期,即存活时间。

若没有 @Retention,则默认是 RetentionPolicy.CLASS。

源码如下: 

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}



public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     * 注解只在源码阶段保留,在编译器完整编译之后,它将被丢弃忽视;
     * 如:@Override, @SuppressWarnings
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     * 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中;
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     * 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们;
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

 

@Target

target:“目标”的意思,当@Target应用到一个注解上时,用来限制该注解的应用场景,即应用范围。

若没有 @Target,则说明该注解可以用于任何地方。

源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}




public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration 
     * 对类、接口、枚举进行注解;
     */
    TYPE,

    /** Field declaration (includes enum constants) 
     * 对属性、成员变量、成员对象(包括 enum 实例)进行注解;
     */
    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
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     * 对使用类型进行注解
     * @since 1.8
     */
    TYPE_USE
}

5.注解的属性

注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。

支持的元素类型

  • 8种基本数据类型(byteshortcharintlongfloatdoubleboolean
  • String
  • Class
  • enum
  • 注解类型
  • 数组(所有上边类型的数组)

使用时,无默认值的元素必须传值。

有两种比较特别的注解

  • 标记注解 : 注解中没有任何元素,使用时直接是 @XxxAnnotation, 不需要加括号
  • 单值注解 : 注解只有一个元素,且名字为value,使用时直接传值,不需要指定元素名,如:@XxxAnnotation(100)

6.java预置注解

  • @Override - 检查该方法是否是重载方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
  • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。

7.自定义注解及使用

注解的格式

修饰符 @interface 注解名 {   
    注解元素类型type elementName();
    注解元素类型type elementName() default value;  // 带默认值
}

  • 自定义注解类型定义为 @interface,所有的注解会自动继承 java.lang.Annotation 这一接口,而且不能再去继承其他的类或接口;定义 Annotation 时,@interface 是必须的。
  • 注解元素只能用 public 或 default 两个关键字修饰;
  • 注解元素只能用基本类型:byte, short, char, int, long, float, double, boolean,以及 String, Enum, Class, Annotations 等数据类型,以及这些类型的数组;
  • 要获取类方法和字段的注解信息,必须通过 Java 的反射技术;
  • 注解也可以不定义成员变量;
  • 自定义注解需要使用元注解进行编写;

示例1

public enum Status {
    GOOD,
    BAD
}


@Target(ElementType.ANNOTATION_TYPE)
public @interface MyAnnotation1 {
    int val();
}


@Target(ElementType.TYPE)
public @interface MyAnnotation2 {
    
    boolean boo() default false;
    
    Class<?> cla() default Void.class;
    
    Status enu() default Status.GOOD;
    
    MyAnnotation1 anno() default @MyAnnotation1(val = 1);
    
    String[] arr();
    
}


//使用时,无默认值的元素必须传值
@MyAnnotation2(
        cla = String.class,
        enu = Status.BAD,
        anno = @MyAnnotation1(val = 2),
        arr = {"a", "b"})
public class MyTest1 {
}


示例2

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
public @interface MyAnnotation {
	String value();
	String value1() default "annotation";
}


@MyAnnotation("class")
public class MyClass {
    
    @MyAnnotation("field")
    private String str;
    
    @MyAnnotation("method")
    public void method() { }
    
}


public class MyTest {
    
    public static void main(String[] args) throws Exception {
    
        MyClass obj = new MyClass();
        Class<?> clazz = obj.getClass();
        
        // 获取类上的注解值
        MyAnnotation anno = clazz.getAnnotation(MyAnnotation.class);
        System.out.println(anno.value());
        
        // 获取属性上的注解
        Field field = clazz.getDeclaredField("str");
        anno = field.getAnnotation(MyAnnotation.class);
        System.out.println(anno.value());
        
        // 获取方法上的注解
        Method method = clazz.getMethod("method");
        anno = method.getAnnotation(MyAnnotation.class);
        System.out.println(anno.value());
    }
    
}


//class
//feild
//method

示例3

//定义水果供应商注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
    /*供应商编号*/
    public int id() default -1;
    /*供应商名称*/
    public String name() default "";
    /*供应商地址*/
    public String address() default "";
}


//定义Apple类使用水果供应商注解
@FruitProvider(id = 1728, name = "why", address = "shenzhen")
public class Apple {
    private int appleID;
    private String appleProvidername;
    private String appleprovideraddress;

    public int getAppleID() {
        return appleID;
    }

    public void setAppleID(int appleID) {
        this.appleID = appleID;
    }

    public String getAppleProvidername() {
        return appleProvidername;
    }

    public void setAppleProvidername(String appleProvidername) {
        this.appleProvidername = appleProvidername;
    }

    public String getAppleprovideraddress() {
        return appleprovideraddress;
    }

    public void setAppleprovideraddress(String appleprovideraddress) {
        this.appleprovideraddress = appleprovideraddress;
    }
}


//注解处理器:利用反射处理注解
public class FruitInfoUtil {

    public static Apple getAApple(Class<?> clazz) throws Exception{
        FruitProvider fb = clazz.getAnnotation(FruitProvider.class);//通过反射获取处理注解
        //通过newInstance()生成Apple实例,利用反射的结果进行设置
        Apple ap = (Apple)clazz.newInstance();
        ap.setAppleID(fb.id());
        ap.setAppleProvidername(fb.name());
        ap.setAppleprovideraddress(fb.address());
        return ap;
    }
}


public class Main {

    public static void main(String[] args) throws  Exception{
        Apple a = FruitInfoUtil.getAApple(Apple.class);
        System.out.println("苹果商的ID为:"+a.getAppleID());
        System.out.println("苹果商的名字为:"+a.getAppleProvidername());
        System.out.println("苹果商的地址为:"+a.getAppleprovideraddress());
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值