`Java`反射機制是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意方法和
屬性;這種動態獲取信息以及動態調用對象方法的功能成為`Java`語言的反射機制。
要想理解反射的原理,首先要了解什么是類型信息。`Java`讓我們在運行時識別對象和類的信息,主要有2種方式:一種是傳統的`RTTI`,
它假定我們在編譯時已經知道了所有的類型信息;另一種是反射機制,它允許我們在運行時發現和使用類的信息。
- 得到`Class`對象的三種方式
```java
//1、通過對象調用 getClass() 方法來獲取,通常應用在:比如你傳過來一個 Object
// 類型的對象,而我不知道你具體是什么類,用這種方法
Person p1 = new Person();
Class c1 = p1.getClass();
//2、直接通過 類名.class 的方式得到,該方法最為安全可靠,程序性能更高
// 這說明任何一個類都有一個隱含的靜態成員變量 class
Class c2 = Person.class;
//3、通過 Class 對象的 forName() 靜態方法來獲取,用的最多,
// 但可能拋出 ClassNotFoundException 異常
Class c3 = Class.forName("com.ys.reflex.Person");
```
**需要注意的是:一個類在 `JVM` 中只會有一個 `Class` 實例,即我們對上面獲取的 `c1,c2,c3`進行 `equals` 比較,發現都是`true`。**
- 通過`Class`類獲取成員變量、成員方法、接口、超類、構造方法等
```java
getName();//獲取類的完整名字
getFields();//獲取類的public類型的屬性,包括繼承過來的。
getDeclaredFields();//獲取類的所有屬性。包括private聲明的屬性。但不包括繼承過來的屬性。
getMethods();//獲得類的public類型的方法。包括繼承過來的。
getDeclaredMethods();//獲得類的所有方法。包括private 聲明的。不包括繼承過來的方法。
getMethod(String name, Class[] parameterTypes);//獲得類的特定方法,name參數指定方法的名字,parameterTypes 參數指定方法的參數類型。
getConstructors();//獲得類的public類型的構造方法。
getConstructor(Class[] parameterTypes);//獲得類的特定構造方法,parameterTypes 參數指定構造方法的參數類型。
newInstance();//通過類的不帶參數的構造方法創建這個類的一個對象。
```
例子:
```java
//獲得類完整的名字
String className = c2.getName();
System.out.println(className);//輸出com.ys.reflex.Person
//獲得類的public類型的屬性。
Field[] fields = c2.getFields();
for(Field field : fields){
System.out.println(field.getName());//age
}
//獲得類的所有屬性。包括私有的
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
System.out.println(field.getName());//name age
}
//獲得類的public類型的方法。這里包括 Object 類的一些方法
Method [] methods = c2.getMethods();
for(Method method : methods){
System.out.println(method.getName());//work waid equls toString hashCode等
}
//獲得類的所有方法。
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
System.out.println(method.getName());//work say
}
//獲得指定的屬性
Field f1 = c2.getField("age");
System.out.println(f1);
//獲得指定的私有屬性
Field f2 = c2.getDeclaredField("name");
//啟用和禁用訪問安全檢查的開關,值為 true,則表示反射的對象在使用時應該取消 java 語言的訪問檢查;反之不取消
f2.setAccessible(true);
System.out.println(f2);
//創建這個類的一個對象
Object p2 = c2.newInstance();
//將 p2 對象的 f2 屬性賦值為 Bob,f2 屬性即為 私有屬性 name
f2.set(p2,"Bob");
//使用反射機制可以打破封裝性,導致了java對象的屬性不安全。
System.out.println(f2.get(p2)); //Bob
//獲取構造方法
Constructor [] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor.toString());//public com.ys.reflex.Person()
}
```
- 根據反射獲取父類屬性
父類:`Parent.java`
```java
public class Parent {
public String publicField = "parent_publicField";
protected String protectField = "parent_protectField";
String defaultField = "parent_defaultField";
private String privateField = "parent_privateField";
}
```
子類:`Son.java`
```java
public class Son extends Parent {
}
```
測試類:
```java
public class ReflectionTest {
@Test
public void testGetParentField() throws Exception{
Class c1 = Class.forName("com.ys.model.Son");
//獲取父類私有屬性值
System.out.println(getFieldValue(c1.newInstance(),"privateField"));
}
public static Field getDeclaredField(Object obj,String fieldName) {
Field field = null;
Class c = obj.getClass();
for(; c != Object.class ; c = c.getSuperclass()){
try {
field = c.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
}catch (Exception e){
//這里甚么都不要做!并且這里的異常必須這樣寫,不能拋出去。
//如果這里的異常打印或者往外拋,則就不會執行c = c.getSuperclass(),最后就不會進入到父類中了
}
}
return null;
}
public static Object getFieldValue(Object object,String fieldName) throws Exception{
Field field = getDeclaredField(object,fieldName);
return field.get(object);
}
}
```
**通過執行上述代碼,我們獲得了父類的私有屬性值,這里要注意的是直接通過反射獲取子類的對象是不能得到父類的屬性值的,
必須根據反射獲得的子類 `Class` 對象在調用 `getSuperclass()` 方法獲取父類對象,然后在通過父類對象去獲取父類的屬性值。**