1. 程式人生 > 實用技巧 >類載入器&反射

類載入器&反射

1.類載入器

1.1類的載入

概述:

當程式要使用某個類時,如果該類還未被載入到記憶體中,則系統會通過載入,連線,初始化三步來實現對這個類的初始化。如果不出現意外情況,JVM將會連續完成這三個步驟,所以有時也把這三個步驟統稱為類載入或者類初始化。

載入:

  • 就是指將class檔案讀入記憶體,併為之建立一個Class物件
  • 任何類被使用時系統都會建立一個Class物件

連線:

  • 驗證:是否有正確的內部結構,並和其他類協調一致
  • 準備:負責為類的靜態成員分配記憶體,並設定預設初始值
  • 解析:將類的二進位制資料中的符號引用替換為直接引用

初始化:

  • 像預設初始化,顯示初始化,構造初始化等

類初始化的時機

  • 1.建立類的例項
  • 2.訪問類的靜態變數,或者為靜態變數賦值
  • 3.呼叫類的靜態方法
  • 4.使用反射方式來強調建立某個類或介面對應的java.lang.Class物件
  • 5.初始化某個類的子類
  • 6.直接使用java.exe命令來執行某個主類

1.2類載入器

概述:類載入器負責將.class檔案載入到記憶體中,併為之生成對應的Class物件,雖然我們不需要關心類載入器機制,但是瞭解這個機制我們就能更好的理解程式的執行。

類載入器的組成:

  • Bootstrap ClassLoader:根類載入器
    • 也被成為引導類載入器,負責java核心類的載入,比如System,String等,在JDK中JRE的lib目錄下的rt.jar檔案中
  • Extension ClassLoader:擴充套件類載入器
    • 負責JRE的擴充套件目錄中jar包的載入,在JDK中JRE的lib目錄下的ext目錄
  • System ClassLoader:系統類載入器
    • 負責在JVM啟動時載入來自java命令的class檔案,以及classpath環境變數所指定的jar包和類路徑

2.反射

概述:是指在執行時去獲取一個類的變數和方法資訊。然後通過獲取到的資訊來建立物件,呼叫方法的一種機制。由於這種動態性,可以極大的增強程式的靈活性,程式不用在編譯期就完成確定,在執行期仍然可以擴充套件。

2.1獲取Class類物件的三種方式

獲取class檔案物件的三種方式

  • 1.類名.class
  • 2.物件.getClass()
  • 3.Class:forName("類名"):這裡的類名是包括包名的全稱類名(推薦使用)

程式碼示例:

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //使用類的class屬性來獲取該類對應的Class物件
        Class<Student> c1 = Student.class;
        System.out.println(c1);
        Class<Student> c2 = Student.class;
        System.out.println(c1 == c2);//true
        System.out.println("--------");
        //呼叫物件的getClass()方法,返回該物件所屬類對應的Class物件
        Student s = new Student();
        Class<? extends Student> c3 = s.getClass();
        System.out.println(c1 == c3);//true
        System.out.println("--------");
        //使用Class類中的靜態方法forName(String className)
        Class<?> c4 = Class.forName("com.classtest.Student");
        System.out.println(c1 == c4);//true
    }
}

2.2通過反射獲取成員變數,構造方法,成員方法

2.2.1Class類獲取構造方法物件的方法

  • public Constructor[] getConstructors():獲得所有的公共構造方法
  • public Constructor[] getDeclaredConstructors():獲取所有的構造方法
  • public ConstructorgetConstructor(Class<?>... parameterTypes):獲取單個公共的構造方法,引數表示的是你要獲取的構造方法的構造引數個數及資料型別的class位元組碼檔案物件
  • public ConstructorgetDeclaredConstructor(Class<?>... parameterTypes):獲取單個構造方法(包括私有構造方法),引數表示的是你要獲取的構造方法的構造引數個數及資料型別的class位元組碼檔案物件
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException,NoSuchMethodException, IllegalAccessException, InvocationTargetException,InstantiationException {
        //獲取Class物件
        Class<?> c = Class.forName("com.classtest.Student");
        //Constructor<?>[] getConstructors() 返回一個包含 Constructor物件的陣列,Constructor物件反映了由該 Class物件表示的類的所有公共建構函式
        // Constructor<?>[] cons = c.getConstructors();
        //Constructor<?>[] getDeclaredConstructors() 返回反映由該 Class物件表示的類宣告的所有建構函式的 Constructor物件的陣列
        Constructor<?>[] cons = c.getDeclaredConstructors();
        for(Constructor con : cons) {
            System.out.println(con);
        }
        System.out.println("--------");
        //Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一個Constructor物件,該物件反映由該 Class物件表示的類的指定公共建構函式
        //Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回一個 Constructor物件,該物件反映由此 Class物件表示的類或介面的指定建構函式
        //引數:你要獲取的構造方法的引數的個數和資料型別對應的位元組碼檔案物件
        Constructor<?> con = c.getConstructor();
        //Constructor提供了一個類的單個建構函式的資訊和訪問許可權
        //T newInstance(Object... initargs) 使用由此 Constructor物件表示的建構函式,使用指定的初始化引數來建立和初始化建構函式的宣告類的新例項
        Object obj = con.newInstance();
        System.out.println(obj);
        // Student s = new Student();
        // System.out.println(s);
    }
}

2.2.2Constructor類用於建立物件的方法

  • public T newInstance(Object... instargs):使用此Constructor物件表示的構造方法來建立該構造方法的宣告類的新例項,並用指定的初始化引數初始化該例項

