作者:奮斗的小子
鏈接:https://www.zhihu.com/question/24304289/answer/38218810
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
**【此反射來自于李興華的java se 實戰經典,非做廣告,網上有視頻教程資源和筆記word素材,如果有需要可以留言】**
**網上百度出來的word文檔,侵刪**
**[魔樂MLDN 13天搞定JAVA 魔樂課堂實戰系列.doc_免費高速下載](https://link.zhihu.com/?target=http%3A//pan.baidu.com/share/link%3Fshareid%3D1689691028%26uk%3D52725210)**
**——————————————————第二次更新——————————————————**
**更新內容:對齊樣式**
**反射之中包含了一個“反”的概念,所以要想解釋反射就必須先從“正”開始解釋,一般而言,當用戶使用一個類的時候,應該先知道這個類,而后通過這個類產生實例化對象,但是“反”指的是通過對象找到類。**
~~~
packagecn.mldn.demo;
classPerson {}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Person per = newPerson() ; // 正著操作
System.out.println(per.getClass().getName()); // 反著來
}
}
~~~
**以上的代碼使用了一個****getClass()****方法,而后就可以得到對象所在的“包****.****類”名稱,這就屬于“反”了,但是在這個“反”的操作之中有一個****getClass()****就作為發起一切反射操作的開端。**
**Person****的父類是****Object****類,而上面所使用****getClass()****方法就是****Object****類之中所定義的方法。**
**·****取得****Class****對象:****public final Class getClass()****,****反射之中的所有泛型都定義為****?****,返回值都是****Object****。**
**而這個****getClass()****方法返回的對象是****Class****類的對象,所以這個****Class****就是所有反射操作的源頭。但是在講解其真正使用之前還有一個需要先解釋的問題,既然****Class****是所有反射操作的源頭,那么這個類肯定是最為重要的,而如果要想取得這個類的實例化對象,****Java****中定義了三種方式:**
**方式一:****通過****Object****類的****getClass()****方法取得,基本不用:**
~~~
packagecn.mldn.demo;
classPerson {}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Person per = newPerson() ; // 正著操作
Class<?> cls = per.getClass() ; // 取得Class對象
System.out.println(cls.getName()); // 反著來
}
}
~~~
**方式二:****使用“類****.class****”取得,在日后學習****Hibernate****開發的時候使用**
~~~
packagecn.mldn.demo;
classPerson {}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Person.class; // 取得Class對象
System.out.println(cls.getName()); // 反著來
}
}
~~~
**方式三:****使用****Class****類內部定義的一個****static****方法,主要使用**
**·****取得****Class****類對象:****public static Class forName(String className) throws ClassNotFoundException****;**
~~~
packagecn.mldn.demo;
classPerson {}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
System.out.println(cls.getName()); // 反著來
}
}
~~~
**那么現在一個新的問題又來了,取得了****Class****類的對象有什么用處呢?對于對象的實例化操作之前一直依靠構造方法和關鍵字****new****完成,可是有了****Class****類對象之后,現在又提供了另外一種對象的實例化方法:**
**·****通過反射實例化對象:****public T newInstance() throws InstantiationException, IllegalAccessException****;**
**范例:****通過反射實例化對象**
~~~
packagecn.mldn.demo;
classPerson {
@Override
publicString toString() {
return"Person Class Instance .";
}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
Object obj = cls.newInstance() ; // 實例化對象,和使用關鍵字new一樣
Person per = (Person) obj ; // 向下轉型
System.out.println(per);
}
}
~~~
**那么現在可以發現,對于對象的實例化操作,除了使用關鍵字****new****之外又多了一個反射機制操作,而且這個操作要比之前使用的****new****復雜一些,可是有什么用?**
**對于程序的開發模式之前一直強調:盡量減少耦合,而減少耦合的最好做法是使用接口,但是就算使用了接口也逃不出關鍵字****new****,所以實際上****new****是造成耦合的關鍵元兇。**
**范例:****回顧一下之前所編寫的工廠設計模式**
~~~
packagecn.mldn.demo;
interfaceFruit {
publicvoideat() ;
}
classApple implementsFruit {
publicvoideat() {
System.out.println("吃蘋果。");
};
}
classFactory {
publicstaticFruit getInstance(String className) {
if("apple".equals(className)){
returnnewApple() ;
}
returnnull;
}
}
publicclassFactoryDemo {
publicstaticvoidmain(String[] args) {
Fruit f = Factory.getInstance("apple") ;
f.eat() ;
}
}
~~~
**以上為之前所編寫最簡單的工廠設計模式,但是在這個工廠設計模式之中有一個最大的問題:如果現在接口的子類增加了,那么工廠類肯定需要修改,這是它所面臨的最大問題,而這個最大問題造成的關鍵性的病因是****new****,那么如果說現在不使用關鍵字****new****了,變為了反射機制呢?**
**反射機制實例化對象的時候實際上只需要“包****.****類”就可以,于是根據此操作,修改工廠設計模式。**
~~~
packagecn.mldn.demo;
interfaceFruit {
publicvoideat() ;
}
classApple implementsFruit {
publicvoideat() {
System.out.println("吃蘋果。");
};
}
classOrange implementsFruit {
publicvoideat() {
System.out.println("吃橘子。");
};
}
classFactory {
publicstaticFruit getInstance(String className) {
Fruit f = null;
try{
f = (Fruit) Class.forName(className).newInstance() ;
} catch(Exception e) {
e.printStackTrace();
}
returnf ;
}
}
publicclassFactoryDemo {
publicstaticvoidmain(String[] args) {
Fruit f = Factory.getInstance("cn.mldn.demo.Orange") ;
f.eat() ;
}
}
~~~
**發現,這個時候即使增加了接口的子類,工廠類照樣可以完成對象的實例化操作,這個才是真正的工廠類,可以應對于所有的變化。如果單獨從開發角度而言,與開發者關系不大,但是對于日后學習的一些框架技術這個就是它實現的命脈,****在日后的程序開發上,如果發現操作的過程之中需要傳遞了一個完整的“包****.****類”名稱的時候幾乎都是反射機制作用。**
3.12.2 、反射的深入應用
**以上只是利用了****Class****類作為了反射實例化對象的基本應用,但是對于一個實例化對象而言,它需要調用類之中的構造方法、普通方法、屬性,而這些操作都可以通過反射機制完成。**
3.12.2 .1、調用構造
**使用反射機制也可以取得類之中的構造方法,這個方法在****Class****類之中已經明確定義了:**
**以下兩個方法**
**取得一個類的全部構造:**
**public Constructor[] getConstructors() throws SecurityException**
**取得一個類的指定參數構造:**
**public Constructor getConstructor(Class... parameterTypes) throws NoSuchMethodException, SecurityException**
**現在發現以上的兩個方法返回的都是****java.lang.reflect.Constructor****類的對象。**
**范例:****取得一個類之中的全部構造**
~~~
packagecn.mldn.demo;
importjava.lang.reflect.Constructor;
classPerson { // CTRL + K
publicPerson() {}
publicPerson(String name) {}
publicPerson(String name,intage) {}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
Constructor<?> cons [] = cls.getConstructors() ; // 取得全部構造
for(intx = 0; x < cons.length; x++) {
System.out.println(cons[x]);
}
}
}
~~~
**驗證:****在之前強調的一個簡單****Java****類必須存在一個無參構造方法**
**范例:****觀察沒有無參構造的情況**
~~~
packagecn.mldn.demo;
classPerson { // CTRL + K
privateString name;
privateintage;
publicPerson(String name,intage) {
this.name= name ;
this.age= age ;
}
@Override
publicString toString() {
return"Person [name="+ name+ ", age="+ age+ "]";
}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
Object obj = cls.newInstance(); // 實例化對象
System.out.println(obj);
}
}
~~~
**此時程序運行的時候出現了錯誤提示“****java.lang.InstantiationException****”,因為以上的方式使用反射實例化對象時需要的是類之中要提供無參構造方法,但是現在既然沒有了無參構造方法,那么就必須明確的找到一個構造方法,而后利用****Constructor****類之中的新方法實例化對象:**
**·****實例化對象:****public T newInstance(Object... initargs) throws InstantiationException, IllegalAccessException,****IllegalArgumentException, InvocationTargetException**
~~~
packagecn.mldn.demo;
importjava.lang.reflect.Constructor;
classPerson { // CTRL + K
privateString name;
privateintage;
publicPerson(String name,intage) {
this.name= name ;
this.age= age ;
}
@Override
publicString toString() {
return"Person [name="+ name+ ", age="+ age+ "]";
}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
// 取得指定參數類型的構造方法
Constructor<?> cons = cls.getConstructor(String.class,int.class) ;
Object obj = cons.newInstance("張三", 20); // 為構造方法傳遞參數
System.out.println(obj);
}
}
~~~
**很明顯,調用無參構造方法實例化對象要比調用有參構造的更加簡單、方便,所以在日后的所有開發之中,凡是有簡單****Java****類出現的地方,都一定要提供無參構造。**
3.12.2 .2、調用普通方法
**當取得了一個類實例化對象之后,下面最需要調用的肯定是類之中的方法,所以可以繼續使用****Class****類取得一個類中所定義的方法定義:**
**·****取得全部方法:****public Method[] getMethods() throws SecurityException****;**
**·****取得指定方法:****public Method getMethod(String name, Class... parameterTypes) throws** **NoSuchMethodException, SecurityException**
**發現以上的方法返回的都是****java.lang.reflect.Method****類的對象。**
**范例:****取得一個類之中所定義的全部方法**
~~~
packagecn.mldn.demo;
importjava.lang.reflect.Method;
classPerson {
privateString name;
publicvoidsetName(String name) {
this.name= name;
}
publicString getName() {
returnname;
}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
Method met [] = cls.getMethods() ; // 取得全部方法
for(intx = 0; x < met.length; x++) {
System.out.println(met[x]);
}
}
}
~~~
**但是取得了****Method****類對象最大的作用不再于方法的列出(方法的列出都在開發工具上使用了),但是對于取得了****Method****類對象之后還有一個最大的功能,就是可以利用反射調用類中的方法:**
**·****調用方法:****public Object invoke(Object obj, Object... args) throws IllegalAccessException,****IllegalArgumentException, InvocationTargetException**
**之前調用類中方法的時候使用的都是“對象****.****方法”,但是現在有了反射之后,可以直接利用****Object****類調用指定子類的操作方法。(同時解釋一下,為什么****setter****、****getter****方法的命名要求如此嚴格)。**
**范例:****利用反射調用****Person****類之中的****setName()****、****getName()****方法**
~~~
packagecn.mldn.demo;
importjava.lang.reflect.Method;
classPerson {
privateString name;
publicvoidsetName(String name) {
this.name= name;
}
publicString getName() {
returnname;
}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
Object obj = cls.newInstance(); // 實例化對象,沒有向Person轉型
String attribute = "name"; // 要調用類之中的屬性
Method setMet = cls.getMethod("set"+ initcap(attribute), String.class);// setName()
Method getMet = cls.getMethod("get"+ initcap(attribute));// getName()
setMet.invoke(obj, "張三") ; // 等價于:Person對象.setName("張三")
System.out.println(getMet.invoke(obj));// 等價于:Person對象.getName()
}
publicstaticString initcap(String str) {
returnstr.substring(0,1).toUpperCase().concat(str.substring(1)) ;
}
}
~~~
**在日后的所有框架技術開發之中,簡單****Java****類都是如此應用的,所以必須按照標準進行。**
3.12.2 .3、調用成員
**類之中最后一個組成部分就是成員(****Field****,也可以稱為屬性),如果要通過反射取得類的成員可以使用方法如下:**
**·****取得本類的全部成員:****public Field[] getDeclaredFields() throws SecurityException****;**
**·****取得指定的成員:****public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException****;**
**這兩個方法的返回值類型是****java.lang.reflect.Field****類的對象,下面首先觀察如何取得一個類之中的全部屬性。**
**范例:****取得一個類之中的全部屬性**
~~~
packagecn.mldn.demo;
importjava.lang.reflect.Field;
classPerson {
privateString name;
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
Field field [] = cls.getDeclaredFields() ; // 取得全部屬性
for(intx = 0; x < field.length; x++) {
System.out.println(field[x]);
}
}
}
~~~
**但是找到****Field****實際上就找到了一個很有意思的操作,在****Field****類之中提供了兩個方法:**
**·****設置屬性內容(類似于:對象****.****屬性****=** **內容):****public void set(Object obj, Object value)**
**throws IllegalArgumentException, IllegalAccessException****;**
**·****取得屬性內容(類似于:對象****.****屬性):****public Object get(Object obj)**
**throws IllegalArgumentException, IllegalAccessException**
**可是從類的開發要求而言,一直都強調類之中的屬性必須封裝,所以現在調用之前要想辦法解除封裝。**
**·****解除封裝:****public void setAccessible(boolean flag) throws SecurityException****;**
**范例:****利用反射操作類中的屬性**
~~~
packagecn.mldn.demo;
importjava.lang.reflect.Field;
classPerson {
privateString name;
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class對象
Object obj = cls.newInstance(); // 對象實例化屬性才會分配空間
Field nameField = cls.getDeclaredField("name") ; // 找到name屬性
nameField.setAccessible(true) ; // 解除封裝了
nameField.set(obj, "張三") ; // Person對象.name = "張三"
System.out.println(nameField.get(obj)); // Person對象.name
}
}
~~~
- JVM
- 深入理解Java內存模型
- 深入理解Java內存模型(一)——基礎
- 深入理解Java內存模型(二)——重排序
- 深入理解Java內存模型(三)——順序一致性
- 深入理解Java內存模型(四)——volatile
- 深入理解Java內存模型(五)——鎖
- 深入理解Java內存模型(六)——final
- 深入理解Java內存模型(七)——總結
- Java內存模型
- Java內存模型2
- 堆內內存還是堆外內存?
- JVM內存配置詳解
- Java內存分配全面淺析
- 深入Java核心 Java內存分配原理精講
- jvm常量池
- JVM調優總結
- JVM調優總結(一)-- 一些概念
- JVM調優總結(二)-一些概念
- VM調優總結(三)-基本垃圾回收算法
- JVM調優總結(四)-垃圾回收面臨的問題
- JVM調優總結(五)-分代垃圾回收詳述1
- JVM調優總結(六)-分代垃圾回收詳述2
- JVM調優總結(七)-典型配置舉例1
- JVM調優總結(八)-典型配置舉例2
- JVM調優總結(九)-新一代的垃圾回收算法
- JVM調優總結(十)-調優方法
- 基礎
- Java 征途:行者的地圖
- Java程序員應該知道的10個面向對象理論
- Java泛型總結
- 序列化與反序列化
- 通過反編譯深入理解Java String及intern
- android 加固防止反編譯-重新打包
- volatile
- 正確使用 Volatile 變量
- 異常
- 深入理解java異常處理機制
- Java異常處理的10個最佳實踐
- Java異常處理手冊和最佳實踐
- Java提高篇——對象克隆(復制)
- Java中如何克隆集合——ArrayList和HashSet深拷貝
- Java中hashCode的作用
- Java提高篇之hashCode
- 常見正則表達式
- 類
- 理解java類加載器以及ClassLoader類
- 深入探討 Java 類加載器
- 類加載器的工作原理
- java反射
- 集合
- HashMap的工作原理
- ConcurrentHashMap之實現細節
- java.util.concurrent 之ConcurrentHashMap 源碼分析
- HashMap的實現原理和底層數據結構
- 線程
- 關于Java并發編程的總結和思考
- 40個Java多線程問題總結
- Java中的多線程你只要看這一篇就夠了
- Java多線程干貨系列(1):Java多線程基礎
- Java非阻塞算法簡介
- Java并發的四種風味:Thread、Executor、ForkJoin和Actor
- Java中不同的并發實現的性能比較
- JAVA CAS原理深度分析
- 多個線程之間共享數據的方式
- Java并發編程
- Java并發編程(1):可重入內置鎖
- Java并發編程(2):線程中斷(含代碼)
- Java并發編程(3):線程掛起、恢復與終止的正確方法(含代碼)
- Java并發編程(4):守護線程與線程阻塞的四種情況
- Java并發編程(5):volatile變量修飾符—意料之外的問題(含代碼)
- Java并發編程(6):Runnable和Thread實現多線程的區別(含代碼)
- Java并發編程(7):使用synchronized獲取互斥鎖的幾點說明
- Java并發編程(8):多線程環境中安全使用集合API(含代碼)
- Java并發編程(9):死鎖(含代碼)
- Java并發編程(10):使用wait/notify/notifyAll實現線程間通信的幾點重要說明
- java并發編程-II
- Java多線程基礎:進程和線程之由來
- Java并發編程:如何創建線程?
- Java并發編程:Thread類的使用
- Java并發編程:synchronized
- Java并發編程:Lock
- Java并發編程:volatile關鍵字解析
- Java并發編程:深入剖析ThreadLocal
- Java并發編程:CountDownLatch、CyclicBarrier和Semaphore
- Java并發編程:線程間協作的兩種方式:wait、notify、notifyAll和Condition
- Synchronized與Lock
- JVM底層又是如何實現synchronized的
- Java synchronized詳解
- synchronized 與 Lock 的那點事
- 深入研究 Java Synchronize 和 Lock 的區別與用法
- JAVA編程中的鎖機制詳解
- Java中的鎖
- TreadLocal
- 深入JDK源碼之ThreadLocal類
- 聊一聊ThreadLocal
- ThreadLocal
- ThreadLocal的內存泄露
- 多線程設計模式
- Java多線程編程中Future模式的詳解
- 原子操作(CAS)
- [譯]Java中Wait、Sleep和Yield方法的區別
- 線程池
- 如何合理地估算線程池大小?
- JAVA線程池中隊列與池大小的關系
- Java四種線程池的使用
- 深入理解Java之線程池
- java并發編程III
- Java 8并發工具包漫游指南
- 聊聊并發
- 聊聊并發(一)——深入分析Volatile的實現原理
- 聊聊并發(二)——Java SE1.6中的Synchronized
- 文件
- 網絡
- index
- 內存文章索引
- 基礎文章索引
- 線程文章索引
- 網絡文章索引
- IOC
- 設計模式文章索引
- 面試
- Java常量池詳解之一道比較蛋疼的面試題
- 近5年133個Java面試問題列表
- Java工程師成神之路
- Java字符串問題Top10
- 設計模式
- Java:單例模式的七種寫法
- Java 利用枚舉實現單例模式
- 常用jar
- HttpClient和HtmlUnit的比較總結
- IO
- NIO
- NIO入門
- 注解
- Java Annotation認知(包括框架圖、詳細介紹、示例說明)