溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java基礎(chǔ)13:反射詳解

發(fā)布時間:2020-08-10 08:20:09 來源:ITPUB博客 閱讀:184 作者:a724888 欄目:編程語言

更多內(nèi)容請關(guān)注微信公眾號【Java技術(shù)江湖】

這是一位阿里 Java 工程師的技術(shù)小站,作者黃小斜,專注 Java 相關(guān)技術(shù):SSM、SpringBoot、MySQL、分布式、中間件、集群、Linux、網(wǎng)絡(luò)、多線程,偶爾講點(diǎn)Docker、ELK,同時也分享技術(shù)干貨和學(xué)習(xí)經(jīng)驗(yàn),致力于Java全棧開發(fā)?。P(guān)注公眾號后回復(fù)”資料“即可領(lǐng)取 3T 免費(fèi)技術(shù)學(xué)習(xí)資源以及我我原創(chuàng)的程序員校招指南、Java學(xué)習(xí)指南等資源)

Java基礎(chǔ)13:反射詳解cdn.xitu.io/2019/4/6/169f1735fd0d1d16?w=900&h=500&f=jpeg&s=109856"> 本節(jié)主要介紹Java反射的原理,使用方法以及相關(guān)的技術(shù)細(xì)節(jié),并且介紹了關(guān)于Class類,注解等內(nèi)容。

具體代碼在我的GitHub中可以找到

https://github.com/h3pl/MyTech

文章首發(fā)于我的個人博客:

https://h3pl.github.io/2018/05/01/javase13

更多關(guān)于Java后端學(xué)習(xí)的內(nèi)容請到我的CSDN博客上查看:

https://blog.csdn.net/a724888

回顧:什么是反射?

反射(Reflection)是Java 程序開發(fā)語言的特征之一,它允許運(yùn)行中的 Java 程序獲取自身的信息,并且可以操作類或?qū)ο蟮膬?nèi)部屬性。 Oracle官方對反射的解釋是

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.

The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

簡而言之,通過反射,我們可以在運(yùn)行時獲得程序或程序集中每一個類型的成員和成員的信息。

程序中一般的對象的類型都是在編譯期就確定下來的,而Java反射機(jī)制可以動態(tài)地創(chuàng)建對象并調(diào)用其屬性,這樣的對象的類型在編譯期是未知的。所以我們可以通過反射機(jī)制直接創(chuàng)建對象,即使這個對象的類型在編譯期是未知的。

反射的核心是JVM在運(yùn)行時才動態(tài)加載類或調(diào)用方法/訪問屬性,它不需要事先(寫代碼的時候或編譯期)知道運(yùn)行對象是誰。

Java反射框架主要提供以下功能:

1.在運(yùn)行時判斷任意一個對象所屬的類;

2.在運(yùn)行時構(gòu)造任意一個類的對象;

3.在運(yùn)行時判斷任意一個類所具有的成員變量和方法(通過反射甚至可以調(diào)用private方法);

4.在運(yùn)行時調(diào)用任意一個對象的方法

重點(diǎn):是運(yùn)行時而不是編譯時

反射的主要用途

很多人都認(rèn)為反射在實(shí)際的Java開發(fā)應(yīng)用中并不廣泛,其實(shí)不然。

當(dāng)我們在使用IDE(如Eclipse,IDEA)時,當(dāng)我們輸入一個對象或類并想調(diào)用它的屬性或方法時,一按點(diǎn)號,編譯器就會自動列出它的屬性或方法,這里就會用到反射。

反射最重要的用途就是開發(fā)各種通用框架。

很多框架(比如Spring)都是配置化的(比如通過XML文件配置JavaBean,Action之類的),為了保證框架的通用性,它們可能需要根據(jù)配置文件加載不同的對象或類,調(diào)用不同的方法,這個時候就必須用到反射——運(yùn)行時動態(tài)加載需要加載的對象。

舉一個例子,在運(yùn)用Struts 2框架的開發(fā)中我們一般會在struts.xml里去配置Action,比如:

