Java反射與註解

1、Annotation(註解)
java

一、Annotation介紹數組

        Annotation,是Java語言中的一種特殊的元數據語法,能夠被添加到Java代碼中。類,方法,變量,參數,包均可以被標註。與Javadoc的標籤不一樣,註解是能夠被反射的,由於他們被編譯器生成嵌入在 編譯後文件,並保留在虛擬機中以便在運行時被索引。Annotation類型是一種接口,不會直接影響到程序的語義,只是做爲註解(標識)存在,可以經過java反射API的方式提供對其信息的訪問。ide


二、Annotation工做原理測試


         在java5.0 中Java.lang.reflect 提供的反射API被擴充了讀取運行時annotation的能力。一個annotation類型被定義爲runtime retention後,它纔是在運行時可見,當class文件被裝載時被保存在class文件中的annotation纔會被虛擬機讀取。  其中java.lang.reflect.AnnotatedElement是重要的接口,它表明了提供查詢annotation能力的程序成員。這個接口被java.lang.Package、java.lang.Class實現,並間接地被Method類、Constructor類、 java.lang.reflect的Field類實現。而annotation中的方法參數能夠經過Method類、Constructor類的 getParameterAnnotations()方法得到。this


三、系統內置標準註解spa


@Override:用於修飾此方法覆蓋了父類的方法,也就是子類要重寫(override)父類的對應方法;
@Deprecated:用於修飾已通過時的方法,不建議被使用;
@SuppressWarnnings:用於通知java編譯器禁止特定的編譯警告。code


四、元註解對象


元註解也就是註解的註解,java提供了四種元註解,其中兩種是經常使用的,分別是Retention和Target。繼承

@Retention元註解,表示須要在什麼級別保存該註釋信息(生命週期)。可選的RetentionPoicy參數包括:RetentionPolicy.SOURCE: 停留在java源文件,編譯器被丟掉
RetentionPolicy.CLASS:停留在class文件中,但會被VM丟棄(默認)
RetentionPolicy.RUNTIME:內存中的字節碼,VM將在運行時也保留註解,所以能夠經過反射機制讀取註解的信息
索引

@Retention(RetentionPolicy.RUNTIME)
// 編譯程序將Annotation存儲於class文件中,能夠由虛擬機讀入
public @interface MyAnnotation {
    String hello() default "hello";
    String world();
}


@Target說明了Annotation所修飾的對象範圍:Annotation可被用於 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。
ElementType.CONSTRUCTOR: 構造器聲明
ElementType.FIELD: 成員變量、對象、屬性(包括enum實例)
ElementType.LOCAL_VARIABLE: 局部變量聲明
ElementType.METHOD: 方法聲明
ElementType.PACKAGE: 包聲明
ElementType.PARAMETER: 參數聲明
ElementType.TYPE: 類、接口(包括註解類型)或enum聲明

demo以下:

   自定義一個用於方法的註解

@Target(ElementType.METHOD)
public @interface MyTarget {
    String hello() default "hello";
}
package annotationVSreflect;
@MyTarget //註解到這編譯器會報錯
public class MyTargetTest {  
    @MyTarget //該註解(@MyTarget)已指明只能註解方法
    public void fun() {        
    }
}

@Documented將註解包含在JavaDoc中

@Inheried容許子類繼承父類中的註解


五、自定義註解

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Target(ElementType.METHOD)//若是Target元註解不存在,那麼該註解就能夠使用在任何程序元素之上。
@Inherited
@Retention(RetentionPolicy.RUNTIME)

//使用@interface來聲明一個註解(其實是自動繼承了java.lang.annotation.Annotation接口)
public @interface AnnotationTest {
    String value1() default "hello";//爲註解設置String類型的屬性Value1,並使用defalut關鍵字設置默認值
    String[] value3();              //設置數組類型的value3
}

自定義註解須要注意的細節:

1. Annotation型定義爲@interface, 全部的Annotation會自動繼承java.lang.Annotation這一接口,而且不能再去繼承別的類或是接口。

2. 參數成員只能用public或默認(default)這兩個訪問權修飾。

3. 參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和String、Enum、Class、annotations等數據類型,以及這一些類型的數組。

4. 要獲取類方法和字段的註解信息,必須經過Java的反射技術來獲取 Annotation對象,由於你除此以外沒有別的獲取註解對象的方法。

