1. 程式人生 > 資訊 >三星:8 月 5 日起 Galaxy Gear 手錶安卓商店停止服務,可升級到 Tizen 系統

三星:8 月 5 日起 Galaxy Gear 手錶安卓商店停止服務,可升級到 Tizen 系統

註解

一、元註解

​ 元註解的作用就是註解其他註解,Java定義了4個標準的meta-annotation型別,他們被用來提供對其他annotation型別做說明。

4個元註解分別為:

  • @Target:用於描述註解的使用範圍

    • ElementType.TYPE 針對類、介面

    • ElementType.FIELD 針對成員變數

    • ElementType.METHOD 針對成員方法

    • ElementType.PARAMETER 針對方法引數

    • ElementType.CONSTRUCTOR 針對構造器

    • ElementType.PACKAGE 針對包

    • ElementType.ANNOTATION_TYPE 針對註解

  • @Retention:用於表示需要在什麼級別儲存註解資訊,用於描述註解的生命週期,(SOURCE<CLASS<RUNTIME)

    • RetentionPolicy.SOURCE 原始碼級別,由編譯器處理,處理之後就不再保留

    • RetentionPolicy.CLASS 註解資訊保留到類對應的 class 檔案中

    • RetentionPolicy.RUNTIME 由 JVM 讀取,執行時使用

  • @Document:說明該註解將被包含在javadoc(文件)中

  • @Inherited:說明子類可以繼承父類中的該註解

二、自定義註解

public class Test {
    //如果註解的引數沒有預設值,則必須給該引數賦值
    @MyAnnotation(schools = {"北京大學","清華大學"})
    public void test(){
    }
    //如果註解只有一個引數且引數名為value,則value可以省略不寫
    @MyAnnotation2("aa")
    public void test2(){

    }
}
//註解的寫法
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
    //註解的引數:型別+引數名() [default 預設值];
    String name() default "";
    int age() default 0;
    int id() default -1;  //如果預設值為-1,代表不存在
    String[] schools();
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    //如果註解只有一個引數,一般引數名取為value
    String[] value();  //不成文的一個規範
}

反射

一、反射概述

​ Java不是動態語言,但Java可以稱為“準動態語言”。因為Java的反射機制可以讓Java獲得類似動態語言的特性,即執行時程式碼可以根據一些條件來改變自身的結構。

​ 反射機制允許程式在執行期間藉助於Reflection API獲取任何類的內部資訊(比如類名,類的介面,類的方法,欄位,屬性…),並且能夠直接操作任意物件的內部屬性及方法。

​ 載入完類之後,在堆記憶體中的方法區中間產生了一個Class型別的物件(一個類只有一個Class物件),這個物件就包含了完整的類的結構資訊。我們可以通過這個物件看到類的結構。這個物件就像一面鏡子,透過這個鏡子看到類的結構,所以,我們形象的稱之為:反射。

反射機制提供的功能:

  • 在執行時判斷任意一個物件所屬的類
  • 在執行時構造任意一個類的物件
  • 在執行時判斷任意一個類所具有的成員變數和方法
  • 在執行時呼叫任意一個物件的成員變數和方法
  • 在執行時獲取泛型資訊
  • 在執行時處理註解
  • 生成動態代理
  • ……

二、Class物件

​ 一個類在記憶體中只有一個Class物件,不可能有多個。

​ 一個類被被載入後,類的整個結構都會被封裝在Class物件中。

​ Class物件只能由系統建立,我們只負責使用。我們要想使用反射機制,首先得要獲得相應的Class物件。

1. 獲得Class物件的方式

(1)如果已知具體的類,通過類的class屬性獲取,是最為安全可靠且效能最高的方法。

Class clz = 類名.class;

(2)如果已知某個類的物件,呼叫此物件的getClass()方法獲取Class物件。

Class clz = 物件名.getClass();

​ 【注】getSuperClass()方法可以返回當前物件的父類Class物件。

(3)已知一個類的全限定類名且該類在類路徑下,可以通過Class類的靜態方法forName()獲取,需要處理異常ClassNotFoundException。

Class clz = Class.forName("類的全限定類名")

(4)內建基本資料型別可以直接使用類名.TYPE,比如:

Class clz = Integer.TYPE; //基本內建型別的包裝類都有一個 TYPE屬性

(5)還可以用ClassLoader。

2. 通過Class物件獲取執行時類的完整結構

(1)獲取類的名字

  • clz.getName() 獲取包名+類名
  • clz.getSimpleName 獲取類名

(2)獲取類的屬性

  • clz.getFields() 獲取本類public屬性

  • clz.getDeclaredFields() 可以獲取到本類所有屬性,包括私有屬性

  • clz.getDeclaredField("屬性名") 獲取指定的屬性

(3)獲取類的方法

  • clz.getMethods() 獲取本類及其父類的所有public方法

  • clz.getDeclaredMethods() 獲取本類的所有方法,包括私有方法,不能獲取父類的方法

  • clz.getMethod("方法名", ...方法引數型別.class)

    clz.getDeclaredMethod("方法名", ...方法引數型別.class)

    獲取指定方法,若方法無參,則傳遞null