<action name="login"
               class="org.ScZyhSoft.test.action.SimpleLoginAction"
               method="execute">
           <result>/shop/shop-index.jsp</result>
           <result name="error">login.jsp</result>
       </action>

配置文件與Action建立了一種映射關(guān)系,當(dāng)View層發(fā)出請求時,請求會被StrutsPrepareAndExecuteFilter攔截,然后StrutsPrepareAndExecuteFilter會去動態(tài)地創(chuàng)建Action實(shí)例。

——比如我們請求login.action,那么StrutsPrepareAndExecuteFilter就會去解析struts.xml文件,檢索action中name為login的Action,并根據(jù)class屬性創(chuàng)建SimpleLoginAction實(shí)例,并用invoke方法來調(diào)用execute方法,這個過程離不開反射。

對與框架開發(fā)人員來說,反射雖小但作用非常大,它是各種容器實(shí)現(xiàn)的核心。而對于一般的開發(fā)者來說,不深入框架開發(fā)則用反射用的就會少一點(diǎn),不過了解一下框架的底層機(jī)制有助于豐富自己的編程思想,也是很有益的。

反射的基礎(chǔ):關(guān)于Class類

更多關(guān)于Class類和Object類的原理和介紹請見上一節(jié)

1、Class是一個類,一個描述類的類(也就是描述類本身),封裝了描述方法的Method,描述字段的Filed,描述構(gòu)造器的Constructor等屬性

2、對象照鏡子后(反射)可以得到的信息:某個類的數(shù)據(jù)成員名、方法和構(gòu)造器、某個類到底實(shí)現(xiàn)了哪些接口。

3、對于每個類而言,JRE 都為其保留一個不變的 Class 類型的對象。一個Class對象包含了特定某個類的有關(guān)信息。

4、Class 對象只能由系統(tǒng)建立對象

5、一個類在 JVM 中只會有一個Class實(shí)例

