前言:
在面向對象的世界裏,萬事萬物皆對象。但是在java語言中,靜態的成員、普通數據類型除外,靜態成員屬於類,而不是對象,而普通數據類型有對應的包裝類來彌補它。類是不是對象呢?類是(哪個類的對象呢?)誰的對象呢?類是對象,類是java.lang.Class類的實例對象
一、反射的概念:
主要是指程序可以訪問,檢測和修改它本身狀態或行爲的一種能力,並能根據自身行爲的狀態和結果,調整或修改應用所描述行爲的狀態和相關的語義。
反射是java中一種強大的工具,能夠使我們很方便的創建靈活的代碼,這些代碼可以再運行時裝配,無需在組件之間進行源代碼鏈接。但是反射使用不當會成本很高!
只看概念會幾乎毫無感覺,通俗的講可以這樣理解:
反射的過程認爲是將.class文件(也就是字節碼文件)反編譯爲.java文件(Java源文件)。然後利用反射機制來得到類的屬性、方法、構造方法。
二、Class類是什麼:
一下通過代碼來演示:
Test test = new Test();
//官網說,以下三種方式c1 ,c2 ,c3表示了Test類的類類型(class type)
//任何一個類都是Class的一個實例對象,Class就是類類型
// 這個實例對象有三種表示方式:
//第一種表示方式:這是實際在告訴我們,任何一個類都有一個隱含的靜態成員變量class;
Class c1 = Test.class;
//第二種表示方式:已經類的實例,通過類的實例來獲取Class的實例對象
Class c2 = test.getClass();
//第三種表示方式:
try {
Class c3 = Class.forName("Test");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//結果是true,表明不管使用哪一種方式,一個類只能是Class類的一個實例對象
System.out.println(c1==c2);//true
System.out.println(c1==c2);//true
//我們可以通過類類型,創建該類的實例對象
//即我們可以通過c1,c2,c3,來創建Test類的實例對象
try {
Test test1 = (Test)c1.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
三、java 動態加載類
上面提到的第三種方法,Class.forName(“類的全稱”),不僅表示了類的類類型,還代表了動態加載類。
我們一定要區分編譯、運行,編譯時刻加載類是靜態加載類、運行時刻加載類是動態加載類。
下面演示一個Java動態加載類的簡單實例:
我們創建一個接口Subject,表示考試科目,並創建兩個科目Mathmatic和Physics來實現Subject接口。
package com.tong.reflect;
public interface Subject {
void printGrade();//打印該科目成績
}
package com.tong.reflect;
public class Mathmatic implements Subject {
@Override
public void printGrade() {
System.out.println("打印數學成績");
}
}
package com.tong.reflect;
public class physics implements Subject {
@Override
public void printGrade() {
System.out.println("打印物理成績");
}
}
然後就可以利用Java反射機制來動態加載類,並創建類的實例。在後續過程中如果還需要增加科目,只要實現Subject接口,並編譯爲字節碼文件。一下爲測試代碼:
public static void main(String[] args) {
try {
//動態加載類
Class c1 = Class.forName(args[0]);
//動態創建類的實例
Subject subject = (Subject) c1.newInstance();
subject.printGrade();//打印成績
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
四、通過Java反射獲取類的成員變量、方法、構造函數
定義類ClassUtils用來獲取類的方法:
public class ClassUtils {
/*
獲取類的方法
*/
public void printMethod(Object obj){
Class cl = obj.getClass();
System.out.println("打印類的名稱:" + cl.getName());
/*
getMethods()獲得的是類的所有的public方法,包括從父類繼承來的方法
getDeclaredMethods()獲取的是自己聲明的方法,不問訪問權限
*/
Method[] method = cl.getMethods();
for(Method m:method){
Class returnType = m.getReturnType();
//打印返回值類型
System.out.print(returnType.getName() + " ");
//打印方法名
System.out.print(m.getName() + "(");
Class[] parameterType = m.getParameterTypes();
for(int i = 0;i<parameterType.length;i++){
if(i==parameterType.length-1)
System.out.print(parameterType[i].getName());
else
System.out.print(parameterType[i].getName() + ",");
}
System.out.println(");");
}
}
/*
獲取類的成員變量
*/
public void printField(Object obj){
Class cl = obj.getClass();
System.out.println("獲取類" + cl.getName() + "的成員變量:");
Field[] fields = cl.getFields();
for(Field f:fields){
Class type = f.getType();
System.out.println(type.getName() + " " + f.getName());
}
}
/*
獲取類的構造方法
*/
public void printConstructor(Object obj){
Class cl = obj.getClass();
System.out.println("獲取類" + cl.getName() + "的構造方法:");
Constructor[] constructors = cl.getDeclaredConstructors();
for(Constructor con:constructors){
System.out.print(con.getName() + "(");
Class[] parametertype = con.getParameterTypes();
for(Class para:parametertype){
System.out.print(para.getName() + ",");
}
System.out.println(")");
}
}
測試:
public static void main(String[] args) {
ClassUtils classUtils = new ClassUtils();
try {
//打印類中的方法
classUtils.printMethod(new Integer(1));
classUtils.printField(new Integer(1));
classUtils.printConstructor(new Integer(1));
} catch (Exception e) {
e.printStackTrace();
}
}
運行結果:
打印類的名稱:java.lang.Integer
int numberOfLeadingZeros(int);
int numberOfTrailingZeros(int);
int bitCount(int);
boolean equals(java.lang.Object);
。。。省略。。。。
獲取類java.lang.Integer的成員變量:
int MIN_VALUE
int MAX_VALUE
java.lang.Class TYPE
int SIZE
int BYTES
獲取類java.lang.Integer的構造方法:
java.lang.Integer(int,)
java.lang.Integer(java.lang.String,)
五、方法的反射
即通過反射來運行類中的方法。
方法反射的操作
method.invoke(對象,參數列表)
public class A {
//調用無參數的print
public void print(){
System.out.println("輸入參數爲空。。。。。");
}
//調用輸入爲整形的print
public void print(int a,int b){
System.out.println("輸入參數爲:" + a + " " + b);
}
//調用輸入參數爲字符串的print
public void print(String a,String b){
System.out.println("輸入參數爲:" + a + " " + b);
}
}
測試代碼:
public static void main(String[] args) {
A a = new A();
Class cl = a.getClass();
Method m = null;
Object obj = null;
try {
//調用無參數的print
m = cl.getMethod("print",new Class[]{});
obj = m.invoke(a,new Object[]{});
//調用輸入爲整形的print
m = cl.getMethod("print",new Class[]{int.class,int.class});
obj = m.invoke(a,new Object[]{1,2});
//調用輸入參數爲字符串的print
m = cl.getMethod("print",new Class[]{String.class,String.class});
obj = m.invoke(a,new Object[]{"Hello","World"});
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
六、通過反射來認識泛型的本質
Java泛型是JDK 5引入的一個特性,它允許我們定義類和接口的時候使用參數類型,泛型在集合框架中被廣泛使用。Java中泛型是防止錯誤輸入的,永遠記住,泛型是一個編譯時的概念,只在編譯階段有效,繞過編譯泛型就無效了,來看下面的代碼。
若是往list1中添加String類型元素,就會編譯報錯,如下代碼:
ArrayList<Integer> list1 = new ArrayList<Integer>();
list1.add(100);
list1.add(200);
//此時如果往list1中添加String類型,就會報錯,編譯不通過
//list1.add("Hello");
ArrayList<String> list2 = new ArrayList<String>();
list2.add("AAAA");
list2.add("BBBB");
list2.add("CCCC");
但是執行下語句時,發現list1和list2竟然指向同一個類類型。說明一個類只對一個類類型。
//輸出爲true
System.out.println(list1.getClass()==list2.getClass());
當通過反射機制來運行方法,就可以往聲明爲存放Integer類型的ArrayList中存放String類型數據。即繞過了編譯就繞過了泛型
ArrayList<Integer> list1 = new ArrayList<Integer>();
list1.add(100);
list1.add(200);
//此時如果往list1中添加String類型,就會報錯,編譯不通過
//list1.add("Hello");
ArrayList<String> list2 = new ArrayList<String>();
list2.add("AAAA");
list2.add("BBBB");
list2.add("CCCC");
Class cl = list1.getClass();
try {
Method m = cl.getMethod("add",new Class[]{Object.class});
Object obj = m.invoke(list1,new Object[]{"Hello"});
System.out.println("list1的大小:"+list1.size());
System.out.println("list1的內容:"+list1);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
運行結果:
list1的大小:3
list1的內容:[100, 200, Hello]