------Java培訓、Android培訓、iOS培訓、.Net培訓、期待與您交流! -------
一、反射的基本描述Java反射機制是在運行狀態中,對于任意一個類(class文件),都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象方法的功能稱為java語言的反射機制。動態獲取類中信息,就是java反射。可以理解為對類的解剖。如果想要對指定名稱的字節碼文件進行加載并獲取其中的內容并調用,這時就使用到了反射技術。
簡單一句話就是:反射就是把java類中的各種成分映射成相應的java類。
二、反射的基石---Class類1、了解Class類
Java程序中把各個對象共性的東西向上抽取得到了Object類,同理各個java類屬于同一類事物,我們向上抽取就能得到描述這類事物的Java類名就是Class。比如:描述很多人的時候我們會定義一個Person類,那么描述很多java類的時候我們就用Class類來描述眾多的class類。Class是Java程序中各個Java類的總稱;它是反射的基石,通過Class類來使用反射。
一個類被類加載器加載到內存中,占用一片存儲空間,這個空間里面的內容就是類的字節碼,不同的類的字節碼是不同的,所以它們在內存中的內容是不同的,這一個個的空間可分別用一個個的對象來表示,這些對象顯然具有相同的類型,這個類型就是Class類型。
用于描述字節碼的類就是Class類,創建對象,可以提取字節碼文件中的內容,如字段、構造函數、一般函數。該類就可以獲取字節碼文件中的所有內容,那么反射就是依靠該類完成的。想要對一個類文件進行解剖,只要獲取到該類的字節碼文件對象即可。java類的Class類提供一系列的方法來獲得其中的變量,方法,構造函數,修飾符,包等信息,這些信息就是用相應類的實例對象來表示,它們就是Field(字段、成員變量)、Method(一般方法)、Constructor(構造函數)、Package(包)等等。
2、獲取字節碼對應的實例對象(Class類型)的三種方法
(1)、Class clazz=對象.getClass();
Class clazz=new Person().getClass();
這種方法是調用了Object類中的getClass()方法。這種方法每次都需要知道具體的類并且創建該類對象,以及調用getClass()方法。非常的繁瑣和不利于后期程序的擴展性。
(2)、Class clazz=類名.class;
Class clazz=System.class;
這種方法相對簡單只需要用到類名就可以了,因為任何數據類型都具備一個靜態的屬性.class來獲取其對應的Class對象,但是還是要明確用到類中的靜態成員。
(3)、Class clazz=Class.forName("包名.類名");
String className="cn.itheima.Person";Class clazz=Class.forName(className);
這種方法是最為常用的方法,該方法不需要創建對象,也不用調用對象的方法,只需要通過給定的類的字符串名稱就可以獲取該類。更為方便,擴展性更強。這種方法在以后的開發中非常的常見應該重點掌握。
注:1.這三種方法獲取的字節碼對象都是相同的,只存在一份。
2.int.class==Integer.TYPE,boolean、byte、char、short、int、long、float 和 double都有對應的裝飾類.TYPE。
3.所有具有相同元素類型和維數的數組都是一個 Class 對象。
4.基本的 Java 類型(boolean、byte、char、short、int、long、float和double)和關鍵字void也表示Class對象。
3、Class類中的常用方法
通過查看API幫助文檔發現:Class類沒有構造函數,所以不可以直接new創建對象,說明內部一定有可以返回本類對象的靜態方法,這個方法就是forName()方法。方然上面的三種方法都可以獲取到Class對象的。既然有了Class對象那么我們就要用其方法。
1 static Class<?> forName(String className);//返回本類Class對象 2 T newInstance();//創建此Class對象的無參 構造函數 3 boolean isPRimitive();//判斷指定的Class對象是否是一個基本類型 4 boolean isArray();//判斷此Class對象是否是一個數組類 5 Class<? super T> getSuperclass();//返回此Class類所表示的實體的父類Class 6 String getName();//返回Class對象所表示的實體String名稱 7 String getSimpleName();//返回源代碼中給出的底層類的簡稱 8 Package getPackage();//獲取此類的包 9 int getModifiers();//返回此類或者接口的修飾符10 ClassLoader getClassLoader();//獲取此類的類加載器11 Class<?>[] getClassess();//返回類中成員所有公共類和接口12 Class<?>[] getDeclaredClasses();//獲取類的成員的所有類和接口13 Constructor<T> getConstructor(Class<?>...參數類型);//獲取類中指定參數的公共(Public)構造函數14 Constructor<?>[] getConstructors();//返回類中所有公有構造函數15 Constructor<T> getDeclaredConstructor(Class<?>...參數類型);//獲取帶有指定參數列表(公共、保護、默認及私有)的構造函數16 Costructor<?>[] getDeclaredConstructors()//獲取類中所有的構造函數17 Field getField(String name);//獲取類中指定公有成員變量。name表示的是指定的所需成員變量名18 Field[] getFields();//獲取類中所有公有的成員變量19 Field getDeclaredField(String name);//獲取類中指定的成員變量20 Field[] getDeclaredFields();//獲取類中所有成員變量21 Method getMethod(String name,Class<?>...參數類型);//獲取類中指定公有成員方法,name代表方法名22 Method[] getMethods();//獲取類中所有公有的成員方法23 Method getDeclaredMethod(String name,Class<?>...參數類型);//獲取類中指定的成員方法24 Method[] getDeclaredMethods();//獲取類中所有的成員方法。包括公共、保護、默認和私有方法,但不包括繼承的方法。25 Class<?>[] getInterfaces();//獲取全部接口26 InputStream getResourceAsStream(String name);//查找指定名稱的資源
需求:用Class方法創建一個無參構造函數
//Person p=new Person();Person p=(Person) Class.forName("com.itheima.Person").newInstance();三、獲取Class中的構造函數(Constructor類) 以前我們創建對象先根據被new的類的名稱找尋該類的字節碼文件,并加載進內存,并創建該字節碼文件對象,并接著創建該字節文件的對應的對象。如:Person p=new Person()。現在我們可以通過反射Class.forName(“類名”).getInstance()來創建無參的構造函數,但是這種方法有一個弊端,就是:假如類中沒有無參構造函數怎么辦或者創建指定的構造函數又該怎么辦?這時候Class類中的newInstance都無法完成。這時候就必須先獲取這個構造函數——Constructor。然后通過Constructor中的newInstance(Object ...參數列表)的方法創建指定的構造函數。
那么首先了解一下Constructor類的繼承體系,以方便我們一下更好的學習。
java.lang.Object
|---java.lang.reflect.Array
|---java.lang.reflect.accessibleObject
|---- java.lang.reflect.Constructor<T>
|----java.lang.reflect.Field
|----java.lang.reflect.Method
這里我們需要知道AccessibleObject類中有void setAccessible(boolean flag) 方法,當flag為true的時候可以對私有成員的訪問取消權限檢查,暴力訪問。
1、Constructor類中的方法
1 String getName();//以字符串形式返回構造方法的名稱2 T newInstance(Object...initargs);//創建實例對象3 void setAccessbie();//暴力訪問私有構造方法4 Class<?>[] getParamterTypes();//獲取對象所表示構造方法的形參類型5 int getModifiers();//獲取構造方法的修飾符的整數形式
2、用Constructor對象創建對象的步驟
(1)、首先獲取Class字節碼對象
Class clazz=Class.forName(String className);
(2)、獲取到字節碼對象后調用其getDeclaredConstructor()方法獲取Constructor對象
Constructor con=clazz.getDeclaredConstructor(String.class,int.class);
(3)、通過獲取到的Constructor對象中的newInstance(Class<?>...參數類型)方法得到指定的構造函數
Object obj=con.newInstance("黑馬",88);
注:
(1)、創建實例時newInstance方法中的參數列表必須與獲取Constructor的方法getConstructor方法中的參數列表一致。
(2)、newInstance()構造出一個實例對象,每調用一次就構造一個對象。
(3)、利用Constructor類來創建類實例的好處是可以指定構造函數,而Class類只能利用無參構造函數創建類實例對象。
3、Constructor綜合練習
(1)、需求:獲取Person類中的構造函數
Person.java文件
1 package com.itheima; 2 public class Person 3 { 4 private String name; 5 private int age; 6 public static String country="CN"; 7 public Person(){ 8 System.out.println("Person run"); 9 }10 public Person(String name,int age){11 this.name=name;12 this.age=age;13 System.out.println("name="+name+",age="+age);14 }15 private Person(String name){16 this.name=name;17 System.out.println("Person run"+name);18 }19 private void method(String name,int age){20 System.out.println("name="+name+"..method run.."+"age="+age);21 }22 public void show(){23 System.out.println("Person show");24 }25 public static void staticMethod(){26 System.out.println("staticMethod run");27 }28 }ConstructorDemo.java文件
1 mport java.lang.reflect.*; 2 import com.itheima.Person; 3 public class ConstructorDemo 4 { 5 public static void main(String[] args) throws Exception 6 { 7 String className="com.itheima.Person"; 8 constructor(className); 9 }10 public static void constructor(String className)throws Exception{11 //獲取Class字節碼文件對象12 Class clazz=Class.forName(className);13 14 //獲取所有公有的構造函數:getConstructors()15 printLine("所有公共構造函數如下:");16 Constructor[] con1=clazz.getConstructors();17 printArray(con1);18 19 //獲取所有的構造函數:getDeclaredConstructors()20 printLine("所有構造函數如下:");21 Constructor[] con2=clazz.getDeclaredConstructors();22 printArray(con2);23 24 //獲取指定的構造函數25 printLine("指定公有構造函數如下:");26 Constructor con3=clazz.getConstructor(String.class,int.class);27 System.out.println(con3);28 }29 public static void printArray(Object[] arr){30 for (Object o:arr )31 {32 System.out.println(o);33 }34 }35 public static void printLine(String str){36 System.out.println();37 System.out.println("--"+str+"------------------"); 38 }39 }運行結果:

(2)、需求:創建Person實例對象
ConstructorDemo.java文件
1 import java.lang.reflect.*; 2 import com.itheima.Person; 3 public class ConstructorDemo 4 { 5 public static void main(String[] args) throws Exception 6 { 7 String className="com.itheima.Person"; 8 createObj(className); 9 }10 public static void createObj(String className)throws Exception{ 11 Class clazz=Class.forName(className);12 13 //以前我們創建對象的方式:new Person(),現在我們用反射創建對象。14 15 //反射第一種方法,直接用字節碼對象的newInstance()創建空參實例對象 16 Person p=(Person)Class.forName(className).newInstance();17 18 //第二種方法:獲取Constructor對象,通過Constructor對象的newInstance()創建實例對象19 //newInstance()方法中的參數列表必須與獲取Constructor的方法getConstructor()方法中的參數列表一致20 Constructor con= Class.forName(className).getConstructor();21 Person p1=(Person)con.newInstance();22 23 //上面的兩種方法貌似都可以創建出來Person的實例對象,那么思考一下:如果創建指定參數的對象呢?24 //那么用字節碼對象創建空參數的對象就無法完成了,這時候我們必須使用獲取Constructor對象來創建對象了25 //Person p2=new Person("黑馬",88);26 Constructor c=clazz.getConstructor(String.class,int.class);27 Person p2=(Person)c.newInstance("黑馬",88);28 }29 }運行結果:

Field類代表某個類中一個成員變量.
1、Field類中的方法
1 Object get(Object obj);//獲取指定對象上的字段值2 int getModifiers();//獲取字段上的修飾符3 String getName();//獲取字段的名稱4 void set(Object obj,Object value);//將指定對象上的字段設置為指定的新值5 Class<?> getType();//獲取字段自身的類型6 void setAccessible(true);//私有字段進行取消權限檢查的能力,暴力訪問
2、獲取并修改指定成員變量的值的步驟
(1)、首先需要創建對象,因為獲取和修改成員變量的值都需要明確修改的是哪個對象身上的
(2)、獲取字節碼對象,用其getDeclaredField(String name)方法來獲取Field對象
(3)、通過Field對象的set(Object obj,Object value)來設置指定對象上的指定成員變量。
在設置之前一般都需要暴力訪問,因為成員變量一般都是私有的。
(4)、通過Field對象中的get(Object obj)獲取其身上的字段值。
3、Field綜合練習
(1)、需求:獲取Person類中的成員變量并修改
注:Person類因為和Constructor練習中的Person類一樣,所以這里就不單獨貼了。
FieldDemo.java文件
1 import java.lang.reflect.*; 2 import com.itheima.Person; 3 public class FieldDemo 4 { 5 public static void main(String[] args) throws Exception 6 { 7 String className="com.itheima.Person"; 8 field(className); 9 }10 public static void field(String className)throws Exception{11 //獲取Class字節碼文件對象12 Class clazz=Class.forName(className);13 14 //獲取所有的成員變量15 printLine("所有成員變量如下:");16 Field[] f1=clazz.getDeclaredFields();17 printArray(f1);18 19 //獲取指定成員變量20 printLine("指定成員變量如下:");21 Field f2=clazz.getDeclaredField("name");22 System.out.println(f2);23 24 //為對象的屬性賦值,首先要有對象25 Person p=(Person)clazz.getConstructor(String.class,int.class).newInstance("黑馬",88);26 //修改成員變量name的值27 Field f3=clazz.getDeclaredField("name");28 //取消訪問檢查,暴力訪問私有成員變量29 f3.setAccessible(true);30 f3.set(p,"小明");31 //獲取某對象的屬性值 32 System.out.println("name="+f3.get(p));33 34 }35 public static void printArray(Object[] arr){36 for (Object o:arr )37 {38 System.out.println(o);39 }40 }41 public static void printLine(String str){42 System.out.println();43 System.out.println("--"+str+"------------------"); 44 }45 }運行結果:

(2)、需求:將任意一個對象中的所有String類型的成員變量所對應的字符串內容中的"a"改成"b"。
1 import java.lang.reflect.*; 2 class Person 3 { 4 private String s1="com.itheima"; 5 private String s2="basketball"; 6 private String s3="我愛黑馬"; 7 } 8 class FieldTest 9 {10 public static void main(String[] args) throws Exception11 {12 Class clazz=Class.forName("Person"); //創建對象13 Person p=(Person)clazz.newInstance(); //獲取全部成員變量14 Field[] field=clazz.getDeclaredFields(); //遍歷Field數組15 for(Field f:field){16 if(f.getType()==String.class){//如果字段的類型和String類型的字節碼相同,則視為同一類型17 f.setAccessible(true);//暴力訪問18 String oldValue=(String)f.get(p);19 f.set(d,oldValue.replace('a','b'));//String類型的成員變量中含有a的字符換成b字符20 System.out.println(f.get(p));21 }22 }23 }24 }運行結果:

1 Object invoke(Object obj,Object...args);//調用方法
注:
(1)、如果傳遞給Method對象的invoke()方法的第一個參數為null,說明Method對象對應的是一個靜態方法。
(2)、jdk1.5以上如果調用的這個方法沒有參數列表Object...args填寫null編譯的時候會提示警告。
所以這里的Object...args可以用new Object[0],來抑制jvm編譯時期的警告提示。
2、Method綜合練習
(1)、獲取Person類中的成員方法并調用
//Person類接Constructor里的Person類1 import java.lang.reflect.*; 2 import com.itheima.Person; 3 public class MethodDemo 4 { 5 public static void main(String[] args) throws Exception 6 { 7 String className="com.itheima.Person"; 8 method(className); 9 }10 public static void method(String className)throws Exception{11 //獲取Class字節碼文件對象12 Class clazz=Class.forName(className);13 14 //獲取所有的成員方法15 printLine("所有成員方法如下:");16 Method[] m1=clazz.getDeclaredMethods();17 printArray(m1);18 19 //獲取指定成員方法20 printLine("指定成員方法如下:");21 Method m2=clazz.getDeclaredMethod("method",String.class,int.class);22 System.out.println(m2);23 24 //創建對象調用方法,Person p=new Person().method(String name,int age);25 System.out.println("hahhaha");26 Person p=(Person)clazz.newInstance();27 Method m3=clazz.getDeclaredMethod("method",String.class,int.class); 28 m3.setAccessible(true);29 m3.invoke(p,"黑馬",66);30 31 //調用靜態方法的時候不需要對象,因為靜態優先于對象存在32 //Person.staticMethod();33 Method m4=clazz.getMethod("staticMethod",new Class[0]);34 m4.invoke(null,new Object[0]);35 /*36 警告: 最后一個參數使用了不準確的變量類型的 varargs 方法的非 varargs 調用;37 [javac] 對于 varargs 調用,應使用 java.lang.Object38 [javac] 對于非 varargs 調用,應使用 java.lang.Object[],這樣也可以抑制此警告39 在jdk1.4下可以編譯通過,但在1.5就不行。40 在調用方法的時候,如果方法的參數列表為null的時候就會有此提示。這是java的友好提示。41 解決方法:就是null的位置上創建空的數組對象即可。如:invoke(null,new Object[0]); 42 43 */44 45 }46 public static void printArray(Object[] arr){47 for (Object o:arr )48 {49 System.out.println(o);50 }51 }52 public static void printLine(String str){53 System.out.println();54 System.out.println("--"+str+"------------------"); 55 }56 }運行結果:
(2)、需求:用反射方式執行某個類中的main方法。 在寫源程序時,并不知道使用者傳入的類名是什么,但是雖然傳入的類名不知道,而知道的是這個類中的方法有main這個方法。所以可以通過反射的方式,通過使用者傳入的類名(可定義字符串型變量作為傳入類名的入口,通過這個變量代表類名),內部通過傳入的類名獲取其main方法,然后執行相應的內容。此時會出現下面的問題:
啟動Java程序的main方法的參數是一個字符串數組,即public static void main(String[] args),通過反射方式來調用這個main方法時,如何為invoke方法傳遞參數呢?按jdk1.5的語法,整個數組是一個參數,而按jdk1.4的語法,數組中的每個元素對應一個參數,當把一個字符串數組作為參數傳遞給invoke方法時,javac會到底按照哪種語法進行處理呢?jdk1.5肯定要兼容jdk1.4的語法,會按jdk1.4的語法進行處理,即把數組打散成為若干個單獨的參數。所以,在給main方法傳遞參數時,不能使用代碼mainMethod.invoke(null,new String[]{“xxx”}),javac只把它當作jdk1.4的語法進行理解,而不把它當作jdk1.5的語法解釋,因此會出現參數類型不對的問題。
解決辦法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"});
這兩種方式編譯器會作特殊處理,編譯時不把參數當作數組看待,也就不會數組打散成若干個參數了。
1 import java.lang.reflect.*; 2 class Demo 3 { 4 public static void main(String[] args){ 5 for(String s:args){ 6 System.out.println(s); 7 } 8 } 9 }10 class MainDemo 11 {12 public static void main(String[] args) throws Exception13 {14 //一般調用Demo類中的main()方法15 Demo.main(new String[]{"abc","123","我愛黑馬"});16 System.out.println("------------------------");17 18 //通過反射方式根據用戶提供的類名,去執行該類中的main方法。19 String className=args[0];20 Class clazz=Class.forName(className);21 Method m=clazz.getMethod("main",String[].class);22 //第一種方式:將數組打包,編譯器拆包后就是一個String[]類型的整體23 m.invoke(null,new Object[]{new String[]{"abc","123","我愛黑馬"}});24 //第二種方式:強制轉換為父類Object,不用拆包 25 m.invoke(null,(Object)new String[]{"abc","123","我愛黑馬"});26 }27 }運行結果:
六、數組的反射(1)、具有相同維數和元素類型的數組屬于同一個類型,即具有相同的Class實例對象。(2)、代表數組的Class實例對象的getSuperClass()方法返回的父類為Object類對應的Class。(3)、基本類型的一維數組可以被當作Object類型使用,不能當作Object[]類型使用;非基本類型的一維數組,既可以當做Object類型使用,又可以當做Object[]類型使用。(4)、Object[] 與String[]沒有父子關系,Object與String有父子關系,所以new Object[]{“aaa”,”bb”}不能強制轉換成new String[]{“aaa”,”bb”};,Object x = “abc”能強制轉換成String x = “abc”。(5)、無法得到某個數組的具體類型,只能得到其中某個元素的類型。如:Obj[0].getClass().getName()得到的是java.lang.String。(6)、Array工具類用于完成對數組的反射操作。1 Array.getLength(Object obj);//獲取數組的長度2 Array.get(Object obj,int x);//獲取數組中的元素
代碼演示:
1 import java.lang.reflect.*; 2 public class ArrayReflectDemo { 3 public static void main(String[] args)throws Exception{ 4 int[] arr1=new int[]{4,12,3,5,485,78}; 5 int[] arr2=new int[5]; 6 int[][] arr3=new int[3][4]; 7 String[] arr4=new String[]{"abc","haha","nihao"}; 8 System.out.println(arr1.getClass()==arr2.getClass());//true 9 // System.out.println(arr1.getClass()==arr3.getClass());//編譯直接報錯,arr1是數組類型,arr3可以看作Object[]類型10 // System.out.println(arr1.getClass()==arr4.getClass());//arr1是Object類型,arr4是Object[]類型11 System.out.println(arr1.getClass().getSuperclass());12 System.out.println(arr2.getClass().getSuperclass());13 System.out.println(arr3.getClass().getSuperclass());14 System.out.println(arr4.getClass().getSuperclass());15 Object obj1=arr1;16 Object obj2=arr2;17 Object[] obj3=arr3;18 Object[] obj4=arr4;//String數組中的元素屬于Object19 // Object[] obj5=arr2;//int不是對象,int[]和String[]是對象.20 printArray(arr1);21 printArray(234343);22 } //打印對象,如果是數組的遍歷打印元素,如果不是直接打印23 public static void printArray(Object obj){24 if(obj.getClass().isArray()){//判斷是否是數組25 int len=Array.getLength(obj);//獲取數組的長度,得到循環的條件26 for(int x=0;x<len;x++){27 System.out.println(Array.get(obj, x));//獲取數組元素28 }29 }else30 System.out.println(obj);31 }32 }七、反射的作用----實現框架功能什么是框架,例如,我們要寫程序掃描.java文件中的注解,要解決哪些問題:讀取每一樣,在每一個中查找@,找到的@再去查詢一個列表,如果@后的內容出現在了列表中,就說明這是一個我能處理和想處理的注解,否則,就說明它不是一個注解或者說至少不是一個我感興趣和能處理的注解。接著就編寫處理這個注解的相關代碼。現在sun提供了一個apt框架,它會完成所有前期工作,只需要我們提供能夠處理的注解列表,以及處理這些注解的代碼。Apt框架找到我們感興趣的注解后通知或調用我們的處理代碼去處理。框架與框架要解決的核心問題
比如:我做房子賣給用戶住,由用戶自己安裝門窗和空調,我做的房子就是框架,用戶需要使用我的框架,把門窗插入進我提供的框架中。 框架與工具類有區別,工具類被用戶的類調用,而框架則是調用用戶提供的類。框架機器要解決的核心問題: 我在寫框架(房子)時,你這個用戶可能還在上小學,還不會寫程序呢?我寫的框架程序怎樣能調用到你以后寫的類(門窗)呢?因為在寫才程序時無法知道要被調用的類名,所以,在程序中無法直接new 某個類的實例對象了,而要用反射方式來做。1、通過反射讀取配置文件信息。
思路:
(1)、右擊項目File命名一個配置文件:config.properties,然后寫入配置信息。鍵值對:className=java.util.ArrayList。
(2)、將文件讀取到讀取流中,要寫出配置文件的絕對路徑.
(3)、用Properties類的load()方法將流中的數據存入集合。
(4)、關閉流:關閉的是讀取流,因為流中的數據已經加載進內存。
(5)、通過getProperty()方法獲取className,即配置的值,也就是某個類名。
(6)、用反射的方式,創建對象newInstance()。
代碼實現:
Person.java文件
1 package ReflectDemo; 2 3 public class Person { 4 private String name; 5 private int age; 6 public Person(String name, int age) { 7 this.name = name; 8 this.age = age; 9 }10 public String getName() {11 return name;12 }13 public void setName(String name) {14 this.name = name;15 }16 public int getAge() {17 return age;18 }19 public void setAge(int age) {20 this.age = age;21 }22 //復寫中hashCode方法23 public int hashCode(){24 25 return name.hashCode()+age*37;26 }27 //復寫了Object中equalss方法28 public boolean equals(Object obj){29 if(!(obj instanceof Person))30 throw new RuntimeException("類型轉換異常");31 Person p=(Person)obj;32 return this.name.equals(p.name)&& this.age==p.age;33 }34 @Override35 public String toString() {36 return "Person =[age=" + age + ", name=" + name + "]";37 }38 }RflectDemo.java文件
1 package com.itheima; 2 import java.util.*; 3 import java.io.*; 4 import java.lang.reflect.*; 5 public class ReflectDemo { 6 public static void main(String[] args)throws Exception { 7 //文件關聯流對象,用字節流讀取配置文件信息 8 FileInputStream in=new FileInputStream("config.properties"); 9 //創建Properties對象,用來操作配置文件信息10 Properties pro=new Properties();11 //把流中的數據加載到集合中12 pro.load(in);13 in.close();14 //獲取集合中的鍵當做className15 String className=pro.getProperty("className");16 //用反射創建ArrayList對象用來存儲Person對象17 Collection<Person> con=(ArrayList)Class.forName(className).newInstance();18 con.add(new Person("黑馬",120));19 con.add(new Person("小明",50));20 con.add(new Person("王五",12));21 con.add(new Person("逗逗",40));22 con.add(new Person("趙四",80));23 con.add(new Person("王五",12));//ArrayList集合的特點是元素可重復,所以這個視為不相同的Person對象24 //遍歷集合中的元素25 for(Person p:con){26 System.out.println(p);27 } 28 29 30 //原運行程序31 Collection<Person> collection=new HashSet<Person>();32 collection.add(new Person("黑馬",120));33 collection.add(new Person("小明",50));34 collection.add(new Person("王五",12));35 collection.add(new Person("逗逗",40));36 collection.add(new Person("趙四",80));37 collection.add(new Person("王五",12));//因為Person復寫了hashCode和equals方法,所以視為重復元素38 for(Person pe:collection){39 System.out.println(pe);40 }41 }42 43 }config.properties文件
1 className=java.util.ArrayList
2、類加載器
類加載器是將.class的文件加載進內存,也可將普通文件中的信息加載進內存。
資源文件的加載:是使用類加載器。
(1)、由類加載器ClassLoader來加載進內存,即用getClassLoader()方法獲取類加載器,然后用類加載器的getResourceAsStream(String name)方法,將配置文件(資源文件)加載進內存。利用類加載器來加載配置文件,需把配置文件放置的包名一起寫上。這種方式只有讀取功能。
(2)、Class類也提供getResourceAsStream方法來加載資源文件,其實它內部就是調用了ClassLoader的方法。這時,配置文件是相對類文件的當前目錄的,也就是說用這種方法,配置文件前面可以省略包名。
如:類名.class.getResourceAsStream(“資源文件名”)
注:菜鳥日記,如有錯誤歡迎大神指正,歡迎探討!!
------Java培訓、Android培訓、iOS培訓、.Net培訓、期待與您交流! -------新聞熱點
疑難解答