01、Java特性--反射
反射
基本概念
反射機制:將類的各個組成部分封裝為其他物件,這就是反射機制。
通俗來講,反射就是來操作Java類的一種機制,通過反射可以操作Java中的位元組碼檔案。
反射的核心類是Class類,它是類的類模板,即該類是用來描述Java類的。
描述的基本上就是一個類的組成部分:包名、類名、介面、父類、成員變數(Filed)、構造方法(Construct)、普通方法(Method)、註解(Annotation)等等。
所以說,反射的操作基本上就是對這些類的元素進行操作。
Java在計算機中會經歷三個階段,分別如下:
Source原始碼階段:*.java檔案會被編譯成*.class位元組碼檔案。
Class物件階段:*.class位元組碼檔案會被類載入器裝進記憶體,並將其封裝成Class物件。
RunTime執行階段:建立物件的過程。
其中,在Class物件階段,Class物件將原位元組碼中所有成員變數封裝成Field[],將建構函式封裝成Construction[],將成員方法封裝成Method[]。
獲取Class物件
獲取Class物件分別對應Java程式碼在計算機中的三個階段:
1、Source原始碼階段:多用於配置檔案的讀取和載入。
Class.forName("全類名")。
2、Class類物件階段:多用於引數的傳遞。
**.class
3、Runtime執行階段:多用於物件的獲取位元組碼的方式
xxx.getClass();
注意:同一個位元組碼檔案(*.class)在一次程式執行過程中,只會被載入一次,無論通過哪一種方式獲取的Class物件都是同一個。
案例分析
下面的案例演示三種Java位元組碼的獲取方式:
public class Main { public static void main(String[] args) throws ClassNotFoundException { // 方式一:Class.forName("全類名") Class clazz1 = Class.forName("com.legend.reflect.Person"); System.out.println("clazz1 = " + clazz1); // 方式二:類名.class Class clazz2 = Person.class; System.out.println("clazz2 = " + clazz2); // 方式三:物件.getClass() Class clazz3 = new Person().getClass(); System.out.println("clazz3 = " + clazz3); boolean isEquals = (clazz1 == clazz2) && (clazz2 == clazz3) ? true : false; System.out.println("clazz1 == clazz2 == clazz3:" + isEquals); } }
輸出結果如下所示:
clazz1 = class com.legend.reflect.Person
clazz2 = class com.legend.reflect.Person
clazz3 = class com.legend.reflect.Person
clazz1 == clazz2 == clazz3:true
Class物件功能
Class物件包含一個類的所有資訊,這裡只介紹主要的。
(1)獲取成員變數
Field[] getFields() :獲取所有public修飾的成員變數
Field getField(String name) 獲取指定名稱的 public修飾的成員變數
Field[] getDeclaredFields() 獲取所有的成員變數,不考慮修飾符
Field getDeclaredField(String name)
(2)獲取建構函式
Constructor<?>[] getConstructors()
Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor<?>[] getDeclaredConstructors()
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
(3)獲取成員方法
Method[] getMethods()
Method getMethod(String name, Class<?>... parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
(4)獲取全類名
String getName()
(5)獲取類載入器
ClassLoader getClassLoader()
Filed成員變數
下面針對Person物件中的三種修飾符:private、protected和public三種類型的成員變數使用反射賦值。
1、定義Person物件,並提供三種修飾符修飾的屬性。
public class Person {
public String name;
protected int age;
private String address;
...get和set...
}
2、使用獲取成員變數的方式進行反射,只能獲取到public的成員變數。
public class Main {
public static void main(String[] args) throws NoSuchFieldException {
Class personClass = Person.class;
// 獲取所有public修飾的成員變數
Field[] fields = personClass.getFields();
for (Field field : fields){
System.out.println(field);
}
}
}
獲取所有的變數,必須使用getDeclaredFields()方法來完成。
public class Main {
public static void main(String[] args) throws NoSuchFieldException {
Class personClass = Person.class;
Field[] fields = personClass.getDeclaredFields();
for (Field field : fields){
System.out.println(field);
}
}
}
注意:如果獲取單個private或protected屬性的值需要使用暴力反射來完成:setAccessible(true);
Constructor建構函式
通過反射的方式來建立物件,使用到的實體類如下:
public class Person {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
...get和set...
}
然後通過反射的方式來建立該物件:
public class Main {
public static void main(String[] args) throws Exception {
Class personClass = Person.class;
Constructor[] constructors = personClass.getConstructors();
for (Constructor constructor : constructors){
System.out.println(constructor);
}
// 獲取有參的建構函式來建立物件
Constructor constructor = personClass.getConstructor(String.class, Integer.class);
Object legend = constructor.newInstance("Legend", 30);
System.out.println(legend);
}
}
如果要獲取private修飾的建構函式,需要使用getDeclaredConstructor方法。
Method方法物件
通過反射呼叫物件中的方法,所用到的例項類如下:
public class Person {
private String name;
private Integer age;
public Person() {
}
...get和set...
public void eat(String food){
System.out.println("eat..." + food);
}
}
然後通過反射的方式來執行eat方法:
public class Main {
public static void main(String[] args) throws Exception {
Class personClass = Person.class;
// 獲取所有public的方法
Method[] methods = personClass.getMethods();
for (Method method : methods){
System.out.println(method);
}
// 執行位元組碼中的方法
Object o = personClass.newInstance();
Method method = personClass.getMethod("eat", String.class);
method.invoke(o, "apple");
}
}
反射的大致用法如上所示,還可以通過反射的方式去修改配置類並執行。