# java註解與反射 - java註解與反射十分重要,是很多框架的底層 ## 註解(Annotataion) - 註解的作用: 1. 不是程式本身,可以對程式作出解釋 1. 可以被其他程式讀取 - 註解的格式:@註釋名,如@override表示重寫方法,而且有些還可以添加一些參數值,如@Suppr ...
java註解與反射
- java註解與反射十分重要,是很多框架的底層
註解(Annotataion)
-
註解的作用:
1. 不是程式本身,可以對程式作出解釋 1. 可以被其他程式讀取
-
註解的格式:@註釋名,如@override表示重寫方法,而且有些還可以添加一些參數值,如@SuppressWarnings(value="unchecjed")
-
註解可以附加在package,class,method,field等上面,相當於添加了額外的輔助信息。可以通過反射機制編程實現對這些元數據的訪問
內置註解
- @Override:重寫方法
- @Deprecated:表示不被鼓勵使用或者已廢棄已過時
- @SuppressWarning:用來抑制編譯時的警告信息,但是需要添加一個或多個參數:如("all"),("unchecked")等,平時最好還是不要用這個
使用方法:
@Override
public void run() {
}
其他類似。
元註解
- 負責註解其他註解
- 包括(@Target,@Retention,@Documented,@Inherited),分別表示
- 描述註解使用範圍
- 需要上面級別保存該註釋信息,用於描述註解的生命周期(SOURCE<CLASS<RUNTIME,分別表示源代碼,編譯後的class文件,運行時)
- 說明該註解將被包含在javadoc中
- 說明子類可以繼承父類中的該註解
//下麵只能用到方法上,value的值可以取多個至於那些可以取CTRL+左鍵點擊ElementType類去看,另外可以省略value=
@Target(value = ElementType.METHOD)
//只能存在於源碼上。值有那些同上,自己點進去看RetentionPolicy類,一般取RUNTIME
@Retention(value = RetentionPolicy.SOURCE)
//這兩個不多說
@Documented
@Inherited
//自定義註解
@interface MyAnnotation{
}
自定義註解
- 使用@interface自定義註解,格式 public @interface 註解名{}
- 其中每一個方法實際上是聲明瞭一個配置參數
- 方法名稱就是參數名稱
- 返回值類型就是參數類型(返回值只能是基本類型或class,String,enum)
- 可以用default來聲明參數的預設值
- 如果只有一個參數,一般參數名為value,只有一個參數時使用可以不寫參數名=
- 註解元素必須有值,我們定義註解元素時,經常使用空字元串,預設值為0
//元註解
@Retention(RetentionPolicy.RUNTIME)
@Inherited
//自定義註解
@interface MyAnnotation{
// 註解參數:參數類型+參數名(),可以設置預設值,沒預設值的話使用時必須傳參
String name() default "";
int a();
int c() default 1;
}
//使用
@MyAnnotation(name = "lihua",a = 1)
反射(Reflection)
java是一個靜態語言(還有c,c++),因為它相對於動態語言來說,運行時結構不變,但是java可以利用反射來獲得類似動態語言的特性,成為“準動態語言”。
動態語言就是運行時可以改變其結構的語言,如:c#,js,php,python等
-
反射機制運行程式在執行的過程中通過反射取得任何類的內部信息,並且能夠直接操作任意對象的內部屬性及方法。
-
主要API是Class
-
反射是什麼?
參考一下Class對象在java編譯運行的那個部分:
反射提供的部分功能:
-
優點與缺點
優點:動態編譯和創建,體現很大的靈活性
缺點:對性能有影響
Class類
除了之前描述的,Class類還有一些特征:
- Class 本身也是一個類
- Class對象只能有系統建立對象
- 一個載入的類在JVM中只會有一個Class實例
- 一個Class對象對應的是一個載入到JVM中的一個.class文件
- 每個類的實例都會記得自己是由那個Class實例所生成的
- 通過Class可以完整得到一個類中的所有被載入結構
- Class類是反射的根源,,你要進行的任何針對類的動態載入運行等反射操作,都需要先得到該類的Class對象
public static void main(String[] args) throws ClassNotFoundException {
/*1.下麵是通過反射獲取Class對象的一種方式,參數是文件路徑
2.一個類在記憶體中只有一個Class類,在多創建幾個該類的Class
也是同一個。
3一個類被載入後,類的整個結構都會被封裝在Class對象中
4.我們經常用的Object的getClass方法就是返回一個Class對象,也是創建Class對象的一種常用方式
5.反射在某種情況下可以理解為通過一個對象獲得類
*/
Class<?> aClass = Class.forName("com.xxx.MyThread");
System.out.println(aClass);
}
//out:class com.xxx.MyThread
Class類的常用方法:
Class類的四種創建方式
雖然上面可能已經描述過Class對象的創建方式的兩種,這裡還是再來具體寫一下:
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Teacher teacher = new Teacher();
// 方式一:通過對象
Class aClass = teacher.getClass();
System.out.println(aClass);
// 方式二:forname
Class<?> class1 = Class.forName("com.xxx.Teacher");
System.out.println(class1);
// 比較兩者
System.out.println(aClass==class1);
// 方式三:通過類名.class獲得
Class<Teacher> teacherClass = Teacher.class;
System.out.println(teacherClass);
// 比較
System.out.println(teacherClass==class1);
// 方式四:基本數據類型的包裝類的type屬性
Class<Integer> type = Integer.TYPE;
System.out.println(type);
// 得到父類Class類型
Class<?> superclass = class1.getSuperclass();
System.out.println(superclass);
// 補充一下,獲得註解和void和Class本身的Class對象
// Override.class
// void.class
//Class.class
}
}
class Teacher extends Person{}
/*
class com.xxx.Teacher
class com.xxx.Teacher
true
class com.xxx.Teacher
true
int
class com.xxx.Person
那些類型可以有Class對象?
包括以下:
註意:數組長度即使不一致,只要類型和維度一樣,就是同一個Class
Class類的方法
獲得類完整結構
public class MyThread {
private int a;
public void run(String a){}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public MyThread(int a) {
this.a = a;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class c1 = Class.forName("com.xxx.MyThread");
//獲取類名
System.out.println(c1.getName());
System.out.println(c1.getSimpleName());
// 獲得類屬性
Field[] fields = c1.getFields();//只能找到public屬性
fields = c1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
// 獲得類方法
System.out.println("-----------------------");
Method[] methods = c1.getMethods();//包括繼承的方法
methods=c1.getDeclaredMethods();//只包括本類的方法
for (Method method : methods) {
System.out.println(method);
}
System.out.println("-------------------------------");
// 獲得指定方法:沒s,兩個參數分別是方法名和方法參數類型的Class
System.out.println(c1.getMethod("run",String.class));
// 獲得構造器
System.out.println("----------------");
Constructor[] constructors = c1.getConstructors();//獲得public構造器
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
// 同理getDeclaredConstructors()獲得的所有構造器,
//獲得指定的構造器,參數為構造器參數的Class
System.out.println("--------------------------");
Constructor constructor = c1.getConstructor(int.class);
System.out.println(constructor);
}
}
/*out:
public void com.xxx.MyThread.run(java.lang.String)
public void com.xxx.MyThread.setA(int)
public int com.xxx.MyThread.getA()
-------------------------------
public void com.xxx.MyThread.run(java.lang.String)
----------------
public com.xxx.MyThread(int)
--------------------------
public com.xxx.MyThread(int)
操作類結構
public class MyThread {
private int a;
public void run(String a){
System.out.println(a);
}
public int getA() {
return a;
}
public MyThread() {
}
public void setA(int a) {
this.a = a;
}
public MyThread(int a) {
this.a = a;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
Class<?> aClass = Class.forName("com.xxx.MyThread");
// 構建一個對象:本質是調用無參構造器,其該類必須有無參構造器
// MyThread myThread = (MyThread)aClass.newInstance();
// 通過構造器創建對象,可以用有參構造函數
// Constructor constructor= aClass.getDeclaredConstructor(int.class);
// MyThread myThread1=(MyThread)constructor.newInstance(1);
// 通過反射調用方法
// 通過反射獲取一個方法
MyThread myThread = (MyThread) aClass.newInstance();
Method setName = aClass.getMethod("run", String.class);
// invoke:激活,參數:對象名,方法參數
setName.invoke(myThread,"helloworld");
// 通過反射操作屬性
Field a=aClass.getDeclaredField("a");
// 取消安全檢查,否則無法操作私有屬性.method,field,consructor都有它,關閉也可以提高反射效率
a.setAccessible(true);
a.set(myThread,5);
System.out.println(myThread.a);
}
/*out:
helloworld
5
類的載入過程
解釋一下符號引用和直接引用:
符號引用你可以理解為要引用的類目前在記憶體中還沒被載入出來,JVM當然不知道它在哪裡。所以就用唯一的符號來表示就好像給它賦了一個變數,等到鏈接的時候類載入器解析地址完成變成直接引用
直接引用就是存儲引用類在真實記憶體中的地址
總而言之還是這張圖:
什麼時候會發生類的初始化
主動引用比較好理解,下麵只演示一下被動引用“
public class Main {
// 初始化時執行一次靜態代碼塊
static {
System.out.println("main函數所在類被載入了");
}
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(son.sonAge);
/*
out:
main函數所在類被載入了
父類被載入了
子類被載入了
10
*/
//------------------------------------------------------
// 這就是第一條的意思
System.out.println(son.fatherAge);
/*
out:
main函數所在類被載入了
父類被載入了
23
*/
//--------------------------------------------------------
// 第二條:本質上只是開闢了一片空間
son[] arr = new son[5];
/*
out:
main函數所在類被載入了
*/
//--------------------------------------------------------
//常量是不會引起初始化的
System.out.println(son.M);
/*
out:
main函數所在類被載入了
1
*/
}
}
class father {
static int fatherAge = 23;
// 初始化時執行一次靜態代碼塊
static {
System.out.println("父類被載入了");
}
}
class son extends father{
static int sonAge = 10;
static final int M = 1;
static {
System.out.println("子類被載入了");
}
}
類載入器
- 類載入的作用:將class文件位元組碼內容載入到記憶體中,並且將這些靜態數據轉換成方法區的運行時數據結構,然後在堆中生成一個代表這個類的java.lang.Class 對象,作為方法區中類數據的訪問入口。簡單來說,類載入器的作用就是把類(class)裝進記憶體
- 類緩存:標準的JavaSE類載入器可以按照要求查找類,但一旦某個類被載入到類載入器中,它將維持載入(緩存)一段數據。不過JVM垃圾回收機制可以回收這些Class對象
類載入器有那些?
java的主要jar包就是rt.jar,引導類載入器就是載入這一類核心jar包
public static void main(String[] args) throws ClassNotFoundException {
// 獲取系統類的載入器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
// 獲取系統類載入器的父類載入器-->拓展載入器
ClassLoader classLoader1 = classLoader.getParent();
System.out.println(classLoader1);
// 獲得拓展類載入器的父類載入器-->根載入器(c/c++):這個java是無法獲得的
ClassLoader classLoader2 = classLoader1.getParent();
System.out.println(classLoader2);
System.out.println("------------------------");
// 測試當前類是那個載入器載入的
ClassLoader classLoader3 = Class.forName("com.xxx.MyThread").getClassLoader();
System.out.println(classLoader3);
// 測試jdk內部類是誰載入的:根載入器,輸出不出來
ClassLoader classLoader4 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader4);
//如何獲得系統類載入器可以載入的路徑
System.out.println(System.getProperty("java.class.path"));
}
/*out:
jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
jdk.internal.loader.ClassLoaders$PlatformClassLoader@5594a1b5
null
------------------------
jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
null
本處按道理來說應該輸出所有jar包路徑,但是只說出了文件根目錄,可能是因為jdk版本的原因,不確定,本處存疑
反射操作泛型
public class MyThread {
public void test01(Map<String,MyThread> map, List<MyThread> list){
}
public Map<String,Map> test02(){
return null;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
//獲得參數泛型
Method method = MyThread.class.getMethod("test01", Map.class,List.class);
//得到的是方法的參數Map,List
Type[] genericExceptionTypes = method.getGenericParameterTypes();
for (Type genericExceptionType : genericExceptionTypes) {
if(genericExceptionType instanceof ParameterizedType){
System.out.println(genericExceptionType);
//得到的是方法參數的泛型
Type[] actualTypeArguments = ((ParameterizedType) genericExceptionType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
//獲得返回值泛型
Method method1 = MyThread.class.getMethod("test02");
Type genericReturnType = method1.getGenericReturnType();
if(genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
/*
java.util.Map<java.lang.String, com.xxx.MyThread>
class java.lang.String
class com.xxx.MyThread
java.util.List<com.xxx.MyThread>
class com.xxx.MyThread
class java.lang.String
interface java.util.Map
反射操作註解
ORM->對象關係映射
public class MyThread {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class<?> aClass = Class.forName("com.xxx.Teacher");
// 通過反射獲得註解
Annotation[] annotations = aClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
// 獲得註解的value值
Table table = (Table) aClass.getAnnotation(Table.class);
String value = table.value();
System.out.println(value);
// 獲得類指定註解
Field f = aClass.getDeclaredField("id");
Table annotation = f.getAnnotation(Table.class);
System.out.println(annotation.value());
}
}
@Table(value = "aaa")
class Teacher{
@Table2(value = "aaa")
private int id;
@Override
public String toString() {
return super.toString();
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
//屬性的註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Table2{
String value();
}
/*
@com.xxx.Table(value="aaa")
aaa