2.2.3反射獲取成員變數並使用

  • public Field[] getFields():獲取所有公共變數
  • public Field[] getDeclaredFields():獲取所有變數
  • public Field getField(String name):獲取單個指定的變數(不能為私有的變數)
  • public Field getDeclaredField(String name):獲取單個變數(可以是私有變數)
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException,NoSuchFieldException, NoSuchMethodException, IllegalAccessException,InvocationTargetException, InstantiationException {
        //獲取Class物件
        Class<?> c = Class.forName("com.classtest.Student");
        //Field[] getFields() 返回一個包含 Field物件的陣列, Field物件反映由該 Class物件表示的類或介面的所有可訪問的公共欄位
        //Field[] getDeclaredFields() 返回一個 Field物件的陣列,反映了由該 Class物件表示的類或介面宣告的所有欄位
        // Field[] fields = c.getFields();
        Field[] fields = c.getDeclaredFields();
        for(Field field : fields) {
            System.out.println(field);
        }
        System.out.println("--------");
        //Field getField(String name) 返回一個 Field物件,該物件反映由該 Class物件表示的類或介面的指定公共成員欄位
        //Field getDeclaredField(String name) 返回一個 Field物件,該物件反映由該Class物件表示的類或介面的指定宣告欄位
        Field addressField = c.getField("address");
        //獲取無參構造方法建立物件
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
        // obj.addressField = "西安";
        //Field提供有關類或介面的單個欄位的資訊和動態訪問
        //void set(Object obj, Object value) 將指定的物件引數中由此 Field物件表示的欄位設定為指定的新值
        addressField.set(obj,"西安"); //給obj的成員變數addressField賦值為西安
        System.out.println(obj);
        // Student s = new Student();
        // s.address = "西安";
        // System.out.println(s);
    }
}

2.2.4Field類用於給成員變數賦值的方法

  • 欄位物件.set(物件, “該欄位的值”):設定某個欄位(成員變數)的值
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        //獲取Class物件
        Class<?> c = Class.forName("com.itheima_02.Student");
        //Student s = new Student();
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
        System.out.println(obj);
        //s.name = "林青霞";
        // Field nameField = c.getField("name"); //NoSuchFieldException:name
        Field nameField = c.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(obj, "林青霞");
        System.out.println(obj);
        //s.age = 30;
        Field ageField = c.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(obj,30);
        System.out.println(obj);
        //s.address = "西安";
        Field addressField = c.getDeclaredField("address");
        addressField.setAccessible(true);
        addressField.set(obj,"西安");
        System.out.println(obj);
    }
}

2.2.5反射獲取成員方法並使用

  • public Method[] getMethods():獲取自己的包括父親的成員方法
  • public Method[] getDeclaredMethods():獲取自己的方法
  • public Method getMethod(String name, Class<?>... parameterTypes):獲取單個方法(不可獲取私有的方法)
  • public Method getDeclaredMethod(String name, Class<?>... parameterTypes):獲取單個方法(可獲得私有的方法)
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        //獲取Class物件
        Class<?> c = Class.forName("com.classtest.Student");
        //Method[] getMethods() 返回一個包含方法物件的陣列, 方法物件反映由該 Class物件表示的類或介面的所有公共方法,包括由類或介面宣告的物件以及從超類和超級介面繼承的類
        //Method[] getDeclaredMethods() 返回一個包含方法物件的陣列, 方法物件反映由Class物件表示的類或介面的所有宣告方法,包括public,protected,default(package)訪問和私有方法,但不包括繼承方法
        // Method[] methods = c.getMethods();
        Method[] methods = c.getDeclaredMethods();
        for(Method method : methods) {
            System.out.println(method);
        }
        System.out.println("--------");
        //Method getMethod(String name, Class<?>... parameterTypes) 返回一個 方法物件,該物件反映由該 Class物件表示的類或介面的指定公共成員方法
        //Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一個 方法物件,它反映此表示的類或介面的指定宣告的方法 Class物件
        //public void method1()
        Method m = c.getMethod("method1");
        //獲取無參構造方法建立物件
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
        // obj.m();
        //在類或介面上提供有關單一方法的資訊和訪問許可權
        //Object invoke(Object obj, Object... args) 在具有指定引數的指定物件上呼叫此方法物件表示的基礎方法
        //Object:返回值型別
        //obj:呼叫方法的物件
        //args:方法需要的引數
        m.invoke(obj);
        // Student s = new Student();
        // s.method1();
    }
}

2.2.6Method類用於執行方法的方法

  • public Object invoke(Object obj, Object... args):返回值是object接受,第一個引數表示物件是誰,第二個引數表示呼叫該方法的實際引數
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        //獲取Class物件
        Class<?> c = Class.forName("com.classtest.Student");
        //Student s = new Student();
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
        //s.method1();
        Method m1 = c.getMethod("method1");
        m1.invoke(obj);
        //s.method2("林青霞");
        Method m2 = c.getMethod("method2", String.class);
        m2.invoke(obj,"林青霞");
        // String ss = s.method3("林青霞",30);
        // System.out.println(ss);
        Method m3 = c.getMethod("method3", String.class, int.class);
        Object o = m3.invoke(obj, "林青霞", 30);
        System.out.println(o);
        //s.function();
        // Method m4 = c.getMethod("function"); //NoSuchMethodException:com.itheima_02.Student.function()
        Method m4 = c.getDeclaredMethod("function");
        m4.setAccessible(true);
        m4.invoke(obj);
    }
}