1. 程式人生 > 實用技巧 >CF1238E.Keyboard Purchase 題解 狀壓/子集劃分DP

CF1238E.Keyboard Purchase 題解 狀壓/子集劃分DP

3 註解
3.1 註解,或者叫做註釋型別,英文單詞是:Annotation
疑問:註解是幹什麼的?


3.2 註解Annotation是一種引用資料型別。編譯之後也是生成xxx.class檔案。

3.3 怎麼自定義註解呢?語法格式?
[修飾符列表]@interface 註解型別名{

}

3.4 註解怎麼使用,用在什麼地方?
第一:註解使用時的語法格式是:
@註解型別名

第二:註解可以出現在類上、屬性上、方法上、變數上等。。。
註解還可以出現在註解型別上。

3.5 JDK內建了哪些註解?

掌握:
Deprecated 用 @Deprecated 註釋的程式元素,不鼓勵程式設計師使用這樣的元素,通常是因為它很危險或存在更好的選擇。


掌握:
Override 表示一個方法宣告打算重寫超類中的另一個方法宣告。

瞭解:
SuppressWarnings 指示應該在註釋元素(以及包含在該註釋元素中的所有程式元素)中取消顯示指定的編譯器警告。

3.6 元註解
什麼是元註解?
用來標註“註解型別”的“註解”,稱為元註解。

常見的元註解有哪些呢?
Target
Retention

關於Target註解:
這是一個元註解,用來標註“註解型別”的“註解”
這個Target註解用來標註“被標註的註解”可以出現在哪些位置上。

@Target(ElementType.METHOD):表示“被標註的註解”只能出現在方法上。
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
表示該註解可以出現在:
構造方法上
欄位上
區域性變數上
方法上
...
類上...

3.7 Retention的原始碼:

// 元註解
public @interface Retention {
// 屬性
RetentionPolicy value();
}

RetentionPolicy的原始碼:
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}


3.8 Target的原始碼


關於Retention註解:
這是一個元註解,用來標註“註解型別”的“註解”
這個Retention註解用來標註“被標註的註解”最終儲存在哪裡。

@Retention(RetentionPolicy.SOURCE):表示該註解紙杯保留在java原始檔中。
@Retention(RetentionPolicy.Class):表示該註解被儲存在class檔案中。
@Retention(RetentionPolicy.RUNTIME):表示該註解被儲存在class檔案中,並且可以被反射機制所讀取。


3.9 註解在開發中有什麼用呢?
需求:
假設有這樣一個註解,叫做:@Id
這個註解只能出現在類上面,當這個類上有這個註解的時候,要求這個類中必須有一個int型別的id屬性。
如果沒有這個屬性就報異常。如果有這個屬性則正常執行!

案例1:

package com.javaSe.annotation;
/*
1 註解,或者叫做註釋,英文單詞是:Annotation
2 註解Annotation是一種引用資料型別。編譯之後也是生成xxx.class檔案。
3 怎麼自定義註解呢?語法格式?
    [修飾符列表]@interface 註解型別名{
    
    }
    
4 預設情況下,註解可以出現在任意位置。
*/
@MyAnnotation
public class AnnotationTest01 {
    
    @MyAnnotation
    private int no;
    
    @MyAnnotation
    public static void m1(){
        @MyAnnotation
        int i = 100;
    }
    
    @MyAnnotation
    public static void m2(@MyAnnotation String name,
                          @MyAnnotation int k){
    
    }
    
    @MyAnnotation
    public AnnotationTest01() {
    }
    
    @MyAnnotation
    public AnnotationTest01(int no) {
        this.no = no;
    }
}


@MyAnnotation
interface MyInterface{


}


@MyAnnotation
enum Season{
    SPRING,SUMMER,AUTUMN,WINTER
}

關於JDK當中的override(編譯)註解:
package com.javaSe.annotation;
/*
關於JDK lang包下的override註解


原始碼:
public @interface Override {}


@Override這個註解只能註解方法。
@Override這個註解是給編譯器參考的,和執行階段沒有關係。
@Override凡是java中的方法帶有這個註解的,編譯器都會進行編譯檢查,如果這個方法不是重寫父類的方法,編譯器報錯。


標識性註解,給編譯器作參考的。
編譯器看到方法上有這個註解的時候,編譯器會自動檢查該方法是否重寫了父類的方法。
如果沒有重寫,報錯。


這個只是在編譯階段起作用,和執行期無關。
*/
public class AnnotationTest02 {
    