//總結(jié)一下就是,JDK有一個類叫做Class,這個類用來封裝所有Java類型,包括這些類的所有信息,JVM中類信息是放在方法區(qū)的。
//所有類在加載后,JVM會為其在堆中創(chuàng)建一個Class<類名稱>的對象,并且每個類只會有一個Class對象,這個類的所有對象都要通過Class<類名稱>來進(jìn)行實(shí)例化。
//上面說的是JVM進(jìn)行實(shí)例化的原理,當(dāng)然實(shí)際上在Java寫代碼時只需要用 類名稱就可以進(jìn)行實(shí)例化了。
public final class Class<T> implements java.io.Serializable,
                          GenericDeclaration,
                          Type,
                          AnnotatedElement {
虛擬機(jī)會保持唯一一
            //通過類名.class獲得唯一的Class對象。
            Class<UserBean> cls = UserBean.class;
            //通過integer.TYPEl來獲取Class對象
            Class<Integer> inti = Integer.TYPE;
          //接口本質(zhì)也是一個類,一樣可以通過.class獲取
            Class<User> userClass = User.class;

反射的基本運(yùn)用

上面我們提到了反射可以用于判斷任意對象所屬的類,獲得Class對象,構(gòu)造任意一個對象以及調(diào)用一個對象。這里我們介紹一下基本反射功能的實(shí)現(xiàn)(反射相關(guān)的類一般都在java.lang.relfect包里)。

1、獲得Class對象方法有三種

(1)使用Class類的forName靜態(tài)方法:

 public static Class<?> forName(String className)
```
在JDBC開發(fā)中常用此方法加載數(shù)據(jù)庫驅(qū)動:
要使用全類名來加載這個類,一般數(shù)據(jù)庫驅(qū)動的配置信息會寫在配置文件中。加載這個驅(qū)動前要先導(dǎo)入jar包
```java
 Class.forName(driver);

(2)直接獲取某一個對象的class,比如:

//Class<?>是一個泛型表示,用于獲取一個類的類型。
Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;

(3)調(diào)用某個對象的getClass()方法,比如:

StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();

判斷是否為某個類的實(shí)例

一般地,我們用instanceof關(guān)鍵字來判斷是否為某個類的實(shí)例。同時我們也可以借助反射中Class對象的isInstance()方法來判斷是否為某個類的實(shí)例,它是一個Native方法:

==public native boolean isInstance(Object obj);==

創(chuàng)建實(shí)例

通過反射來生成對象主要有兩種方式。

(1)使用Class對象的newInstance()方法來創(chuàng)建Class對象對應(yīng)類的實(shí)例。

注意:利用newInstance創(chuàng)建對象:調(diào)用的類必須有無參的構(gòu)造器

//Class<?>代表任何類的一個類對象。
//使用這個類對象可以為其他類進(jìn)行實(shí)例化
//因?yàn)閖vm加載類以后自動在堆區(qū)生成一個對應(yīng)的*.Class對象
//該對象用于讓JVM對進(jìn)行所有*對象實(shí)例化。
Class<?> c = String.class;
//Class<?> 中的 ? 是通配符,其實(shí)就是表示任意符合泛類定義條件的類,和直接使用 Class
//效果基本一致,但是這樣寫更加規(guī)范,在某些類型轉(zhuǎn)換時可以避免不必要的 unchecked 錯誤。
Object str = c.newInstance();

(2)先通過Class對象獲取指定的Constructor對象,再調(diào)用Constructor對象的newInstance()方法來創(chuàng)建實(shí)例。這種方法可以用指定的構(gòu)造器構(gòu)造類的實(shí)例。

//獲取String所對應(yīng)的Class對象
Class<?> c = String.class;
//獲取String類帶一個String參數(shù)的構(gòu)造器
Constructor constructor = c.getConstructor(String.class);
//根據(jù)構(gòu)造器創(chuàng)建實(shí)例
Object obj = constructor.newInstance("23333");
System.out.println(obj);

獲取方法

獲取某個Class對象的方法集合,主要有以下幾個方法:

getDeclaredMethods()方法返回類或接口聲明的所有方法,==包括公共、保護(hù)、默認(rèn)(包)訪問和私有方法,但不包括繼承的方法==。

public Method[] getDeclaredMethods() throws SecurityException

getMethods()方法返回某個類的所有公用(public)方法,==包括其繼承類的公用方法。==

public Method[] getMethods() throws SecurityException

getMethod方法返回一個特定的方法,其中第一個參數(shù)為方法名稱,后面的參數(shù)為方法的參數(shù)對應(yīng)Class的對象

public Method getMethod(String name, Class<?>... parameterTypes)

只是這樣描述的話可能難以理解,我們用例子來理解這三個方法: 本文中的例子用到了以下這些類,用于反射的測試。

//注解類,可可用于表示方法,可以通過反射獲取注解的內(nèi)容。
    //Java注解的實(shí)現(xiàn)是很多注框架實(shí)現(xiàn)注解配置的基礎(chǔ)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Invoke {
}

userbean的父類personbean

public class PersonBean {
private String name;
int id;
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}

}

接口user

public interface User {
    public void login ();
}

userBean實(shí)現(xiàn)user接口,繼承personbean

public class UserBean extends PersonBean implements User{
    @Override
    public void login() {
    }
    class B {
    }
    public String userName;
    protected int i;
    static int j;
    private int l;
    private long userId;
    public UserBean(String userName, long userId) {
        this.userName = userName;
        this.userId = userId;
    }
    public String getName() {
        return userName;
    }
    public long getId() {
        return userId;
    }
    @Invoke
    public static void staticMethod(String devName,int a) {
        System.out.printf("Hi %s, I'm a static method", devName);
    }
    @Invoke
    public void publicMethod() {
        System.out.println("I'm a public method");
    }
    @Invoke
    private void privateMethod() {
        System.out.println("I'm a private method");
    }
}

