空口說反射,很難理解這是個什么東西,舉個例子就明了了。
假設現在我們有一張動漫的圖片,我們通過這張圖片,在大腦中搜索有關記憶,就清楚這張圖片是什么動漫里的誰;假設我們現在有一個名字,在大腦中搜索有關記憶,就自然而然地想到了該名字人物的具體形象,這種思維過程就是反射。
假設一個類已經加載進JVM,那么,給定該類名,我們就可以通過反射來獲取該類的相應信息。
具體定義:反射是被視為動態語言的關鍵,反射機制允許程序在執行期借助反射API取得任何類的內部信息,並能直接操作在任何對象的內部屬性和方法。
java反射機制提供的功能:
- 在運行時判斷任意一個對象所屬的類;
- 在運行時構造任意一個類的對象;
- 在運行時判斷一個類所具有的成員變量和方法;
- 在運行時調用任意一個對象的成員變量和方法,生成動態代理;
反射相關的API:
- java.lang.Class:代表一個類
- java,lang.reflect.Method:代表類的方法
- java.lang.reflect.Field:代表類的成員變量
- jav.lang.reflect.Constructor:代表類的構造方法
一。class類
在Object類中定義了以下的方法,此方法將被所有子類繼承:public final Class getClass()。
以上方法返回值是一個Class類,此類是java反射的源頭,實際上所謂反射從程序的運行結果來看也很好理解,即:通過對象反射求出類的名稱。
反射可以得到的信息:某個類的屬性、方法、構造器、某個類到底實現了哪些接口。對於每個類而言,JRE都為其保留一個Class類型的對象。一個Class對象包含了特定某個類的有關信息。
- Class本身也是一個類;
- Class對象只能由系統建立;
- 一個類在JVM中只含有一個實例;
- 一個Class對象對應的是一個加載進JVM中的一個.class文件;
- 每個類的實例都會記得自己是由哪個Class實例所生成;
- 通過Class可以完整地得到一個類中的完整結構;
實例化Class類的四種方式:
- Class clazz = String.Class();通過類名.class()
- Class clazz = p.getClass();通過對象的實例.getClass()
- Class clazz = Class.forName("java.lang.String);已知一個類的全類名,且在該類的類路徑下,可用過forName()獲取
- ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass("類的全類名");
二、通過反射調用類的完整結構
Person.java
package ref; public class Person { public String name; public int age; }
Move.java
package ref; interface Move { void move(); }
Study.java
package ref; public interface Study { void study(); }
Student.java
package ref; public class Student extends Person implements Move,Study{ public String school; private String privateField; public void showInfo() { System.out.println("學校是"+this.school); } @Override public void study() { // TODO Auto-generated method stub System.out.println("學習到中學的知識"); } @Override public void move() { // TODO Auto-generated method stub System.out.println("騎自行車上學"); } public Student() { System.out.println("調用的是Student()"); } public Student(String school) { this.school = school; System.out.println("調用的是Student(String school)"); } private Student(String name,int age) { this.name = name; this.age = age; System.out.println("調用的是Student(String name,int age)"); } private void test(String name) {} public String testGetSchool() { return school; } }
test.java
package ref; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Test { public static void main(String[] args) { try { //這里是相關代碼 } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
1.獲取類的父類和接口
Class clazz = Class.forName("ref.Student"); Class superClass = clazz.getSuperclass(); //獲取父類的名字 System.out.println("父類:"+superClass.getName()); //獲取實現的接口 Class[] interfaces = clazz.getInterfaces(); for(Class itf:interfaces) { System.out.println("接口名字:"+itf.getName()); }
父類:ref.Person
接口名字:ref.Move
接口名字:ref.Study
2.獲取類的構造方法
//獲取公有的構造方法 Constructor[] cons = clazz.getConstructors(); for(Constructor con:cons) { System.out.println("--------------------------"); System.out.println("構造方法:"+con.getName());//獲取構造方法名稱 //返回1代表public System.out.println("構造方法修飾符:"+con.getModifiers());//獲取構造方法修飾符 //獲取構造方法的參數類型 Class[] parameterClass = con.getParameterTypes(); for(Class param:parameterClass){ System.out.println("構造方法:"+con.getName()+"的參數類型為"+param.getName()); } System.out.println("--------------------------"); } //獲取私有的構造方法 Constructor[] cons2 = clazz.getDeclaredConstructors(); for(Constructor con:cons2) { System.out.println("--------------------------"); System.out.println("構造方法:"+con.getName());//獲取構造方法名稱 System.out.println("構造方法修飾符:"+con.getModifiers());//獲取構造方法修飾符 //獲取構造方法的參數類型 Class[] parameterClass = con.getParameterTypes(); for(Class param:parameterClass){ System.out.println("構造方法:"+con.getName()+"的參數類型為"+param.getName()); } System.out.println("--------------------------"); }
--------------------------
構造方法:ref.Student
構造方法修飾符:1
--------------------------
--------------------------
構造方法:ref.Student
構造方法修飾符:1
構造方法:ref.Student的參數類型為java.lang.String
--------------------------
--------------------------
構造方法:ref.Student
構造方法修飾符:1
--------------------------
--------------------------
構造方法:ref.Student
構造方法修飾符:2
構造方法:ref.Student的參數類型為java.lang.String
構造方法:ref.Student的參數類型為int
--------------------------
--------------------------
構造方法:ref.Student
構造方法修飾符:1
構造方法:ref.Student的參數類型為java.lang.String
--------------------------
3.通過反射來創建對象
//相當於調用無參構造方法 Object obj = clazz.newInstance(); Student stu = (Student) obj; //相當於調用Student(String school)構造方法 Constructor con = clazz.getConstructor(String.class); Student stu1 = (Student) con.newInstance("一中"); //通過反射機制可以強制獲取私有方法 Constructor con2 = clazz.getDeclaredConstructor(String.class,int.class); //解除私有的封裝 con2.setAccessible(true); Student stu2 = (Student) con2.newInstance("jack",23); System.out.println(stu2.name); System.out.println(stu2.age);
調用的是Student()
調用的是Student(String school)
調用的是Student(String name,int age)
jack
23
4.獲取類的方法
//獲取所有公有方法 Method[] ms = clazz.getMethods(); for(Method m:ms) { System.out.println("--------------------------"); System.out.println("方法名:"+m.getName()); System.out.println("返回值類型:"+m.getReturnType()); System.out.println("修飾符:"+m.getModifiers()); Class[] mrs = m.getParameterTypes(); if(mrs!=null && mrs.length>0) { for(Class mr:mrs){ System.out.println("方法參數類型:"+mr.getName()); } } System.out.println("--------------------------"); } //獲取所有方法,包括公有和私有 Method[] ms2 = clazz.getDeclaredMethods(); for(Method m:ms2) { System.out.println("--------------------------"); System.out.println("方法名:"+m.getName()); System.out.println("返回值類型:"+m.getReturnType()); System.out.println("修飾符:"+m.getModifiers()); Class[] mrs = m.getParameterTypes(); if(mrs!=null && mrs.length>0) { for(Class mr:mrs){ System.out.println("方法參數類型:"+mr.getName()); } } System.out.println("--------------------------"); }
--------------------------
方法名:showInfo
返回值類型:void
修飾符:1
--------------------------
--------------------------
方法名:study
返回值類型:void
修飾符:1
--------------------------
--------------------------
方法名:move
返回值類型:void
修飾符:1
--------------------------
--------------------------
方法名:testGetSchool
返回值類型:class java.lang.String
修飾符:1
--------------------------
--------------------------
方法名:wait
返回值類型:void
修飾符:17
--------------------------
--------------------------
方法名:wait
返回值類型:void
修飾符:17
方法參數類型:long
方法參數類型:int
--------------------------
--------------------------
方法名:wait
返回值類型:void
修飾符:273
方法參數類型:long
--------------------------
--------------------------
方法名:equals
返回值類型:boolean
修飾符:1
方法參數類型:java.lang.Object
--------------------------
--------------------------
方法名:toString
返回值類型:class java.lang.String
修飾符:1
--------------------------
--------------------------
方法名:hashCode
返回值類型:int
修飾符:257
--------------------------
--------------------------
方法名:getClass
返回值類型:class java.lang.Class
修飾符:273
--------------------------
--------------------------
方法名:notify
返回值類型:void
修飾符:273
--------------------------
--------------------------
方法名:notifyAll
返回值類型:void
修飾符:273
--------------------------
--------------------------
方法名:test
返回值類型:void
修飾符:2
方法參數類型:java.lang.String
--------------------------
--------------------------
方法名:showInfo
返回值類型:void
修飾符:1
--------------------------
--------------------------
方法名:study
返回值類型:void
修飾符:1
--------------------------
--------------------------
方法名:move
返回值類型:void
修飾符:1
--------------------------
--------------------------
方法名:testGetSchool
返回值類型:class java.lang.String
修飾符:1
--------------------------
5.獲取類的屬性
//獲取公有屬性 Field[] fs = clazz.getFields(); for(Field f:fs) { System.out.println("--------------------------"); System.out.println("屬性名:"+f.getName()); System.out.println("屬性類型:"+f.getType()); System.out.println("修飾符:"+f.getModifiers()); System.out.println("--------------------------"); } //獲取所有屬性 Field[] fs2 = clazz.getDeclaredFields(); for(Field f:fs2) { System.out.println("--------------------------"); System.out.println("屬性名:"+f.getName()); System.out.println("屬性類型:"+f.getType()); System.out.println("修飾符:"+f.getModifiers()); System.out.println("--------------------------"); }
--------------------------
屬性名:school
屬性類型:class java.lang.String
修飾符:1
--------------------------
--------------------------
屬性名:name
屬性類型:class java.lang.String
修飾符:1
--------------------------
--------------------------
屬性名:age
屬性類型:int
修飾符:1
--------------------------
--------------------------
屬性名:school
屬性類型:class java.lang.String
修飾符:1
--------------------------
--------------------------
屬性名:privateField
屬性類型:class java.lang.String
修飾符:2
--------------------------
6.獲取類所在的包
//獲取類所在的包 Package p = clazz.getPackage(); System.out.println(p.getName());
ref
三、通過反射來調用類中指定的方法和屬性
向Student.java中插入以下方法:
private void test(String name) { System.out.println("這是私有的test(String name)方法"); } public void setInfo(String name,String school) { this.name = name; this.school = school; System.out.println("這是公有的setInfo(String name,String school)方法"); } public void setInfo(int age) { this.age = age; System.out.println("這是公有的setInfo(int age)方法"); }
1.調用類中指定的方法
Constructor con = clazz.getConstructor(); Object obj = con.newInstance(); //先獲取到方法 Method m = clazz.getMethod("setInfo", String.class,String.class); //再進行調用 m.invoke(obj, "tom","三中"); //如果想調用私有的方法 Method m2 = clazz.getDeclaredMethod("test", String.class); m2.setAccessible(true); m2.invoke(obj, "jack"); //調用重載的方法 Method m3 = clazz.getMethod("setInfo", int.class); m3.invoke(obj, 12); //調用具有返回值的方法 Method m4 = clazz.getMethod("testGetSchool"); String school = (String) m4.invoke(obj); System.out.println(school);
調用的是Student()
這是公有的setInfo(String name,String school)方法
這是私有的test(String name)方法
這是公有的setInfo(int age)方法
三中
2.調用類中指定的屬性
Constructor con = clazz.getConstructor(); Object obj = con.newInstance(); Student stu = (Student) obj; Field f = clazz.getField("school"); //對stu屬性school設置值 f.set(stu,"第三中學"); String school = (String) f.get(stu); System.out.println(school); //如果是私有的屬性 Field f2 = clazz.getDeclaredField("privateField"); f2.setAccessible(true); f2.set(stu,"私有屬性"); String privateField = (String) f2.get(stu); System.out.println(privateField);
調用的是Student()
第三中學
私有屬性