三十二、JavaSE之反射(這個部分很重要)

1.類加載器

1.1類加載【理解】

  • 類加載的描述
    • 當程序要使用某個類時,如果該類還未被加載到內存中,則系統會通過類的加載,類的連接,類的初始化這三個步驟來對類進行初始化。如果不出現意外情況,JVM將會連續完成這三個步驟,所以有時也把這三個步驟統稱爲類加載或者類初始化
  • 類的加載
    • 就是指將class文件讀入內存,併爲之創建一個 java.lang.Class 對象
    • 任何類被使用時,系統都會爲之建立一個 java.lang.Class 對象
  • 類的連接
    • 驗證階段:用於檢驗被加載的類是否有正確的內部結構,並和其他類協調一致
    • 準備階段:負責爲類的類變量分配內存,並設置默認初始化值
    • 解析階段:將類的二進制數據中的符號引用替換爲直接引用
  • 類的初始化
    • 在該階段,主要就是對類變量進行初始化
  • 類的初始化步驟
    • 假如類還未被加載和連接,則程序先加載並連接該類
    • 假如該類的直接父類還未被初始化,則先初始化其直接父類
    • 假如類中有初始化語句,則系統依次執行這些初始化語句
    • 注意:在執行第2個步驟的時候,系統對直接父類的初始化步驟也遵循初始化步驟1-3
  • 類的初始化時機
    • 創建類的實例
    • 調用類的類方法
    • 訪問類或者接口的類變量,或者爲該類變量賦值
    • 使用反射方式來強制創建某個類或接口對應的java.lang.Class對象
    • 初始化某個類的子類
    • 直接使用java.exe命令來運行某個主類

1.2類加載器【理解】

1.2.1類加載器的作用

  • 負責將.class文件加載到內存中,併爲之生成對應的 java.lang.Class 對象。雖然我們不用過分關心類加載機制,但是瞭解這個機制我們就能更好的理解程序的運行!

1.2.2JVM的類加載機制

  • 全盤負責:就是當一個類加載器負責加載某個Class時,該Class所依賴的和引用的其他Class也將由該類加載器負責載入,除非顯示使用另外一個類加載器來載入
  • 父類委託:就是當一個類加載器負責加載某個Class時,先讓父類加載器試圖加載該Class,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類
  • 緩存機制:保證所有加載過的Class都會被緩存,當程序需要使用某個Class對象時,類加載器先從緩存區中搜索該Class,只有當緩存區中不存在該Class對象時,系統纔會讀取該類對應的二進制數據,並將其轉換成Class對象,存儲到緩存區

1.2.3Java中的內置類加載器

  • Bootstrap class loader:它是虛擬機的內置類加載器,通常表示爲null ,並且沒有父null
  • Platform class loader:平臺類加載器可以看到所有平臺類 ,平臺類包括由平臺類加載器或其祖先定義的Java SE平臺API,其實現類和JDK特定的運行時類
  • System class loader:它也被稱爲應用程序類加載器 ,與平臺類加載器不同。 系統類加載器通常用於定義應用程序類路徑,模塊路徑和JDK特定工具上的類
  • 類加載器的繼承關係:System的父加載器爲Platform,而Platform的父加載器爲Bootstrap

1.2.4ClassLoader 中的兩個方法

  • 方法分類

    方法名 說明
    static ClassLoader getSystemClassLoader() 返回用於委派的系統類加載器
    ClassLoader getParent() 返回父類加載器進行委派
  • 示例代碼

    public class ClassLoaderDemo {
        public static void main(String[] args) {
            //static ClassLoader getSystemClassLoader():返回用於委派的系統類加載器
            ClassLoader c = ClassLoader.getSystemClassLoader();
            System.out.println(c); //AppClassLoader
    
            //ClassLoader getParent():返回父類加載器進行委派
            ClassLoader c2 = c.getParent();
            System.out.println(c2); //PlatformClassLoader
    
            ClassLoader c3 = c2.getParent();
            System.out.println(c3); //null
        }
    }
    

2.反射

2.1反射的概述【理解】

  • 是指在運行時去獲取一個類的變量和方法信息。然後通過獲取到的信息來創建對象,調用方法的一種機制。由於這種動態性,可以極大的增強程序的靈活性,程序不用在編譯期就完成確定,在運行期仍然可以擴展

2.2獲取Class類對象的三種方式【應用】

2.2.1三種方式分類

  • 類名.class屬性
  • 對象名.getClass()方法
  • Class.forName(全類名)方法

