天天看點

Java進階特性之注解

概念

Java注解是JDK1.5的新特性,與注釋比較類似,不同的是注釋是給我們開發人員看的,注解是給代碼看的,它是代碼層面的解釋說明。

注解的使用也很簡單,文法規則:@注解名稱,比如我們常見的“@Override”。

作用

①生成doc文檔;

②使用反射對代碼進行分析;

③編譯檢查。

JDK中預定義的一些注解:

①@Override: 檢測方法是否是重寫父類或父接口的;

②@Deprecated: 辨別過時的内容;

③@SuppressWarnings: 壓制警告,一般傳入參數all,@SuppressWarnings("all")

自定義注解:

格式:

[元注解]

public @interface 注解名稱{

    屬性清單;

}

eg.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface ZnkAnno {
String value();
}      

ps:[ ] 表示可選。

注解本質:

通過javap指令反編譯class檔案,可以發現注解的本質就是繼承 java.lang.annotation.Annotation的接口。

Java進階特性之注解

注解中的屬性(其實就是接口中的抽象方法):

文法:

1>屬性的傳回值必須是基本資料類型、String、Class、枚舉、注解或者以上類型的數組;

2>定義屬性後,在使用注解時要給屬性指派,如果定義屬性時使用default關鍵字給了屬性預設值,則可以不做指派;如果隻有一個名稱為value的屬性,則value可以省掉,直接定義值即可;數組指派時需要{},如果數組中隻有一個值,則可省略{}。 

Java進階特性之注解

元注解

元注解就是用來解釋說明注解的注解。

①@Target:描述注解能夠作用的位置。

 --ElementType常用取值:

  • TYPE:可以作用在類上;
  • FIELD:可以作用在屬性上;
  • METHOD:可以作用在方法上。

PS:所有取值見下圖。

Java進階特性之注解

②@Retention:描述注解被保留的階段(java代碼SOURCE(源碼階段)、CLASS(位元組碼檔案)、RUNTIME(運作時階段)三個階段)

    -- @Retention(RetentionPolicy.RUNTIME) 表示目前被描述的注解會被保留到class位元組碼檔案中,并會被JVM讀取到

③@Documented:描述注解是否被抽取到api文檔中

④@Inherited:描述注解是否被子類繼承

注解的使用場景

上面我們已經了解到注解的保留級别包括SOURCE、CLASS、RUNTIME三個階段,那我們根據保留級别不同,自然能想到注解的使用也存在不同場景。

級别 技術 備注
源碼 APT

1.在編譯期能夠擷取注解與注解聲明的類,包括類中所有成員資訊,一般用于生成額外的輔助類。

2.也可以用作文法檢查。

位元組碼 位元組碼增強 在編譯出Class後,通過修改Class資料以實作修改代碼邏輯目的。對于是否需要修改的區分或者修改為不同邏輯的判斷可以使用注解。
運作時 反射 在程式運作期間,通過反射技術動态擷取注解與其元素,進而完成不同的邏輯判定。

--源碼級别的注解示例:

1>APT(Annotation Processor Tools)

Java進階特性之注解

2>文法檢查:在Android開發過程中,當我們需要設計接口供使用者調用時,如果需要對入參類型做限定,比如限定為資源ID、布局ID等類型參數,就可以通過@IdRes、@LayoutRes分别限定。同時我們可以利用@IntDef、@StringDef、@LongDef 定義自己的入參類型檢查。

Java進階特性之注解

--位元組碼級别的注解在熱修複技術中會用到,這裡暫時不做較長的描述(關鍵我自己還沒整明白{>-<})。

--運作時的注解主要是與反射結合做一些操作,比如我們常用的ButterKnife,最初就是通過注解加反射的方式實作的,我給出的git代碼裡面也有實作findViewById的案例,感興趣的小夥伴可以瞄一眼。

Java進階特性之注解

反射擷取泛型真實類型(很少用到,了解即可)

當我們對泛型類進行反射時,需要拿到泛型資訊來完成像Json反序列化的操作時,需要借助Type體系來完成。Type接口包含一個實作類Class和四個實作接口:

Java進階特性之注解

我們開發過程中經常會有這樣一種場景,我們請求背景接口,背景傳回的資料類型可能是這樣的:

{

    "data":{

        "name":"luffy",

        "age":18,

        "sex":"male"

    },

    "code":200,

    "message":"查詢成功"

}

我們會将背景傳回的資料封裝成一個Bean:

static class Response<T> {
T data;
int code;
String message;

public Response(T data, int code, String message) {
this.data = data;
this.code = code;
this.message = message;
        }

@Override
public String toString() {
return "Response{" +
"data=" + data +
", code=" + code +
", message='" + message + '\'' +
'}';
        }
    }      

 因為每個接口傳回的data多數情況下類型是不同的,是以我們一般用泛型處理。比如現在某個接口傳回的資料是

{

    "name":"luffy",

    "age":18,

    "sex":"male"

}

我們将它封裝成Bean:

static class Data {
String name;
int age;
String sex;

public Data(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
        }

@Override
public String toString() {
return "Data{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
        }
    }      

 我們測試如上的資料是否可以進行反序列化:如下圖所示,出現了ClassCastException。

Java進階特性之注解

解決方案:

Type type = new TypeToken<Response<Data>>() {
}.getType();
Response<Data> response = gson.fromJson(result, type);      
Java進階特性之注解

 我們來看看TypeToken裡面的具體實作:

Java進階特性之注解

我也按照這個思路試着實作了一下: 

package com.luffy.reflect.Type;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
* 作者甯科CSDN首頁</a><p>
* 建立時間:2020/10/27 15:51<p>
* 描述:參考TypeToken 解決Json反序列化問題
*/
public class TypeRefrence<T> {
Type type;
T t;

protected TypeRefrence() {
//1.獲得泛型類型
Type genericSuperclass = getClass().getGenericSuperclass();
System.out.println("genericSuperclass的類型是: " + genericSuperclass.getClass());
System.out.println("genericSuperclass ====== " + (genericSuperclass instanceof Class));
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
//2.因為泛型類型可以定義多個: MainActivity<T,E,K...> 是以傳回是一個數組
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
type = actualTypeArguments[0];
    }

public Type getType() {
return type;
    }
}