淺嘗註解和反射
註解和反射
一、註解
格式:
可以被其他程式讀取,格式“@+註釋名”如:@Override, 還可以加一些引數值,如:@SuppressWarnings(value = "unchecked").
使用:
可以附加在package、class、method、field等上面,相當於添加了額外的輔助資訊,可以通過反射機制程式設計實現對這些資料元的訪問,如,在使用資料庫時用註解標識出具體資訊,可以通過讀出註解拼出SQL語句,生成表(即用來解釋類和表結構的對映關係)
內建註解:
@Override //重寫
@Deprecated //過時的,不推薦使用,或者存在更好的方式
@SuppressWarnings("all")//鎮壓警告,消除警告
元註解
負責註解其他註解
@Target //*用於描述註解的使用範圍(“註解可以在什麼地方使用”)
//Field用於屬性、Method用於方法、Type用於類...
@Retention//*表示需要在什麼級別儲存該註釋資訊,用於描述註解的生命週期(SOURCE < CLASS < RUNTIME)
@Document//說明該註解將被包含在Javadoc中
@Inherit//說明子類可以繼承父類中的該註解
//測試元註解 package annotation; import java.lang.annotation.*; @MyAnnotation public class test02 { @MyAnnotation void run(){ } } //定義一個註解 @Target(value = {ElementType.METHOD, ElementType.TYPE}) //表示註解可以用在哪些地方, METHOD用在方法上 @Retention(value = RetentionPolicy.RUNTIME) //表示註解在什麼地方還有效 原始碼時 < class時 < 執行時 @Documented @Inherited @interface MyAnnotation{ }
例:
package annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; public class test03 { @MyAnnotation01(name = "gw") //若所需的引數只有一個則可以省略 = public void test(){ } } @Target(value = {ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation01{ //註解的引數, 引數型別 + 引數名 String name() default "";//如果沒有預設值且重寫沒有賦值,則報錯 int age() default 0; int id() default -1; String[] schools() default {"CDUT", "CUDT"}; }
二、反射
建立執行時類的物件、獲取執行時類的完整結構、呼叫執行時類的指定結構、可以獲取註解、泛型(即通過物件得到類),java的反射機制增加了java的動態性
使用場景:
如果程式物件是自己new的,那程式相當於寫死了給jvm去跑。假如一個伺服器上突然遇到某個請求哦要用到某個類,哎呀但沒載入進jvm,是不是要停下來自己寫段程式碼,new一下,哦啟動一下伺服器,(腦殘)!反射就是在伺服器不停止工作的情況下滿足使用者提出的未載入至程式中的類中
例如: 例項化一個 person()物件, 不使用反射, new person(); 如果想變成例項化其他類, 那麼必須修改原始碼,並重新編譯。
使用反射: class.forName("person").newInstance(); 而且這個類描述可以寫到配置檔案中,如 **.xml, 這樣如果想例項化其他類,只要修改配置檔案的"類描述"就可以了,不需要重新修改程式碼並編譯。
//***一個類在記憶體中只有一個Class物件***
//***一個類被載入後類的整個結構都會被封裝在該Class物件中***
獲取Class類的例項
a)若已知具體的類,通過類的class屬性獲取,該方法最為安全可靠,效能最高
Class c1 = Person.class;//Person為類名
b)已知某個類的例項,呼叫該例項的getClass()方法獲取Class物件
Class c2 = person.getClass();//person為Person類下的例項
c)已知一個類的全類名, 且該類在類路徑下,可通過Class類的靜態方法forName()獲取,可能丟擲ClassNotFoundException
Class c3 = Class.forName("demo01.Student");
d)內建基本資料型別可以直接用類名.Type
e)還可以用ClassLoader
可以通過c1.getSuperclass()獲得父類的型別
可以獲得Class物件的型別
- class:外部類,成員(成員內部類、靜態內部類),區域性內部類,匿名內部類
- interface:介面
- []陣列
- enum:列舉
- annotation:註解@interface
- 基本資料型別
- void
public class test02 {
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());
}
}
三、類的記憶體分析,類的生成過程
如何生成Class物件
載入:將class檔案位元組碼內容載入到記憶體中,並將這些靜態資料轉化成方法區的執行時資料結構,然後生成java.lang.Class物件
連結:驗證、準備、解析
初始化:執行類構造器
類什麼時候初始化
主動引用:
- 虛擬機器啟動,先初始化main所在的類
- new一個類的物件時
- 呼叫類的靜態成員(除了final常量)和靜態方法
- 使用java.lang.reflect包的方法對內進行優先反射呼叫
- 當初始化一個類,其父類為被初始化,優先初始化父類
被動引用(不會發生初始化):
- 當訪問一個靜態域時,只有真正宣告這個域的類才會被初始化(通過子類呼叫父類的靜態變數不會導致子類的初始化)
- 通過陣列定義類的引用時,不會觸發類的初始化
- 引用常量不會觸發此類的初始化(常量在連結階段已經存入呼叫類的常量池中)
四、通過反射獲取執行時類的Method、Field、Constructor、Superclass、Interface、Annotation
獲得類的一些基本屬性
package reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//獲得類的資訊
public class test03 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("reflection.User");
//獲得類的名字
System.out.println(c1.getName());
System.out.println(c1.getSimpleName());
//獲得類的屬性Field
Field[] fields = c1.getFields();//只能找到public屬性
fields = c1.getDeclaredFields();//能找到全部的屬性
for (Field field : fields) {
System.out.println(field);
}
//獲得指定屬性的值
Field name = c1.getDeclaredField("name");
System.out.println(name);
//獲得類的方法Method
System.out.println("========================");
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
methods = c1.getDeclaredMethods();
for (Method method : methods) {
System.out.println("declared:" + method);
}
//獲得指定方法
Method getName = c1.getMethod("getName",null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
//獲得指定的構造器Constructor
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println(declaredConstructor);
}
}
通過反射獲取物件
package reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//通過反射建立物件
public class test04 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class c1 = Class.forName("reflection.User");
//通過newInstance()建立(不推薦)
User user = (User)c1.newInstance();
System.out.println(user);
//通過構造器呼叫方法
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user2 = (User)declaredConstructor.newInstance("123", 1, 2);
System.out.println(user2);
//通過反射獲取一個方法
User user3 = (User)c1.newInstance();
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user3, "kuang");
System.out.println(user3.getName());
//獲得方法Method後用invoke(物件名, 引數)函式來呼叫該方法
//通過反射操作屬性
User user4 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true); //通過該方法可以操作private變數
name.set(user4, "kuangshen");
System.out.println(user4.getName());
}
}
獲取註解資訊
package reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class test05 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("reflection.Student2");
//通過反射獲得註解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//獲得註解value的值
TableGw tableGw = (TableGw)c1.getAnnotation(TableGw.class);
String value = tableGw.value();
System.out.println(value);
//獲得類的指定註解
Field f = c1.getDeclaredField("id");
FieldGw annotation = f.getAnnotation(FieldGw.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@TableGw("db_student")
class Student2{
@FieldGw(columnName = "db_id", type = "int", length = 10)
private int id;
@FieldGw(columnName = "db_age", type = "int", length = 10)
private int age;
@FieldGw(columnName = "db_name", type = "varchar", length = 3)
private String name;
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
//類名的註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableGw{
String value();
}
//屬性的註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldGw{
String columnName(); //列名
String type();
int length();
}