2.2.2示例代碼

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);
        System.out.println("--------");

        //調用對象的getClass()方法,返回該對象所屬類對應的Class對象
        Student s = new Student();
        Class<? extends Student> c3 = s.getClass();
        System.out.println(c1 == c3);
        System.out.println("--------");

        //使用Class類中的靜態方法forName(String className)
        Class<?> c4 = Class.forName("com.itheima_02.Student");
        System.out.println(c1 == c4);
    }
}

2.3反射獲取構造方法並使用【應用】

2.3.1Class類獲取構造方法對象的方法

  • 方法分類

    方法名 說明
    Constructor<?>[] getConstructors() 返回所有公共構造方法對象的數組
    Constructor<?>[] getDeclaredConstructors() 返回所有構造方法對象的數組
    Constructor getConstructor(Class<?>… parameterTypes) 返回單個公共構造方法對象
    Constructor getDeclaredConstructor(Class<?>… parameterTypes) 返回單個構造方法對象
  • 示例代碼

    public class ReflectDemo01 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            //獲取Class對象
            Class<?> c = Class.forName("com.itheima_02.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.3.2Constructor類用於創建對象的方法

方法名 說明
T newInstance(Object…initargs) 根據指定的構造方法創建對象

2.4反射獲取構造方法並使用練習1【應用】

  • 案例需求

    • 通過反射獲取公共的構造方法並創建對象
  • 代碼實現

    • 學生類

      public class Student {
          //成員變量:一個私有,一個默認,一個公共
          private String name;
          int age;
          public String address;
      
          //構造方法:一個私有,一個默認,兩個公共
          public Student() {
          }
      
          private Student(String name) {
              this.name = name;
          }
      
          Student(String name, int age) {
              this.name = name;
              this.age = age;
          }
      
          public Student(String name, int age, String address) {
              this.name = name;
              this.age = age;
              this.address = address;
          }
      
          //成員方法:一個私有,四個公共
          private void function() {
              System.out.println("function");
          }
      
          public void method1() {
              System.out.println("method");
          }
      
          public void method2(String s) {
              System.out.println("method:" + s);
          }
      
          public String method3(String s, int i) {
              return s + "," + i;
          }
      
          @Override
          public String toString() {
              return "Student{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      ", address='" + address + '\'' +
                      '}';
          }
      }
      
    • 測試類

      public class ReflectDemo02 {
          public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
              //獲取Class對象
              Class<?> c = Class.forName("com.itheima_02.Student");
      
              //public Student(String name, int age, String address)
              //Constructor<T> getConstructor(Class<?>... parameterTypes)
              Constructor<?> con = c.getConstructor(String.class, int.class, String.class);
              //基本數據類型也可以通過.class得到對應的Class類型
      
              //T newInstance(Object... initargs)
              Object obj = con.newInstance("林青霞", 30, "西安");
              System.out.println(obj);
          }
      }
      

2.5反射獲取構造方法並使用練習2【應用】

  • 案例需求

    • 通過反射獲取私有構造方法並創建對象
  • 代碼實現

    • 學生類:參見上方學生類

    • 測試類

      public class ReflectDemo03 {
          public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
              //獲取Class對象
              Class<?> c = Class.forName("com.itheima_02.Student");
      
              //private Student(String name)
              //Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
              Constructor<?> con = c.getDeclaredConstructor(String.class);
      
              //暴力反射
              //public void setAccessible(boolean flag):值爲true,取消訪問檢查
              con.setAccessible(true);
      
              Object obj = con.newInstance("林青霞");
              System.out.println(obj);
          }
      }
      

2.6反射獲取成員變量並使用【應用】