1 getMethods和getDeclaredMethods的區(qū)別

public class 動態(tài)加載類的反射 {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("com.javase.反射.UserBean");
            for (Field field : clazz.getDeclaredFields()) {
//                field.setAccessible(true);
                System.out.println(field);
            }
            //getDeclaredMethod*()獲取的是類自身聲明的所有方法,包含public、protected和private方法。
            System.out.println("------共有方法------");
//        getDeclaredMethod*()獲取的是類自身聲明的所有方法,包含public、protected和private方法。
//            getMethod*()獲取的是類的所有共有方法,這就包括自身的所有public方法,和從基類繼承的、從接口實(shí)現(xiàn)的所有public方法。
            for (Method method : clazz.getMethods()) {
                String name = method.getName();
                System.out.println(name);
                //打印出了UserBean.java的所有方法以及父類的方法
            }
            System.out.println("------獨(dú)占方法------");
            for (Method method : clazz.getDeclaredMethods()) {
                String name = method.getName();
                System.out.println(name);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

2 打印一個類的所有方法及詳細(xì)信息:

public class 打印所有方法 {
    public static void main(String[] args) {
        Class userBeanClass = UserBean.class;
        Field[] fields = userBeanClass.getDeclaredFields();
        //注意,打印方法時無法得到局部變量的名稱,因?yàn)閖vm只知道它的類型
        Method[] methods = userBeanClass.getDeclaredMethods();
        for (Method method : methods) {
            //依次獲得方法的修飾符,返回類型和名稱,外加方法中的參數(shù)
            String methodString = Modifier.toString(method.getModifiers()) + " " ; // private static
            methodString += method.getReturnType().getSimpleName() + " "; // void
            methodString += method.getName() + "("; // staticMethod
            Class[] parameters = method.getParameterTypes();
            Parameter[] p = method.getParameters();
            for (Class parameter : parameters) {
                methodString += parameter.getSimpleName() + " " ; // String
            }
            methodString += ")";
            System.out.println(methodString);
        }
        //注意方法只能獲取到其類型,拿不到變量名
/*        public String getName()
        public long getId()
        public static void staticMethod(String int )
        public void publicMethod()
        private void privateMethod()*/
    }
}

獲取構(gòu)造器信息

獲取類構(gòu)造器的用法與上述獲取方法的用法類似。主要是通過Class類的getConstructor方法得到Constructor類的一個實(shí)例,而Constructor類有一個newInstance方法可以創(chuàng)建一個對象實(shí)例:

public class 打印構(gòu)造方法 {
    public static void main(String[] args) {
        // constructors
        Class<?> clazz = UserBean.class;
        Class userBeanClass = UserBean.class;
        //獲得所有的構(gòu)造方法
        Constructor[] constructors = userBeanClass.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            String s = Modifier.toString(constructor.getModifiers()) + " ";
            s += constructor.getName() + "(";
            //構(gòu)造方法的參數(shù)類型
            Class[] parameters = constructor.getParameterTypes();
            for (Class parameter : parameters) {
                s += parameter.getSimpleName() + ", ";
            }
            s += ")";
            System.out.println(s);
            //打印結(jié)果//public com.javase.反射.UserBean(String, long, )
        }
    }
}

獲取類的成員變量(字段)信息

主要是這幾個方法,在此不再贅述:

getFiled: 訪問公有的成員變量 getDeclaredField:所有已聲明的成員變量。但不能得到其父類的成員變量 getFileds和getDeclaredFields用法同上(參照Method)

public class 打印成員變量 {
    public static void main(String[] args) {
        Class userBeanClass = UserBean.class;
        //獲得該類的所有成員變量,包括static private
        Field[] fields = userBeanClass.getDeclaredFields();
        for(Field field : fields) {
            //private屬性即使不用下面這個語句也可以訪問
//            field.setAccessible(true);
            //因?yàn)轭惖乃接杏蛟诜瓷渲心J(rèn)可訪問,所以flag默認(rèn)為true。
            String fieldString = "";
            fieldString += Modifier.toString(field.getModifiers()) + " "; // `private`
            fieldString += field.getType().getSimpleName() + " "; // `String`
            fieldString += field.getName(); // `userName`
            fieldString += ";";
            System.out.println(fieldString);
            
            //打印結(jié)果
//            public String userName;
//            protected int i;
//            static int j;
//            private int l;
//            private long userId;
        }
    }
}

調(diào)用方法

當(dāng)我們從類中獲取了一個方法后,我們就可以用invoke()方法來調(diào)用這個方法。invoke方法的原型為:

public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
       
public class 使用反射調(diào)用方法 {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException {
        Class userBeanClass = UserBean.class;
        //獲取該類所有的方法,包括靜態(tài)方法,實(shí)例方法。
        //此處也包括了私有方法,只不過私有方法在用invoke訪問之前要設(shè)置訪問權(quán)限
        //也就是使用setAccessible使方法可訪問,否則會拋出異常
//       // IllegalAccessException的解釋是
//        * An IllegalAccessException is thrown when an application tries
// * to reflectively create an instance (other than an array),
// * set or get a field, or invoke a method, but the currently
// * executing method does not have access to the definition of
// * the specified class, field, method or constructor.
//        getDeclaredMethod*()獲取的是類自身聲明的所有方法,包含public、protected和private方法。
//            getMethod*()獲取的是類的所有共有方法,這就包括自身的所有public方法,和從基類繼承的、從接口實(shí)現(xiàn)的所有public方法。
        //就是說,當(dāng)這個類,域或者方法被設(shè)為私有訪問,使用反射調(diào)用但是卻沒有權(quán)限時會拋出異常。
        Method[] methods = userBeanClass.getDeclaredMethods(); // 獲取所有成員方法
        for (Method method : methods) {
            //反射可以獲取方法上的注解,通過注解來進(jìn)行判斷
            if (method.isAnnotationPresent(Invoke.class)) { // 判斷是否被 @Invoke 修飾
                //判斷方法的修飾符是是static
                if (Modifier.isStatic(method.getModifiers())) { // 如果是 static 方法
                    //反射調(diào)用該方法
                    //類方法可以直接調(diào)用,不必先實(shí)例化
                    method.invoke(null, "wingjay",2); // 直接調(diào)用,并傳入需要的參數(shù) devName
                } else {
                    //如果不是類方法,需要先獲得一個實(shí)例再調(diào)用方法
                    //傳入構(gòu)造方法需要的變量類型
                    Class[] params = {String.class, long.class};
                    //獲取該類指定類型的構(gòu)造方法
                    //如果沒有這種類型的方法會報錯
                    Constructor constructor = userBeanClass.getDeclaredConstructor(params); // 獲取參數(shù)格式為 String,long 的構(gòu)造函數(shù)
                    //通過構(gòu)造方法的實(shí)例來進(jìn)行實(shí)例化
                    Object userBean = constructor.newInstance("wingjay", 11); // 利用構(gòu)造函數(shù)進(jìn)行實(shí)例化,得到 Object
                    if (Modifier.isPrivate(method.getModifiers())) {
                        method.setAccessible(true); // 如果是 private 的方法,需要獲取其調(diào)用權(quán)限
//                        Set the {@code accessible} flag for this object to
//     * the indicated boolean value.  A value of {@code true} indicates that
//     * the reflected object should suppress Java language access
//     * checking when it is used.  A value of {@code false} indicates
//                                * that the reflected object should enforce Java language access checks.
                        //通過該方法可以設(shè)置其可見或者不可見,不僅可以用于方法
                        //后面例子會介紹將其用于成員變量
                                            //打印結(jié)果
//            I'm a public method
// Hi wingjay, I'm a static methodI'm a private method
                    }
                    method.invoke(userBean); // 調(diào)用 method,無須參數(shù)
                    
                    
                }
            }
        }
    }
}

利用反射創(chuàng)建數(shù)組

數(shù)組在Java里是比較特殊的一種類型,它可以賦值給一個Object Reference。下面我們看一看利用反射創(chuàng)建數(shù)組的例子:

public class 用反射創(chuàng)建數(shù)組 {
    public static void main(String[] args) {
        Class<?> cls = null;
        try {
            cls = Class.forName("java.lang.String");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Object array = Array.newInstance(cls,25);
        //往數(shù)組里添加內(nèi)容
        Array.set(array,0,"hello");
        Array.set(array,1,"Java");
        Array.set(array,2,"fuck");
        Array.set(array,3,"Scala");
        Array.set(array,4,"Clojure");
        //獲取某一項(xiàng)的內(nèi)容
        System.out.println(Array.get(array,3));
        //Scala
    }
}

其中的Array類為java.lang.reflect.Array類。我們通過Array.newInstance()創(chuàng)建數(shù)組對象,它的原型是:

public static Object newInstance(Class<?> componentType, int length)
        throws NegativeArraySizeException {
        return newArray(componentType, length);
    }

而newArray()方法是一個Native方法,它在Hotspot JVM里的具體實(shí)現(xiàn)我們后邊再研究,這里先把源碼貼出來

private static native Object newArray(Class<?> componentType, int length)
        throws NegativeArraySizeException;

阿丙的博客園 把每一件簡單的事情做好,就是不簡單;把每一件平凡的事情做好,就是不平凡!相信自己,創(chuàng)造奇跡~~ 博客園首頁聯(lián)系管理 隨筆 - 441 文章 - 0 評論 - 53 注解Annotation實(shí)現(xiàn)原理與自定義注解例子 什么是注解? 對于很多初次接觸的開發(fā)者來說應(yīng)該都有這個疑問?Annontation是Java5開始引入的新特征,中文名稱叫注解。它提供了一種安全的類似注釋的機(jī)制,用來將任何的信息或元數(shù)據(jù)(metadata)與程序元素(類、方法、成員變量等)進(jìn)行關(guān)聯(lián)。為程序的元素(類、方法、成員變量)加上更直觀更明了的說明,這些說明信息是與程序的業(yè)務(wù)邏輯無關(guān),并且供指定的工具或框架使用。Annontation像一種修飾符一樣,應(yīng)用于包、類型、構(gòu)造方法、方法、成員變量、參數(shù)及本地變量的聲明語句中。 ??Java注解是附加在代碼中的一些元信息,用于一些工具在編譯、運(yùn)行時進(jìn)行解析和使用,起到說明、配置的功能。注解不會也不能影響代碼的實(shí)際邏輯,僅僅起到輔助性的作用。包含在 java.lang.annotation 包中。

注解的用處: 1、生成文檔。這是最常見的,也是java 最早提供的注解。常用的有@param @return 等 2、跟蹤代碼依賴性,實(shí)現(xiàn)替代配置文件功能。比如Dagger 2依賴注入,未來java開發(fā),將大量注解配置,具有很大用處; 3、在編譯時進(jìn)行格式檢查。如@override 放在方法前,如果你這個方法并不是覆蓋了超類方法,則編譯時就能檢查出。

注解的原理: ??注解本質(zhì)是一個繼承了Annotation的特殊接口,其具體實(shí)現(xiàn)類是Java運(yùn)行時生成的動態(tài)代理類。而我們通過反射獲取注解時,返回的是Java運(yùn)行時生成的動態(tài)代理對象$Proxy1。通過代理對象調(diào)用自定義注解(接口)的方法,會最終調(diào)用AnnotationInvocationHandler的invoke方法。該方法會從memberValues這個Map中索引出對應(yīng)的值。而memberValues的來源是Java常量池。

元注解: java.lang.annotation提供了四種元注解,專門注解其他的注解(在自定義注解的時候,需要使用到元注解): @Documented –注解是否將包含在JavaDoc中 @Retention –什么時候使用該注解 @Target –注解用于什么地方 @Inherited – 是否允許子類繼承該注解

1.)@Retention– 定義該注解的生命周期 ● RetentionPolicy.SOURCE : 在編譯階段丟棄。這些注解在編譯結(jié)束之后就不再有任何意義,所以它們不會寫入字節(jié)碼。@Override, @SuppressWarnings都屬于這類注解。 ● RetentionPolicy.CLASS : 在類加載的時候丟棄。在字節(jié)碼文件的處理中有用。注解默認(rèn)使用這種方式 ● RetentionPolicy.RUNTIME : 始終不會丟棄,運(yùn)行期也保留該注解,因此可以使用反射機(jī)制讀取該注解的信息。我們自定義的注解通常使用這種方式。

2.)Target – 表示該注解用于什么地方。默認(rèn)值為任何元素,表示該注解用于什么地方??捎玫腅lementType參數(shù)包括 ● ElementType.CONSTRUCTOR:用于描述構(gòu)造器 ● ElementType.FIELD:成員變量、對象、屬性(包括enum實(shí)例) ● ElementType.LOCAL_VARIABLE:用于描述局部變量 ● ElementType.METHOD:用于描述方法 ● ElementType.PACKAGE:用于描述包 ● ElementType.PARAMETER:用于描述參數(shù) ● ElementType.TYPE:用于描述類、接口(包括注解類型) 或enum聲明

