初識 Java 註解

正在學習的小白。不當之處,請多多指教java

目錄程序員

  • 前言
  • 自定義註解的使用
  • 元註解
  • 註解的解析反射
  • 解析註解的做用(引入IOC概念)

前言

java中有四大類型,其中三個是:枚舉,接口,類。編程

今天簡單認識一下第四個類型:註解。數組

什麼是註解bash

Annotation 這裏是"註解"的意思。
除此以外,這個單詞患有一個「註釋」的意思。咱們都知道,註釋是給程序員看的。那麼註解呢?
註解是給程序看的,因此Annotation既有註解也有註釋的意思
複製代碼

咱們很早就見過一些註解:jdk中的@Override,@FunctionalInterface,Junit框架的@Test等。框架

註解的基本語法

枚舉,接口,類,這個三個類型咱們都寫過(他們的源碼 ),基本語法,想必你們都知道。jvm

那麼註解有源碼嗎?答案是確定的,jvm沒有那麼厲害,不可能僅憑一個@+一個單詞就知道程序想表達什麼。ide

咱們以@FunctionalInterface(關於這個註解不清楚的能夠參考函數式編程的內容)爲例:函數式編程

@FunctionalInterface
interface UsB{
    void show();
}
複製代碼

按住Ctrl點擊註解進入,就能夠看到@FunctionalInterface的源碼:函數

package java.lang;
import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
複製代碼

先不着急認識這些是什麼意思,看懂其構成就能夠,而後咱們照貓畫虎,本身寫一個:

/** * 自定義註解 */
@interface MyAnnotation{ }
複製代碼
  1. 因而,咱們完成了一個自定義註解,由此,能夠基本知道註解的基本構成:

@interface 註解名字{ }

那麼,註解有像其餘普通類同樣的屬性嗎?

  1. 有,可是,註解的屬性不太同樣,他是這樣寫的,

String name(); 註解中 這是屬性,不是接口中的方法。

  1. 既然是屬性,那就能夠賦值:

註解屬性賦值語法:String name() default "屬性";

(ps:能夠賦值,但通常再也不內部賦值,給外部使用者賦值)

  1. 那麼屬性這樣寫,註解有方法嗎?有的話的方法該怎麼寫?

很差意思,註解是沒有方法的!

  1. 普通類中的屬性,能夠是任意類型,那麼註解也同樣嗎??

註解對屬性類型是有要求的:

8個基本數據類型 / 字符串類型 / Class類型 / 註解類型 / 枚舉類型 及其一維數組

  1. 屬性補充:

註解中只有一個屬性, 那麼請將該屬性定義爲 value 名稱. 好處: 使用該註解時能夠省略 value=

以上6點就是註解的基本語法。

自定義註解的使用

以前咱們使用註解,都是固定的,好比@Override只能在(重寫)方法使用,@FunctionalInterface只能在(有且僅有一個要實現的方法的)接口使用。乳溝隨便使用,就會馬上編譯報錯。 那麼,使用咱們剛剛自定義的註解,使用上有限制嗎?

@MyAnnotation("省略了value")//能夠用在類上
public class AnnotationDemo {
    @MyAnnotation("zhangsan") String name;//能夠用在屬性上
    @MyAnnotation("show") //能夠用在方法上
    public void show(){}
}

//自定義註解
@interface MyAnnotation{
    //屬性
   String value();
}
複製代碼

元註解

這樣看來,咱們目前自定義的註解是沒有任何使用位置的限制的,再回頭看看,前面@FunctionalInterface註解的源碼,或者@Override的源碼,發現咱們自定義的註解少了幾樣東西。沒錯,少了一些註解。準確說,少了一些 「元註解」

package java.lang;
import java.lang.annotation.*;
//三個元註解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
複製代碼

什麼是元註解,「元」意爲:最開始,初始的意思,那麼元註解就是其實的註解,或者叫,註解的註解。是用來修飾說明註解的。

元註解都來自於:java.lang.annotation.* 下

先來認識一下元註解:

@Target

意爲目標,也就是說明註解的使用範圍

來看一下@Target的源碼:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}
複製代碼

能夠看到,他也有本身的元註解,和一個一維數組屬性ElementType[],進入ElementType[],咱們能夠看到,ElementType是一個枚舉類(控制文本長度,去除了全部源碼的註釋):

public enum ElementType {
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,
    TYPE_USE
}
複製代碼

這些枚舉類型很容易看出,Target註解的value值,能夠是這些枚舉元素,例如:FIELD表示使用在屬性上,METHOD可使用在方法上,等等,不在一一說明。

使用: 、

@Target({ElementType.FIELD,ElementType.METHOD}) 注意,多個值用中括號,屬性名爲value,可省略

能夠加在咱們前面寫的自定義註解上看看效果。

@Retention

意爲,保留策略,又稱之爲生命週期。

咱們繼續進入@Retention的源碼:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}
複製代碼

也只有一個屬性,進入RetentionPolicy查看:

public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}
複製代碼

RetentionPolicy也是一個枚舉類,只有三個值。

這三個值很好理解,他與java程序的聲明週期是一一對應的:

源碼階段(SOURCE),編譯階段(CLASS),運行階段(RUNTIME)。

@Retention的屬性不是數組,因此只能選擇一個值

