Java 注解 —— 注解的理解晃危、注解的使用與自定義注解

參考網(wǎng)址:

《秒懂锦聊,Java 注解 (Annotation)你可以這樣學》
《Java注解基本原理》
《注解Annotation實現(xiàn)原理與自定義注解例子》
《框架開發(fā)之Java注解的妙用》


一. 注解基本介紹

1.1 什么是注解?

什么是注解嗽冒?嚴謹?shù)膩碚f礁扮,注解提供了一種安全的類似注釋的機制知举,用來將任何的信息或元數(shù)據(jù)(metadata)與程序元素(類、方法太伊、成員變量等)進行關(guān)聯(lián)雇锡。為程序的元素(類、方法僚焦、成員變量)加上更直觀的說明锰提,這些說明信息是與程序的業(yè)務邏輯無關(guān),并且供指定的工具或框架使用芳悲。Annontation像一種修飾符一樣欲账,應用于包、類型芭概、構(gòu)造方法、方法惩嘉、成員變量罢洲、參數(shù)及本地變量的聲明語句中。
Java 注解是附加在代碼中的一些元信息文黎,用于一些工具在編譯惹苗、運行時進行解析和使用,起到說明耸峭、配置的功能桩蓉。注解不會也不能影響代碼的實際邏輯,僅僅起到輔助性的作用劳闹。注解包含在 java.lang.annotation 包中院究。
具體定義如下:

注解 (Annotation),也叫元數(shù)據(jù)本涕。一種代碼級別的說明业汰。它是 JDK1.5 及以后版本引入的一個特性,與類菩颖、接口样漆、枚舉是在同一個層次。它可以聲明在包晦闰、類放祟、字段鳍怨、方法、局部變量跪妥、方法參數(shù)等的前面鞋喇,用來對這些元素進行說明,注釋骗奖。
——摘自百度百科

上面的說明雖然嚴謹确徙,但比較難懂。筆者認為《秒懂执桌,Java 注解 (Annotation)你可以這樣學》一文中鄙皇,作者 frank909 大佬的解釋十分親民:可以完全將注解當做生活中我們對人對物貼的標簽

拿筆者最喜歡的一部動畫電影來打個比方吧:《Zootopia》仰挣“橐荩《Zootopia》整個電影將動物們擬人化,性格各異膘壶。不管是兔子错蝴,狐貍,羚羊颓芭,豹子等等顷锰,每個動物都有一張固有標簽:兔子乖巧,狐貍狡黠亡问,羚羊溫順官紫,豹子兇猛
但它們又有著自己真實的性格:想當警察的兔子州藕,狡黠卻不失善良的狐貍束世,披著狼皮的腹黑羚羊,吃著甜甜圈有少女心的豹子床玻。

Zootopia

《Zootopia》這個電影的內(nèi)核是在講毁涉,我們要試圖沖破外界對自己所貼的標簽的限制。但在這里筆者要稍微的當一下杠精锈死,吹一下標簽的作用:貼標簽是較為精準的了解一個事物的最高效率方法贫堰。瘋狂動物城中的動物們,外界對他們的第一印象馅精,往往都是直接引用了該物種性格的固有標簽严嗜。同樣的在 Java 中,注解的作用就是告訴開發(fā)人員洲敢,被注解的內(nèi)容是用來做什么的漫玄,換句話說,注解就是 Java 代碼的標簽。
在 Java 中睦优,給代碼貼合適的標簽是很重要的渗常,它很大程度的提高了效率。雖然寫代碼的時候開發(fā)人員也可以致敬《Zootopia》主旨汗盘,嘗試突破標簽的限制(比如給實現(xiàn)了 @Controller 功能的代碼加了 @Service 注解)皱碘,但筆者不保證寫下這樣代碼開發(fā)人員的后續(xù)人身安全,太睿智的人肯定是要被針對的……

