
什么是Java的反射
Java反射是可以讓我們?cè)谶\(yùn)行時(shí)獲取類(lèi)的函數(shù),屬性,父類(lèi),接口等Class內(nèi)部信息的機(jī)制。通過(guò)反射還可以讓我們?cè)谶\(yùn)行期實(shí)例化對(duì)象,調(diào)用方法,通過(guò)調(diào)用get/set方法獲取變量的值,即使方法或?qū)傩允撬接械囊部梢酝ㄟ^(guò)反射的形勢(shì)調(diào)用,這種看透class的能力被稱(chēng)為內(nèi)省,這種能力在框架開(kāi)發(fā)中尤為重要。有些情況下,我們要使用的類(lèi)在運(yùn)行時(shí)才會(huì)確定,這個(gè)時(shí)候我們不能在編譯器就使用它,因此只能通過(guò)反射的形勢(shì)來(lái)使用在運(yùn)行才存在的類(lèi)(該類(lèi)符合某種特定的規(guī)范,例如JDBC),這是反射用得比較多的場(chǎng)景。
還有一個(gè)比較常見(jiàn)的場(chǎng)景就是編譯時(shí)我們對(duì)于類(lèi)的內(nèi)部信息不可知,必須得到運(yùn)行時(shí)才能獲取類(lèi)的具體信息。比如ORM框架,在運(yùn)行時(shí)才能夠獲取類(lèi)中的各個(gè)屬性,然后通過(guò)反射的形式獲取其屬性名和值,存入數(shù)據(jù)庫(kù)。這也是反射比較經(jīng)典的應(yīng)用場(chǎng)景之一。
下面我會(huì)為大家演示反射的一些常用的api,從代碼的角度理解反射。
反射的相關(guān)api列舉出來(lái):
接口說(shuō)明
<pre>// 加載指定的 Class 對(duì)象,參數(shù) 1 為要加載的類(lèi)的完整路徑,例如"com.simple.Student". ( 常用方式 )
public static Class<?> forName (String className)
// 加載指定的 Class 對(duì)象,參數(shù) 1 為要加載的類(lèi)的完整路徑,例如"com.simple.Student";
// 參數(shù) 2 為是否要初始化該 Class 對(duì)象,參數(shù) 3 為指定加載該類(lèi)的 ClassLoader.
public static Class<?> forName (String className, boolean shouldInitialize, ClassLoader classLoader)</pre>
獲取構(gòu)造函數(shù)接口
<pre>// 獲取一個(gè)公有的構(gòu)造函數(shù),參數(shù)為可變參數(shù),如果構(gòu)造函數(shù)有參數(shù),那么需要將參數(shù)的類(lèi)型傳遞給 getConstructor 方法
public Constructor<T> getConstructor (Class...<?> parameterTypes)
// 獲取目標(biāo)類(lèi)所有的公有構(gòu)造函數(shù)
public Constructor[]<?> getConstructors ()</pre>
獲取類(lèi)中的方法接口
<pre>// 獲取 Class 對(duì)象中指定函數(shù)名和參數(shù)的函數(shù),參數(shù)一為函數(shù)名,參數(shù) 2 為參數(shù)類(lèi)型列表
public Method getDeclaredMethod (String name, Class...<?> parameterTypes)
// 獲取該 Class 對(duì)象中的所有函數(shù)( 不包含從父類(lèi)繼承的函數(shù) )
public Method[] getDeclaredMethods ()
// 獲取指定的 Class 對(duì)象中的公有函數(shù),參數(shù)一為函數(shù)名,參數(shù) 2 為參數(shù)類(lèi)型列表
public Method getMethod (String name, Class...<?> parameterTypes)
// 獲取該 Class 對(duì)象中的所有公有函數(shù) ( 包含從父類(lèi)和接口類(lèi)集成下來(lái)的函數(shù) )
public Method[] getMethods ()</pre>
獲取類(lèi)中的成員屬性接口
<pre>// 獲取 Class 對(duì)象中指定屬性名的屬性,參數(shù)一為屬性名
public Method getDeclaredField (String name)
// 獲取該 Class 對(duì)象中的所有屬性( 不包含從父類(lèi)繼承的屬性 )
public Method[] getDeclaredFields ()
// 獲取指定的 Class 對(duì)象中的公有屬性,參數(shù)一為屬性名
public Method getField (String name)
// 獲取該 Class 對(duì)象中的所有公有屬性 ( 包含從父類(lèi)和接口類(lèi)集成下來(lái)的公有屬性 )
public Method[] getFields ()</pre>
獲取Class對(duì)象
在你想檢查一個(gè)類(lèi)的信息之前,你首先需要獲取類(lèi)的 Class 對(duì)象。Java 中的所有類(lèi)型包括基本類(lèi)型,即使是數(shù)組都有與之關(guān)聯(lián)的 Class 類(lèi)的對(duì)象。如果你在編譯期知道一個(gè)類(lèi)的名字的話,那么你可以使用如下的方式獲取一個(gè)類(lèi)的 Class 對(duì)象。
<pre>Class<?> myObjectClass = MyObject.class;</pre>
如果你已經(jīng)得到了某個(gè)對(duì)象,但是你想獲取這個(gè)對(duì)象的 Class 對(duì)象,那么你可以通過(guò)下面的方法得到:
<pre>Student me = new Student("mr.simple");
Class<?> clazz = me.getClass();</pre>
如果你在編譯期獲取不到目標(biāo)類(lèi)型,但是你知道它的完整類(lèi)路徑,那么你可以通過(guò)如下的形式來(lái)獲取 Class 對(duì)象:
<pre>Class<?> myObjectClass = Class.forName("com.simple.User");</pre>
在使用 Class.forName()方法時(shí),你必須提供一個(gè)類(lèi)的全名,這個(gè)全名包括類(lèi)所在的包的名字。例如 User 類(lèi)位于 com.simple 包,那么他的完整類(lèi)路徑就是 com.simple.User。如果在調(diào)用 Class.forName()方法時(shí),沒(méi)有在編譯路徑下(classpath)找到對(duì)應(yīng)的類(lèi),那么將會(huì)拋出 ClassNotFoundException。
通過(guò)Class對(duì)象構(gòu)造目標(biāo)類(lèi)型的對(duì)象
一旦你拿到 Class 對(duì)象之后,你就可以為所欲為了!當(dāng)你善用它的時(shí)候它就是神兵利器,當(dāng)你心懷鬼胎之時(shí)它就會(huì)變成惡魔。但獲取 Class 對(duì)象只是第一步,我們需要在執(zhí)行那些強(qiáng)大的行為之前通過(guò) Class 對(duì)象構(gòu)造出該類(lèi)型的對(duì)象,然后才能通過(guò)該對(duì)象釋放它的能量。 我們知道,在 java 中要構(gòu)造對(duì)象,必須通過(guò)該類(lèi)的構(gòu)造函數(shù),那么其實(shí)反射也是一樣一樣的。但是它們確實(shí)有區(qū)別的,通過(guò)反射構(gòu)造對(duì)象,我們首先要獲取類(lèi)的 Constructor(構(gòu)造器)對(duì)象,然后通過(guò) Constructor 來(lái)創(chuàng)建目標(biāo)類(lèi)的對(duì)象。還是直接上代碼的。
<pre> private static void classForName() {
try {
// 獲取 Class 對(duì)象
Class<?> clz = Class.forName("org.java.advance.reflect.Student");
// 通過(guò) Class 對(duì)象獲取 Constructor,Student 的構(gòu)造函數(shù)有一個(gè)字符串參數(shù)
// 因此這里需要傳遞參數(shù)的類(lèi)型 ( Student 類(lèi)見(jiàn)后面的代碼 )
Constructor<?> constructor = clz.getConstructor(String.class);
// 通過(guò) Constructor 來(lái)創(chuàng)建 Student 對(duì)象
Object obj = constructor.newInstance("mr.simple");
System.out.println(" obj : " + obj.toString());
} catch (Exception e) {
e.printStackTrace();
}
}</pre>
Person.java
<pre>public class Person {
String mName;
public Person(String aName) {
mName = aName;
}
private void sayHello(String friendName) {
System.out.println(mName + " say hello to " + friendName);
}
protected void showMyName() {
System.out.println("My name is " + mName);
}
public void breathe() {
System.out.println(" take breathe ");
}
}</pre>
Student.java
<pre>public class Student extends Person implements Examination {
// 年級(jí)
int mGrade;
public Student(String aName) {
super(aName);
}
public Student(int grade, String aName) {
super(aName);
mGrade = grade;
}
private void learn(String course) {
System.out.println(mName + " learn " + course);
}
public void takeAnExamination() {
System.out.println(" takeAnExamination ");
}
public String toString() {
return " Student : " + mName;
}</pre>
Examination.java
<pre>// 考試接口
public interface Examination {
public void takeAnExamination();
}</pre>
反射獲取類(lèi)中定義的方法
要獲取當(dāng)前類(lèi)中定義的所有方法可以通過(guò) Class 中的 getDeclaredMethods 函數(shù),它會(huì)獲取到當(dāng)前類(lèi)中的 public、default、protected、private 的所有方法。而 getDeclaredMethod(String name, Class...<?> parameterTypes)則是獲取某個(gè)指定的方法。代碼示例如下 :
<pre> private static void showDeclaredMethods() {
Student student = new Student("mr.simple");
Method[] methods = student.getClass().getDeclaredMethods();
for (Method method : methods) {
System.out.println("declared method name : " + method.getName());
}
try {
Method learnMethod = student.getClass().getDeclaredMethod("learn", String.class);
// 獲取方法的參數(shù)類(lèi)型列表
Class<?>[] paramClasses = learnMethod.getParameterTypes() ;
for (Class<?> class1 : paramClasses) {
System.out.println("learn 方法的參數(shù)類(lèi)型 : " + class1.getName());
}
// 是否是 private 函數(shù),屬性是否是 private 也可以使用這種方式判斷
System.out.println(learnMethod.getName() + " is private "
+ Modifier.isPrivate(learnMethod.getModifiers()));
learnMethod.invoke(student, "java ---> ");
} catch (Exception e) {
e.printStackTrace();
}
}</pre>
獲取當(dāng)前類(lèi),父類(lèi)中定義的公有方法
要獲取當(dāng)前類(lèi)以及父類(lèi)中的所有 public 方法可以通過(guò) Class 中的 getMethods 函數(shù),而 getMethod 則是獲取某個(gè)指定的方法。代碼示例如下 :
<pre>private static void showMethods() {
Student student = new Student("mr.simple");
// 獲取所有方法
Method[] methods = student.getClass().getMethods();
for (Method method : methods) {
System.out.println("method name : " + method.getName());
}
try {
// 通過(guò) getMethod 只能獲取公有方法,如果獲取私有方法則會(huì)拋出異常,比如這里就會(huì)拋異常
Method learnMethod = student.getClass().getMethod("learn", String.class);
// 是否是 private 函數(shù),屬性是否是 private 也可以使用這種方式判斷
System.out.println(learnMethod.getName() + " is private " + Modifier.isPrivate(learnMethod.getModifiers()));
// 調(diào)用 learn 函數(shù)
learnMethod.invoke(student, "java");
} catch (Exception e) {
e.printStackTrace();
}
}</pre>
這里需要注意的是 getDeclaredMethod 和 getDeclaredMethods 包含 private、protected、default、public 的函數(shù),并且通過(guò)這兩個(gè)函數(shù)獲取到的只是在自身中定義的函數(shù),從父類(lèi)中集成的函數(shù)不能夠獲取到。而 getMethod 和 getMethods 只包含 public 函數(shù),父類(lèi)中的公有函數(shù)也能夠獲取到。
反射當(dāng)前類(lèi)中定義的屬性
要獲取當(dāng)前類(lèi)中定義的所有屬性可以通過(guò) Class 中的 getDeclaredFields 函數(shù),它會(huì)獲取到當(dāng)前類(lèi)中的 public、default、protected、private 的所有屬性。而 getDeclaredField 則是獲取某個(gè)指定的屬性。代碼示例如下 :
<pre>private static void showDeclaredFields() {
Student student = new Student("mr.simple");
// 獲取當(dāng)前類(lèi)和父類(lèi)的所有公有屬性
Field[] publicFields = student.getClass().getDeclaredFields();
for (Field field : publicFields) {
System.out.println("declared field name : " + field.getName());
}
try {
// 獲取當(dāng)前類(lèi)和父類(lèi)的某個(gè)公有屬性
Field gradeField = student.getClass().getDeclaredField("mGrade");
// 獲取屬性值
System.out.println(" my grade is : " + gradeField.getInt(student));
// 設(shè)置屬性值
gradeField.set(student, 10);
System.out.println(" my grade is : " + gradeField.getInt(student));
} catch (Exception e) {
e.printStackTrace();
}
}</pre>
獲取當(dāng)前類(lèi)、父類(lèi)中定義的公有屬性
要獲取當(dāng)前類(lèi)以及父類(lèi)中的所有 public 屬性可以通過(guò) Class 中的 getFields 函數(shù),而 getField 則是獲取某個(gè)指定的屬性。代碼示例如下 :
<pre> private static void showFields() {
Student student = new Student("mr.simple");
// 獲取當(dāng)前類(lèi)和父類(lèi)的所有公有屬性
Field[] publicFields = student.getClass().getFields();
for (Field field : publicFields) {
System.out.println("field name : " + field.getName());
}
try {
// 獲取當(dāng)前類(lèi)和父類(lèi)的某個(gè)公有屬性
Field ageField = student.getClass().getField("mAge");
System.out.println(" age is : " + ageField.getInt(student));
} catch (Exception e) {
e.printStackTrace();
}
}</pre>
這里需要注意的是 getDeclaredField 和 getDeclaredFields 包含 private、protected、default、public 的屬性,并且通過(guò)這兩個(gè)函數(shù)獲取到的只是在自身中定義的屬性,從父類(lèi)中集成的屬性不能夠獲取到。而 getField 和 getFields 只包含 public 屬性,父類(lèi)中的公有屬性也能夠獲取到。
獲取Class對(duì)象中實(shí)現(xiàn)的接口
<pre>private static void showInterfaces() {
Student student = new Student("mr.simple");
Class<?>[] interfaceses = student.getClass().getInterfaces();
for (Class<?> class1 : interfaceses) {
System.out.println("Student's interface is : " + class1.getName());
}
}</pre>
獲取Class對(duì)象的父類(lèi)
<pre>Student student = new Student("mr.simple");
Class<?> superClass = student.getClass().getSuperclass();
while (superClass != null) {
System.out.println("Student's super class is : " + superClass.getName());
// 再獲取父類(lèi)的上一層父類(lèi),直到最后的 Object 類(lèi),Object 的父類(lèi)為 null
superClass = superClass.getSuperclass();
}</pre>