反射

首先要了解什麼是「動態類型語言」:類型的檢查是在運行時檢查的,程序在運行時能夠改變程序的結構和類型。常見的語言:javascript、Python、Ruby,而靜態性語言是在程序編譯的階段,對程序進行檢查。相對的來講有"動態的類型語言",就會有"靜態性語言"的存在的,常見的"靜態性語言"有 java、c、c++等,這些靜態性語言都是在程序編譯的時候檢查程序。javascript

靜態類型的語言的特色:java

優勢:結構很是的規範,便於調試,類型安全。c++

缺點:爲此須要寫更多的類型相關代碼,致使不便於閱讀、不清晰明瞭。程序員

動態類型的語言的特色:編程

優勢:方便閱讀,不須要寫很是多的類型相關的代碼。數組

缺點:天然就是不方便調試,命名不規範時會形成讀不懂,不利於理解等。安全

 

靜態語言的開發效率可由IDE工具提升,而動態語言對程序員我的要求更高。框架

反射能夠更好的幫助咱們理解java中所說的"一切皆對象"的論述,正由於java中含有反射的技術,因此能夠認爲java語言是"準動態性的語言",咱們能夠利用反射機制、字節碼操做得到相似動態語言的特性ide

從而讓編程的時候更加靈活!函數

反射機制:

   原先咱們想調用一個類中的方法的時候,那麼咱們最初的作法是,先建立一個對象,而後使用對象.方法名()來調用方法,固然若是方法是靜態的方法的話,那麼咱們可使用類名.方法名來調用;咱們知道,java源代碼通過javac變成.class的字節碼,而後被類加載器加載,加載完成後在堆內存的方法區產生一個Class的對象(一個類中對應一個對象),這個對象對應類中的完整的結構,這個對象就像一面鏡子,咱們經過這個鏡子能夠看見這個類的完整的結構,這就是反射。

反射是在程序運行的時候,經過獲取某個類的Class類的對象,進而能夠操做此類的全部的屬性和方法。那麼問題就轉換成如何得到Class的對象。

獲取Class的對象的方式:

1.類名.class :前提是知道該類的類名。

2.對象.getClass:前提是知道該類的對象。

3.Class對象.forName("類的全路徑 = 包名 + 類名");通常用於加載配置文件,來獲取相應的class的對象(好比說好多框架的原理)

4.使用類的加載器:ClassLoader.loadClass("類的全類名");

咱們知道類的加載器將字節碼的文件加載進來,一個類只對應一個class,那麼這個類中的成員也有相對應的class,這也就對應了,萬物皆對象的概念,正如 jdkApi所說:

Class的實例表示正在運行的 Java 應用程序中的類和接口。

枚舉是一種類,enum

註釋(指的是註解,是給計算機看的)是一種接口。annocation

每一個數組([])屬於被映射爲 Class 對象的一個類,全部具備相同元素類型和維數的數組都共享該 Class 對象。

基本的 Java 類型(primitive type)(booleanbytecharshortintlongfloatdouble)和關鍵字 void 也表示爲 Class 對象。

Class 沒有公共構造方法。

Class 對象是在加載類時由 Java 虛擬機以及經過調用類加載器中的 defineClass 方法自動構造的。

那麼能夠將各個類型 的Class的對象,打印一下,看看都是什麼,代碼以下

基本數據類型的Class的對象仍然是他們自己(void的class對象也是他自己):

public static void main(String[] args) {
        //基本數據類型:
        Class bytes = byte.class;
        System.out.println(bytes);
        
        Class shorts = short.class;
        System.out.println(shorts);
        
        Class ints = int.class;
        System.out.println(ints);
        
        Class longs = long.class;
        System.out.println(longs);
        
        Class floats = float.class;
        System.out.println(floats);
        
        Class doubles = double.class;
        System.out.println(doubles);
        
        Class bolleans = boolean.class;
        System.out.println(bolleans);
        
        Class chars = char.class;
        System.out.println(chars);
    }

結果:

數組:只要是數據類型相同,維度相同,那麼他們是存在同一個數據的class中的。

class中對應的狀況:

java.lang.Class:表明一個類

java.lang.reflect.Method:表明類的方法

java.lang.reflect.Field:表明類的成員變量

java.lang.reflect.Constructor:表明類的構造方法

Class.getPackage()獲取包

getName():全限定名

動態的構造對象分爲兩種狀況:

1.含有無參的構造函數:能夠直接使用Class的對象來調用newInstance();建立對象。

/*
     * 動態的獲取對象的屬性:
     */
    @Test
    public void getObj() throws Exception {
        //1.構造方法非私有的。
        Class clazz = Student.class;
        Object instance = clazz.newInstance();
        System.out.println(instance);//Student [name=null, age=0]
        //2.給屬性賦值:
        //獲取某個屬性在字節碼文件 中Field類型的對象
        Field name = clazz.getDeclaredField("name");
        //因爲屬性被私有化了,那麼須要打開訪問的權限才能夠訪問
        name.setAccessible(true);
        //設置屬性name的值爲"張三"
        name.set(instance, "張三");
        System.out.println(name.get(instance));//獲取哪一個對象的實例
        //3.設置id的值:與上面的設置屬性是同理的。
        Field fieldId = clazz.getDeclaredField("age");
        fieldId.setAccessible(true);
        fieldId.set(instance, 1);
        System.out.println(fieldId.get(instance));
    }

實體類:

package com.atguigu.reflect.bean;

public class Student {

    private String name;
    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
    
    /**
     * 獲取學生的信息
     * @return
     */
    public String getInfo() {
        return "名稱:" + name + ",年齡:" + age;
    }
    

}

 