(4)獲取類的構造方法

  • clz.getConstructors() 獲取本類public構造方法
  • clz.getDeclaredConstructors() 獲取本類所有構造方法
  • clz.getDeclaredConstructor(...構造方法引數型別.class) 獲取指定構造方法

3. 獲取到類的結構資訊後能做什麼

(1)通過反射建立類的物件

​ 方式1:呼叫Class物件的newInstance()方法

​ 前提:

​ 必須要有一個無參構造器;

​ 類的構造器的訪問許可權要夠。

`User user = (User) clz.newInstance(); //本質上呼叫了類的無參構造器`

​ 方式2:通過指定的構造器建立

​ 首先通過Class物件獲取指定的構造器,然後呼叫Constructor物件的newInstance(...構造方法引數值)方法。

Constructor constructor = clz.getDeclaredConstructor(String.class, int.class);
User user = (User) constructor.newInstance("小明", 18);

(2)通過反射呼叫方法

User user = (User) clz.newInstance();
//通過反射獲取一個方法
Method setName = clz.getMethod("setName", String.class);

//invoke: 啟用的意思  (物件, "方法需要的引數")
setName.invoke(user,"小張");
System.out.println(user.getName());//輸出:小張

Object invoke(Object obj, Object ... args)

  • invoke方法的返回值對應原方法的返回值,若原方法無返回值,則返回null。
  • 若原方法為靜態方法,此時形參Object obj可為null。
  • 若原方法形參列表為空,則Object ... args為null。
  • 若原方法宣告為private,則需要在呼叫此invoke()方法之前,顯示呼叫Method物件的setAccessible(true)方法,這樣才可以訪問私有方法。

(3)通過反射操作屬性

User user= (User) clz.newInstance();
Field name = clz.getDeclaredField("name");

//不能直接操作私有屬性,我們需要關閉程式的安全檢查,Field或Method物件呼叫setAccessible(true)方法
//setAccessible 預設為false,如果沒有關閉將會報沒有訪問private的許可權
//can not access a member of class com.javacto.reflection.User with modifiers "private"
name.setAccessible(true);
name.set(user,"小紅"); //修改屬性值
System.out.println(user.getName());//輸出:小紅

【注】使用反射機制會影響程式的執行效率。關閉安全檢查在一定程度上能改善一些反射的效率問題。

三、通過反射獲取泛型資訊

​ Java採用泛型擦除的機制來引入泛型,Java中的泛型僅僅是給編譯器javac使用的,目的是確保資料的安全性和免去強制型別轉換的問題,但是,一旦編譯完成,所有和泛型有關的型別將被全部擦除。

​ 思考該怎麼獲取泛型資訊呢? (之前有說過,類載入的時候就產生了Class物件,故class物件裡面應該是有保留泛型資訊的)

public class Test {

    /**
     * 通過泛型傳參
     * @param map
     * @param list
     */
    public void test01(Map<String,User> map, List <User> list){
        System.out.println("test01");
    }

    /**
     * 通過泛型返回值
     * @return
     */
    public Map<String,User> test02(){
        System.out.println("test02");
        return null;
    }


    public static void main(String[] args) throws NoSuchMethodException {
        Method method = Test.class.getMethod("test01", Map.class, List.class);
        //獲取引數型別  即Map 和 List
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("1:"+genericParameterType);

            //判斷genericParameterType引數型別 是否屬於 ParameterizedType 引數化型別
            if (genericParameterType instanceof ParameterizedType){
                //如果屬於引數化型別,獲得他的真實型別 getActualTypeArguments
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();

                //再次輸出真實的泛型資訊
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("2:"+actualTypeArgument);
                }
            }
        }
        
        //獲得返回值型別
        method = Test.class.getMethod("test02",null);
        Type genericReturnType = method.getGenericReturnType();
        if (genericReturnType instanceof ParameterizedType){
            //如果genericReturnType返回值型別屬於引數化型別,獲得他的真實型別 getActualTypeArguments
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            
            //再次輸出真實的泛型資訊
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("3:"+actualTypeArgument);
            }
        }
    }
}

四、通過反射獲取註解資訊

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class clz = Class.forName("com.javacto.reflection.Student");

        //通過反射獲取註解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //獲得註解的value的值   獲取指定註解值
        MyTable myTable =(MyTable) c1.getAnnotation(MyTable.class);
        String value = myTable.value();
        System.out.println(value);

        //獲得類指定的註解
        System.out.println("=====獲得類指定的註解======");
        Field field= clz.getDeclaredField("name");
        MyField annotation = field.getAnnotation(MyField.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }

}

@MyTable("db_students")
class Student{
    @MyField(columnName = "db_id",type = "int",length = 10)
    private int id;
    @MyField(columnName = "db_age",type = "int",length = 10)
    private int age;
    @MyField(columnName = "db_name",type = "varchar",length = 50)
    private String name;

    public Student() {
    }

    public Student(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", age=" + age +
                ", name=" + name +
                '}';
    }
}

//類名的註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTable{
    String value();
}

//屬性的註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyField{
    String columnName(); //列名
    String type(); //型別
    int length(); //長度
}