thinking-in-java(20)注解
【1】注解介紹
1)注解是什么?
注解(元數據)是 java提供的一種對程序元素(如類,方法,變量)打標記的方法;以便運行程序或編譯器可以識別這些元素進行特殊處理;最典型的用法是,框架代碼啟動時掃描某注解修飾的所有類并將其加載到內存,以構建系統底層框架,更直白點,類,方法,變量被注解修飾后,更容易被識別以便加載;
補充: 顯然,開發或學習底層框架的同學肯定必不可少要學習注解;所以注解是相當重要的;
【2】java內置的三種注解?
1)@Override: 表示被標記的方法將覆蓋超類中的方法;
2)@Deprecated:表示被標記的元素(類或方法)被廢棄了 ; 如果代碼使用該元素,則編譯器會發出警告;
3)@SuppressWarnings:關閉不當的編譯器警告信息;
【3】4種標準元注解
1)元注解:元注解的作用是負責修飾其他注解(參與其他注解的定義或聲明)。 java5.0 定義了4個標準的元注解類型; (干貨——你也可以認為被注解修飾,就是被注解打標記)
1.1)@Target元注解,用于指定被修飾注解可以應用到哪些元素上;
如 注解可以被應用到 packages,type(類,接口,枚舉,Annotation類),類型成員(方法,構造方法,成員變量,枚舉值),方法參數和本地變量等;
注解定義例子1:
@Target(ElementType.METHOD) // 表示用于修飾(標記)方法 @Retention(RetentionPolicy.RUNTIME) // 表示運行時可用 public @interface Test {} ///:~注解應用例子1:?
public class Testable {public void execute() {System.out.println("Executing..");}@Test // Test 注解用于修飾方法void testExecute() {execute();} }1.2)@Retention元注解,用于指定被修飾注解的生命周期;
- SOURCE:源文件有效;被編譯器丟棄;
- CLASS:class文件有效;被JVM丟棄;
- RUNTIME :運行時有效(常用);VM將在運行期也保留注解,通過反射機制讀取注解;
1.3)@Document? 用于指定被修飾的注解可以被 javadoc 工具文檔化;
1.4)@Inherited?? 用于指定被修飾的注解是可以被繼承的
2)上面例子中修飾的注解 Test,是沒有任何成員方法的。下面我們來看有成員方法的注解UseCase;·
// 下面定義了一個注解UseCase:該注解作用于方法,該注解在運行時發揮作用 // 該注解UseCase應用于什么地方 @Target(ElementType.METHOD) // 該注解UseCase的應用級別,源代碼-SOURCE, 類文件中-CLASS, 運行時-RUNTIME @Retention(RetentionPolicy.RUNTIME) public @interface UseCase {public int id();public String description() default "no description"; // no description 是默認值 } /* D:\workbench_idea\study4vw\thinkinjava\src>javap chapter20.UseCase Compiled from "UseCase.java" public interface chapter20.UseCase extends java.lang.annotation.Annotation {public abstract int id();public abstract java.lang.String description(); } */反編譯(javap)后的源碼:可以看到,注解就是一個接口,該接口的父類是 Annotation 注解接口類型,當然了,接口中的方法都是抽象方法。
2.1)使用 UseCase注解;
// 荔枝-有3個方法被注解UseCase修飾為用例 public class PasswordUtils {// 將 UseCase 注解作用于方法@UseCase(id = 47, description = "validatePassword method")public boolean validatePassword(String password) {return (password.matches("\\w*\\d\\w*"));}@UseCase(id = 48) // description 默認為 no descriptionpublic String encryptPassword(String password) {return new StringBuilder(password).reverse().toString();}@UseCase(id = 49, description = "checkForNewPassword method")public boolean checkForNewPassword(List<String> prevPasswords, String password) {return !prevPasswords.contains(password);} } /* 反編譯簡要結果如下: D:\workbench_idea\study4vw\thinkinjava\src>javap chapter20.PasswordUtils Compiled from "PasswordUtils.java" public class chapter20.PasswordUtils {public chapter20.PasswordUtils();public boolean validatePassword(java.lang.String);public java.lang.String encryptPassword(java.lang.String);public boolean checkForNewPassword(java.util.List<java.lang.String>, java.lang.String); } */解析注解
/*** 注解解析測試用例*/ public class AnnotationDiyTest {public final static <T> void f1(Class<T> c1) {for (Method method : c1.getMethods()) { // 獲取類定義方法UseCase useCase = method.getAnnotation(UseCase.class); // 獲取方法上某個注解if (useCase != null) {System.out.println(useCase.id() + "-" + useCase.description()); // 解析注解}}}public static void main(String[] args) {f1(PasswordUtils.class);} } /* 47-validatePassword method 48-no description 49-checkForNewPassword method */【4】自定義注解
UseCase注解有兩個方法,方法返回類型有 int,String;此外還有其他返回類型,包括
所有基本類型; String; Class enum; Annotation;// 注解的成員方法的返回值類型還可以是注解類型 以及以上類型的數組;下面定義一個嵌套注解(即注解的方法返回類型是注解類型)
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SQLString {int value() default 0;String name() default "";Constraints constraints() default @Constraints();// 嵌套注解 } // /:~ // 定義注解(并定義嵌套注解) @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SQLInteger {int value() default 0;String name() default "";// 嵌套注解,因為 Constraints 也是注解類型Constraints constraints() default @Constraints; } // /:~ // 荔枝-定義注解(并定義嵌套注解) @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SQLDouble {double value() default 0f;// 嵌套注解,因為 Constraints 也是注解類型Constraints constraints() default @Constraints; } // 荔枝-定義注解用于生成一個數據庫表 @Target(ElementType.TYPE) // 注解作用于哪里(注解的作用對象) // Applies to classes only @Retention(RetentionPolicy.RUNTIME) // 注解的作用時間(這里是運行時) public @interface DBTable {// DBTable 有一個name元素// 這個注解通過使用 value元素 為處理器創建數據庫表提供的表名public String value() default ""; } // /:~ // 荔枝-為修飾 javabean域 準備的注解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Constraints {boolean primaryKey() default false; // 主鍵boolean allowNull() default true; // 允許為空boolean unique() default false; // 唯一鍵 } // /:~使用注解創建數據庫表對應的javabean
// DBTable 注解作用于類 @DBTable("MEMBER") public class Member {@SQLString(30) String firstName;@SQLString(value=50, name = "lastName") String lastName;@SQLInteger(name = "age", constraints = @Constraints(primaryKey=true, allowNull=false, unique=true)) Integer age;@SQLString(value = 30 , name = "handle", constraints = @Constraints(primaryKey = true))String handle;static int memberCount;public String getHandle() {return handle;}public String getFirstName() {return firstName;}public String getLastName() {return lastName;}public String toString() {return handle;}public Integer getAge() {return age;} } // /:~使用注解的快捷方式:如果注解中定義了名為value元素(value方法),且在使用注解時,該元素是唯一一個需要復制的元素,那么只需要給出value元素的值即可,無需寫鍵值對形式
@DBTable("MEMBER")-快捷方式 , @DBTable(value="MEMBER")-普通方式
【5】使用apt處理注解
1)問題, 上述代碼中,都需要為注解編寫處理器以解析注解; 很麻煩;
使用 apt類庫方法可以生成注解處理器,apt 是使用源代碼生成注解的,無法通過class文件獲取類的屬性;但 mirror api 可以允許程序員在源代碼中查看類屬性,如方法,屬性;
2)注解
// 抽取注解 @Target(ElementType.TYPE) // @Retention(RetentionPolicy.SOURCE) // 在源文件起作用 public @interface ExtractInterface {public String value(); } // /:~【6】小結
java se5僅提供了很少的注解,大多數情況下,需要自定義注解及注解處理器(特別是開發底層框架的時候,需要自定義注解);
javassist 能夠用來操作字節碼;mirror api 能夠用來找出java源代碼中的元素 (apt-mirror-api maven repo);
總結
以上是生活随笔為你收集整理的thinking-in-java(20)注解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑图片太大导致内存不够电脑图片占内存太
- 下一篇: DevExperience(1801)