Java反射機制——學習總結
前幾天上REST課,因為涉及到Java的反射機制,但是老師當時只是帶過一下,沒有細講,周末找了些資料來學習,現在總結一下,加深印象。
什么是反射機制?
參考百度百科對java反射機制的定義:
“JAVA反射機制是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為java語言的 反射機制。”
我們通過一些例子,更好理解反射機制。
Class類
我們知道Java是一門面向對象語言,在Java的世界里,萬物皆為對象,比如我們有一個Person類:
public class Person {
}
我們創建一個Person類的實例,Person person = new Person(); 那么這個person,就是Person類的實例對象。
那么既然萬物都為對象,所以類也是對象。
類是什么的對象呢? 類是Class類的對象 ,表示方式有三種:
//第一種,任何一個類都有一個隱含的靜態成員變量class
Class c1 = Person.class;
//第二種,已經知道該類的對象,通過getClass()獲得
Class c2 = person.getClass();
//第三種,Class類的forName()方法
Class c3 = Class.forName("Person");
//這里,c1,c2,c3都是Class類的實例,我們稱c1, c2 ,c3為Person類的類類型
//不難看出,c1 == c2結果是true, c2 == c3結果也是true
通過類的類類型,我們經常會用到的方法就是newInstance()方法,通過該方法可以創建該類的實例:
Person personA = new Person(); //直接new一個實例
Person personB = Person.class.newInstance(); //通過newInstance()方法獲得Person的實例
//在學習JAVAEE時候,newInstance()方法我們最常見于獲取數據庫驅動
Class.forName("com.mysql.jdbc.Driver").newInstance();
//需要注意的是,在使用newInstance()方法的前提是該類必須要有無參構造方法
動態加載類:
編譯時刻加載類稱為靜態加載,運行時刻加載類稱為動態加載,使用new方法新建實例即為靜態加載類,在編譯時候就要加載全部類。這里我們舉一個例子:
為了更好的區分編譯和運行,我們不適用IDE工具,而使用記事本來實現這個例子:
//我們舉女媧造人的例子,創建一個CreatePerson類
public class CreatePerson {
public static void main(String[] args) {
if(args[0].equalsIgnoreCase("man")) { //如果從命令行傳入的參數為man 則創建Man的實例并調用say()方法
new Man().say();
}
if(args[0].equalsIgnoreCase("woman")) { //如果從命令行傳入的參數為woman 則創建Woman的實例并調用say()方法
new Woman().say();
}
}
}
//CreatePerson類用到了2個類,分別是Man和Woman。
//但是我們現在只創建Man類
public class Man {
public void say() {
System.out.println("I am a man !");
}
}
我們在CMD中編譯CreatePerson,看看會發生什么:
提示我們找不到Woman這個類。 這就是靜態加載,在編譯時刻需要加載全部類。那么問題來了,如果我要寫一個程序,里面有100個功能,這100個功能分別由100個類實現,那么一旦缺少一個類,這整個程序是不是就不能用了。
為了解決這個問題,我們可以使用動態加載。我們把上面這個例子改一下:
//創建一個Person接口
public interface Person {
void say();
}
//修改Man類,繼承Person接口
public class Man implements Person{
public void say() {
System.out.println("I am a man !");
}
}
//修改CreatePerson類,實現動態加載類
public class CreatePerson{
public static void main(String[] args) {
try{
//動態加載類
Class c = Class.forName(args[0]);
//通過類的類類型,創建該類實例對象
Person person = (Person)c.newInstance();
person.say();
} catch(Exception e) {
e.printStackTrace();
}
}
}
這個時候,我們再進行編譯:
沒有報錯了,那么我們繼續把Man編譯,再運行一下:
如果我們在命令行中輸入的參數是Woman呢?
拋出ClassNotFoundException異常。因為我們并沒有創建Woman類,所以如果以后需要創建Woman類實例,我們只需要新建一個Woman類,并實現Person接口就行了。
通過反射機制,獲得類的信息
在Java反射機制的定義中有:JAVA反射機制是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法。下面是一個實例:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ClassUtil {
/**
* 獲取成員函數的信息
*/
public static void getClassMethodMessage(Object obj) {
Class c = obj.getClass();
//獲取類的名稱
//System.out.println(c.getName());
/**
*
* Method類,方法對象
*getMethods方法獲得所有public的方法,包括繼承而來
*getDeclaredMethods是獲取該類自己的聲明的方法
*/
Method[] ms = c.getMethods();
for(int i = 0; i < ms.length; i++) {
//得到方法的返回值類型的類類型
Class returnType = ms[i].getReturnType();
System.out.print(returnType.getName() + " ");
//得到方法的名稱
System.out.print(ms[i].getName() + "(");
//獲取參數類型
Class[] paramTypes = ms[i].getParameterTypes();
for(Class class1:paramTypes) {
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}
public static void getFieldMessage(Object obj) {
Class c = obj.getClass();
/**
* 成員變量也是對象
* java.lang.reflect.Field
*
*/
Field[] fs = c.getDeclaredFields();
for(Field field:fs) {
//得到成員變量類型的類類型
Class fieldType = field.getType();
String typeName = fieldType.getName();
//得到成員變量的名稱
String fieldName = field.getName();
System.out.println(typeName + " " + fieldName);
}
}
public static void printConMessage(Object obj) {
Class c = obj.getClass();
/**
* 構造函數也是對象
*
*/
Constructor[] cs = c.getConstructors();
for (Constructor constructor : cs) {
System.out.print(constructor.getName() + "(");
//獲取構造函數參數列表------>參數列表的參數類型
Class[] paramType = constructor.getParameterTypes();
for (Class class1 : paramType) {
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}
public static void main(String[] args) {
String s = "hello";
getFieldMessage(s);
ClassUtil.printConMessage(s);
getClassMethodMessage(s);
}
}
方法的反射操作
可以通過方法的反射操作實現方法的調用:
import java.lang.reflect.Method;
public class MethodDemo1 {
public static void main(String[] args) {
//要獲取print(int, int)
//要獲取類的方法就要獲取類的信息,獲取類的信息就要獲取類的類類型
A a1 = new A();
Class c = a1.getClass();
//2,獲取方法 名稱和參數列表
//getMethod獲取的是public的方法
try {
Method m = c.getDeclaredMethod("print", int.class,int.class);
//方法的反射操作
//a1.print(10, 20);方法的反射操作,用m來進行方法調用和前者效果一致
Object obj = m.invoke(a1, 10,20);//如果方法有返回值返回值,沒有就null
} catch (Exception e) {
e.printStackTrace();
}
}
}
class A {
public void print(int a , int b) {
System.out.println(a + b);
}
public void print(String a , String b) {
System.out.println(a.toUpperCase() + "," + b.toUpperCase());
}
}
以上就是我對Java反射機制的學習和理解。
來自:http://www.cnblogs.com/zhuangcy4567/p/5972947.html