反射和註解筆記
註解筆記
1.作用
(1)Annotation的,不是程式本身,但是可以對程式作出解釋,
(2)可以被其他程式(如編譯器)讀取;
2.格式
-
@註釋名,還可以新增引數;
-
它的使用範圍:package,class,method,field等上面,相當於給他們添加了額外的輔助資訊,我們可以通過反射機制程式設計實現對這些元素的訪問。
@Override :重寫的註解,如果你加上這個註解,一定要重寫父類的方法。
@Deprecated:廢棄註解,當使用這個註解時,表示這個方法已經過時,或者它是危險的,不推薦使用,但是可以使用,或者存在更好的方式。
@SuppressWarnings("all"):鎮壓警告,當你的程式出現警告的時候,帶上這個註解,警告會消失。
3.元註解
-
它的作用就是負責註解其他的註解,Java定義了四個meta註解(@Target,@Retention,@Documented,@Inherited)
-
@Target:用於描述註解的適用範圍
-
@Retention:表示需要在什麼級別儲存該註解資訊,用於買哦書註解的生命週期
-
@Documented:說明該註解是否生成在javadoc中,
-
@Inherited:說明子類可以繼承父類中的該註解。
4.自定義註解
//定義一個元註解 @Target(vlaue={ElementType,METHOD,ElementType,TYPE})//它可以定義在方法和類上面 @Retention(value=RerentionPololicy.RUNTIME)//表示註解在程式執行時有效。 //(RESOURCE>CLASS>RUNTIME) @interface MyAnnotation{ //註解的引數:引數型別+引數名,它不是一個方法,而是一個引數 String name() default "";//還可以新增一個預設值,如果有了引數,那麼可以不顯示賦值,也可以顯示 //如果沒有預設值,那麼必須給註解賦值。 int age() default 0; int id default -1;//對於預設值為-1,表示不存在 } //測試元註解 @MyAnnotation//但是當它只有一個method引數的時候,他只能定義在方法上面 public class test{ @MyAnnotation(name="chanchan")//多個引數之間沒有順序,同時,如果有預設值可以不寫,但是沒有預設值一定寫 public void test(){} }
5.註解練習
//定義一個註解
@Target(vlaue={ElementType,METHOD,ElementType,TYPE}))//適用範圍
@Retention(value=RerentionPololicy.RUNTIME) //使用的有效時間
@interface mytest{
//定義一個引數
String value();
}
//測試註解
public calss demo(){
@mytest("zhangsan")//只有value可以不顯示引數的名稱,
public void test2(){}
}
反射筆記:
6.反射概述(Reflection)
JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫物件方法的功能稱為java語言的反射機制。
-
動態語言:
是一類在執行時改變結構的語言,就是說在執行時程式碼可以根據某些條件改變自身的結構。
如:js、c#等;
靜態語言則相反。
Java語言可以利用反射機制獲得類似動態語言的特性。
-
java reflection:
反射機制允許程式在執行期藉助於reflection API取得任何類的內部資訊,並能直接操作任意物件的內部屬性及方法。
載入完類之後,在對堆記憶體的方法去中就產生了一個class型別的物件,
這個物件就包含了完整地結構資訊,我們可以通過這個物件看到類的結構,
這個物件就像一面鏡子,透過著鏡子,我們可以看到類的結構,所以,我麼形象的稱之為:反射。
——正常方式:引入需要的包類名稱-->通過new例項化-->取得例項化物件
——反射方式:例項化物件-->getCLass()方法-->得到完整的包類。
- class類,管理反射的類,通過Object 中的getclass方法返回這個類,
7.反射的優缺點
-
優點:
可以世襲那都櫃檯的建立物件和編譯,很靈活。
-
缺點:
對效能有些影響,總是慢於執行相同的操作。
-
反射機制:我們告訴虛擬機器我們希望做什麼,,並且,虛擬機器會滿足我們的需求。
8.反射思考
通過例項化物件正常的獲取物件,可以使用物件方法和屬性。
通過反射,獲取我們想要的類的Class物件,傳遞給我例項化Class物件的方法,這樣就例項化出來一個Class物件,能夠呼叫它的任意方法和屬性,那麼,他們的區別是什麼?
只是在於這個任意和限制?
//正常例項化物件
Demo1 d=new Demo();
d.fnagfa();
//反射
Class c1=Class.forName();//通過反射獲取類的Class物件
c1.fangfa();
一個是正常的物件,一個是Class物件。
在日常的第三方應用開發過程中,經常會遇到某個類的某個成員變數、方法或是屬性是私有的或是隻對系統應用開放,這時候就可以利用Java的反射機制通過反射來獲取所需的私有成員或是方法。
9.反射的功能
(1).在執行時判斷任意一個物件所屬的類。
(2).在執行時構造任意一個類的物件。
(3).在執行時判斷任意一個類所具有的成員變數和方法。
(4)在執行時呼叫任意一個物件的方法。
10.特性
-
一個類中只有一個Class物件,無論你獲取幾個不同名稱的類物件,他們都是相等的。
-
一個類被載入後,類的整個結構都會被封裝到反射物件Class物件中,通過著個物件,我們可以或者這個類裡面的所有東西了。(透視掛?)
-
對於每個類而言,他們可以有多個物件,子物件,但是,只有一個共同的Class類,由系統建立。
11.獲得Class的方法
- Class c1=Person.class; 已知具體的類。通過 類的class屬性獲取;安全性和效能最高
- Class c2=person.getClass(); 已知類的例項,通過getClass()方法獲取;
- Class c3=Class.forName("demo01.student"); 已知類的全名和路徑,通過Class的靜態方法forName()獲取。需要包名,
- Class c4=Integer.TYPE; 基本內建型別的包裝類都有一個TYPE屬性。
對於每個類而言,他們可以有多個物件,子物件,但是,只有一個共同的Class類,由系統建立。
12.從記憶體分析
當程式使用某個類時 ,它會將class檔案的位元組碼載入到記憶體中,然後生成一個java.lang.class物件。
我們在使用反射是就是在呼叫這個物件。
13.獲取執行時類的完整結構
- 實現全部的介面
- 所繼承的父類
- 全部的構造器
- 全部的方法
- 全部的Field
- 註解
public class reflection {
public static void main(String[] args){
Class c1=test1.class;//獲取類的Class物件
//獲得類的名字
c1.getName();
//-全部的方法
c1.getMethods();
// 實現全部的介面
c1.getSimpleName();
// 所繼承的父類
//-全部的Field
c1.getField();//只能找到他的public 修飾的屬性,獲取私有的屬性需要:getDeclarField方法獲取;
c1.getClasses();
// 全部的構造器
c1.getConstructors();
// 註解
c1.getAnnotation();
}
14.Class物件的應用
-
如何通過反射獲取一個物件
-
(1)通過無參構造器傳概念一個物件
-
(2)建立一個類的物件:呼叫Class物件的newInstance()方法
類必須有一個無引數的構造器
類的構造訪問許可權需要足夠
-
如果沒有無參構造器。需要在操作的時候明確的呼叫類中的構造器,並將引數傳遞進去之後次啊可以例項化操作。
???
-
既然說,利用反射機制建立了Class物件,這個物件可以呼叫類中的一切方法和欄位,那麼為什麼還需要通過Class類建立一個類的例項化物件?
-
既然說要建立,那麼為什麼不一開始就直接建立類的例項化物件?
//通過反射,動態的建立一個物件
public class test1{
public static void main(String[] args){
Class c1=Class.forName("com.chanchan.People");
//構造類的一個物件
People pep=(People)c1.newInstance();//本質上是呼叫了類的無參構造器
System.out.println();//People{name='null',id=0,age=0}
//通過構造器建立物件
Construct construct=c1.getDeclarConstruct(傳遞引數);
People peop=construct.newInstance(傳遞引數的值);
System.out.println(peop);
}
}
-
通過這個物件來獲取普通方法
//通過反射獲取一個方法 Method setName=c1.getDeclarMethod("setName",String.class) setName.invoke(peop,"chanchan"); System,out.println(peop,getName()); //invoke:啟用的意思。(物件,“方法的值”)
-
思路:
(1)通過Clss物件例項化一個類的物件,然後,然後,又通過反射物件Class,獲取類的一個方法,傳遞引數,那個方法,得到的值試試麼型別。
然後通過方法invoke()啟用,方法是那個物件,傳遞進去,然後知道是這個物件的這個方法,然後設定一個值。
(不懂就問,既然可以通過Class拿到類的方法,為什麼話需要例項化物件來獲取方法?)
(2)獲取物件屬性:
通過Class物件獲得一個例項化物件,然後通過反射獲取這個物件的屬性,,傳遞一個指定一個屬性,它會返回一個值,然後,給這個屬性賦值,傳遞引數,那個物件,想要給它賦什麼值。
//建立一個Class物件 Class c2=class.forName("包名加類名(student)"); //通過無參構造器例項化類的物件 Student stu=(Student)c1.newInstance(); //通過Class物件獲取類的屬性 Field name=c1.getDeclarName("name"); name.setAccessible(true);//如果這個屬性是私有的,那麼要設定這個屬性的開關。 //我們不能直接訪問類的私有屬性,我們需要關閉程式的安全檢測 //這個屬性屬於什麼物件,要賦給它什麼值 name.invoke(stu,"chanchan") //列印這個 System.out.println(stu.getName());
-
獲取方法
再一次捋一下獲取方法思路:
(1)首先,獲取這個類的Class物件,
(2)再通過這個反射物件,例項化這個類的屬性,無參構造器或者有參的都行
(3)通過著反射物件,獲取類的方法,需要傳遞一個引數,還有返回的型別
(4)然後,給這個方法傳遞引數,還有得到的值,通過invoke
(5)打印出來
//獲取類的Class物件 Class c3=Teacher.class; //通過構造器的方法例項化類的物件 //第一步,獲取構造器 Construct con=c4.getDeclrarConstruct(String name,int age,int id); //第二步,通過構造器例項化物件 Teacher tt=con.newInstance("chanchan",14,1233) //例項化完成之後,需要通過class物件,獲取類的方法,引數就是那個方法,還有方法的引數型別 Method setName=c3.getDeclearMethod("setName",String.class); //給這個方法傳遞引數。屬於哪個物件,還有賦值返回的值 setName.invoke(tt,zhangsan) System.out.println(tt.getName());
注意:class物件只能獲取或者檢視所有的類的物件,而不能直接修改,它需要通過類的例項化物件來修改
這個反射更像一個透視器,他能看到你的所有的屬性和欄位,但是,它沒有許可權來更改你的身上的部位,
那麼你可以通過它來共享一些目光視野,看到你所看不到的,
如果,你想要知道你更改一些你不知道的東西,那麼,通過它來獲取名字,告訴你,你要在哪裡改,
如果那個屬性欄位是私有的,那麼就需要更改他的安全檢,讓你能夠看到。
15.效能對比分析
- setAccessible:作用是啟動和禁用訪問安全檢查的開關。提高了效能。
16.通過反射操作泛型(瞭解)
- ParameterizedType:表示一種引數化型別,比如collection
- GenericArrtype:表示一種元素型別是引數化型別或者型別變數的陣列型別
- TypeVariable:是各種型別變數的公共父介面
- WildcardType:代表一種萬用字元型別表示式
這幾種型別代表不能被歸一到Class類中的型別但是又和原型別齊名的型別
17.獲取註解資訊
- 練習反射操作註解
public class test17{
public static void main(String[] args){
Class c1=Class.forName("com.chanchan.student");
//通過反射獲得註解
Annountation ant=c1.getAnnountation();
for(Annountation antt:ant){
System.out.println(antt);
}
//獲得註解value的值
Announcation a=(table)c1.getAnnountation(table.class);//獲得指定的註解
//獲得指定註解的value的值
String value=table.value();
//獲得類指定的註解,在類裡面額度註解
Field f=c1.getDeclareField("name");
Field announcation=f.getAnnouncation(Field.class);
System.out.println(announcation.columName);
}
}
//建立一個實體類
@table("db_student")
class student{
@Filed(columName="db_id",type="int",length=10)
private int id;
@Filed(columName="db_id",type="int",length=10)
private int age;
@Filed(columName="db_id",type="int",length=10)
private String name;
public class student(){//無參構造器
}
public student(int id,int age,String name){
this.name=name;
this.age=age;
this.id=id;
}
//setter和getter
//toString(){}
}
//建立一個註解,類名的註解
@Target(引數)
@Runtention(引數)
@interface table{
String value;//表名
}
//屬性的註解
@Target(ElementType.Field)
@Runtention(引數)
@interface Filed{
String columName();
String type();
int length();
}