天天看點

Java注解and反射注解反射

注解

注解和注釋類似,注釋是寫給程式員看的,為了友善程式員看懂程式的代碼。

而注解不單單是給程式員看,更重要的是給程式看。不同的注解有不同的功能,除了系統定義的注解以外,我們也可以自定義注解。

注解是以@開始的。

系統内置的常見注解

@Override

這個用于判斷方法是否是重寫放到的注解,它會檢查你的方法名是不是與父類中的方法同名,如果不是會報錯,如果是則不報錯。@Override注解隻能用于方法。

@Deprecated

單詞的意思是過時的。可以用于修飾方法,表示這個方法已經過時,應該選擇别的方法使用,即使用了不會報錯,隻是不建議用。

@SuppressWarnings

壓制警告,主要的作用是消除警告。究竟要壓制什麼警告,寫在括号中。

@SuppressWarnings("unused")
int a = 10;
		
@SuppressWarnings({"rawtypes","unused"})
ArrayList list = new ArrayList();
           

@FunctionalInterface

函數式接口,用來修飾接口,接口中隻能有一個方法。

自定義注解

想要建立一個注解,文法格式如下:

public @interface 注解名 {
	注解的屬性
}
           

定義一個注解,需要指定以下資訊:

  1. 注解能用在什麼地方。(屬性,方法,構造器,類…)

    @Target({ElementType.FIELD, ElementType.METHOD,…})

    元注解:@Target是元注解的一種。

    所謂的元注解就是注解注解的注解。

    @Target的取值範圍:

    TYPE:用于注解類,接口,枚舉。

    FIELD:用于注解屬性。

    METHOD:用于注解方法。

    PARAMETER:用于注解方法的參數

    CONSTRUCTOR:用于注解構造器

    LOCAL_VARIABLE:用于注解局部變量

    ANNOTATION_TYPE:用于注解一個注解

    PACKAGE:用于注解包

    TYPE_PARAMETER:用于注解類型參數

    TYPE_USE:用于注解用了一個類型

  2. 注解的聲明周期多久。(原碼階段,位元組碼階段,運作期)

    @Retention(RetentionPolicy.SOURCE)

    @Retention也是一個元注解。

    取值範圍

    RetentionPolicy.SOURCE:僅源代碼有效,不會被編譯成class檔案。一般定義為SOURCE的注解,僅僅做檢查用,檢查是不是方法重寫等。

    RetentionPolicy.CLASS:會編譯成class檔案,但是運作期無效。

    RetentionPolicy.RUNTIME:運作期有效,也就意味着,在運作期可以擷取注解對象,讀取注解中寫入的資訊。-----很有用,尤其是自己寫類庫的時候。

    其他元注解:

    @Documented用于表示注解會一起寫入API文檔(javadoc.exe)

    @Inherited一旦一個注解定義為可繼承的,注解如果加給了父類,那麼之類會把注解繼承過來。

  3. 注解有哪些屬性。

    注解可以包含屬性,屬性由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;
}
           

注解的一些注意事項:

  1. 注解中的屬性名後面必須要有().
  2. 如果注解中的屬性是一個數組,使用時,如果數組隻有一個值,大括号可以省略
  3. 如果注解隻有一個屬性,且屬性名為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類有以下幾種擷取方式:

  1. 類名.class
    基本資料類型也可以擷取對應的Class執行個體 用法是int.class double.class 普通類擷取Class對象的方法:String.class Person.class
  2. 對象.getClass()
  3. 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對象。

反射多用于寫架構,幾乎所有的架構底層都是靠反射實作的。