1.2 注解的作用

  1. 能夠讀懂別人寫的代碼(尤其是框架相關(guān)的代碼)隐孽;
  2. 實現(xiàn)替代配置文件的功能癌椿。比如可能原本需要很多配置文件以及很多邏輯才能實現(xiàn)的內(nèi)容,如果使用合理的注解菱阵,就可以使用一個或多個注解來實現(xiàn)相同的功能踢俄。這樣就使得代碼更加清晰和整潔;
  3. 編譯時進行格式檢查晴及。
    • 如 @Override 注解放在方法前都办,如果該方法不是覆蓋了某個超類方法,編譯的時候編譯器就能檢查出來虑稼。
  4. 裝逼琳钉。
    • 做技術(shù)的怎么可以沒有一點用技術(shù)吹牛逼的心理呢?如果會在合適的地方恰好的使用注解或者自定義注解的話蛛倦,老板肯定會雙手送你 666 的歌懒。當然筆者現(xiàn)在只是初學而已,距離用技術(shù)吹牛逼的道路還遠溯壶。

1.3 注解的原理

注解本質(zhì)是一個繼承了 Annotation 的特殊接口歼培,其具體實現(xiàn)類是 Java 運行時生成的動態(tài)代理類。而我們通過反射獲取注解時茸塞,返回的是 Java 運行時生成的動態(tài)代理對象 $Proxy1。通過代理對象調(diào)用自定義注解(接口)的方法查剖,會最終調(diào)用 AnnotationInvocationHandler 的 invoke 方法钾虐。該方法會從 memberValues 這個 Map 中索引出對應的值。而 memberValues 的來源是 Java 常量池笋庄。
——摘自《注解Annotation實現(xiàn)原理與自定義注解例子》

這里涉及的內(nèi)容比較深入效扫,筆者目前不能理解。先貼上來直砂,以后慢慢來吧菌仁。

二. 元注解

元注解是可以注解到注解上的注解,或者說元注解是一種基本注解静暂,但是它能夠應用到其它的注解上面济丘。或者可以理解為:元注解也是一張標簽,但是它是一張?zhí)厥獾臉撕災∶裕?strong>作用和目的就是給其他普通的標簽進行解釋說明的疟赊。

基本的元標簽有 @Retention, @Documented, @Target, @Inherited 四種(后來到了 Java 8 又加入了 @Repeatable)。

2.1 @Retention

@Retention 定義了該注解的生命周期峡碉。當 @Retention 應用到一個注解上的時候近哟,作用就是說明這個注解的存活時間。

  • RetentionPolicy.SOURCE: 注解只在源碼階段保留鲫寄,在編譯器完整編譯之后吉执,它將被丟棄忽視;
    • 例:@Override, @SuppressWarnings
  • RetentionPolicy.CLASS: 注解只被保留到編譯進行的時候地来,它并不會被加載到 JVM 中戳玫;
  • RetentionPolicy.RUNTIME: 注解可以保留到程序運行的時候,它會被加載進入到 JVM 中靠抑,所以在程序運行時可以獲取到它們量九;筆者接觸到大部分的注解都是 RUNTIME 的生命周期。

以 SpringMVC 中的 @Service 的源碼為例:

package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
    String value() default "";
}

這里 @Service 擁有 @Retention(RetentionPolicy.RUNTIME) 注解颂碧,所以在程序運行時可以捕獲到它們荠列。

2.2 @Target

@Target 表示該注解用于什么地方,可以理解為:當一個注解被 @Target 注解時载城,這個注解就被限定了運用的場景肌似。可以使用的 ElementType 參數(shù):

  • ElementType.CONSTRUCTOR: 對構(gòu)造方法進行注解诉瓦;
  • ElementType.ANNOTATION_TYPE: 對注解進行注解川队;
  • ElementType.FIELD: 對屬性、成員變量睬澡、成員對象(包括 enum 實例)進行注解固额;
  • ElementType.LOCAL_VARIABLE: 對局部變量進行注解;
  • ElementType.METHOD: 對方法進行注解煞聪;
  • ElementType.PACKAGE: 對包進行注解斗躏;
  • ElementType.PARAMETER: 對描述參數(shù)進行注解;
  • ElementType.TYPE: 對類昔脯、接口啄糙、枚舉進行注解;

如上面的 @Service 所示云稚,@Service 的 @Target 注解值為 ElementType.TYPE隧饼,即 @Service 只能用于修飾類。

2.3 @Documented

@Documented 是一個簡單的標記注解静陈,表示是否將注解信息添加在 Java 文檔燕雁,即 Javadoc 中。

