Thinking in java:RTTI和反射機制
摘抄別人的一句話:要想理解反射的原理,首先要了解什麼是型別資訊。Java讓我們在執行時識別物件和類的資訊,主要有2種方式:一種是傳統的RTTI,它假定我們在編譯時已經知道了所有的型別資訊;另一種是反射機制,它允許我們在執行時發現和使用類的資訊
一.RTTI
為理解RTTI在Java裡如何工作,首先必須瞭解型別資訊在執行期是如何表示的。這時要用到一個名為“Class物件”的特殊形式的物件,其中包含了與類有關的資訊(有時也把它叫作“元類”)。事實上,我們要用Class物件建立屬於某個類的全部“常規”或“普通”物件。RTTI在java裡表現為3種形式:
- 經典造型,即下溯造型,造型失敗產生一個ClassCastException違例
- 代表物件型別的Class物件。可查詢Class物件,獲取有用的執行期資料
- instanceof,它會返回一個布林值
第一種和第三種忽略,主要是第二種。
關於Class類
每一個class都有一個Class物件,可以理解為類物件,有且僅有一個。在執行期,一旦我們需要new一個class的物件時,JVM首先會去檢查這個class是否已經載入,如果沒有載入,JVM會去查同名的.class檔案,並將其載入,載入時便會生成一個Class物件,它儲存了這個class的相關資訊,靜態變數也是在類載入的時候初始化。在java1.0種,使用Class實現RTTI有3種形式:
- Class.forName(“類名”);
- 型別.class;
- 物件.getClass();
3種形式得到的都是一個class的Class物件,第二種相較第一種要更安全和快捷,因為第二種在編譯器會進行檢查,並且沒有呼叫方法,但就實用性而言我覺得第一種較為常用。
二.反射
在java1.1種,Class類得到了擴充套件,我們可以從一個Class物件種獲取這個class型別的成員資訊,但要注意的一點是:獲取的資訊是Class物件的,並不是某個具體的class物件的資訊,因此我們要用某些資訊的時候都要與一個具體的class物件相繫結。
1.通過反射獲取構造方法並使用
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Test
{
public static void main(String[] args)
{
try {
Class clazz=Class.forName("Student");
//Constructor[] constructors=clazz.getConstructors();獲取所有公有的構造方法
//Constructor[] constructors=clazz.getDeclaredConstructors();//獲取所有的構造方法
Constructor con=clazz.getConstructor(String.class);//獲取公有的引數型別為String的構造方法,注意引數為對應型別Class物件!!
Student st=(Student) con.newInstance("老李");//用構造方法生成一個Student物件
System.out.println(st.name);
con=clazz.getDeclaredConstructor(String.class,int.class);//獲取構造方法種引數為String和int的方法,這兒獲取的私有構造方法
con.setAccessible(true);//將私有構造的訪問許可權設為true,否則無法用構造方法建立物件
st =(Student) con.newInstance("小李",7991);
System.out.println(st.name+" "+st.ID);
}catch (Exception e){
e.printStackTrace();
}
}
}
class Student
{
String name;
public int ID;
public Student(String name)
{
this.name=name;
}
public Student()
{
this.name="老張";
}
private Student(String name,int id)
{
this.name=name;
this.ID=id;
}
public void print()
{
System.out.println("name="+name+",ID="+ID);
}
private void add(int num){ID+=num;}
private void change(String name){this.name=name;}
}
輸出如下:
2.通過反射獲取成員變數並使用
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Test
{
public static void main(String[] args)
{
Student student=new Student("老李");
student.ID=7881;
try {
Class clazz=Class.forName("Student");
//Field[] fields=clazz.getFields();獲取公有的成員變數
//Field[] fields=clazz.getDeclaredFields();獲取所有的成員變數
Field name=clazz.getDeclaredField("name");//獲取name變數
Field id=clazz.getDeclaredField("ID");//獲取ID變數
name.set(student,"老張");
id.set(student,5000);
System.out.println(student.name+" "+student.ID);
}catch (Exception e){
e.printStackTrace();
}
}
}
class Student
{
String name;
public int ID;
public Student(String name)
{
this.name=name;
}
public Student()
{
this.name="老張";
}
private Student(String name,int id)
{
this.name=name;
this.ID=id;
}
public void print()
{
System.out.println("name="+name+",ID="+ID);
}
private void add(int num){ID+=num;}
private void change(String name){this.name=name;}
}
輸出如下:
3.通過反射獲取方法並使用
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test
{
public static void main(String[] args)
{
Student student=new Student();
try {
Class clazz=Class.forName("Student");
//Method[] methods=clazz.getMethods();獲取公有的方法
//Method[] methods=clazz.getDeclaredMethods();獲取所有的方法
Method me=clazz.getMethod("print");//獲取print方法
me.invoke(student);//呼叫student物件的print方法
me=clazz.getDeclaredMethod("add",int.class);//獲取add方法
me.setAccessible(true);//設定訪問許可權為true;
me.invoke(student,50);//呼叫student物件的add方法
student.print();
me=clazz.getDeclaredMethod("down");
me.invoke(null);//呼叫靜態方法不需要物件,靜態成員同理
}catch (Exception e){
e.printStackTrace();
}
}
}
class Student
{
String name;
public int ID;
public Student(String name)
{
this.name=name;
}
public Student()
{
this.name="老張";
}
private Student(String name,int id)
{
this.name=name;
this.ID=id;
}
public void print()
{
System.out.println("name="+name+",ID="+ID);
}
private void add(int num){ID+=num;}
private void change(String name){this.name=name;}
public static void down(){System.out.println("呼叫靜態方法");}
}
輸出如下:
推薦一篇關於反射的部落格:https://blog.csdn.net/sinat_38259539/article/details/71799078