3.)@Documented–一個簡單的Annotations標(biāo)記注解,表示是否將注解信息添加在java文檔中。

4.)@Inherited – 定義該注釋和子類的關(guān)系 @Inherited 元注解是一個標(biāo)記注解,@Inherited闡述了某個被標(biāo)注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用于一個class,則這個annotation將被用于該class的子類。

常見標(biāo)準(zhǔn)的Annotation: 1.)Override java.lang.Override是一個標(biāo)記類型注解,它被用作標(biāo)注方法。它說明了被標(biāo)注的方法重載了父類的方法,起到了斷言的作用。如果我們使用了這種注解在一個沒有覆蓋父類方法的方法時,java編譯器將以一個編譯錯誤來警示。 2.)Deprecated Deprecated也是一種標(biāo)記類型注解。當(dāng)一個類型或者類型成員使用@Deprecated修飾的話,編譯器將不鼓勵使用這個被標(biāo)注的程序元素。所以使用這種修飾具有一定的“延續(xù)性”:如果我們在代碼中通過繼承或者覆蓋的方式使用了這個過時的類型或者成員,雖然繼承或者覆蓋后的類型或者成員并不是被聲明為@Deprecated,但編譯器仍然要報警。 3.)SuppressWarnings SuppressWarning不是一個標(biāo)記類型注解。它有一個類型為String[]的成員,這個成員的值為被禁止的警告名。對于javac編譯器來講,被-Xlint選項(xiàng)有效的警告名也同樣對@SuppressWarings有效,同時編譯器忽略掉無法識別的警告名。 ??@SuppressWarnings("unchecked")