2.4 @Inherited

Inherited 是指繼承,@Inherited 定義了一個注釋與子類的關(guān)系贵白。如果一個超類帶有 @Inherited 注解率拒,那么對于該超類,它的子類如果沒有被任何注解應用的話禁荒,那么這個子類就繼承了超類的注解猬膨。

《秒懂,Java 注解 (Annotation)你可以這樣學》一文中的例程與解釋來說明:

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


@Test
public class A {}


public class B extends A {}

注解 Test 被 @Inherited 修飾呛伴,之后類 A 被 Test 注解勃痴,類 B 繼承 A,類 B 也擁有 Test 這個注解∪瓤担可以這樣理解:
老子非常有錢沛申,所以人們給他貼了一張標簽叫做富豪。
老子的兒子長大后姐军,只要沒有和老子斷絕父子關(guān)系铁材,雖然別人沒有給他貼標簽,但是他自然也是富豪奕锌。
老子的孫子長大了著觉,自然也是富豪。
這就是人們口中戲稱的富一代惊暴,富二代饼丘,富三代。雖然叫法不同辽话,好像好多個標簽肄鸽,但其實事情的本質(zhì)也就是他們有一張共同的標簽,也就是老子身上的那張富豪的標簽油啤。

2.5 @Repeatable

@Repeatable 是 Java 8 中加入的典徘,是指可重復的意思。通常使用 @Repeatable 的時候指注解的值可以同時取多個益咬。依舊用《秒懂烂斋,Java 注解 (Annotation)你可以這樣學》一文中的例程與解釋來說明:一個人既是程序員,又是產(chǎn)品經(jīng)理础废,同時也是畫家。

@interface Persons {
    Person[] value();
}

@Repeatable(Persons.class)
@interface Person {
    String role default "";
}

@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan {
    ...
}

上面的代碼通過 @Repeatable 定義了 Person罕模,而 @Repeatable 后面括號的類相當于一個容器注解评腺。容器注解就是用來存放其它注解的地方,它本身也是一個注解淑掌。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    Class<? extends Annotation> value();
}

上面是 @Repeatable 的源碼蒿讥。按照規(guī)定,如果使前面的 Persons 里面可以重復調(diào)用某個注解,則 Persons 必須有一個 value 的屬性芋绸,且屬性類型必須為被 @Repeatable 注解的 Person媒殉。

三. 注解的屬性

注解的屬性也叫做成員變量。注解只有成員變量摔敛,沒有方法廷蓉。注解的成員變量在注解的定義中以無形參的方法形式來聲明,其方法名定義了該成員變量的名字马昙,其返回值定義了該成員變量的類型桃犬。以下面的例程為例:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Coder {
    int id();
    String name();
    String language();
    String company();
}

上面假設定義了一個名為 @Coder 的注解,該注解有 id, name, language, company 三個屬性行楞。使用的時候攒暇,我們應該對其賦值。賦值的方式類似于 key="value" 的方式進行子房,屬性之間用 "," 隔開:

@Coder(id = 10086, name = "GRQ", language = "JAVA", company = "cetc")
public class coderGRQ() {

}

此外形用,注解可以有默認值,需要用 default 關(guān)鍵字指定证杭。例如上例:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Coder {
    public int id() default -1;
    public String name() default "GRQ";
    public String language() default "C++";
    public String company() default "China_Company";
}

如果:

@Coder
public class coderXX {}

由于在 @Coder 注解中設置了默認值田度,所以就不需要再 @Coder 后面的括號里進行賦值了。

此外躯砰,如果注解內(nèi)只有一個名為 value 的屬性時每币,應用該屬性時可以將值直接寫到括號內(nèi),不用寫 value = "..."琢歇。例如:

public @interface language {
    String value();
}

那么下面兩種聲明是相同的:

// 第一種聲明
@language("JAVA")
int coderA;
// 第二種聲明
@language(value = "JAVA")
int coderA;

四. 常用注解

Java 中自帶且常用的幾種注解有 @Override, @Deprecated, @SuppresWarninngs, @SafeVarargs兰怠。

