首先要了解什麼是「動態類型語言」:類型的檢查是在運行時檢查的,程序在運行時能夠改變程序的結構和類型。常見的語言: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)(boolean
、byte
、char
、short
、int
、long
、float
和 double
)和關鍵字 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中的類型可是又和原始類型齊名的類型。
TypeVariable:是各類類型變量的公共父接口。好比:T,E
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!!!"); } }
反射的應用之一:動態的代理。
動態代理的演示將會在後續的碼出。