1. 程式人生 > 實用技巧 >實現文字橫向滑動

實現文字橫向滑動

目錄

1. 什麼是反射?

Java的反射是指在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫物件方法的功能稱為Java語言的反射機制。

2. 反射的用途

我們知道反射機制允許程式在執行時取得任何一個已知名稱的class的內部資訊,包括包括其modifiers(修飾符)、fields(屬性)、methods(方法)等,並可於執行時改變fields內容或呼叫methods。

那麼我們便可以更靈活的編寫程式碼,程式碼可以在執行時裝配,無需在元件之間進行原始碼連結,降低程式碼的耦合度;還有動態代理的實現等等;但是需要注意的是反射使用不當會造成很高的資源消耗!

3. 反射的API介紹

我們以一個Person類為例:

public class Person<T> {
    private int age=18;
    public String name="tom";
    public Person() {
    }
    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }
    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;
    }

    public void show(){
        System.out.println("show run!");
    }
    private void say(){
        System.out.println("say run!");
    }
}

要對該類進行反射,我們先要獲取這個類對應的位元組碼檔案物件。

3.1 獲取位元組碼檔案物件的三種方式

//1、通過物件呼叫 getClass() 方法來獲取,通常應用在:比如你傳過來一個 Object
//  型別的物件,而我不知道你具體是什麼類,用這種方法
  Person p1 = new Person();
  Class c1 = p1.getClass();

//2、直接通過 類名.class 的方式得到,該方法最為安全可靠,程式效能更高
//  這說明任何一個類都有一個隱含的靜態成員變數 class
  Class c2 = Person.class;

//3、通過 Class 物件的 forName() 靜態方法來獲取,用的最多,引數為類的全名
//   但可能丟擲 ClassNotFoundException 異常
  Class c3 = Class.forName("com.lfx.test.Person");

注意:一個類在虛擬機器中只會有一個 Class 例項,即我們對上面獲取的 c1,c2,c3進行 equals 比較,發現都是true

查閱API之後,我們發現位元組碼檔案物件(即Class物件)有很多獲取類資訊的方法:

  • getName():獲得類的完整名字。   
  • getFields():獲得類的public型別的屬性。   
  • getDeclaredFields():獲得類的所有屬性,包括private宣告的。 
  • getMethods():獲得類的public型別的方法。   
  • getDeclaredMethods():獲得類的所有方法,包括private宣告的 
  • getMethod(String name, Class[] parameterTypes):獲得類的特定方法,name引數指定方法的名字,parameterTypes 引數指定方法的引數型別。   
  • getConstructors():獲得類的public型別的構造方法。   
  • getConstructor(Class[] parameterTypes):獲得類的特定構造方法,parameterTypes 引數指定構造方法的引數型別。   
  • newInstance():通過類的不帶引數的構造方法建立這個類的一個物件。

3.2 獲取物件的屬性、方法、構造器

獲取到了Class物件後,我們可以呼叫Class的方法,獲取物件的屬性、方法、構造器。

示例程式碼:

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
    //通過第二種方式獲取Class物件
    Class c2 = Person.class;
    //獲得類完整的名字
    String className = c2.getName();
    System.out.println(className);

    //獲得類的public型別的屬性。
    Field[] fields = c2.getFields();
    for(Field field : fields){
        System.out.println(field.getName());
    }

    //獲得類的所有屬性。包括私有的
    Field [] allFields = c2.getDeclaredFields();
    for(Field field : allFields){
        System.out.println(field.getName());
    }

    //獲得類的public型別的方法。這裡包括 Object 類的一些方法
    Method[] methods = c2.getMethods();
    for(Method method : methods){
        System.out.println(method.getName());//wait equls toString hashCode等
    }

    //獲得類的所有方法。
    Method [] allMethods = c2.getDeclaredMethods();
    for(Method method : allMethods){
        System.out.println(method.getName());//show say
    }

    //獲得指定的屬性
    Field f1 = c2.getField("name");
    System.out.println(f1);
    //獲得指定的私有屬性
    Field f2 = c2.getDeclaredField("age");
    //啟用和禁用訪問安全檢查的開關,值為 true,則表示反射的物件在使用時應該取消 java 語言的訪問檢查;反之不取消
    f2.setAccessible(true);
    System.out.println(f2);

    //建立這個類的一個物件
    Object p2 =  c2.newInstance();
    //將 p2 物件的  f2 屬性賦值為 20,f2 屬性即為 私有屬性 age
    f2.set(p2,20);
    //使用反射機制可以打破封裝性,導致了java物件的屬性不安全。
    System.out.println(f2.get(p2)); //20

    //獲取構造方法
    Constructor[] constructors = c2.getConstructors();
    for(Constructor constructor : constructors){
        System.out.println(constructor.toString());
    }
}

3.3 獲取泛型型別

我們定義一個Person的子類來演示如何獲取泛型型別:

public class Student extends Person<String> {
    public static void main(String[] args) {
        Student st=new Student();
        Class clazz=st.getClass();
        //getSuperclass()獲得該類的父類:class com.lfx.test.Person
        System.out.println(clazz.getSuperclass());
        //getGenericSuperclass()獲得帶有泛型的父類
        //Type是 Java 程式語言中所有型別的公共高階介面。它們包括原始型別、引數化型別、陣列型別、型別變數和基本型別。
        Type type=clazz.getGenericSuperclass();
        //輸出type: com.lfx.test.Person<java.lang.String>
        System.out.println(type);
        //ParameterizedType是Type的子類,表示引數化型別,即泛型
        ParameterizedType p=(ParameterizedType)type;
        //輸出p: com.lfx.test.Person<java.lang.String>
        System.out.println(p);
        //getActualTypeArguments獲取引數化型別的陣列,泛型可能有多個,例如Map<String,Object>
        Class c=(Class) p.getActualTypeArguments()[0];
        //輸出泛型型別:class java.lang.String
        System.out.println(c);
    }
}

3.4 獲取註解

我們先自定義一些執行期的註解:

public class MyAnnotation {
    /**
     * 註解類
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyClassAnnotation {
        String value();
    }

    /**
     * 構造方法註解
     */
    @Target(ElementType.CONSTRUCTOR)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyConstructorAnnotation {
        String value();
    }

    /**
     * 方法註解
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyMethodAnnotation {
        String value();
    }

    /**
     * 欄位註解
     */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyFieldAnnotation {
        String value();
    }
    /**
     * 可以同時應用到類和方法上
     */
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyClassAndMethodAnnotation {
       String value();
    }

}

我們對Person類新增自定義的註解,用於測試反射是如何獲取註解的:

@MyClassAnnotation("測試獲取類註解")
@MyClassAndMethodAnnotation("測試獲取多個類註解")
public class Person<T> {
    @MyFieldAnnotation("測試獲取私有屬性註解")
    private int age=18;
    @MyFieldAnnotation("測試獲取公共屬性註解")
    public String name="tom";
    @MyConstructorAnnotation("測試獲取空參構造器註解")
    public Person() {
    }
    @MyConstructorAnnotation("測試獲取有參構造器註解")
    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }
    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;
    }
    @MyMethodAnnotation("測試獲取公共方法註解")
    public void show(){
        System.out.println("show run!");
    }
    @MyMethodAnnotation("測試獲取私有方法註解")
    private void say(){
        System.out.println("say run!");
    }
}

反射獲取註解程式碼:

public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
        //通過第二種方式獲取Class物件
        Class c2 = Person.class;
        //獲得類完整的名字
        String className = c2.getName();
        System.out.println(className);

        //獲取類上指定名稱的註解
        MyClassAnnotation myClassAnnotation = (MyClassAnnotation) c2.getAnnotation(MyClassAnnotation.class);
        //輸出類指定註解的value值:
        System.out.println(myClassAnnotation.value());

        //獲取類上所有註解
        Annotation[] annotations = c2.getAnnotations();
        //輸出類上所有註解:
        for(Annotation annotation : annotations){
            System.out.println(annotation.toString());
        }

        //獲取所有構造方法的指定註解:
        Constructor[] constructors = c2.getConstructors();
        for(Constructor constructor : constructors){
            MyConstructorAnnotation myConstructorAnnotation = (MyConstructorAnnotation) constructor.getAnnotation(MyConstructorAnnotation.class);
            //輸出所有構造方法指定註解的value值
            System.out.println(myConstructorAnnotation.value());
        }

        //獲得公有欄位指定名稱的註解。
        Field field = c2.getField("name");
        MyFieldAnnotation fieldAnnotation = field.getAnnotation(MyFieldAnnotation.class);
        //輸出公有欄位指定名稱註解的value值
        System.out.println(fieldAnnotation.value());

        //獲得私有欄位指定名稱的註解。
        Field privateField = c2.getDeclaredField("age");
        MyFieldAnnotation privateFieldAnnotation = privateField.getAnnotation(MyFieldAnnotation.class);
        //輸出私有欄位指定名稱註解的value值
        System.out.println(privateFieldAnnotation.value());

        //獲得公有方法指定名稱的註解
        Method method = c2.getMethod("show");
        MyMethodAnnotation myMethodAnnotation = method.getAnnotation(MyMethodAnnotation.class);
        //輸出公有方法指定名稱的註解的value
        System.out.println(myMethodAnnotation.value());

        //獲得私有方法指定名稱的註解
        Method privateMethod = c2.getDeclaredMethod("say");
        MyMethodAnnotation myPrivateMethodAnnotation =privateMethod.getAnnotation(MyMethodAnnotation.class);
        //輸出公有方法指定名稱的註解的value
        System.out.println(myPrivateMethodAnnotation.value());
    }