5. 註解也能夠沒有定義成員, 只是這樣註解就沒有做用。


2、反射

       JAVA反射機制是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。

       通俗說:能夠經過一個類名來獲取關於這個類的信息,好比:屬性名、屬性名的修飾符、方法名、方法的返回值、方法的修飾符等。反射還能夠生成類的實例,經過這個實例定義屬性、調用方法。

import java.lang.reflect.Method;
public class Reflect {
    public static void main(String[] args) {       
        try {
            Class c=Class.forName("java.util.HashSet");
            System.out.println("Test======"+c);
            Object o=c.newInstance();
            Method[] methods=c.getDeclaredMethods();
            for(Method method:methods){
            System.out.println("------------------------------------");
            System.out.println(method);
            }
            Method m1=c.getMethod("add", Object.class);
            m1.invoke(o, "cyq");
            m1.invoke(o, "hello");
            m1.invoke(o, "java");
            System.out.println(o);
            } catch (Exception e) {
            e.printStackTrace();
            }
    }
}

控制檯打印信息:

Test======class java.util.HashSet
------------------------------------
public boolean java.util.HashSet.add(java.lang.Object)
------------------------------------
public boolean java.util.HashSet.remove(java.lang.Object)
------------------------------------
public java.lang.Object java.util.HashSet.clone()
------------------------------------
public void java.util.HashSet.clear()
------------------------------------
public boolean java.util.HashSet.contains(java.lang.Object)
------------------------------------
public boolean java.util.HashSet.isEmpty()
------------------------------------
public int java.util.HashSet.size()
------------------------------------
public java.util.Iterator java.util.HashSet.iterator()
------------------------------------
private void java.util.HashSet.readObject(java.io.ObjectInputStream) throws java.io.IOException,java.lang.ClassNotFoundException
------------------------------------
private void java.util.HashSet.writeObject(java.io.ObjectOutputStream) throws java.io.IOException
[hello, java, cyq]


3、綜合實例:

首先自定義兩個註解:

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String hello() default "hello";
    String world();
}
@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation2 {

    String hello() default "hello";
}

而後作一個測試類MyTest

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class MyTest {
 
    @SuppressWarnings("unchecked")//java自帶的註解Retention的policy爲SOURCE
    @Deprecated                   //java自帶的註解Retention的policy爲RUNTIME
    @MyAnnotation(world = "jia")  //自定義的註解Retention的policy爲RUNTIME
    @MyAnnotation2                //自定義的註解Retention的policy爲CLASS
    public void TestMethod() {
        System.out.println("this is a method");
    }
    
    public static void main(String[] args) throws Exception {
        MyTest myTest=new MyTest();
        Class<MyTest> clazz=MyTest.class;
        Method method =clazz.getMethod("TestMethod",new Class[]{});//返回一個 Method 對象,
                                                                   //它反映此 Class 對象所表示的類或接口的指定公共成員方法。
        
        System.out.println("test==========="+clazz);
        System.out.println("test==========="+method);
        //AnnotatedElement接口中的方法isAnnotationPresent(),判斷傳入的註解類型是否存在
        if (method.isAnnotationPresent(MyAnnotation.class)) {
            method.invoke(myTest,new Object[]{});
            //AnnotatedElement接口中的方法getAnnotation(),獲取傳入註解類型的註解
            MyAnnotation myAnnotation=method.getAnnotation(MyAnnotation.class);
            String hello=myAnnotation.hello();
            String world=myAnnotation.world();
            System.out.println("hello:"+hello+"world:"+world);
        } 
        System.out.println("-----------------------------------");

        //AnnotatedElement接口中的方法getAnnotations(),獲取全部註解
        Annotation[] annotations = method.getAnnotations();
        //循環註解數組打印出註解類型的名字
        for (Annotation annotation : annotations) {
          System.out.println(annotation.annotationType().getName());
       }
      }
}

控制檯打印信息:

test===========class annotationVSreflect.MyTest
test===========public void annotationVSreflect.MyTest.TestMethod()
this is a method
hello:helloworld:jia
-----------------------------------
java.lang.Deprecated
annotationVSreflect.MyAnnotation

分割線上:介紹瞭如何使用AnnotatedElement接口中的方法和反射去調用註解

分割線下:證實了只有定義了Retention的Policy爲Runtime的註解才能夠被反射讀取出來

相關文章
相關標籤/搜索