Java關於反射的加深理解,Java反射加深理解
一、反射的基礎
java程式中各個java類屬於同一類事物,描述這類事物的java類名就是Class
比如說,很多人,用java來表示就用Person類,很多類,就用Class,Person類的執行個體對象比如張三、李四代表著一個個具體的人,而Class類就代表著各個類在記憶體中的位元組碼
一個類被類載入器載入進記憶體,會佔用一片儲存空間,這個空間的內容就是類的位元組碼,不同的類的位元組碼不同,所以他們在記憶體中的內容是不同,這些空間分別用一個一個對象來表示,這些對象具有相同的類型,這個類型就是Class
面試題:Class.forName("java.lang.String"),forName()的作用:
返回位元組碼,返回的方式有兩種:1.這份位元組碼曾經被載入過,已經存在jvm中,那麼就可以直接返回。2.jvm中沒有,用類載入器去載入,載入後,位元組程式碼快取到jvm中,以後再這份位元組碼就不需要載入了。
得到一個類的位元組碼有三種方法:
1.String.class2.Person p = new Person(); p.getClass();對象.class3.Class.forName("java.lang.String");
瞭解:一共有9個預定義Class對象,8個基本類型+void.class
數群組類型的class執行個體對象,int[].class.isArray();int數組的位元組碼是數組
總之:只要是在來源程式中出現的類型都有各自的class執行個體對象,int[]、void
示範:
<span style="white-space:pre"></span>public static void main(String[] args)throws Exception {String str1 = "abc";Class class1 = str1.getClass();Class class2 = String.class;Class class3 = Class.forName("java.lang.String");System.out.println(class1==class2);System.out.println(class2==class3);System.out.println(class1.isPrimitive());//是否是原始類型,false,它是類System.out.println(int.class.isPrimitive());//trueSystem.out.println(int.class==Integer.class);//falseSystem.out.println(int.class==Integer.TYPE);//trueSystem.out.println(int[].class.isArray());//true}
二、理解反射的概念
一位大牛總結的一句經典:反射就是把Java類中的各種成分映射成相應的Java類。
一個java類中用一個Class類的對象來表示,一個類的組成成分:建構函式,方法,成員變數,包等也同java類來表示,車本身是一個類,它的輪胎、發動機等等也是一個個類。java的Class類提供一系列方法來獲得建構函式、方法、成員變數等資訊,這些資訊就是相應類的執行個體對象,Field、Method、Contructor、Package等。
一個類中的每個成員都有可以用相應的反射API類的一個執行個體對象來表示
System.exit(0);
System.gc();
System是一個類
exit、gc也是一個類
Method method1 代表exit()
method2 代表gc()
他們都屬於Method類
這也就可以很好的理解java的核心思想:萬物皆對象。不論是什麼,都是對象
三、反射的應用
1.構造方法
Constructor類代表某個類中的一個構造方法
得到類的所有構造方法: Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
得到類的一個構造方法: Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
也可以是:Constructor constructor = String.class.getConstructor(StringBuffer.class);
在獲得執行個體對象的時候要注意,指定執行個體對象的類型,因為編譯器只知道是個對象,但是不知道是什麼類型的對象String str = (String) constructor.newInstance(new StringBuffer("abc"));
2.成員變數
Field類代表某個類中的一個成員變數
注意fiedx代表的是x的定義,而不是具體的x變數,要想得到x的變數,就必須從對象上擷取
public class Reflection {private int x;public int y;public Reflection(int x, int y) {super();this.x = x;this.y = y;}}import java.lang.reflect.Field;public class LreanReflect {public static void main(String[] args)throws Exception{Reflection re = new Reflection(3, 4);Field fieldy = re.getClass().getField("y");//fieldy是多少?4?錯,fieldy不是對象身上的變數,而是類上,要用它取某個對象上的值int st = (Integer)fieldy.get(re);System.out.println(st);//x是私人的Field fieldx = re.getClass().getDeclaredField("x");//允許你看見,但是不讓你用fieldx.setAccessible(true);//暴力反射,不管它同不同意,我都要用st = (Integer)fieldx.get(re);System.out.println(st);}}
成員變數反射的綜合案例
將任意一個對象中的所有String類型的成員變數所對應的字串內容中的"b"改成"a"
import java.lang.reflect.Field;public class LreanReflect {public static void main(String[] args) throws Exception {Reflection reflection = new Reflection(3, 4);ChangeStringValue(reflection);System.out.println(reflection);}private static void ChangeStringValue(Object obj) throws Exception{Field[] fields = obj.getClass().getFields();for(Field f : fields){//if(f.getType().equals(String.class))//因為只有一份String的位元組碼if(f.getType()==String.class){String str = (String)f.get(obj);//String value = str.replaceAll("b", "a");String value = str.replace('b', 'a');f.set(obj,value);//將更改後的值更新給Reflection}}}}package ReflectLearn;public class Reflection {private int x;public int y;public String str1 = "ball";public String str2 = "basketball";public String str3 = "mail";public Reflection(int x, int y) {super();this.x = x;this.y = y;}@Overridepublic String toString(){return str1+":"+str2+":"+str3; }}
3.成員方法
(1)調用方法
Method method = String.class.getMethod("charAt", int.class);String str1 = "abc";System.out.println(method.invoke(str1, 1));//invoke,調用//如果xxx.invoke(null,1)第一參數是null,那麼這個方法肯定是靜態,因為不需要對象
(2)對接收數組參數的成員方法進行反射
寫一個程式,這個程式能夠根據使用者提供的類名,取執行該類中的main方法
import java.lang.reflect.Method;public class LreanReflect {public static void main(String[] args) throws Exception {//MyMain.main(new String[]{"11","22","33"});String startingClassName = args[0];//這裡需要設定一下main參數,因為我不知道要運行那個main方法//run as ->run Configurations->Arguments(ReflectLearn.MyMain)System.out.println(startingClassName);Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);mainMethod.invoke(null, new Object[]{new String[]{"11","22","33"}});//打包//mainMethod.invoke(null, (Object)new String[]{"11","22","33"});//為什麼要打包?main方法接受一個參數(字串數組),我們傳遞給它一個字串數組//在JDK1.5一個數組就是對應一個參數,但是在JDK1.4,數組中的每一個元素對應一個參數//我們傳遞一個String[],也就是一個Object數組,一開啟,其中的每個元素都是一個參數,它就會認為收到3各參數//所以,用Object數組,再打一個包,這樣,我給你一包東西,開啟後,是一個數組,這樣就OK了//正式因為,要拆開,所以我提前就給你打一個包}}class MyMain{public static void main(String[] args){for(String s : args){System.out.println(s);}}}
(3)數組的反射
一個數組也是一個Object,每一個相同元素類型,以及相同維度數組,都是同一個class
import java.lang.reflect.Array;import java.util.Arrays;public class LreanReflect {public static void main(String[] args) throws Exception {int[] arr1 = new int[3];int[] arr2 = new int[4];int[][] arr3 = new int[3][4];String[] arr4 = new String[]{"a","b","c"};System.out.println(arr1.getClass() == arr2.getClass());System.out.println(arr1.getClass().getName());System.out.println(arr1.getClass().getSuperclass().getName());Object obj1 = arr1;//Object[] obj2 = arr2;這樣不行Object[] obj3 = arr3;Object obj4 = arr4;Object[] obj5 = arr4;System.out.println(Arrays.asList(arr1));System.out.println(Arrays.asList(arr4));//字串可以printElement(arr1);}private static void printElement(Object arr1) {Class c = arr1.getClass();if(c.isArray()){int len = Array.getLength(arr1);for(int i = 0;i<len;i++){System.out.println(Array.get(arr1,i));}}else{System.out.println(arr1);}}}
延伸:關於hashCode
當一個對象被儲存進HashSet集合中,就不能修改這個對象中那些參與計算雜湊值的欄位了,否則,對象修改後的雜湊值與最初儲存進HasgSet集合中時的雜湊值不同,這就導致了使用當前集合對象的contains取檢查修改後的對象,發生檢索失敗的現象,從而導致了無法從HashSet集合中刪除這個修改前的對象,進而致使記憶體泄露
public class Reflection {private int x;public int y;@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + x;result = prime * result + y;return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Reflection other = (Reflection) obj;if (x != other.x)return false;if (y != other.y)return false;return true;}public Reflection(int x, int y) {super();this.x = x;this.y = y;}}public class ReflectTest2 {public static void main(String[] args) {Collection conCollection = new HashSet();Reflection[] rs = new Reflection[]{new Reflection(3, 3),new Reflection(4, 4),new Reflection(3, 3)};for(Reflection r : rs)conCollection.add(r);//rs[0].y = 1;System.out.println(conCollection.contains(rs[0]));boolean b = conCollection.remove(rs[0]);System.out.println(b);System.out.println(conCollection.size());}}truetrue1把注釋開啟falsefalse2
所以說,如果我們往集合中存對象,又改對象,再刪對象,對象並沒有被刪除,以此日積月累,記憶體泄露。所以存進集中的對象,就不要
修改了
四、架構的概念及用反射技術開發架構的原理
反射的作用->實現架構
類比一個超小型架構
config.propertiesclassName=java.util.ArrayListpublic class Reflection {private int x;public int y;public Reflection(int x, int y) {super();this.x = x;this.y = y;}}public class ReflectTest2 {public static void main(String[] args) throws Exception {InputStream in = new FileInputStream("D:/JavaSourue/config.properties");Properties pro = new Properties();//從設定檔中,得到是什麼類pro.load(in);in.close();String className = pro.getProperty("className");Collection conCollection = (Collection)Class.forName(className).newInstance();Reflection[] rs = new Reflection[]{new Reflection(3, 3),new Reflection(4, 4),new Reflection(3, 3)};for(Reflection r : rs)conCollection.add(r);System.out.println(conCollection.size());}}
架構:
一個毛坯房就是一個架構,而使用者自己裝修門、窗等,使用者使用的是架構,門和窗插入到架構中
架構和工具類的區別就是:工具類被使用者所調用,而架構調用使用者所提供的類
架構解決的問題:
房地產商在蓋房子時(做架構),可能使用者還沒有買房,自然也就不會想到裝修,那麼架構怎麼調用使用者以後寫的類?
因為在寫架構時,無處得知以後要調用什麼類,也就無法new對象,所以就必須用反射來解決這個問題