    @Override
    public String toString() {
        return "toString()";
    }
    
    public static void main(String[] args) {
    
    }
}

關於JDK當中的Deprecated(已過時)註解:

package com.javaSe.annotation;


// 表示這個類已過時。
// @Deprecated
public class AnnotationTest03 {
    public static void main(String[] args) {
        AnnotationTest03 at = new AnnotationTest03();
        at.doSome();
    }
    
    
    @Deprecated
    public void doSome(){
        System.out.println("do something!");
    }
    
    // Deprecated這個註解標註的元素已過時。
    // 這個註解主要是向其他程式設計師傳達一個資訊,告知已過時,有更好的解決方案存在。
    @Deprecated
    public static void doOther(){
        System.out.println("do other...");
    }
}


class T{
    public static void main(String[] args) {
        AnnotationTest03 at = new AnnotationTest03();
        at.doSome();
    
        AnnotationTest03.doOther();
    }
}

自定義註解:

package com.javaSe.annotation;
/*
自定義註解:MyAnnotation
*/
public @interface MyAnnotation {
}

註解修飾註解:

package com.javaSe.annotation;


// 註解修飾註解
@MyAnnotation
public @interface OtherAnnotation {


}

自定義註解:

package com.javaSe.annotation2;


public @interface MyAnnotation {
    
    /**
     * 我們通常在註解當中可以定義屬性,以下是MyAnnotation的name屬性。
     * 看著像一個方法,但實際上我們稱之為屬性name。
     * @return
     */
    String name();
    
    /**
     * 顏色屬性
     * @return
     */
    String color();
    
    /**
     * 年齡屬性
     * @return
     */
    int age() default 25; // 屬性指定預設值
}

註解中的屬性用法:

package com.javaSe.annotation2;


public class MyAnnotationTest {
    
    // 報錯的原因:如果一個註解當中有屬性,那麼必須給屬性賦值。(除非該屬性使用了default指定了預設值)
    /*@MyAnnotation()
    public void doSome(){
    
    }*/
    
    // @MyAnnotation(屬性名=屬性值)
    // 指定name的屬性值就好了
    @MyAnnotation(name="zhangsan",color = "紅色")
    public void doSome(){
    
    }
}

自定義註解:

package com.javaSe.annotation3;


public @interface MyAnnotation {
    
    /**
     * 指定一個value屬性
     * @return
     */
    String value();
    
    // String email();
}

如果註解中的屬性是value,而且只有一個屬性的時候,value可以省略:

package com.javaSe.annotation3;
/*
如果一個註解的屬性的名字是value的話,並且只有一個屬性的話,在使用的時候,該屬性名可以省略。
*/
public class MyAnnotationTest {
    
    // 報錯原因:沒有指定屬性的值。
    /*@MyAnnotation()
    public void doSome(){
    
    }*/
    
    @MyAnnotation(value = "hehe")
    public void doSome(){
    
    }
    
    @MyAnnotation("haha")
    public void doOther(){
    
    }
}

自定義註解:

package com.javaSe.annotation3;


public @interface OtherAnnotation {
    String name();
}

如果註解中的屬性不是value,那麼會報錯

package com.javaSe.annotation3;


public class OtherAnnotationTest {
    
    // 報錯了,因為屬性名是name,不能省略。
    // @OtherAnnotation("test")
    
    // 正確的
    @OtherAnnotation(name = "test")
    public void doSome(){
    
    }
}

自定義註解(註解當中的屬性可以是哪一種型別?):
package com.javaSe.annotation4;


public @interface MyAnnotation {
    /*
    註解當中的屬性可以是哪一種型別?
        byte short int long float double double char String Class 列舉型別 以及以上每一種陣列形式
     */
    
    int value1();
    
    String value2();
    
    int[] value3();
    
    String[] value4();
    
    Season value5();
    
    Season[] vakye6();
    
    Class parameterType();
    
    Class[] parameterTypes();
    
}

自定義列舉:

package com.javaSe.annotation4;


public enum Season {
    SPRING,SUMMER,ANTUMN,WINTER
}

自定義註解:

package com.javaSe.annotation4;