自定義注解: 自定義注解類編寫的一些規(guī)則:

  1. Annotation型定義為@interface, 所有的Annotation會自動繼承java.lang.Annotation這一接口,并且不能再去繼承別的類或是接口.
  2. 參數(shù)成員只能用public或默認(rèn)(default)這兩個訪問權(quán)修飾
  3. 參數(shù)成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類型和String、Enum、Class、annotations等數(shù)據(jù)類型,以及這一些類型的數(shù)組.
  4. 要獲取類方法和字段的注解信息,必須通過Java的反射技術(shù)來獲取 Annotation對象,因?yàn)槟愠酥鉀]有別的獲取注解對象的方法
  5. 注解也可以沒有定義成員, 不過這樣注解就沒啥用了 PS:自定義注解需要使用到元注解

自定義注解實(shí)例: FruitName.java

按 Ctrl+C 復(fù)制代碼

import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**

  • 水果名稱注解 */ @Target(FIELD) @Retention(RUNTIME) @Documented public @interface FruitName { String value() default ""; } 按 Ctrl+C 復(fù)制代碼

FruitColor.java

按 Ctrl+C 復(fù)制代碼

import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**

  • 水果顏色注解  / @Target(FIELD) @Retention(RUNTIME) @Documented public @interface FruitColor { / *

    • 顏色枚舉 */ public enum Color{ BLUE,RED,GREEN};

    /**

    • 顏色屬性 */ Color fruitColor() default Color.GREEN;

} 按 Ctrl+C 復(fù)制代碼

