一.註解Annotation
1.什麼是註解
註解是以“@註釋名”在程式碼中存在。
程式碼中的特殊標記,這些標記可以在編譯、類載入、執行時被讀取,並執行相對應的處理。
減少了配置,大量減少了程式碼量,透過反射機制實現對這些元資料(為其他資料提供資訊的資料)的訪問。
在開發中使用也是比較常見的,比如:@Controller、@Param、@Data等,Java原生(標記&檢查)有@Overried、@Deprecated、@Functional
2.基本內建註解
@Override 表示這個方法重寫了父類的方法
@Deprecated 表示這個方法已經過期,不建議開發者使用
@SuppressWarnings 表示忽略警告資訊
3.自定義註解
利用反射讀取註解
public class Test3 { public static void main(String[] args) { try { // 1.反射 Class clazz = Class.forName("com.annotation.Student"); // 2.獲得這個類的註解 Annotation[] annotations = clazz.getAnnotations(); for (Annotation annotation:annotations){ System.out.println(annotation); } // 3.獲得類的註解value的值 TableKuang table = (TableKuang)clazz.getAnnotation(TableKuang.class); System.out.println(table.value()); // 4.獲得類指定註解的值 Field name = clazz.getDeclaredField("name"); FieldKuang fieldKuang = name.getAnnotation(FieldKuang.class); System.out.println(fieldKuang.columnName()+"-->"+fieldKuang.type()+"-->"+fieldKuang.length()); } catch (ClassNotFoundException | NoSuchFieldException e) { e.printStackTrace(); } } }@TableKuang("db_student")class Student{ @FieldKuang(columnName = "db_id",type="int",length = 10) private int id; @FieldKuang(columnName = "db_name",type="varchar",length = 10) private String name; @FieldKuang(columnName = "db_age",type="int",length = 3) private int age; ......} // 表名註解@Target(value = {ElementType.TYPE})@Retention(value = RetentionPolicy.RUNTIME)@interface TableKuang{ String value();} // 屬性註解@Target(value = {ElementType.FIELD})@Retention(value = RetentionPolicy.RUNTIME)@interface FieldKuang{ String columnName(); // 列名 String type(); // 型別 int length(); // 長度}
4.元註解
元註解的作用就是負責註解其他註解
@Tartget:用於描述註解的使用範圍(Type修飾類、介面或列舉型別;Filed成員變數;Method方法;)
@Retention:表示生命週期(source<class<runtime)
@Inherited:表示子類可以繼承父類的註解
@Ducumented:表示註解將被包含在javadoc中
二.反射
這裡講一下 「 執行時 」,.java檔案經過javac編譯變成.class檔案,class檔案會被JVM裝載執行。
我記得在工作中,我有一個需求是主系統對使用者表、部門表等增量變化的同時要求去增量同步子系統對應的使用者表、部門表資料。增量同步的這個工具類就有用到了反射技術。一個好的工具就要相容各種情況(使用者、部門等)。
還有就是SpringMVC中在方法寫上物件,傳入的引數就會封裝到物件上。
這些例子都有涉及到反射:約定大於配置,配置大於硬編碼。
2.1 什麼是反射
反射是Java被視為動態語言的關鍵,反射機制允許程式在執行期藉助於反射API得到類的內部資訊,並能直接操作任意物件的內部屬性以及方法。
載入類之後,在堆記憶體的方法區就產生了一個Class型別的物件,這個物件就包含了完整的類的結構資訊。
優點:可以實現動態建立物件和編譯,體現出很大的靈活性。
缺點:對效能有影響,慢於直接執行相同的操作。
2.2 類的載入過程
2.2.1 載入
將class檔案位元組碼內容載入到記憶體,並將這些靜態資料轉換成方法區的執行時資料結構,然後生成一個代表這個類的java.lang.Class物件
2.2.2 連結
將Java類的二進位制程式碼合併到JVM的執行狀態之中的過程
驗證:確保載入的類資訊符合JVM規範
準備:正式為類變數分配記憶體並設定類變數預設初始值的階段(方法區)
解析:虛擬機器常量池內的符號引用替換為直接引用的過程
2.2.3 初始化
執行類構造器方法的過程
當初始化一個類的時候,如果發現其父類還沒有初始化,則需要先觸發其父類的初始化
虛擬機器會保證一個類的方法在多執行緒環境中被正確加鎖和同步
2.3 類的載入器
類載入器的作用是將class檔案位元組碼內容載入記憶體中,並將靜態資料轉換成方法資料結構,然後在堆中生成一個代表這個類的java.lang.class物件,作為方法區中類資料的訪問入口
2.4 獲取類物件
wistchen.getClass()Class.forName("包名+類名")Wistchen.classClassLoader classLoader = this.getClass().getClassLoader(); classLoader.loadClass(className);2.5 類物件的常用方法
static ClassforName(String name) 返回指定類名name的class物件
Object newInstance() 呼叫預設建構函式,返回Class物件的一個例項
getName() 返回此Class物件所表示的實體的名稱
Class getSuperClass() 返回當前CLass物件的父類的Class物件
Class[] getinterfaces() 獲取當前Class物件的介面
ClassLoader getClassLoader() 返回該類的類載入器
Constructor[] getConstructors() 返回一個包含某些Constructor物件的陣列
Method getMethed(String name,Class T) 返回一個Method物件
Field getDeclaredFields() 可以獲取本類所有的欄位,包括private的,但是不能獲取繼承來的欄位。 (注: 這裡只能獲取到private的欄位,但並不能訪問該private欄位的值,除非加上 setAccessible(true))
Field getField 只能獲取public的,包括從父類繼承來的欄位。
2.5 建立物件
Class pClass = Class.forName(className); 類物件
Constructor c = pClass.getConstructor();構造器
c.newInstance() 透過構造器例項化
2.6 訪問屬性
public class TestReflection { public static void main(String[] args) { Hero hero = new Hero(); hero.name = "garen"; try{ Field f1 = hero.getClass().getDeclaredField(("name")); f1.set(hero,"teemo"); System.out.println(hero.name); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } }} class Hero{ public String name; //為了訪問屬性,把name修改為public public float hp; public int damage; public int id; public String getName() { return name; } public void setName(String name) { this.name = name; } public Hero(){ } public Hero(String string) { name =string; } @Override public String toString() { return "Hero [name=" + name + "]"; } public boolean isDead() { // TODO Auto-generated method stub return false; } public void attackHero(Hero h2) { System.out.println(this.name+ " 正在攻擊 " + h2.getName()); }}
2.7 呼叫方法
public class TestReflection2 { public static void main(String[] args) { Hero1 hero1 = new Hero1(); try{ // 獲取這個名字叫做setName,引數型別是String的方法 Method m = hero1.getClass().getMethod("setName", String.class); // 對h物件,呼叫這個方法 m.invoke(hero1,"wistchen"); System.out.println(hero1.getName()); } catch (Exception e) { e.printStackTrace(); } } }class Hero1 { public String name; public float hp; public int damage; public int id; public String getName() { return name; } public void setName(String name) { this.name = name; } public Hero1(){ } public Hero1(String string) { name =string; } @Override public String toString() { return "Hero1 [name=" + name + "]"; } public boolean isDead() { // TODO Auto-generated method stub return false; } public void attackHero(Hero h2) { // TODO Auto-generated method stub } }
學習參考來自:
B站狂神說
HOW2J.CN