2.6.1Class類獲取成員變量對象的方法

  • 方法分類

    方法名 說明
    Field[] getFields() 返回所有公共成員變量對象的數組
    Field[] getDeclaredFields() 返回所有成員變量對象的數組
    Field getField(String name) 返回單個公共成員變量對象
    Field getDeclaredField(String name) 返回單個成員變量對象
  • 示例代碼

    public class ReflectDemo01 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            //獲取Class對象
            Class<?> c = Class.forName("com.itheima_02.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.6.2Field類用於給成員變量賦值的方法

方法名 說明
voidset(Object obj,Object value) 給obj對象的成員變量賦值爲value

2.7反射獲取成員變量並使用練習【應用】

  • 案例需求

    • 通過反射獲取成員變量並賦值
  • 代碼實現

    • 學生類:參見上方學生類

    • 測試類

      public class ReflectDemo02 {
          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.8反射獲取成員方法並使用【應用】

2.8.1Class類獲取成員方法對象的方法

  • 方法分類

    方法名 說明
    Method[] getMethods() 返回所有公共成員方法對象的數組,包括繼承的
    Method[] getDeclaredMethods() 返回所有成員方法對象的數組,不包括繼承的
    Method getMethod(String name, Class<?>… parameterTypes) 返回單個公共成員方法對象
    Method getDeclaredMethod(String name, Class<?>… parameterTypes) 返回單個成員方法對象
  • 示例代碼

    public class ReflectDemo01 {
        public static void main(String[] args) throws Exception {
            //獲取Class對象
            Class<?> c = Class.forName("com.itheima_02.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.8.2Method類用於執行方法的方法

方法名 說明
Objectinvoke(Object obj,Object… args) 調用obj對象的成員方法,參數是args,返回值是Object類型

2.9反射獲取成員方法並使用練習【應用】

  • 案例需求

    • 通過反射獲取成員方法並調用
  • 代碼實現

    • 學生類:參見上方學生類

    • 測試類

      public class ReflectDemo02 {
          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();
      
              //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);
          }
      }
      

2.10反射的案例【應用】

2.10.1反射練習之越過泛型檢查

  • 案例需求

    • 通過反射技術,向一個泛型爲Integer的集合中添加一些字符串數據
  • 代碼實現

    public class ReflectTest01 {
        public static void main(String[] args) throws Exception {
            //創建集合
            ArrayList<Integer> array = new ArrayList<Integer>();
    
    //        array.add(10);
    //        array.add(20);
    //        array.add("hello");
    
            Class<? extends ArrayList> c = array.getClass();
            Method m = c.getMethod("add", Object.class);
    
            m.invoke(array,"hello");
            m.invoke(array,"world");
            m.invoke(array,"java");
    
            System.out.println(array);
        }
    }
    

2.10.2運行配置文件中指定類的指定方法

  • 案例需求

    • 通過反射運行配置文件中指定類的指定方法
  • 代碼實現

    public class ReflectTest02 {
        public static void main(String[] args) throws Exception {
            //加載數據
            Properties prop = new Properties();
            FileReader fr = new FileReader("myReflect\\class.txt");
            prop.load(fr);
            fr.close();
    
            /*
                className=com.itheima_06.Student
                methodName=study
             */
            String className = prop.getProperty("className");
            String methodName = prop.getProperty("methodName");
    
            //通過反射來使用
            Class<?> c = Class.forName(className);//com.itheima_06.Student
    
            Constructor<?> con = c.getConstructor();
            Object obj = con.newInstance();
    
            Method m = c.getMethod(methodName);//study
            m.invoke(obj);
        }
    }
    

3.模塊化

3.1模塊化概述【理解】

Java語言隨着這些年的發展已經成爲了一門影響深遠的編程語言,無數平臺,系統都採用Java語言編寫。但是,伴隨着發展,Java也越來越龐大,逐漸發展成爲一門“臃腫” 的語言。而且,無論是運行一個大型的軟件系統,還是運行一個小的程序,即使程序只需要使用Java的部分核心功能, JVM也要加載整個JRE環境。
爲了給Java“瘦身”,讓Java實現輕量化,Java 9正式的推出了模塊化系統。Java被拆分爲N多個模塊,並允許Java程序可以根據需要選擇加載程序必須的Java模塊,這樣就可以讓Java以輕量化的方式來運行

其實,Java 7的時候已經提出了模塊化的概念,但由於其過於複雜,Java 7,Java 8都一直未能真正推出,直到Java 9才真正成熟起來。對於Java語言來說,模塊化系統是一次真正的自我革新,這種革新使得“古老而龐大”的Java語言重新煥發年輕的活力

3.2模塊的基本使用【應用】

  1. 在項目中創建兩個模塊。一個是myOne,一個是myTwo

  2. 在myOne模塊中創建以下包和以下類,並在類中添加方法

在這裏插入圖片描述

在這裏插入圖片描述

  1. 在myTwo模塊中創建以下包和以下類,並在類中創建對象並使用

在這裏插入圖片描述
在這裏插入圖片描述

  1. 在myOne模塊中src目錄下,創建module-info.java,並寫入以下內容

在這裏插入圖片描述

  1. 在myTwo模塊中src目錄下,創建module-info.java,並寫入以下內容

在這裏插入圖片描述

3.3模塊服務的基本使用【應用】

  1. 在myOne模塊中新建一個包,提供一個接口和兩個實現類

在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

  1. 在myOne模塊中修改module-info.java文件,添加以下內容

在這裏插入圖片描述

  1. 在myTwo模塊中新建一個測試類

在這裏插入圖片描述
在這裏插入圖片描述
4. 在myTwo模塊中修改module-info.java文件,添加以下內容

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章