FruitProvider.java

按 Ctrl+C 復(fù)制代碼

import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**

  • 水果供應(yīng)者注解  / @Target(FIELD) @Retention(RUNTIME) @Documented public @interface FruitProvider { / *

    • 供應(yīng)商編號 */ public int id() default -1;

    /**

    • 供應(yīng)商名稱 */ public String name() default "";

    /**

    • 供應(yīng)商地址 */ public String address() default ""; } 按 Ctrl+C 復(fù)制代碼

FruitInfoUtil.java

按 Ctrl+C 復(fù)制代碼

import java.lang.reflect.Field;

/**

  • 注解處理器 */ public class FruitInfoUtil { public static void getFruitInfo(Class<?> clazz){

     String strFruitName=" 水果名稱:";
     String strFruitColor=" 水果顏色:";
     String strFruitProvicer="供應(yīng)商信息:";
     
     Field[] fields = clazz.getDeclaredFields();
     
     for(Field field :fields){
         if(field.isAnnotationPresent(FruitName.class)){
             FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
             strFruitName=strFruitName+fruitName.value();
             System.out.println(strFruitName);
         }
         else if(field.isAnnotationPresent(FruitColor.class)){
             FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
             strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
             System.out.println(strFruitColor);
         }
         else if(field.isAnnotationPresent(FruitProvider.class)){
             FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
             strFruitProvicer=" 供應(yīng)商編號:"+fruitProvider.id()+" 供應(yīng)商名稱:"+fruitProvider.name()+" 供應(yīng)商地址:"+fruitProvider.address();
             System.out.println(strFruitProvicer);
         }
     }
    

    } } 按 Ctrl+C 復(fù)制代碼

