1. 程式人生 > 實用技巧 >反射和註解筆記

反射和註解筆記

註解筆記

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();
}