從學習Java開始,就入手面向對象、Java反射等等,也懵逼在面向對象的世界中,反問道個人對象是誰,很差意思,可能尚未建立!java
反射是Java的一個特色,也是使本來爲靜態語言的Java,多了那麼一些靈活性,在理解各個框架源碼以及組件內容的時候是一個不錯的知識點,好比註解,這是一個很是常見,又很好使的玩意,以前也有簡單的學習---Java 註解 基礎、Java 註解 實踐 (最好先學習Java反射再去玩註解,會頗有幫助)spring
從主要如下幾點開始學習數組
在面向對象的環境中,萬事萬物皆對象,但也總有例外,Java中有兩個不屬於對象,一個是普通數據類型,一個是靜態的成員框架
普通數據類型有封裝類的彌補,靜態的屬於類,那麼類是否是對象呢,類是對象,是java.lang.Class類的實例對象,看文字還比較容易理解,中文說出來就比較繞口, 英文: there is a class named Classeclipse
一個普通的類的實例對象表示函數
public class Coo { //Doo的實例對象 以doo表示 Doo doo=new Doo(); } class Doo{}
那麼一個Class類的實例對象,有三種表示方式,但不能是new Class,由於下面源碼中也解釋爲何工具
/* * Private constructor. Only the Java Virtual Machine creates Class objects. * This constructor is not used and prevents the default constructor being * generated. */ private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }
上面是Class類中的一個構造器,是私有的,並且註釋說只有JVM建立Class對象,在之前的Java版本中你可能會看到一個無參的構造器,不要緊,那你的構造器也絕對是私有,不能直接建立,在上面中出現了一個JIT編譯的關鍵詞,有興趣的小夥伴能夠研究擴展學習
任何類都是Class的實例對象,下面三種方式:this
public class Coo { //Doo的實例對象 以doo表示 Doo doo=new Doo(); //①能夠看出Doo類有一個隱含的靜態成員變量class Class first=Doo.class; //②已知類的對象,經過getClass獲取 Class second=doo.getClass(); //重理解一次:doo表明Doo類的實例對象,first、second表明的是Class的實例對象 //這個Class的實例對象又證實說Doo這個類自己是一個實例對象的存在 //一本正經的胡說八道,那麼給一個官方給出的說法是這樣的 :first、second表示了Doo類的類 類型(class type) //全部東西都是對象,類也是對象,是Class的實例對象,這個對象稱爲該類的類類型 //就能夠分析到,Doo的對象是doo,Doo的類類型是Class的對象first、second //無論哪一種表達方式表示Doo的類類型,一個類只多是Class類的一個實例對象,因此first == second //③須要異常處理,參數爲類的全稱"com.cloud.eureka.Doo" Class third=null; { try { third = Class.forName("com.cloud.eureka.Doo"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } //依然存在 first == second == third //因而可知,咱們能夠經過類的類類型建立該類的對象,經過first、second、third建立 //須要異常處理,是誰的類的類類型對象,建立的對象就是誰,須要強轉 //newInstance前提須要無參構造方法 { try { Doo dooFirst= (Doo) first.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } class Doo{}
三種表示Class的實例對象中,第三種具備很好的動態加載類③spa
不少時候,你們都是經過工具(IDEA、eclipse等)進行辦公或者學習,編譯和運行都是由工具來輔助完成的,那麼咱們須要知道編譯、運行的區別
1.2..3...好,咱們獲得了編譯、運行知識的技能
只要是在類裏面用到的,都隱含class,對應的類的類類型,以下:
public class Coo { Class c0=int.class; Class c1=String.class; Class c2=Double.class; Class c3=void.class; // package不是在類裏面的,error // Class c4=package.class; }
在Doo類中,寫個方法
class Doo{ public static void staticVoidMethod(Object o){ //傳遞的是什麼類型,就是什麼類型 Class co=o.getClass(); } }
o傳遞的是什麼對象,co就是該類的類類型,那麼底層怎麼實現的,可能會比較複雜,貼一份源碼
public final native Class<?> getClass();
這是一個native聲明的一個方法,稱爲本地方法,Java中有一項技術JNR,使用Java聲明,C語言實現,Java 中調用...一堆,有興趣的能夠了解了解,效果就是上面說的,返回類的類類型
下面是簡單的經過Class獲取類的信息:
class Doo { public static void staticVoidMethod(Object o) { //傳遞的是什麼類型,就是什麼類型 Class co = o.getClass(); System.out.println("類的全名稱:" + co.getName()); System.out.println("類的名字:" + co.getSimpleName()); //Method類,方法對象 //一個成員方法 就是 一個Method對象 //getMethods 獲取全部public的方法,其中包括父類繼承的函數 Method[] allMethods = co.getMethods(); //getDeclaredMethods獲取該類本身聲明的方法 Method[] thisMethods = co.getDeclaredMethods(); for (Method method : allMethods) { //method.getReturnType()獲得的是類的類類型 //好比返回值是String,那麼獲得的是String.class的類類型,經過getName獲取名稱 System.out.println("返回類型:" + method.getReturnType().getName()); System.out.println("方法名稱:" + method.getName()); //獲取參數類型 Class[] parameterTypes = method.getParameterTypes(); for (Class c : parameterTypes) { System.out.println("參數類型:" + c.getName()); } System.out.println("===================================="); } //成員變量 =》對象 //屬於java.lang.reflect.Field //Field封裝了關於成員變量的操做 //getFields獲取全部public的成員變量 Field[] field=co.getFields(); //獲得本身聲明的成員變量 Field[] declaredFields=co.getDeclaredFields(); for (Field fields:field) { System.out.println("成員變量類型"+fields.getType()); System.out.println("成員變量名稱"+fields.getName()); } } }
簡單來一個main方法,加入一個String類
public class Coo { public static void main(String[] args) { String hello=new String(); Doo.staticVoidMethod(hello); } }
控制檯打印 , 全部String內的方法信息:
類的全名稱:java.lang.String 類的名字:String 返回類型:boolean 方法名稱:equals 參數類型:java.lang.Object ==================================== 返回類型:java.lang.String 方法名稱:toString ==================================== 返回類型:int 方法名稱:hashCode ==================================== 返回類型:int 方法名稱:compareTo 參數類型:java.lang.Object ==================================== //......
能夠總結出來,getDeclaredXXX()方法都是獲取本身聲明的內容,包括成員變量,構造器,方法等等,直接的getXXX()方法部分會獲取全部內容包括父類的內容,另外數組是一個特殊的存在,打印的是「0]」差很少的樣子,在JVM對數組的存儲方式也比較VIP,有興趣的能夠理解擴展
上面有獲取全部的方法的示例,下面來學習如何獲取某一個方法以及方法的反射操做
①方法的名稱和方法的參數列表能夠惟必定位某一個方法
②method.invoke(對象,參數列表)
public class MethodReflect { //獲取getMethod方法,獲取①號 public static void main(String[] args) { MethodDemo demo = new MethodDemo(); //1.獲取類信息 Class c0 = demo.getClass(); //2.獲取方法 try { //第一種寫法 Method method1 = c0.getDeclaredMethod("getMethod", new Class[]{String.class, String.class}); //第二種寫法 Method method2 = c0.getDeclaredMethod("getMethod", String.class, String.class); //平時正常的調用方法: demo.getMethod(str0,str1) //如今使用method1來調用--public Object invoke(Object obj, Object... args) //第一個參數是調用的類,第二個參數是可用可無,按定義的方法來錄入(str0,str1) //invoke的方法若是有返回值,則返回Object的值,void的返回值爲null try { Object object1 = method1.invoke(demo, new Object[]{"hello", " world"}); Object object2 = method2.invoke(demo, "hello", " world"); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } catch (NoSuchMethodException e) { e.printStackTrace(); } } } class MethodDemo { //① public void getMethod(String a, String b) { System.out.println("concat: " + a + b); } //② public void getMethod(int a, int b) { System.out.println("sum: " + a + b); } }
泛型不說了,很是的經常使用,好比list,map等等,約定類型,很少作解釋,直接先來一個操做,比對List和List<String>是否相等
public class Coo { public static void main(String[] args) { //無泛型 List list0=new ArrayList(); //String泛型 List<String> list1=new ArrayList<>(); list1.add("hello"); Class c0=list0.getClass(); Class c1=list1.getClass(); //輸出 System.out.println(c0==c1); } }
輸出的結果是true, 編譯後的class文件也能夠當成字節碼,說明反射的操做都是編譯以後的操做,並且返回true說明編譯以後list的泛型被抹去了,去泛型化的,獲得Java的泛型是一種規範,只在編譯時有效,跳過編譯編譯就無效了,爲了驗證這一點,恰好可使用反射來作一個驗證
//獲取list1<String> 中的 add方法 ,向裏面加一個int類型的值 Method method=c1.getMethod("add",Object.class); method.invoke(list1,100); System.out.println(list1.size());
list1的大小改變了,說明添加成功了,也驗證了Java泛型只在編譯期有效,運行時則去泛型化,若是去遍歷這個list1是會報類型轉化異常的
反射的用處有不少,好比工具類,源碼理解,註解解析等等,再例如excel導出導入這樣的操做,網上也有很是多的poi操做案例,也能夠用反射+註解的方式很是簡潔的實現; 例如spring源碼中不少的註解@Autowired、@SpringCloudApplication、@Service...等等不少不少
好好學習,每天向上
------------------------------------------------------------