Apple.java

按 Ctrl+C 復(fù)制代碼

import test.FruitColor.Color;

/**

  • 注解使用 */ public class Apple {

    @FruitName("Apple") private String appleName;

    @FruitColor(fruitColor=Color.RED) private String appleColor;

    @FruitProvider(id=1,name="陜西紅富士集團(tuán)",address="陜西省西安市延安路89號紅富士大廈") private String appleProvider;

    public void setAppleColor(String appleColor) { this.appleColor = appleColor; } public String getAppleColor() { return appleColor; }

    public void setAppleName(String appleName) { this.appleName = appleName; } public String getAppleName() { return appleName; }

    public void setAppleProvider(String appleProvider) { this.appleProvider = appleProvider; } public String getAppleProvider() { return appleProvider; }

    public void displayName(){ System.out.println("水果的名字是:蘋果"); } } 按 Ctrl+C 復(fù)制代碼

FruitRun.java

按 Ctrl+C 復(fù)制代碼

/**

  • 輸出結(jié)果 */ public class FruitRun { public static void main(String[] args) { FruitInfoUtil.getFruitInfo(Apple.class); } } 按 Ctrl+C 復(fù)制代碼

運(yùn)行結(jié)果是:

水果名稱:Apple 水果顏色:RED 供應(yīng)商編號:1 供應(yīng)商名稱:陜西紅富士集團(tuán) 供應(yīng)商地址:陜西省西安市延安路89號紅富士大廈

參考鏈接: [1] http://www.cnblogs.com/peida/archive/2013/04/26/3038503.html [2] http://www.cnblogs.com/whoislcj/p/5671622.html [3] http://blog.csdn.net/lylwo317/article/details/52163304


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI