Java反射機制詳解一
1反射機制是什麽
反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。
在面向對象的世界裏,萬事萬物皆對象.在java語言裏,靜態的成員,普通數據類型是不是對象呢?
類又是誰的對象呢?
首先類是對象,類是java.lang.Class類的實例對象.
新創建一個Foo類
Foo這個類也是一個實例對象,是Class類的實例對象,這個對象在官網被稱為(class type).
反射是java程序開發語言的特性之一,它允許運行中的java程序獲取自身的信息,並且可以操作類或者對象內部的屬性.
反射的核心是JVM在運行時才動態加載類或調用方法/訪問屬性,它不需要事先(寫代碼的時候或編譯期)知道運行對象是誰。
了解反射其實需要了解JVM,不過一般的資料不會在這一部分講到JVM,畢竟學習也是要從淺入深的.
2反射機制能做什麽
反射機制主要提供了以下功能:
在運行時判斷任意一個對象所屬的類;
在運行時構造任意一個類的對象;
在運行時判斷任意一個類所具有的成員變量和方法;
在運行時調用任意一個對象的方法;
生成動態代理。
註意;是運行時獲取而不是編譯時獲取.其實很多時候我們直接用eclipse寫代碼忽略了編譯的過程
在Eclipse,當我們輸入一個點的時候(比如 a.) 編譯器就會自動列出它的屬性和方法,這裏就會用到反射
3反射機制的相關API (在這裏只說反射機制五種功能的前兩種)
java的反射機制的實現要借助於4個類: class,Constructor,Field,Method;
通過一個對象獲得完整的包名和類名
package cn.xins08.boke; public class TestReflect { public static void main(String[] args) { TestReflect testReflect = new TestReflect(); System.out.println(testReflect.getClass().getName()); // 結果:cn.xins08.boke.TestReflect } }
實例化Class對象:
實現反射機制獲取類有三種方法:
package cn.xins08.boke; public class TestReflect { public static void main(String[] args) throws Exception { Class<?> class1 = null; Class<?> class2 = null; Class<?> class3 = null; //第一種方式(在JDBC開發中常用此方法加載數據驅動) class1 = Class.forName("cn.xins08.boke.TestReflect"); //第三種方式:java中任何一個對象都有getClass方法 class2 = new TestReflect().getClass(); //第三種方式java中任何一個對象都有class屬性 class3 = TestReflect.class; System.out.println("類名稱: " + class1.getName()); System.out.println("類名稱: " + class2.getName()); System.out.println("類名稱: " + class3.getName()); // 打印結果: /* * 類名稱: cn.xins08.boke.TestReflect 類名稱: cn.xins08.boke.TestReflect 類名稱: * cn.xins08.boke.TestReflect */ } }
獲取一個對象的父類與實現的接口:
package cn.xins08.boke; import java.io.Serializable; public class TestReflect implements Serializable { private static final long serialVersionUID = -2862585049955236662L; public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("cn.xins08.boke.TestReflect"); // 取得父類 Class<?> parentClass = clazz.getSuperclass(); System.out.println("clazz的父類為:" + parentClass.getName()); // clazz的父類為: java.lang.Object // 獲取所有的接口 Class<?> intes[] = clazz.getInterfaces(); System.out.println("clazz實現的接口有:"); for (int i = 0; i < intes.length; i++) { System.out.println((i + 1) + ":" + intes[i].getName()); } // clazz實現的接口有: // 1:java.io.Serializable } }
創建實例:
通過反射來生成對象主要有兩種方式:
(1)使用Class對象的newInstance()方法來創建Class對象對應類的實例。
Class<?> c = String.class; Object str = c.newInstance();
初始化一個類,生成一個實例的時候,new與newInstance() 有什麽區別?
區別在於創建對象的方式不一樣,前者是使用類加載機制,那麽為什麽會有兩種創建對象方式?這個就要從可伸縮、可擴展,可重用等軟件思想上解釋了。
newInstance: 弱類型。低效率。只能調用無參構造。
new: 強類型。相對高效。能調用任何public構造。
newInstance()是實現IOC、反射、面對接口編程 和 依賴倒置 等技術方法的必然選擇,new 只能實現具體類的實例化,不適合於接口編程。
(2)先通過Class對象獲取指定的Constructor對象,再調用Constructor對象的newInstance()方法來創建實例。這種方法可以用指定的構造器構造類的實例。
// 獲取String所對應的Class對象 Class<?> c = String.class; // 獲取String類帶一個String參數的構造器 Constructor constructor = c.getConstructor(String.class); // 根據構造器創建實例 Object obj = constructor.newInstance("23333"); System.out.println(obj);
註意事項:反射會額外的消耗一定的系統資源,如果不需要動態的創建一個對象,那麽就不需要用反射
在文章的最後我們討論下工廠模式:
package cn.xins08.boke; public interface Fruit { public void eat(); }
package cn.xins08.boke; public class Apple implements Fruit{ public void eat(){ System.out.println("吃蘋果"); } }
package cn.xins08.boke; public class Factory { public static Fruit getInstance(String className){ if("apple".equals(className)){ return new Apple(); } return null; } }
package cn.xins08.boke; public class FactoryDemo { public static void main(String[] args) { Fruit f = Factory.getInstance("apple"); f.eat(); } } //返回的結果是"吃蘋果"
這種是一個最簡單的工廠設計模式,但是有一個很大的問題是,如果現在接口的子類增加了,那麽工廠類肯定需要改.而造成這個問題的病因就是new,如果變成反射機制就不一樣了.反射的實例化對象只需要包.類就可以了.
package cn.xins08.boke; public interface Fruit { public void eat(); }
package cn.xins08.boke; public class Orange implements Fruit{ public void eat(){ System.out.println("吃橘子"); } }
package cn.xins08.boke; public class Apple implements Fruit{ public void eat(){ System.out.println("吃蘋果"); } }
package cn.xins08.boke; public class Factory { public static Fruit getInstance(String className){ Fruit fruit = null; try{ //這裏是Fruit的類類型,做強制類型轉換 fruit = (Fruit) Class.forName(className).newInstance() ; } catch(Exception e) { e.printStackTrace(); } return f ; } } }
package cn.xins08.boke; public class FactoryDemo { public static void main(String[] args) { Fruit f = Factory.getInstance("cn.xins08.boke.Apple"); f.eat(); } }
這時候我們發現即使增加幾個接口的子類,工廠類照樣可以完成對象的實例化操作,可以對應所有的變化.
到目前為止你已經會了最基本的反射使用了,在學習Spring之前先講這些,等學習了Spring的依賴註入,反轉控制之後,相信會對反射有更好的理解.
關於反射以下功能,等寫完後會在本文加鏈接
在運行時判斷任意一個類所具有的成員變量和方法;
在運行時調用任意一個對象的方法;
生成動態代理。
--java一萬小時成神之路--本文首發與51CTO博客---
近期讀書計劃:
《思考,快與慢》
《浪潮之巔》
本文出自 “xinsz08の平行時空” 博客,請務必保留此出處http://xinsz08.blog.51cto.com/10565212/1946912
Java反射機制詳解一