站長資訊網
最全最豐富的資訊網站

詳細解析Java反射機制原理和幾種Class獲取方式

本篇文章給大家帶來了關于java的相關知識,其中主要介紹了反射機制原理以及幾種class獲取方式和應用場景的相關問題,希望對大家有幫助。

詳細解析Java反射機制原理和幾種Class獲取方式

推薦學習:《java學習教程》

學習Java的小伙伴,可能聽過Java反射機制,但是熟悉又有點陌生,本文主要是通過思考面試中經常被問到的幾個Java反射機制的問題,再通過理論知識結合代碼實例應用場景進行講解,加深自己對Java反射機制的認知和理解,也希望能幫助到有需要的小伙伴~
詳細解析Java反射機制原理和幾種Class獲取方式

一、Java反射機制是什么?

1.1 反射原理

(1)Java反射機制(Java Reflection)是Java語言中一種動態(運行時)訪問、檢測 & 修改它本身的能力,主要作用是動態(運行時)獲取類的完整結構信息 & 調用對象的方法~
更簡單點的說就是Java程序在運行時(動態)通過創建一個類的反射對象,再對類進行相關操作,比如:

  • 獲取該對象的成員變量 & 賦值
  • 調用該對象的方法(含構造方法,有參/無參)
  • 判斷該對象所屬的類

PS:不過說實話,直接看比較官方的定義還是有點難理解,再來更加通俗點的說吧~

(2)一般情況下,我們使用某個類,都會知道這個類,以及要用它來做什么,可以直接通過new實例化創建對象,然后使用這個對象對類進行操作,這個就屬于正射~

(3)而反射則是一開始并不知道要初始化的是什么類,無法使用new來實例化創建對象,主要是通過JDK提供的反射API來實現,在運行時才知道要操作的是什么類,并且可以獲取到類的完整構造以及調用對應的方法,這就是反射~

1.2 反射例子

代碼如下:

