1. 程式人生 > >Thinking in java:RTTI和反射機制

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