@Override 是一個標記類型注解,用于提示子類要復寫父類中被 @Override 修飾的方法李茫,它說明了被標注的方法重載了父類的方法揭保,起到了斷言的作用。如果我們使用了這種注解在一個沒有覆蓋父類方法的方法時魄宏,java編譯器將以一個編譯錯誤來警示秸侣。

@Deprecated 也是一個標記類型注解,用于標記過時的元素宠互。比如如果開發(fā)人員正在調(diào)用一個過時的方法味榛、類或成員變量時,可以用該注解進行標注予跌。

@SuppressWarnings 并不是一個標記類型注解搏色,它可以阻止警告的提示。它有一個類型為 String[] 的成員券册,其值為被禁止的警告名频轿。

@SafeVarargs 是一個參數(shù)安全類型注解垂涯。它的目的是提醒開發(fā)人員,不要用參數(shù)做一些不安全的操作航邢。它的存在會阻止編譯器產(chǎn)生 unchecked 的警告耕赘。例如對于可變長度參數(shù),如果和泛型一起使用膳殷,會產(chǎn)生比較多的編譯器警告操骡。如下面的方法:

public static <T> T useVarargs(T... args) {  
    return args.length > 0 ? args[0] : null;  
} 

如果參數(shù)傳遞的是不可具體化的類型(類似于 List<String> 的泛型類型),每調(diào)用一次該方法秽之,都會產(chǎn)生警告信息当娱。如果希望禁止這個警告信息,可以使用 @SuppressWarnings("unchecked") 注解進行聲明考榨。同時在 Java 7 版本之后的 @SafeVarargs 注解針對 "unchecked" 警告進行了屏蔽跨细,我們也可以用 @SafeVarargs 獲得 @SuppressWarnings("unchecked") 同樣的效果。

五. 自定義注解

此處參考《注解Annotation實現(xiàn)原理與自定義注解例子》的原理介紹和水果例程河质。

自定義注解類編寫的規(guī)則:

  1. 注解類型定義為 @interface冀惭,所有的注解會自動繼承 java.lang.Annotation 這一接口,而且不能再去繼承其他的類或接口掀鹅;
  2. 參數(shù)成員只能用 public 或 default 兩個關(guān)鍵字修飾散休;
  3. 參數(shù)成員只能用基本類型:byte, short, char, int, long, float, double, boolean,以及 String, Enum, Class, Annotations 等數(shù)據(jù)類型乐尊,以及這些類型的數(shù)組戚丸;
  4. 要獲取類方法和字段的注解信息,必須通過 Java 的反射技術(shù)扔嵌;
  5. 注解也可以不定義成員變量限府,但這樣的注解沒有什么卵用;
  6. 自定義注解需要使用元注解進行編寫痢缎;

以水果與水果供應商為例:

水果名稱注解 FruitName.java:

package com.grq.FruitAnnotation;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 水果名稱注解
 */
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitName {
    String value() default "";
}

水果顏色注解 FruitColor.java:

package com.grq.FruitAnnotation;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 水果顏色注解
 */
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitColor {
    /**
     * 顏色枚舉
     */
    public enum Color{ BLUE,RED,GREEN};

    /**
     * 顏色屬性
     */
    Color fruitColor() default Color.GREEN;

}

水果供應者注解 FruitProvider.java:

package com.grq.FruitAnnotation;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 水果供應者注解
 */
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface FruitProvider {
    /**
     * 供應商編號
     */
    public int id() default -1;

    /**
     * 供應商名稱
     */
    public String name() default "";

    /**
     * 供應商地址
     */
    public String address() default "";
}

注解處理器 FruitInfoUtil.java:

package com.grq.FruitAnnotation;

import java.lang.reflect.Field;

/**
 * 注解處理器
 */
public class FruitInfoUtil {
    public static void getFruitInfo(Class<?> clazz){

        String strFruitName=" 水果名稱:";
        String strFruitColor=" 水果顏色:";
        String strFruitProvicer="供應商信息:";

        Field[] fields = clazz.getDeclaredFields();

        for(Field field :fields){
            if(field.isAnnotationPresent(FruitName.class)){
                FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
                strFruitName=strFruitName+fruitName.value();
                System.out.println(strFruitName);
            }
            else if(field.isAnnotationPresent(FruitColor.class)){
                FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
                strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
                System.out.println(strFruitColor);
            }
            else if(field.isAnnotationPresent(FruitProvider.class)){
                FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
                strFruitProvicer=" 供應商編號:"+fruitProvider.id()+" 供應商名稱:"+fruitProvider.name()+" 供應商地址:"+fruitProvider.address();
                System.out.println(strFruitProvicer);
            }
        }
    }
}