package com.justin.java.lang;import java.lang.reflect.Constructor;import java.lang.reflect.Method;/**  * @program: Jdk1.8 Test  * @description: 正射、反射簡單調用示例  * @author: JustinQin  * @create: 2021/8/22 13:23  * @version: v1.0.0  **/public class Student {     private int id;      public void setId(int id) {         this.id = id;     }     public int getId() {         return id;     }      public static void main(String[] args) throws Exception{         //一、正射調用過程         Student student = new Student();         student.setId(1);         System.out.println("正射調用過程Student id:" + student.getId());          //二、反射調用過程         Class clz = Class.forName("com.justin.java.lang.Student");         Constructor studentConstructor = clz.getConstructor();         Object studentObj = studentConstructor.newInstance();                  Method setIdMethod = clz.getMethod("setId", int.class);         setIdMethod.invoke(studentObj, 2);         Method getIdMethod = clz.getMethod("getId");         System.out.println("正射調用過程Student id:" + getIdMethod.invoke(studentObj));     }}

輸出結果:

正射調用過程Student id:1反射調用過程Student id:2

上述例子反射的調用過程,可以看到獲取一個類的反射對象,主要過程為:

  • 獲取類的Class實例對象
  • 根據Class實例對象獲取Constructor對象
  • 再根據Constructor對象的newInstance方法獲取到類的反射對象

獲取到類的反射對象后,就可以對類進行操作了~ 例如,上述示例中對類的方法進行調用過程為:

  • 根據Class實例對象獲取到類的Method對象
  • 再根據Method對象的invoke方法調用到具體類的方法

前面一點也提到了獲取到類的Class實例對象,上面示例反向調用過程中我們是通過Class.forName("類的全局定名")這種方式來獲取到類的Class實例對象,除了這種,常用的還有其他兩種,往下講解~

二、Java反射機制中獲取Class的三種方式及區別?

詳細解析Java反射機制原理和幾種Class獲取方式

2.1 Class的幾種獲取方式

(1)獲取類的java.lang.Class實例對象,常見的三種方式分別為:

  • 通過MyClass.class獲取,這里的MyClass指具體類~~
  • 通過Class.forName("類的全局定名")獲取,全局定名為包名+類名
  • 通過new MyClass().getClass()獲取,這里的MyClass指具體類~

(2)通過MyClass.class獲取,JVM會使用ClassLoader類加載器將類加載到內存中,但并不會做任何類的初始化工作,返回java.lang.Class對象

(3)通過Class.forName("類的全局定名")獲取,同樣,類會被JVM加載到內存中,并且會進行類的靜態初始化工作,返回java.lang.Class對象

(4)通過new MyClass().getClass()獲取,這種方式使用了new進行實例化操作,因此靜態初始化和非靜態初始化工作都會進行getClass方法屬于頂級Object類中的方法,任何子類對象都可以調用,哪個子類調用,就返回那個子類的java.lang.Class對象

PS: 這3種方式,最終在JVM堆區對應類的java.lang.Class對象都屬于同一個,也就是內存地址相同,進行==雙等號比較結果為true,原因是JVM類加載過程中使用的是同一個ClassLoader類加載器加載某個類,不論加載多少次,生成到堆區的java.lang.Class對象始終只有一個,除非自定義類加載器,破壞JVM的雙親委派機制,使得同一個類被不同類加載器加載,JVM才會把它當做兩個不同的java.lang.Class對象

2.2 代碼演示幾種方式的區別

創建一個實體類,分別在實體類中創建類的靜態代碼塊、動態代碼塊有參構造方法、無參構造方法,方便測試幾種方式的區別及內存地址是否相同~

(1)實體類:

public class MyClass {     private static final String staticStr = "Hi";     private static int staticInt = 2021;     private String id;      static {         System.out.println("靜態代碼塊:staticStr=" + staticStr + ",staticInt=" + staticInt);     }      {         System.out.println("動態代碼塊~");     }      public MyClass() {         System.out.println("無參構造方法~");     }      public MyClass(String id) {         System.out.println("有參構造方法~");         this.id = id;     }      public String getId() {         return id;     }      public void setId(String id) {         this.id = id;     }      @Override     public String toString() {         return "MyClass{" +                 "id='" + id + ''' +                 '}';     }}

(2)單元測試類:
通過@Test注解對三種方式分別進行單元測試,再對這三種方式的組合進行單元測試~

package com.justin.java.lang;import org.junit.Test;/**  * @program: Jdk1.8Test  * @description: Java反射機制中獲取類的Class實例對象的常見三種方式及區別對比  * @author: JustinQin  * @create: 2021/8/22 15:04  * @version: v1.0.0  **/public class MyClassTest {      @Test     public void test1() {         System.out.println("一、MyClass.class方式=========");         Class<?> class1 = MyClass.class;     }      @Test     public void test2() throws ClassNotFoundException {         System.out.println("二、Class.forName方式=========");         Class class2 = Class.forName("com.justin.java.lang.MyClass");     }       @Test     public void test3() {         System.out.println("三、new MyClass().getClass方式=========");         Class class3 = new MyClass().getClass();     }      @Test     public void test12() throws ClassNotFoundException {         System.out.println("一、MyClass.class方式=========");         Class<?> class1 = MyClass.class;         System.out.println("二、Class.forName方式=========");         Class class2 = Class.forName("com.justin.java.lang.MyClass");     }      @Test     public void test13() {         System.out.println("一、MyClass.class方式=========");         Class<?> class1 = MyClass.class;         System.out.println("三、new MyClass().getClass方式=========");         Class class3 = new MyClass().getClass();     }      @Test     public void test23() throws ClassNotFoundException {         System.out.println("二、Class.forName方式=========");         Class class2 = Class.forName("com.justin.java.lang.MyClass");         System.out.println("三、new MyClass().getClass方式=========");         Class class3 = new MyClass().getClass();     }      @Test     public void test() throws ClassNotFoundException {         System.out.println("四、三種方式內存地址比較=========");         Class<?> class1 = MyClass.class;         Class class2 = Class.forName("com.justin.java.lang.MyClass");         Class class3 = new MyClass().getClass();         System.out.println("比較結果=========");         System.out.println("MyClass.class和Class.forName內存地址比較是否相同:" + (class1 == class2));         System.out.println("MyClass.class和new MyClass().getClass內存地址比較是否相同:" + (class1 == class3));         System.out.println("Class.forName和new MyClass().getClass內存地址比較是否相同:" + (class2 == class3));     }}

逐個執行單元,得出測試結果為:

* test1()方法 一、MyClass.class方式=========*  test2()方法 二、Class.forName方式=========靜態代碼塊:staticStr=Hi,staticInt=2021*  test3()方法 三、new MyClass().getClass方式=========靜態代碼塊:staticStr=Hi,staticInt=2021動態代碼塊~無參構造方法~*  test12()方法 一、MyClass.class方式=========二、Class.forName方式=========靜態代碼塊:staticStr=Hi,staticInt=2021*  test13()方法 一、MyClass.class方式=========三、new MyClass().getClass方式=========靜態代碼塊:staticStr=Hi,staticInt=2021動態代碼塊~無參構造方法~*  test23()方法 二、Class.forName方式=========靜態代碼塊:staticStr=Hi,staticInt=2021三、new MyClass().getClass方式=========動態代碼塊~無參構造方法~*  test()方法 四、三種方式內存地址比較=========靜態代碼塊:staticStr=Hi,staticInt=2021動態代碼塊~無參構造方法~比較結果=========MyClass.class和Class.forName內存地址比較是否相同:trueMyClass.class和new MyClass().getClass內存地址比較是否相同:trueClass.forName和new MyClass().getClass內存地址比較是否相同:true

通過test1、test2、test3的測試結果驗證了2.1 三種方式及區別中黃色標記部分的區別說明,即:

  • MyClass.class不會做任何類的初始化工作
  • Class.forName會進行類的靜態初始化工作
  • new MyClass().getClass靜態初始化和非靜態初始化工作都會進行
  • 使用這三種方式任意一種最終在JVM加載到內存中都會是內存地址相同

test23組合得到的測試結果,說明靜態代碼塊只會被加載一次~

講了這么多,除了知道基本原理和基本使用之外,更重要的還是要知道它的一些比較實際的應用場景,往下介紹~

三、Java反射機制的應用場景有哪些?

詳細解析Java反射機制原理和幾種Class獲取方式

3.1 應用場景

  • 工廠模式中的簡單工廠模式優化
  • 代理模式中的動態代理方式實現
  • Java JDBC數據庫操作

3.2 簡單工廠模式優化

3.2.1 什么是簡單工廠模式?

Java中主要有23種設計模式,其中工廠模式就是其中一種,而簡單工廠模式,顧名思義,也是屬于工廠模式中的一種,只不過比較簡單。簡單工廠模式也可以叫做靜態方法模式(因為工廠類一般都是在內部定義了一個靜態方法)。
從現實生活角度來理解的話,工廠是專門負責生產產品的,同樣在設計模式中,簡單工廠模式我們可以理解為專門負責生產對象的一個類,稱為“工廠類”。

3.2.2 簡單工廠模式有什么用?

簡單工廠模式通過創建一個對應的工廠類,將類實例化的操作使用對象的操作進行分開,讓使用者不用知道具體參數就可以實例化出所需要的具體產品類,從而避免了在客戶端代碼中顯式指定,實現了解耦。即使用者可直接消費產品而不需要知道其生產的細節~

3.2.3 如何實現簡單工程模式?

實現簡單工程模式的核心是創建一個工廠類,并且在內部定義了一個靜態方法,傳入不同的參數標識通過switch進行分組,通過new實例化創建不同的子類對象返回~

實現例子:

步驟1:創建抽象產品類

public interface Product {     public abstract void show();}

步驟2:創建具體產品類:

public class ProductA implements Product {     @Override     public void show() {         System.out.println("生產了產品A");     }}public class ProductB implements Product {     @Override     public void show() {         System.out.println("生產了產品B");     }}public class ProductC implements Product {     @Override     public void show() {         System.out.println("生產了產品C");     }}

步驟3:創建簡單工廠類

public class SimpleFactory {     /**      * 實現簡單工廠模式      * @param pName 產品標識      * @return 返回具體的產品      */     public static Product createProduct(String pName){         switch (pName){             case "A":                 return new ProductA();             case "B":                 return new ProductB();             case "C":                 return new ProductC();             default:                 return null;         }     }}

步驟4:調用簡單工廠類

public class SimpleFactoryTest {     public static void main(String[] args) {         try {             SimpleFactory.createProduct("A").show();         } catch (NullPointerException e) {             System.out.println("沒有A這款產品,無法生產~");         }         try {             SimpleFactory.createProduct("B").show();         } catch (NullPointerException e) {             System.out.println("沒有B這款產品,無法生產~");         }         try {             SimpleFactory.createProduct("C").show();         } catch (NullPointerException e) {             System.out.println("沒有C這款產品,無法生產~");         }         try {             SimpleFactory.createProduct("D").show();         } catch (NullPointerException e) {             System.out.println("沒有D這款產品,無法生產~");         }     }}

3.2.4 簡單工廠模式優化

(1)簡單工廠模式弊端

  • 操作成本高:每增加一個接口的子類,必須修改工廠類的邏輯
  • 系統復雜性提高:每增加一個接口的子類,都必須向工廠類添加邏輯

這兩點弊端從前面的例子SimpleFactory工廠類的實現,可以看出簡單工廠模式中對工廠類SimpleFactory的維護成本有點大,因為實際中可能會很頻繁的去更新具體產品類,每一次變更都需要去修改工廠類,此時就可以利用Java反射機制對簡單工廠模式進行優化~

(2)簡單工廠模式的優化思路
采用Java反射機制,通過傳入子類全局定名(包名+類名) 動態的創建不同的子類對象實例,從而使得在不增加產品接口子類和修改工廠類的邏輯的情況下還能實現了工廠類對子類實例對象的統一創建~

(3)簡單工廠模式的優化步驟
步驟1:創建工廠類
采用Java反射機制對工廠類進行優化,主要是將className子類全局定名(包名+類名)作為入參,通過Class.forName方式獲取類的java.lang.Class實例對象,再通過Class實例對象的getInstance方法獲取到具體子類的實例對象~

public class Factory {     public static Product getInstance(String className) {         Product realProduct = null;         try {             Class pClass = Class.forName(className);             realProduct = (Product) pClass.newInstance();         } catch (Exception e) {             e.printStackTrace();         }         return realProduct;     }}

步驟2:調用工廠類

public class FactoryTest {     public static void main(String[] args) {         try {             Product productA = Factory.getInstance("com.justin.java.lang.ProductA");             productA.show();         } catch (NullPointerException e) {             System.out.println("沒有A這款產品,無法生產~");         }          try {             Product productB = Factory.getInstance("com.justin.java.lang.ProductB");             productB.show();         } catch (NullPointerException e) {             System.out.println("沒有B這款產品,無法生產~");         }          try {             Product productC = Factory.getInstance("com.justin.java.lang.ProductC");             productC.show();         } catch (NullPointerException e) {             System.out.println("沒有C這款產品,無法生產~");         }          try {             Product productD = Factory.getInstance("com.justin.java.lang.ProductD");             productD.show();         } catch (Exception e) {             System.out.println("沒有D這款產品,無法生產~");         }       }}

優化結果:

使用Java反射機制優化簡單工廠模式后,可以看到,不論具體產品類更新多頻繁,都不需要再修改工廠類,從而解決了普通簡單工廠模式操作成本高系統復雜性高的問題~

3.2.5 簡單工廠模式再次優化

(1)再次優化背景

簡單工廠模式的工廠類采用Java反射機制進行優化后,此時的仍然存在這樣一個問題,子類的全局定名(包名+類名)是寫死的,但是實際上開發者在寫代碼時是很難提前預知所有的子類的全局定名(包名+類名)的,因此需要進行二次優化~

(2)再次優化實現思路

通過配置文件方式,統一定義類名對應全局定名(包名+類名),將配置文件存放到資源目錄下,程序運行時通過ClassLoader類加載器動態獲取到配置文件中定義的子類的全局定名~

(3)再次優化實現步驟

再次優化步驟1:相關優化與第一次優化保持不變~

再次優化步驟2:配置類名對應全局定名(包名+類名)
創建屬性配置文件Product.properties

//產品抽象類Product相關子類的全局定名(包名+類名)定義ProductA = com.justin.java.lang.ProductAProductB = com.justin.java.lang.ProductBProductC = com.justin.java.lang.ProductC

注意:將Product.properties需要存放在src/main/resources資源目錄下,若資源目錄不存在則需要手動創建~

再次優化步驟3:修改調用工廠類

public class FactoryTest {     @Test     public void test() throws IOException {         ClassLoader classLoader = this.getClass().getClassLoader();         Properties prop = new Properties();         prop.load(classLoader.getResourceAsStream("Product.properties"));          String className = "";         try {             className = prop.getProperty("ProductA");             Product productA = Factory.getInstance(className);             productA.show();         } catch (NullPointerException e) {             System.out.println("沒有A這款產品,無法生產~");         }          try {             className = prop.getProperty("ProductB");             Product productA = Factory.getInstance(className);             productA.show();         } catch (NullPointerException e) {             System.out.println("沒有B這款產品,無法生產~");         }          try {             className = prop.getProperty("ProductC");             Product productA = Factory.getInstance(className);             productA.show();         } catch (NullPointerException e) {             System.out.println("沒有C這款產品,無法生產~");         }     }}

運行結果:

生產了產品A生產了產品B生產了產品C

3.3 代理模式中的動態代理實現

詳細解析Java反射機制原理和幾種Class獲取方式

3.3.1 什么是代理模式?

代理(Proxy)模式是一種設計模式,通過代理對象來訪問目標對象,還可以在不修改目標對象的情況下,對代理對象進行拓展,增強目標對象的功能~

什么?還是不太理解?

更通俗一點的說代理模式,就是想做某件事(買火車票),自己能買(直接去火車站買),卻委托別人去買(沒空還是代理點買吧),還可以讓別人幫自己做其他事(訂好酒店)~

詳細解析Java反射機制原理和幾種Class獲取方式

代理模式又分為靜態代理、動態代理,往下介紹~

3.3.2 什么是靜態代理?

(1)靜態代理屬于代理模式的一種代理方式,需要代理對象目標對象實現相同的接口
(2)靜態代理的代理類是由程序員編寫源碼,編譯后即可獲取到代理類的class字節碼文件,也就是在程序運行前就已經得到實際的代理類class字節碼文件了

3.3.2 什么是動態代理?

動態代理

(1)動態代理也屬于代理模式的一種代理方式,不過只需要目標對象實現接口,代理對象不需要實現接口~
(2)動態代理的代理類編譯后是沒有class字節碼文件的,而是在運行時利用Java反射機制動態的生成代理類的class字節碼文件~

動態代理最常用的是JDK原生動態代理cglib動態代理,往下介紹~

JDK 原生動態代理

JDK 原生動態代理,主要利用了JDK API
java.lang.reflect.Proxyjava.lang.relfect.InnvocationHandler 這兩個類來實現~

通過java.lang.reflect.Proxy代理類的newProxyInstance方法,傳遞3個參數,分別是:
目標對象的加載器 通過MyClass.getClass().getClassLoader方式獲取
目標對象的實現接口類型 通過Object.getClass().getInterfaces()方式獲取
InnvocationHandler事件處理器 通過new實例化對象并重寫invoke方法方式獲取

例子:

用戶接口類IUserDao

public interface IUserDao {     //添加數據     public void insert();}

目標對象類UserDao

/**  * @program: DataStructures  * @description:  * @author: JustinQin  * @create: 2021/8/23 23:32  * @version: v1.0.0  **/public class UserDao implements IUserDao{      @Override     public void insert() {         System.out.println("添加數據");     }}

動態代理類UserProxy

/**  * @program: Jdk1.8Test  * @description: 動態代理類  * @author: JustinQin  * @create: 2021/8/23 23:31  * @version: v1.0.0  **/public class UserProxy {     private Object target; //目標對象      public UserProxy(Object target) {         this.target = target;     }      /**      * 利用JDK API獲取到代理對象      * @return      */     public Object getProxyInstance() {         //目標對象的加載器         ClassLoader loader = target.getClass().getClassLoader();          //目標對象的實現接口類型         Class<?>[] interfaces = target.getClass().getInterfaces();          //InnvocationHandler事件處理器實例對象         InvocationHandler h = new InvocationHandler() {             @Override             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                 System.out.println("添加數據前:手動開啟事務");                 // 執行目標對象方法                 Object value = method.invoke(target, args);                 System.out.println("添加數據后:手動提交事務");                 return null;             }         };         //傳入3個參數,創建代理類的實例對象,并返回         return Proxy.newProxyInstance(loader, interfaces,h);     }}

動態代理單元測試類

/**  * @program: 動態代理單元測試類  * @description:  * @author: JustinQin  * @create: 2021/8/23 23:42  * @version: v1.0.0  **/public class UserProxyTest {     @Test     public void test() {         IUserDao target = new UserDao();         System.out.println("目標對象信息:" + target.getClass());         //獲取代理類實例對象         IUserDao proxy = (IUserDao) new UserProxy(target).getProxyInstance();         System.out.println("代理對象信息:" + proxy.getClass());         //執行代理方法         proxy.insert();     }}

單元測試執行結果

目標對象信息:class com.justin.java.reflect.UserDao代理對象信息:class com.sun.proxy.$Proxy2添加數據前:手動開啟事務 添加數據 添加數據后:手動提交事務

cglib動態代理

cglib (Code Generation Library )是一個第三方代碼生成類庫,運行時在內存中動態生成一個子類對象從而實現對目標對象功能的擴展。

Spring AOP結合了cglib動態代理JDK原生動態代理來實現,這里不過多介紹,有興趣小伙伴可以查閱資料學習下~

3.3.3 動態代理中如何利用Java反射機制?

JDK原生動態代理中,獲取代理示例對象過程中,獲取目標對象的類加載器,通過target.getClass().getClassLoader(獲取到目標對象的類加載器,target.getClass()方式獲取目標對象的Class實例對象使用的就是Java反射機制來實現的~

3.4 Java JDBC數據庫操作實現

3.4.1 利用反射加載JDBC驅動

相信很多小伙伴都知道Java JDBC連接數據庫主要分為七大步驟,其中第一步加載JDBC驅動,利用Java反射機制通過傳入不同的驅動名稱,加載不同數據庫的驅動~

Class.forName("com.mysql.jdbc.Driver");  //加載MySQL驅動Class.forName("oracle.jdbc.driver.OracleDriver");  //加載Oracle驅動

3.4.2 Java JDBC連接示例

創建測試庫表及數據

create DATABASE test;-- DROP TABLE IF EXISTS test.user;create table test.user(id int(7) primary key not null auto_increment,name varchar(255),sex char(1),age int(3))ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;insert into TEST.user(name,sex,age) values('張一','男',21);insert into TEST.user(name,sex,age) values('張二','女',22);insert into TEST.user(name,sex,age) values('張三','男',23);

Java MySQL JDBC連接七大步驟~

    public static void main(String[] args) throws ClassNotFoundException, SQLException {         //1.加載JDBC驅動         Class.forName("com.mysql.jdbc.Driver");         //2.獲取數據庫的連接(Connection)對象         Connection connection = DriverManager.getConnection(                 "jdbc:mysql://localhost/test", //mysql連接url,test表示你要連接的數據庫名                 "root", //數據庫用戶名                 "abc@123456"); //密碼         //3.獲取數據庫的操作(PrepareStatement)對象         PreparedStatement prepareStatement = connection.prepareStatement("select * from TEST.user where id = ?");         //4.設置傳入參數         prepareStatement.setInt(1, 1);         //5.上傳sql語句到服務器執行(excute),并返回結果集(ResultSet)         ResultSet result = prepareStatement.executeQuery();         //6.處理返回的ResultSet結果集         while (result.next()) {             System.out.print(result.getInt("id") + ",");             System.out.print(result.getString("name") + ",");             System.out.print(result.getString("sex") + ",");             System.out.print(result.getInt("age"));             System.out.print("n");         }         //7.釋放相關資源:Connection對象、PrepareStatement對象、ResultSet對象。         connection.close();         prepareStatement.close();         result.close();     }

執行結果:

1,張一,男,21

Java Oracle JDBC連接七大步驟~

public class JdbcOracleTest {     public static void main(String[] args) throws ClassNotFoundException, SQLException {         //1.加載JDBC驅動         Class.forName("oracle.jdbc.driver.OracleDriver");         //2.獲取數據庫的連接(Connection)對象         Connection connection = DriverManager.getConnection(                 "jdbc:oracle:thin:@127.0.0.1:1521:orcl",	//oracle連接url                 "root", //數據庫用戶名                 "abc@123456"); //密碼         //3.獲取數據庫的操作(PrepareStatement)對象         PreparedStatement prepareStatement = connection.prepareStatement("select * from TEST.user where id = ?");         //4.設置傳入參數         prepareStatement.setInt(1, 1);         //5.上傳sql語句到服務器執行(excute),并返回結果集(ResultSet)         ResultSet result = prepareStatement.executeQuery();         //6.處理返回的ResultSet結果集         while (result.next()) {             System.out.print(result.getInt("id")+",");             System.out.print(result.getString("name")+",");             System.out.print(result.getString("sex")+",");             System.out.print(result.getInt("age"));             System.out.print("n");         }         //7.釋放相關資源:Connection對象、PrepareStatement對象、ResultSet對象。         connection.close();         prepareStatement.close();         result.close();     }}

PS:上面通過Java JDBC連接數據庫并進行操作,這里的連接是單一連接,直接通過DriverManager.getConnection這種Java原生的數據庫連接方式建立的連接,現在實際的Java Spring項目當中,都是通過配置mybatis的數據庫連接池來實現的,不過原理都是一樣的,加載驅動也是利用了Java反射機制指定不同的驅動名稱,實現不同數據庫驅動的加載~

數據庫連接池配置spring-mybatis.xml

	<!-- 基于tomcat jdbc連接池的數據源  -->     <bean id="dataSource" class="com.justin.datasource.TomcatDataSource" init-method="createPool"> 		<!-- 基于dbcp連接池的數據源 		<bean id="dataSource" class="com.justin.datasource.DbcpDataSource" destroy-method="close"> --> 		<!-- 基于阿里druid連接池的數據源         <bean id="dataSource" class="com.justin.datasource.DruidDataSource" destroy-method="close"> -->  		<property name="driverClassName" value="${app-data-source.driverClassName}" /> 		<property name="url" value="${app-data-source.url}" /> 		<property name="username" value="${app-data-source.username}" /> 		<property name="password" value="${app-data-source.password}" /> 		<!-- 初始化連接大小 --> 		<property name="initialSize" value="${app-data-source.initialSize}" /> 		<!-- 連接池最大數量 --> 		<property name="maxActive" value="${app-data-source.maxActive}" /> 		<!-- 連接池最大空閑 --> 		<property name="maxIdle" value="${app-data-source.maxIdle}" /> 		<!-- 連接池最小空閑 --> 		<property name="minIdle" value="${app-data-source.minIdle}" /> 		<!-- 獲取連接最大等待時間 --> 		<property name="maxWait" value="${app-data-source.maxWait}" /> 	</bean>

數據庫配置信息jdbc.propertis

#數據庫連接驅動 app-data-source.driverClassName=com.mysql.jdbc.Driver#數據庫連接url app-data-source.url=jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=UTF-8#數據庫用戶 app-data-source.username=root #數據庫用戶密碼(加密)app-data-source.password=abc@123456#連接池初始化大小 app-data-source.initialSize=10#連接池最大數量 app-data-source.maxActive=50#連接池最大空閑 app-data-source.maxIdle=20#連接池最小空閑 app-data-source.minIdle=5#獲取連接最大等待時間 app-data-source.maxWait=30000

面試總結

一、Java反射機制是什么?

1、Java反射機制(Java Reflection)是Java語言中一種動態(運行時)訪問、檢測 & 修改它本身的能力,主要作用是動態(運行時)獲取類的完整結構信息 & 調用對象的方法~
更簡單點的說就是Java程序在運行時(動態)通過創建一個類的反射對象,再對類進行相關操作,比如:

  • 獲取該對象的成員變量 & 賦值
  • 調用該對象的方法(含構造方法,有參/無參)
  • 判斷該對象所屬的類

2、更通俗點的說,我們使用某個類,都會知道這個類,以及要用它來做什么,可以直接通過new實例化創建對象,然后使用這個對象對類進行操作,這個就屬于正射~

3、而反射則是一開始并不知道要初始化的是什么類,無法使用new來實例化創建對象,主要是通過JDK提供的反射API來實現,在運行時才知道要操作的是什么類,并且可以獲取到類的完整構造以及調用對應的方法,這就是反射~

二、Java反射機制中獲取Class的三種方式及區別?

1、獲取類的java.lang.Class實例對象,常見的三種方式分別為:

  • 通過MyClass.class獲取
  • 通過Class.forName("類的全局定名")獲取
  • 通過new MyClass().getClass()獲取

2、通過MyClass.class獲取,JVM會使用ClassLoader類加載器將類加載到內存中,但并不會做任何類的初始化工作,返回java.lang.Class對象

3、通過Class.forName("類的全局定名")獲取,同樣,類會被JVM加載到內存中,并且會進行類的靜態初始化工作,返回java.lang.Class對象

4、通過new MyClass().getClass()獲取,這種方式使用了new進行實例化操作,因此== 靜態初始化和非靜態初始化工作都會進行 == ,getClass方法屬于頂級Object類中的方法,任何子類對象都可以調用,哪個子類調用,就返回那個子類的java.lang.Class對象

5、這3種方式,最終在JVM堆區對應類的java.lang.Class對象都屬于同一個,也就是內存地址相同,進行==雙等號比較結果為true,原因是JVM類加載過程中使用的是同一個ClassLoader類加載器加載某個類,不論加載多少次,生成到堆區的java.lang.Class對象始終只有一個,除非自定義類加載器,破壞JVM的雙親委派機制,使得同一個類被不同類加載器加載,JVM才會把它當做兩個不同的java.lang.Class對象

三、Java反射機制的應用場景有哪些?

  • 工廠模式中的簡單工廠模式優化
  • 代理模式中的動態代理方式實現
  • Java JDBC數據庫操作

推薦學習:《java教程》

贊(0)
分享到: 更多 (0)
網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
久久久久久国产精品无码超碰| 中文国产成人久久精品小说| 在线精品免费视频无码的| 97精品视频在线观看| 久久精品视频免费播放| 日本精品啪啪一区二区三区| 日韩一区二区免费视频| 日韩一区二区三区精品| 国产精品蜜芽tv在线观看| MM1313亚洲国产精品| 久9视频这里只有精品8| 亚洲精品成a人在线观看夫| 国产美女在线精品观看| 国产精品成人观看视频国产| 精品99在线观看| 亚洲欧美日韩中文字幕在线一区| 亚洲国产精品18久久久久久| 久久精品中文字幕大胸| 99视频全部免费精品全部四虎| 2022国产成人精品福利网站| 久久精品国产亚洲精品2020| 蜜臀久久99精品久久久久久小说| 亚洲国产精品无码专区| 久草视频在线这里精品| 亚洲AV无码乱码精品国产| 亚洲系列国产精品制服丝袜第| 99re在线精品视频| 2022国产精品手机在线观看| 2020国产精品视频| 精品国产一区二区三区麻豆| 精品国产AV一区二区三区| 精品一区二区视频在线观看| 日韩精品久久久久久久电影| 亚洲精品永久在线观看| 亚洲日韩VA无码中文字幕| 久久91这里精品国产2020| 无码A级毛片日韩精品| 天天视频国产精品| 国产精品亚洲高清一区二区| 三上悠亚日韩精品| 99麻豆久久久国产精品免费|