反射 Reflection
靜態語言和動態語言
動態語言
- 是一類在執行時可以改變其結構的語言:例如新的函式、物件、甚至程式碼可以被引進,已有的函式可以被刪除或是其他結構上的變化。通俗點說就是在執行時,程式碼可以根據某些條件改變自身結構。
- 主要動態語言:Object-C、C#、JavaScript、PHP、Python等。
靜態語言
- 與動態語言相對應的,執行時結構不可變的語言就是靜態語言。如Java、C、C++
- Java不是動態語言,但Java可以稱之為“準動態語言”。即Java有一定的動態性,我們可以利用反射機制獲得類似動態語言的特性。Java的動態性讓程式設計的時候更加靈活!
Java Reflection
-
Reflection(反射)是Java被視為動態語言的關鍵,反射機制允許程式在執行期藉助於Reflection API取得任何類的內部資訊,並能直接操作任意物件的內部屬性及方法。
Class c = Class.forName("java.lang.String")
-
載入完類之後,在堆記憶體的方法區中就產生了一個Class型別的物件(一個類只有一個Class物件),這個物件就包含了完整的類的結構資訊。我們可以透過這個物件看到類的結構。這個物件就像一面鏡子,透過這個鏡子看到類的結構,所以,我們形象的稱之為:反射
實體類:pojo,entity 只有一些屬性的類
package OOP.reflection;
/**
* @version: java version 1.8
* @Author: 14
*/
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
//透過反射獲取類的class物件
Class c1 = Class.forName("OOP.reflection.User");
System.out.println(c1);
Class c2 = Class.forName("OOP.reflection.User");
Class c3 = Class.forName("OOP.reflection.User");
Class c4 = Class.forName("OOP.reflection.User");
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());
}
}
//實體類
class User{
int id;
String name;
int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Class類
物件照鏡子後可以得到的資訊:某個類的屬性、方法和構造器、某個類到底實現了哪些介面。對於每個類而言,JRE都為其保留一個不變的Class型別的物件。
一個Class物件包含了特定某個結構(class/interface/enum/annotation/primitive type/void/)的有關資訊。
- Class本身也是一個類
- Class物件只能由系統建立物件
- 一個載入的類在JVM中只會有一個Class例項
- 一個Class物件對應的是一個載入到JVM中的一個.class檔案>每個類的例項都會記得自己是由哪個Class 例項所生成透過Class可以完整地得到一個類中的所有被載入的結構
- Class類是Reflection的根源,針對任何你想動態載入、執行的類,唯有先獲得相應的Class物件
在Object類中定義了以下的方法,此方法被所有子類繼承
public final Class getClass()
以上的方法返回值的型別是一個Class類,此類是Java反射的源頭,實際上所謂反射從程式的執行結果來看也很好理解,即:可以透過物件反射求出類的名稱。
Class類的常用方法
方法名 | 功能說明 |
---|---|
static ClassforName(Srting name) | 返回指定類名name的Class物件 |
Object newInstance() | 呼叫預設建構函式,返回Class物件的一個例項 |
getName() | 返回此Class物件所表示的實體(類、介面、陣列類或void)的名稱 |
Class getSuperClass() | 返回當前Class物件的父類的Class物件 |
Class[] getinterfaces() | 獲取當前Class物件的介面 |
ClassLoader getClassLoader() | 返回該類的類載入器 |
Constructor[]getConstructors() | 返回一個包含某些Constructor物件的陣列 |
Method getMothed(String name,Class.. T) | 返回一個Method物件,此物件的形參型別為paramType |
Field[]getDeclaredFields() | 返回Field物件的一個陣列 |
獲取Class類的幾種方式
-
若已知具體的類,透過類的class屬性獲取,該方法最為安全可靠,程式效能最高。
Class clazz= Person.class;
-
已知某個類的例項,呼叫該例項的getClass()方法獲取Class物件
Class clazz = person.getClass();
-
已知一個類的全類名,且該類在類路徑下,可透過Class類的靜態方法forName()獲取,可能丟擲ClassNotFoundException
Class clazz= Class.forName("demo01.Student");
-
內建基本資料型別可以直接用類名.Type
Class clazz = Integer.TYPE;
-
ClassLoader
package OOP.reflection;
/**
* @version: java version 1.8
* @Author: 14
*/
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println(person);
//方式一:透過物件獲得
Class c1 = person.getClass();
System.out.println(c1.hashCode());
//方式二:forname獲得
Class c2 = Class.forName("OOP.reflection.Student");
System.out.println(c2.hashCode());
//方式三:透過類名.class獲得
Class c3 = Student.class;
System.out.println(c3.hashCode());
//方法四:基本內建型別的包裝類都有一個Type屬性
Class c4 = Integer.TYPE;
System.out.println(c4);
//獲取父類
Class c5 = c2.getSuperclass();
System.out.println(c5);
}
}
class Person{
String name;
public Person(){
}
public Person(String name){
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student(){
this.name = "Student";
}
}
class Teacher extends Person{
public Teacher(){
this.name = "Teacher";
}
}