注解
注解和注釋類似,注釋是寫給程式員看的,為了友善程式員看懂程式的代碼。
而注解不單單是給程式員看,更重要的是給程式看。不同的注解有不同的功能,除了系統定義的注解以外,我們也可以自定義注解。
注解是以@開始的。
系統内置的常見注解
@Override
這個用于判斷方法是否是重寫放到的注解,它會檢查你的方法名是不是與父類中的方法同名,如果不是會報錯,如果是則不報錯。@Override注解隻能用于方法。
@Deprecated
單詞的意思是過時的。可以用于修飾方法,表示這個方法已經過時,應該選擇别的方法使用,即使用了不會報錯,隻是不建議用。
@SuppressWarnings
壓制警告,主要的作用是消除警告。究竟要壓制什麼警告,寫在括号中。
@SuppressWarnings("unused")
int a = 10;
@SuppressWarnings({"rawtypes","unused"})
ArrayList list = new ArrayList();
@FunctionalInterface
函數式接口,用來修飾接口,接口中隻能有一個方法。
自定義注解
想要建立一個注解,文法格式如下:
public @interface 注解名 {
注解的屬性
}
定義一個注解,需要指定以下資訊:
-
注解能用在什麼地方。(屬性,方法,構造器,類…)
@Target({ElementType.FIELD, ElementType.METHOD,…})
元注解:@Target是元注解的一種。
所謂的元注解就是注解注解的注解。
@Target的取值範圍:
TYPE:用于注解類,接口,枚舉。
FIELD:用于注解屬性。
METHOD:用于注解方法。
PARAMETER:用于注解方法的參數
CONSTRUCTOR:用于注解構造器
LOCAL_VARIABLE:用于注解局部變量
ANNOTATION_TYPE:用于注解一個注解
PACKAGE:用于注解包
TYPE_PARAMETER:用于注解類型參數
TYPE_USE:用于注解用了一個類型
-
注解的聲明周期多久。(原碼階段,位元組碼階段,運作期)
@Retention(RetentionPolicy.SOURCE)
@Retention也是一個元注解。
取值範圍
RetentionPolicy.SOURCE:僅源代碼有效,不會被編譯成class檔案。一般定義為SOURCE的注解,僅僅做檢查用,檢查是不是方法重寫等。
RetentionPolicy.CLASS:會編譯成class檔案,但是運作期無效。
RetentionPolicy.RUNTIME:運作期有效,也就意味着,在運作期可以擷取注解對象,讀取注解中寫入的資訊。-----很有用,尤其是自己寫類庫的時候。
其他元注解:
@Documented用于表示注解會一起寫入API文檔(javadoc.exe)
@Inherited一旦一個注解定義為可繼承的,注解如果加給了父類,那麼之類會把注解繼承過來。
-
注解有哪些屬性。
注解可以包含屬性,屬性由3部分構成。 類型 屬性名() 預設值;
屬性名的寫法與 無參方法相同。
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.FIELD, ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface MyAnnotation {
int a();
String[] b();
double c() default 3.14;
}
注解的一些注意事項:
- 注解中的屬性名後面必須要有().
- 如果注解中的屬性是一個數組,使用時,如果數組隻有一個值,大括号可以省略
- 如果注解隻有一個屬性,且屬性名為value,在使用時,value可以省略不寫。
public class Person {
@MyAnnotation(a = 10, b = {"hello","hehe"})
private String name;
private int age;
@MyAnnotation(a = 20, b = "world")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@MyAnnotation(a = 100, b = "lanou" ,c = 25.8)
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Deprecated
public void test() {
}
@MyAnnotation2(12)
public void test2() {
@SuppressWarnings(value = "unused")
int a = 10;
@SuppressWarnings({"rawtypes","unused"})
ArrayList list = new ArrayList();
}
}
學習注解的第一個目标:能看懂注解,看懂系統或者第三方類庫中注解的含義,能會使用注解。
學習注解的終極目标:某天自己寫類庫,寫注解,讓别人用。注解往往是結合反射使用的。
反射
生活中的反射
太陽光照射到鏡子上,會發生反射。反射時光的方向會發生改變(略微的反向)。
代碼中的反射,改變了我們使用對象的方式。之前:對象調方法,對象設定屬性。反射剛好反了一下。方法調用對象,屬性調用對象。
在Java中反射是比較底層的内容,想要實作反射需要使用Class類。
什麼是Class類
類是相同僚物的抽象。類在描述一類事物。例如:人都有姓名,性别,年齡,還有吃飯、睡覺、打豆豆。
通過建立對象,可以形成各種不同的人。
類是由誰來描述的呢?Class就是描述類的類。類也有共性,屬性,方法,構造器,父類,接口。每一個類都有對應一個Class執行個體。你可以認為一個Class對象對應的是一個xx.class檔案。
Class沒有建立方法,每個類的Class執行個體是在這個類的class檔案加載到記憶體中的時候,由ClassLoader建立。我們隻能擷取Class類的對象。
Class類有以下幾種擷取方式:
- 類名.class
基本資料類型也可以擷取對應的Class執行個體 用法是int.class double.class 普通類擷取Class對象的方法:String.class Person.class
- 對象.getClass()
- Class.forName(“類的完整字元串描述”)
//擷取Class對象的方式一: 類名.class
Class class1 = int.class;
System.out.println(class1);
Class class2 = String.class;
System.out.println(class2);
Class class3 = Person.class;
System.out.println(class3);
//擷取Class對象的方式二: 對象.getClass();
String str = "hello";
Class class4 = str.getClass();
System.out.println(class4);
System.out.println(class4 == class2);
Person p = new Person();
Class class5 = p.getClass();
System.out.println(class5);
System.out.println(class3 == class5);
//擷取Class對象的方式三: Class.forName("完整類名")
Class class6 = Class.forName("java.util.ArrayList");
System.out.println(class6);
Class class7 = Class.forName("com.lanou.lessonclass.Person");
System.out.println(class7);
反射
在Java中提供了一套機制,這種機制允許你通過Class對象來擷取類的全部資訊(屬性,方法,構造器,接口,父類,注解等等),擷取之後,你可以對他們做你想做的事情。哪怕是私有的也可以操作。
通過Class對象擷取類的屬性
在Java中Filed類是用于描述屬性的類。
通過Class對象擷取類的屬性的方式有4個:
getFields() //擷取類中所有的public屬性(含繼承鍊上的public屬性)
getField(String name) //擷取類中指定名稱的public屬性。(含繼承鍊上的 public屬性)
getDeclaredFields() //擷取類中所有的屬性(不含繼承鍊上的屬性)
getDeclaredFiled(String name) //擷取類中指定名稱的屬性
通過Class對象擷取類的構造器
在Java中Constructor類用于描述類的構造器。
通過Class對象擷取類的構造器的方式有4個:
getConstructor(Class …parameterTypes) // 擷取本類中指定參數的public構造器
getConstructors() //擷取本類中全部的public構造器
getDeclaredConstructor(Class…parameterTypes) //擷取本類中指定參數的構造器
getDeclaredConstructors() //擷取本類中全部的構造器。
通過Class對象擷取類的方法
在Java中Method是用于描述類的方法。
通過Class對象擷取類的方法的方式有4種:
getMethods() // 擷取全部的public方法(含繼承鍊上的方法)
getMethod(String methodName, Class…parameterTypes) //擷取指定名稱的public方法
getDeclaredMethods() //擷取本類中所有的方法
getDeclaredMethod(String methodName, Class…paremeterTypes) //擷取本類中指定名稱的方法。
反射的作用
反射給的第一感覺:脫了褲子放屁!
實際上反射的價值就在于能動态擷取資訊!
雖然寫了很多很多代碼,但是代碼一旦寫成,複用性很高。而且不需要導包!因為Class.forName(“類名”)可以以字元串的方式擷取類名,再通過類名得到類的Class對象。
反射多用于寫架構,幾乎所有的架構底層都是靠反射實作的。