public @interface OtherAnnotation {
    
    /**
     * 年齡屬性
     * @return
     */
    int age();
    
    /**
     * 郵箱陣列,支援多個
     * @return
     */
    String[] email();
    
    /**
     * 季節陣列,Season是列舉型別
     * @return
     */
    Season[] seasonArray();
}

如果註解是陣列的話,該如何使用:

package com.javaSe.annotation4;


import java.lang.annotation.Retention;


public class OtherAnnotationTest {
    
    // 陣列是大括號
    @OtherAnnotation(age = 25, email = {"123","234","345","456"},seasonArray = Season.WINTER)
    public void doSome(){
    
    }
    
    @Deprecated
    // 如果陣列中只有一個元素:大括號可以省略
    @OtherAnnotation(age = 25, email = "789",seasonArray = {Season.SPRING,Season.SUMMER})
    public void doOther(){
    
    }
}

自定義註解:

package com.javaSe.annotation5;


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


// 只允許註解可以標註類、方法
@Target({ElementType.TYPE,ElementType.METHOD})
// 希望這個註解可以被反射到
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "黑龍江";
}

使用註解:

package com.javaSe.annotation5;


/*
為什麼註解不能用在成員變數和區域性變數中還有構造方法中,因為你沒有在註解類中定義。
*/


@MyAnnotation("四川雅安")
public class MyAnnotationTest {
    
    // @MyAnnotation
    int i;
    
    @MyAnnotation
    public void doSome(){
        // @MyAnnotation
        int i;
    }
    
    // @MyAnnotation
    public MyAnnotationTest() {
    
    }
}

通過反射機制獲取註解:

package com.javaSe.annotation5;


public class ReflectAnnotationTest {
    public static void main(String[] args) throws Exception{
        
        // 獲取這個類
        Class c = Class.forName("com.javaSe.annotation5.MyAnnotationTest");
        
        // 判斷類上面是否有這個註解
        boolean b = c.isAnnotationPresent(MyAnnotation.class);
        // System.out.println(b);// true
        if(b){
            // 獲取該註解物件
            MyAnnotation mt = (MyAnnotation) c.getAnnotation(MyAnnotation.class);
            // System.out.println("類上面的註解物件" + mt); //類上面的註解物件@com.javaSe.annotation5.MyAnnotation()
            // 獲取註解物件的屬性怎麼辦?和調介面沒區別。
            String value = mt.value();
            System.out.println(value);
        }
        
        // 判斷String類上面是否存在這個註解
        Class stringClass = Class.forName("java.lang.String");
        boolean b1 = stringClass.isAnnotationPresent(String.class);
        System.out.println(b1);// false
    }
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

註解在程式中的用處:程式碼如下:

自定義@Id註解

package com.javaSe.annotation7;

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)
public @interface Id {

}

// 這個註解@Id用來標註類,被標註的類中必須有一個int型別的id屬性,沒有就報異常

User使用者業務類:

package com.javaSe.annotation7;

@Id
public class User {
    int id;
    String name;
    String password;
}

測試程式:

package com.javaSe.annotation7;

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception{
        // 獲取類
        Class userClass = Class.forName("com.javaSe.annotation7.User");
        // 判斷類上是否存在@Id註解
        if(userClass.isAnnotationPresent(Id.class)){
            // 當一個類上有@Id註解的時候,要求類中必須存在int型別的id屬性
            // 如果沒有int型別的id屬性則報異常。
            // 獲取類的屬性
            Field[] fields = userClass.getDeclaredFields();
            boolean isOk = false;// 給一個預設的標記
            for (Field field : fields){
                if("id".equals(field.getName()) && "int".equals(field.getType().getSimpleName())){
                    // 表示這個類是合法的類。有@Id註解,則這個類中必須有int型別的id
                    isOk = true; // 表示合法
                    break;
                }
            }
            
            // 判斷是否合法
            if(!isOk){
                throw new HasNotIdPropertyException("被@Id註解標註的類中必須要有一個int型別的id屬性!");
            }
        }
    }
}

自定義異常:

package com.javaSe.annotation7;

/*
自定義異常
 */
public class HasNotIdPropertyException extends RuntimeException{
    public HasNotIdPropertyException(){
    
    }
    
    public HasNotIdPropertyException(String s){
        super(s);
    }
}