Java所有的框架底層是通過注解和反射實現
Java.Annotation
-
Annotation是從JDK5.0開始引入的新技術
-
Annotation的作用:
-
不是程序本身,可以對程序作出解釋。(這一點和注釋(comment)沒什么區別)
-
-
-
Annotation的格式:
-
注解是以"@注釋名"在代碼中存在的,還可以添加一些參數值,例如:@SupperessWarnings(value="unchecked")。
-
-
Annotation在那里使用?
-
可以附加在package,class,method,field等上面,相當於給他們添加了額外的輔助信息,我們可以通過反射機制編程實現對這些數據的訪問。
-
內置注解
元注解
-
元注解的作用就是負責注解其他注解,Java定義了4個標准meta-annotation類型,它們被用來提供對其他annotation類型做說明
-
這些類型和它們所支持的類在java.lang.annotation包中可以找到(@Target,@Retention,@Docunmented,@Inherited)
-
@Target:用於描述注解的使用范圍(即:被描述的注解可以用在什么地方)
-
@Retendtion:表示需要在什么級別保存該注釋信息,用於描述注解的生命周期
-
(SOURCE<CLASS<RUNTIME)
-
-
@Document:說明該注解被包含在javadoc中
-
@Innherited:說明子類可以繼承父類中的該注解
-
package com.zhou.annotation;
import java.lang.annotation.*;
//測試元注解
自定義注解
-
使用@interface自定義注解時,自定繼承了java.lang.annotation.Annotation
-
分析:
-
@interface用來聲明一個注解,格式:public @interface 注解名{定義內容}
-
其中的每一個方法實際上是聲明了一個配置參數。
-
方法的名稱就是參數的名稱
-
返回值類型就是參數的類型(返回值只能是基本類型,class,String,enum)
-
可以通過default來聲明參數的默認值
-
如果只有一個參數成員,一般參數名為value
-
注解元素必須要有值,我們定義注解元素時,經常使用空字符串,作為默認值。
-
package com.zhou.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//自定義一個注解
public class Test03 {
//注解可以顯式賦值,如果沒有默認值,我們就必須給注解賦值
反射機制
Java反射機制概述
靜態VS動態語言
動態語言 ➢是一類在運行時可以改變其結構的語言:例如新的函數、對象、甚至代碼可以被刪除或是其他結構上的變化。通俗點說就是在運行時代碼可以根據某些條件改變自身結構。 ➢主要動態語言:Object-C、 C#、 JavaScript、 PHP、 Python等。
靜態語言 ➢與動態語言相對應的,運行時結構不可變的語言就是靜態語言。Java、C、C++ ➢Java不是動態語言,但Java可以稱之為“准動態語言” 。即Java有一定的動態性,我們可以利用反射機制獲得類似動態語言的特性。Java的動態性讓編程的時候更加靈活!
Java Reflection
➢Reflection (反射) 是Java被視為動態語言的關鍵,反射機制允許程序在執行期借助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法。 Class c= Class.forNam(java lang .String") ➢加載完類之后,在堆內存的方法區中就產生了一一個Class類型的對象(一個類只有一個Class對象), 這個對象就包含了完整的類的結構信息。我們可以通過這個對象看到類的結構。這個對象就像一面鏡子,透過這個鏡子看到類的結構,所以,我們形象的稱之為:反射
package com.zhou.reflect;
//什么叫反射
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException{
//通過反射獲取類的class對象
Class c=Class.forName("com.zhou.reflect.User");
System.out.println(c);
Class c1=Class.forName("com.zhou.reflect.User");
Class c2=Class.forName("com.zhou.reflect.User");
//一個類在內存中只有一個class對象
//一個類被加載后,類的整個結構都會被封裝在class對象中。
System.out.println(c.hashCode());
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
}
}
//實體類 entity
class User{
private String name;
private int id;
private int age;
public User(){
}
Java反射機制提供的功能
➢在運行時判斷任意一個對象所屬的類 ➢在運行時構造任意一個類的對象 ➢在運行時判斷任意一個類所具有的成員變量和方法 ➢在運行時獲取泛型信息 ➢在運行時調用任意一個對象的成員變量和方法 ➢在運行時處理注解 ➢生成動態代理
Java反射優點和缺點
優點:可以實現動態創建對象和編譯,體現了很大的靈活性
缺點:對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么並且它滿足我們的要求。這類操作總是慢於直接執行相同的操作。
理解Class類並獲取Class實例
Class類
在Object類中定義了以下方法,此方法將被所有子類繼承
public final Class getClass()
-
以上的方法返回值的類型是一個Class類,此類是Java反射的源頭,實際上所謂的反射從程序的運行結果來看也很好理解,即:可以通過對象反射求出類的名稱。
-
對象照鏡子后可以得到的信息:某個類的屬性、方法和構造器、某個類到底實現了哪些接口。對於每個類而言,JRE 都為其保留一個不變的Class類型的對象。一個Class對象包含了特定某個結(class/interface/enum/annotation/primitive type/void/[])的有關信息。 ➢Class本身也是一個類 ➢Class 對象只能由系統建立對象 ➢一個加載的類在JVM中只會有一個Class實例 ➢一個Class對象對應的是一個加載到JVM中的一個.class文件 ➢每個類的實例都會記得自己是由哪個Class實例所生成 ➢通過Class可以完整地得到一個類中的所有被加載的結構 ➢Class類 是Reflection的根源,針對任何你想動態加載、運行的類,唯有先獲得相應的Class對象
Class類的常用方法
獲取Class類的實例
哪些類型可以有Class對象
-
class:外部類,成員(成員內部類,靜態內部類),局部內部類,匿名內部類
-
interface:接口
-
[]:數組
-
enum:枚舉
-
annototion:注解@interface
-
primitive type:基本數據類型
-
void
package com.zhou.reflect;
import java.lang.annotation.ElementType;
//所有類型的class
public class Test04 {
public static void main(String[] args) {
Class c1=Object.class;
Class c2=Comparable.class;
Class c3=String[].class;
Class c4=int[][].class;
Class c5=Override.class;
Class c6= ElementType.class;
Class c7=Integer.class;
Class c8=void.class;
Class c9=Class.class;
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
//驗證同一類型的對象,不論數組長度如何,一種類型是否只有一個Class
int[] a=new int[10];
int[] b=new int[100];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
}
}
Java內存分析
類的加載與ClassLoader
➢加載:將class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法區的運行時數據結構,然后生成一個代表這個類的java.lang.Class對象. ➢鏈接:將Java類的二進制代碼合並到JVM的運行狀態之中的過程。 ➢驗證: 確保加載的類信息符合JVM規范,沒有安全方面的問題 ➢准備:正式為類變量(static) 分配內存並設置類變量默認初始值的階 段,這些內存都將在方法區中進行分配。 ➢解析:虛擬機常量池內的符號引用(常量名)替換為直接引用(地址)的過程。 ➢初始化: ➢執行類構造器< clinit> ()方法的過程。類構造器< clinit> ()方法是由編譯期自動收集類中所有類變量的賦值動作和靜態代碼塊中的語句合並產生的。(類構造器 是構造類信息的,不是構造該類對象的構造器)。 ➢當初始化一個類的時候, 如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化。 ➢虛擬機會保證一 個類的<clinit> ()方法在多線程環境中被正確加鎖和同步。
類加載器的作用
-
類加載器的作用:將class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法區的運行時數據結構,然后在堆中生成一個代表這個類的java.lang.Class對象,作為方法區中類的數據訪問入口。
-
類緩存:標准的JavaSE類加載器可以按要求查找類,但一旦某個類加載到類加載器中,它將維持加載(緩存)一段時間。不過jvm垃圾回收機制可以回收這些Class對象。
-
創建運行時類的對象
-
創建類的對()象:調用Class對象的new Instance()方法
-
類必須有一個無參數的構造器
-
類的構造器的訪問權限需要足夠
-
-
沒有無參構造器的時候,可以在操作的時候明確的調用類中構造器
步驟如下
-
通過Class類的getDeclaredConstructor(Class ... parameterTypes)取得本類的指定參數類型的構造器
-
向構造器的形參中傳遞用一個對象數組進去,里面包含了構造器中所需的各個參數
-
通過Constructor實例化對象
-
獲取運行時類的完整結構
調用運行時類的指定結構
利用反射進行注解
-
getAnnotation
-
getAnnotations
package com.zhou.reflect;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class Test11 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.zhou.reflect.Student01");
Annotation[] annotations = c1.getAnnotations();
//通過反射獲得注解
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//通過反射獲得注解的value
Annota a1 = (Annota)c1.getAnnotation(Annota.class);
System.out.println(a1.value());
//獲得類中的指定屬性的注解
Field name = c1.getDeclaredField("name");
Fil f1 = (Fil)name.getAnnotation(Fil.class);
System.out.println(f1.type());
System.out.println(f1.length());
System.out.println(f1.name());
}
}
//創建一個實體類