蘋果 Apple.java:

package com.grq.FruitAnnotation;

/**
 * 注解使用
 */
public class Apple {

    @FruitName("Apple")
    private String appleName;

    @FruitColor(fruitColor = FruitColor.Color.RED)
    private String appleColor;

    @FruitProvider(id=1,name="陜西紅富士集團",address="陜西省西安市延安路89號紅富士大廈")
    private String appleProvider;

    public void setAppleColor(String appleColor) {
        this.appleColor = appleColor;
    }
    public String getAppleColor() {
        return appleColor;
    }

    public void setAppleName(String appleName) {
        this.appleName = appleName;
    }
    public String getAppleName() {
        return appleName;
    }

    public void setAppleProvider(String appleProvider) {
        this.appleProvider = appleProvider;
    }
    public String getAppleProvider() {
        return appleProvider;
    }

    public void displayName(){
        System.out.println("水果的名字是:蘋果");
    }
}

測試輸出水果信息 FruitTestAnnotation:

package com.grq.FruitAnnotation;

public class TestFruitAnnotation {
    public static void main(String[] args) {
        FruitInfoUtil.getFruitInfo(Apple.class);
    }
}

運行后的測試結(jié)果為:

水果名稱:Apple
水果顏色:RED
供應商編號:1 供應商名稱:陜西紅富士集團 供應商地址:陜西省西安市延安路89號紅富士大廈

后記

這段時間雖然在 SpringMVC 中用注解用的飛起胁勺,各種 @RequestMapping, @Service, @Controller 等注解信手拈來,但還是不了解它的運作原理到底是什么樣的独旷。尤其是在框架中署穗,大量運用到了注解與反射操作,所以以后也會認真了解一下如 Spring 框架中注解的運行原理嵌洼,想必這無論是對理解框架案疲,還是對理解注解本身,都會有很大的幫助麻养。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末褐啡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子回溺,更是在濱河造成了極大的恐慌春贸,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遗遵,死亡現(xiàn)場離奇詭異萍恕,居然都是意外死亡,警方通過查閱死者的電腦和手機车要,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門允粤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人翼岁,你說我怎么就攤上這事类垫。” “怎么了琅坡?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵悉患,是天一觀的道長。 經(jīng)常有香客問我榆俺,道長售躁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任茴晋,我火速辦了婚禮陪捷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诺擅。我一直安慰自己市袖,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布烁涌。 她就那樣靜靜地躺著苍碟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪烹玉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天二打,我揣著相機與錄音,去河邊找鬼继效。 笑死症杏,一個胖子當著我的面吹牛瑞信,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凡简,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼精肃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了帜乞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤黎烈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后照棋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體资溃,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年烈炭,在試婚紗的時候發(fā)現(xiàn)自己被綠了溶锭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡梳庆,死狀恐怖暖途,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情膏执,我是刑警寧澤驻售,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站更米,受9級特大地震影響欺栗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜征峦,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一迟几、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧栏笆,春花似錦类腮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至针饥,卻和暖如春厂抽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背丁眼。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工筷凤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人苞七。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓藐守,卻偏偏與公主長得像挪丢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子卢厂,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 本文章涉及代碼已放到github上annotation-study 1.Annotation為何而來 What:A...
    zlcook閱讀 29,119評論 15 116
  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一種元程序中的元素關(guān)聯(lián)任何信息和...
    九尾喵的薛定諤閱讀 3,143評論 0 2
  • step 1: 新建一個文件夾放字體文件吃靠。 step 2 : 在css文件中寫下如下代碼: @font-face ...
    槿時_xy閱讀 969評論 1 0
  • 【22.1】圣人之學,心學也足淆。堯、舜礁阁、禹之相授受曰:“人心惟危巧号,道心惟微,惟精惟一姥闭,允執(zhí)厥中∨锲罚”此心學之源也。中也...
    靜_8831閱讀 1,389評論 0 0