如:@Retention(RetentionPolicy.CLASS)

ps : RetentionPolicy.RUNTIME 最經常使用,由於一般和反射結合使用,而反射是在運行時操做類。

註解的反射解析

(不瞭解反射能夠參考個人另外一篇筆記:Java 反射機制那些事

咱們經過代碼的方式,簡單說明下,如何利用反射解析註解

先來準備一個 Student類:

public class Student {
    //一個屬性
    public String name;
    public Student() { }
    //一個構造
    public Student(String name) { this.name = name; }
    //一個方法
    public void show(String msg){
        System.out.println("show方法 = " + msg);
    }
    //重寫toString
    @Override
    public String toString() {
        return "Student{name= "+name+"}';
    }
}
複製代碼

再來寫一個自定義註解:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.CONSTRUCTOR})
public @interface MyAnnotation{
   String value();
}
複製代碼

而後,根據自定義註解的做用範圍,在Student類上加上咱們自定義的註解,而後賦上一些值:

@MyAnnotation("小明")
    public Student(String name) {
        this.name = name;
    }
    @MyAnnotation("小紅來了")
    public void show(String msg){
        System.out.println("show方法 = " + msg);
    }
複製代碼

在新建一個類,實現咱們的反射部分代碼,這裏個人類就叫作:AnnotationDemo

補充:

註解 某種意義上講能夠做爲 一種配置文件(常見的配置文件 .properties 或者 .xml)

​ 既然寫了一個文件,就要去對他進行一些 讀寫 操做,若是不去讀取並使用他的內容,呢這個(配置)文件存在在程序中有什麼意義??文件是保存信息數據的,因此文件不讀出來,是沒有意義的。

​ 而讀出配置文件的內容 稱之爲 解析!

​ 註解有個解析技術,叫作: 反射!

​ 寫了註解,就至關於,寫了配置文件不讀取!是沒有意義的。

反射是在運行時,操做Class對象,註解寫在Student類中,因此,反射能夠操做Student的Class對象。

咱們就先AnnotationDemo類中在利用反射獲取一個Student裏面的自定義註解:

public class AnnotationDemo {
	public static void main(String[] args) throws NoSuchMethodException {
        //1.獲取Student的Class對象
        Class<?> clazz = Student.class;
        //2.先從構造器開刀,找到構造器
        Constructor<?> constructor = clazz.getConstructor(String.class);
        /** * 3.使用構造起的方法 isAnnotationPresent(), * 方法意爲:有沒有(參數)註解存在?注意:(一個方法。類等能夠有多個註解) * 參數:註解類型的class對象 * 返回值:存在(true), */
        boolean annotation = constructor.isAnnotationPresent(MyAnnotation.class);
        //若是存在,來獲取這個註解
        if (annotation) {
            /* getAnnotation(註解.class)獲取註解 此時獲取到 Student滿參構造上的註解 */
            MyAnnotation myAnnotation = constructor.getAnnotation(MyAnnotation.class);
            //註解有個屬性叫value
            String value = myAnnotation.value();
            System.out.println("value = " + value);        //value = 小明
        }
    }
}
複製代碼

以上代碼,就將 Student滿參構造上的註解的屬性值讀取出來了。

解析註解做用(引入IOC概念)

仍是和反射同樣的問題?這樣解析註解有什麼用?

這要結合具體場景,有些項目中可能要本身定義註解使用,而最多使用的地方就是框架。

題外話——引入IOC概念

上面的示例代碼咱們能夠看到,AnnotationDemo類中的一系列代碼,就獲取到了Student的滿參構造的註解的value值。那麼獲取到註解的屬性值,咱們就能夠將值反轉到(傳入)這個構造裏面去。

這個就叫作控制反轉(IOC)

怎麼傳值呢?繼續看代碼!

//部分代碼和上面同樣,註釋省略
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> clazz = Student.class;
        Constructor<?> constructor = clazz.getConstructor(String.class);
        boolean annotation = constructor.isAnnotationPresent(MyAnnotation.class);
        if (annotation) {
            MyAnnotation myAnnotation = constructor.getAnnotation(MyAnnotation.class);
            String value = myAnnotation.value();
            System.out.println("value = " + value);        //value = 小明
            
            /* 利用 newInstance 方法,就能夠得到Student實例 */
            Object obj = constructor.newInstance(value);
            System.out.println("obj = " + obj);

        }
    }
複製代碼

​ 咱們能夠想一下,假如AnnotationDemo類和自定義註解,不是咱們所寫,是一種別人寫的框架,本身歷來不知道這樣一些代碼,而只是用一個註解,傳了個值,就構造出了一個實例對象。這就是框架技術的一部分底層原理。

​ 接下來解析Student的show方法的註解:

public class AnnotationDemo {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //獲取Class對象
        Class<?> clazz = Student.class;
        //獲取方法
        Method show = clazz.getMethod("show", String.class);
        boolean b = show.isAnnotationPresent(MyAnnotation.class);
        if (b) {
            //每一個反射對象都有這樣一個方法,獲取註解
            MyAnnotation annotation = show.getAnnotation(MyAnnotation.class);
            String value = annotation.value();
           show.invoke(clazz.newInstance(), value);
           //運行,查看結果
                //show方法 = 小紅來了
        }
    }
}
複製代碼

相關文章
相關標籤/搜索