2.若是沒有無參數的構造函數:就不能直接使用字節碼的對象來調用newInstance();來直接建立對象;能夠建立Constructor的對象,而後再去newInstance()

/*
     * 動態的獲取成員的方法。
     * 在沒有無參數的構造函數
     */
    @Test
    public void getObj_3() throws Exception {
        //1.一樣也是先獲取該類的Class的對象
        Class clazz = Student.class;
        //2.實例化相應的對象(要求對象有默認的無參構造)
        //Object instance = clazz.newInstance();
        //若是沒有無參數構造函數的話,那麼:須要使用有參構造
        Constructor constructor = clazz.getConstructor(String.class,int.class);
        Object instance = constructor.newInstance("張三丰",12);
        //3.使用Class的對象去獲取相應的Method
        //想要肯定一個方法:1.方法名;2.參數列表
        Method method = clazz.getDeclaredMethod("getInfo");
        //方法被某個對象調用。
        Object object = method.invoke(instance);
        //由於設計者並不知道,具體的什麼方法會被執行,因此設置的是任意的類型,
        //因此想使用具體的那個方法的話,那麼只有是將其向下轉型處理
        String s = (String) object;
        System.out.println(s);
 }

實體類:

package com.atguigu.reflect.bean;

public class Student {

    private String name;
    private int age;

    /*public Student() {
    }*/

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
    
    /**
     * 獲取學生的信息
     * @return
     */
    public String getInfo() {
        return "名稱:" + name + ",年齡:" + age;
    }
    

}

經過反射來獲取泛型

Java採用泛型擦除的機制來引入泛型。Java中的泛型僅僅是給編譯器javac使用的,確保數據的安全性和免去強制類型轉換的麻煩。可是,一旦編譯完成,全部和泛型有關的類型所有擦除。因此原先直接獲取不到泛型信息。爲了經過反射操做這些類型以迎合實際開發的須要,Java5.0就新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType幾種類型來表明不能被歸一到Class中的類型可是又和原始類型齊名的類型

  • GenericArrayType:表示一種元素類型是參數化類型或者類型變量的數組類型,好比:T[],ArrayList<T>[]
  • TypeVariable:是各類類型變量的公共父接口。好比:T,E

  • Type[] getBounds()能夠繼續獲取上限,若是類型變量指定了上限例如T extends Person就返回上限Person,若是未指定就是Object
  • ParameterizedType:表示一種參數化的類型,好比:Comparable<Cat>,ArrayList<?>,ArrayList<? extends/super  T/Cat>

  • Type[]  getActualTypeArguments()返回表示此類型實際類型參數的 Type 對象的數組。

     WildcardType:表明一種通配符類型表達式,(Wildcard通配符)好比?,? extends/super  Person/T

    Type[]  getUpperBounds()上限,若是有的話,沒有就默認是Object

    Type[]  getLowerBounds()下限,若是有的話,沒有就返回長度爲0的數組

泛型類和泛型接口:

Type getGenericSuperclass():若是父類有泛型,那麼返回ParameterizedType參數化類型,即extends後面的父類寫法

例如:com.reflection.type.DAOImpl<T, P>保留

      com.reflection.type.DAOImpl<User, Integer>

      com.reflection.type.DAOImpl 擦除

 若是此 Class 表示 Object 類、接口、基本類型或 void,則返回 null。若是此對象表示一個數組類,則返回表示 Object 類的 Class 對象。

2)Type[] getGenericInterfaces():同上

3)TypeVariable[] clazz.getTypeParameters():返回本Class對象對應類型的泛型的類型變量

  例如:ArrayList類的E,若是有E extends 父類,能夠經過TypeVariable的getBounds()方法獲得上限。若是沒有指定上限,默認上限是Object

經過反射讀取註解配置參數的值:

package com.atguigu.reflect.annocation;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;

import org.junit.Test;

/**
 * 所用的知識: a:自定義註解 b:使用反射讀取註解中的值
 * 
 * @author DHB
 *
 */
public class AnnocationTest {
    public static void main(String[] args) {

    }

    /**
     * 獲取方法上的註解
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws SecurityException 
     * @throws NoSuchMethodException 
     */
    @Test
    public void test_2() throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException {
        // 獲取一個類上的註解:
        Class clazz = UseAnnocation.class;
        Method method = clazz.getMethod("test");
        // 獲取相應的註解的對象:
        Annotation annotation = method.getDeclaredAnnotation(MyAnnocation.class);
        //向下轉型:有風險
        MyAnnocation my = (MyAnnocation) annotation;
        System.out.println(my.value());
    }
    
    
    /**
     * 獲取類上的註解
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    @Test
    public void test() throws InstantiationException, IllegalAccessException {
        // 獲取一個類上的註解:
        Class clazz = UseAnnocation.class;
        // 獲取相應的註解的對象:
        Annotation annotation = clazz.getAnnotation(MyAnnocation.class);
        // 獲取的對象的時候,通常須要對其進行向下轉型。
        MyAnnocation m = (MyAnnocation) annotation;
        System.out.println(m.value());
    }

}

// 自定義一個註解:定義其做用的位置(類上、方法上)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME) // 指定runtime的生命週期,反射訪問的到
@interface MyAnnocation {
    // 聲明配置參數
    String value() default "atguigu";
}

// 定義一個類去使用註解:
@MyAnnocation("生命在於靜止....")
class UseAnnocation {
    @MyAnnocation
    public void test() {
        System.out.println("hello world!!!");
    }
}

反射的應用之一:動態的代理。

動態代理的演示將會在後續的碼出。

相關文章
相關標籤/搜索