這篇文章主要講解了“Java反射機(jī)制原理和Class獲取方式是什么”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Java反射機(jī)制原理和Class獲取方式是什么”吧!
員工經(jīng)過長(zhǎng)期磨合與沉淀,具備了協(xié)作精神,得以通過團(tuán)隊(duì)的力量開發(fā)出優(yōu)質(zhì)的產(chǎn)品。創(chuàng)新互聯(lián)公司堅(jiān)持“專注、創(chuàng)新、易用”的產(chǎn)品理念,因?yàn)椤皩W⑺詫I(yè)、創(chuàng)新互聯(lián)網(wǎng)站所以易用所以簡(jiǎn)單”。公司專注于為企業(yè)提供做網(wǎng)站、網(wǎng)站建設(shè)、微信公眾號(hào)開發(fā)、電商網(wǎng)站開發(fā),微信小程序,軟件定制制作等一站式互聯(lián)網(wǎng)企業(yè)服務(wù)。
(1)Java反射機(jī)制(Java Reflection
)是Java語言中一種動(dòng)態(tài)(運(yùn)行時(shí))訪問、檢測(cè) & 修改它本身
的能力,主要作用是動(dòng)態(tài)(運(yùn)行時(shí))獲取類的完整結(jié)構(gòu)信息 & 調(diào)用對(duì)象的方法
~
更簡(jiǎn)單點(diǎn)的說就是Java程序在運(yùn)行時(shí)(動(dòng)態(tài))通過創(chuàng)建一個(gè)類的反射對(duì)象,再對(duì)類進(jìn)行相關(guān)操作,比如:
獲取該對(duì)象的成員變量 & 賦值
調(diào)用該對(duì)象的方法(含構(gòu)造方法,有參/無參)
判斷該對(duì)象所屬的類
PS:不過說實(shí)話,直接看比較官方的定義還是有點(diǎn)難理解,再來更加通俗點(diǎn)的說吧~
(2)一般情況下,我們使用某個(gè)類,都會(huì)知道這個(gè)類,以及要用它來做什么,可以直接通過new
實(shí)例化創(chuàng)建對(duì)象,然后使用這個(gè)對(duì)象對(duì)類進(jìn)行操作,這個(gè)就屬于正射
~
(3)而反射
則是一開始并不知道要初始化的是什么類,無法使用new
來實(shí)例化創(chuàng)建對(duì)象,主要是通過JDK提供的反射API來實(shí)現(xiàn),在運(yùn)行時(shí)才知道要操作的是什么類,并且可以獲取到類的完整構(gòu)造以及調(diào)用對(duì)應(yīng)的方法,這就是反射
~
代碼如下:
package com.justin.java.lang;import java.lang.reflect.Constructor;import java.lang.reflect.Method;/** * @program: Jdk1.8 Test * @description: 正射、反射簡(jiǎn)單調(diào)用示例 * @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{ //一、正射調(diào)用過程 Student student = new Student(); student.setId(1); System.out.println("正射調(diào)用過程Student id:" + student.getId()); //二、反射調(diào)用過程 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("正射調(diào)用過程Student id:" + getIdMethod.invoke(studentObj)); }}
輸出結(jié)果:
正射調(diào)用過程Student id:1反射調(diào)用過程Student id:2
上述例子反射的調(diào)用過程,可以看到獲取一個(gè)類的反射對(duì)象
,主要過程為:
獲取類的Class
實(shí)例對(duì)象
根據(jù)Class
實(shí)例對(duì)象獲取Constructor
對(duì)象
再根據(jù)Constructor
對(duì)象的newInstance
方法獲取到類的反射對(duì)象
獲取到類的反射對(duì)象
后,就可以對(duì)類進(jìn)行操作了~ 例如,上述示例中對(duì)類的方法進(jìn)行調(diào)用過程為:
根據(jù)Class
實(shí)例對(duì)象獲取到類的Method
對(duì)象
再根據(jù)Method
對(duì)象的invoke
方法調(diào)用到具體類的方法
前面一點(diǎn)也提到了獲取到類的Class
實(shí)例對(duì)象,上面示例反向調(diào)用過程
中我們是通過Class.forName("類的全局定名")
這種方式來獲取到類的Class
實(shí)例對(duì)象,除了這種,常用的還有其他兩種,往下講解~
(1)獲取類的java.lang.Class
實(shí)例對(duì)象,常見的三種方式分別為:
通過MyClass.class
獲取,這里的MyClass指具體類~~
通過Class.forName("類的全局定名")
獲取,全局定名為包名+類名
通過new MyClass().getClass()
獲取,這里的MyClass指具體類~
(2)通過MyClass.class
獲取,JVM會(huì)使用ClassLoader
類加載器將類加載到內(nèi)存中,但并不會(huì)做任何類的初始化工作,返回java.lang.Class
對(duì)象
(3)通過Class.forName("類的全局定名")
獲取,同樣,類會(huì)被JVM加載到內(nèi)存中,并且會(huì)進(jìn)行類的靜態(tài)初始化工作,返回java.lang.Class
對(duì)象
(4)通過new MyClass().getClass()
獲取,這種方式使用了new
進(jìn)行實(shí)例化操作,因此靜態(tài)初始化和非靜態(tài)初始化工作都會(huì)進(jìn)行,getClass
方法屬于頂級(jí)Object
類中的方法,任何子類對(duì)象都可以調(diào)用,哪個(gè)子類調(diào)用,就返回那個(gè)子類的java.lang.Class
對(duì)象
PS:這3種方式,最終在JVM堆區(qū)對(duì)應(yīng)類的
java.lang.Class
對(duì)象都屬于同一個(gè),也就是內(nèi)存地址相同,進(jìn)行==
雙等號(hào)比較結(jié)果為true
,原因是JVM類加載過程中使用的是同一個(gè)ClassLoader
類加載器加載某個(gè)類,不論加載多少次,生成到堆區(qū)的java.lang.Class
對(duì)象始終只有一個(gè),除非自定義類加載器,破壞JVM的雙親委派機(jī)制,使得同一個(gè)類被不同類加載器加載,JVM才會(huì)把它當(dāng)做兩個(gè)不同的java.lang.Class
對(duì)象
創(chuàng)建一個(gè)實(shí)體類,分別在實(shí)體類中創(chuàng)建類的靜態(tài)代碼塊
、動(dòng)態(tài)代碼塊
、有參構(gòu)造方法
、無參構(gòu)造方法
,方便測(cè)試幾種方式的區(qū)別及內(nèi)存地址是否相同~
(1)實(shí)體類:
public class MyClass { private static final String staticStr = "Hi"; private static int staticInt = 2021; private String id; static { System.out.println("靜態(tài)代碼塊:staticStr=" + staticStr + ",staticInt=" + staticInt); } { System.out.println("動(dòng)態(tài)代碼塊~"); } public MyClass() { System.out.println("無參構(gòu)造方法~"); } public MyClass(String id) { System.out.println("有參構(gòu)造方法~"); 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)單元測(cè)試類:
通過@Test
注解對(duì)三種方式分別進(jìn)行單元測(cè)試,再對(duì)這三種方式的組合進(jìn)行單元測(cè)試~
package com.justin.java.lang;import org.junit.Test;/** * @program: Jdk1.8Test * @description: Java反射機(jī)制中獲取類的Class實(shí)例對(duì)象的常見三種方式及區(qū)別對(duì)比 * @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("四、三種方式內(nèi)存地址比較========="); Class<?> class1 = MyClass.class; Class class2 = Class.forName("com.justin.java.lang.MyClass"); Class class3 = new MyClass().getClass(); System.out.println("比較結(jié)果========="); System.out.println("MyClass.class和Class.forName內(nèi)存地址比較是否相同:" + (class1 == class2)); System.out.println("MyClass.class和new MyClass().getClass內(nèi)存地址比較是否相同:" + (class1 == class3)); System.out.println("Class.forName和new MyClass().getClass內(nèi)存地址比較是否相同:" + (class2 == class3)); }}
逐個(gè)執(zhí)行單元,得出測(cè)試結(jié)果為:
* test1()方法 一、MyClass.class方式=========* test2()方法 二、Class.forName方式=========靜態(tài)代碼塊:staticStr=Hi,staticInt=2021* test3()方法 三、new MyClass().getClass方式=========靜態(tài)代碼塊:staticStr=Hi,staticInt=2021動(dòng)態(tài)代碼塊~無參構(gòu)造方法~* test12()方法 一、MyClass.class方式=========二、Class.forName方式=========靜態(tài)代碼塊:staticStr=Hi,staticInt=2021* test13()方法 一、MyClass.class方式=========三、new MyClass().getClass方式=========靜態(tài)代碼塊:staticStr=Hi,staticInt=2021動(dòng)態(tài)代碼塊~無參構(gòu)造方法~* test23()方法 二、Class.forName方式=========靜態(tài)代碼塊:staticStr=Hi,staticInt=2021三、new MyClass().getClass方式=========動(dòng)態(tài)代碼塊~無參構(gòu)造方法~* test()方法 四、三種方式內(nèi)存地址比較=========靜態(tài)代碼塊:staticStr=Hi,staticInt=2021動(dòng)態(tài)代碼塊~無參構(gòu)造方法~比較結(jié)果=========MyClass.class和Class.forName內(nèi)存地址比較是否相同:trueMyClass.class和new MyClass().getClass內(nèi)存地址比較是否相同:trueClass.forName和new MyClass().getClass內(nèi)存地址比較是否相同:true
通過test1
、test2
、test3
的測(cè)試結(jié)果驗(yàn)證了2.1 三種方式及區(qū)別
中黃色標(biāo)記部分的區(qū)別說明,即:
MyClass.class
不會(huì)做任何類的初始化工作
Class.forName
會(huì)進(jìn)行類的靜態(tài)初始化工作
new MyClass().getClass
靜態(tài)初始化和非靜態(tài)初始化工作都會(huì)進(jìn)行
使用這三種方式任意一種最終在JVM加載到內(nèi)存中都會(huì)是內(nèi)存地址相同
的
而test23
組合得到的測(cè)試結(jié)果,說明靜態(tài)代碼塊只會(huì)被加載一次
~
講了這么多,除了知道基本原理和基本使用之外,更重要的還是要知道它的一些比較實(shí)際的應(yīng)用場(chǎng)景
,往下介紹~
工廠模式
中的簡(jiǎn)單工廠模式優(yōu)化
代理模式
中的動(dòng)態(tài)代理方式實(shí)現(xiàn)
Java JDBC
數(shù)據(jù)庫(kù)操作
Java中主要有23種設(shè)計(jì)模式,其中工廠模式就是其中一種,而簡(jiǎn)單工廠模式,顧名思義,也是屬于工廠模式中的一種,只不過比較簡(jiǎn)單。簡(jiǎn)單工廠模式也可以叫做靜態(tài)方法模式(因?yàn)楣S類一般都是在內(nèi)部定義了一個(gè)靜態(tài)方法)。
從現(xiàn)實(shí)生活角度來理解的話,工廠是專門負(fù)責(zé)生產(chǎn)產(chǎn)品的,同樣在設(shè)計(jì)模式中,簡(jiǎn)單工廠模式我們可以理解為專門負(fù)責(zé)生產(chǎn)對(duì)象的一個(gè)類,稱為“工廠類”。
簡(jiǎn)單工廠模式通過創(chuàng)建一個(gè)對(duì)應(yīng)的工廠類,將
類實(shí)例化的操作
與使用對(duì)象的操作
進(jìn)行分開,讓使用者不用知道具體參數(shù)就可以實(shí)例化出所需要的具體產(chǎn)品
類,從而避免了在客戶端代碼中顯式指定,實(shí)現(xiàn)了解耦。即使用者可直接消費(fèi)產(chǎn)品而不需要知道其生產(chǎn)的細(xì)節(jié)~
實(shí)現(xiàn)簡(jiǎn)單工程模式的核心是創(chuàng)建一個(gè)
工廠類
,并且在內(nèi)部定義了一個(gè)靜態(tài)方法,傳入不同的參數(shù)標(biāo)識(shí)
通過switch
進(jìn)行分組,通過new
實(shí)例化創(chuàng)建不同的子類對(duì)象返回~
實(shí)現(xiàn)例子:
步驟1:創(chuàng)建抽象產(chǎn)品類
public interface Product { public abstract void show();}
步驟2:創(chuàng)建具體產(chǎn)品類:
public class ProductA implements Product { @Override public void show() { System.out.println("生產(chǎn)了產(chǎn)品A"); }}public class ProductB implements Product { @Override public void show() { System.out.println("生產(chǎn)了產(chǎn)品B"); }}public class ProductC implements Product { @Override public void show() { System.out.println("生產(chǎn)了產(chǎn)品C"); }}
步驟3:創(chuàng)建簡(jiǎn)單工廠類
public class SimpleFactory { /** * 實(shí)現(xiàn)簡(jiǎn)單工廠模式 * @param pName 產(chǎn)品標(biāo)識(shí) * @return 返回具體的產(chǎn)品 */ 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:調(diào)用簡(jiǎn)單工廠類
public class SimpleFactoryTest { public static void main(String[] args) { try { SimpleFactory.createProduct("A").show(); } catch (NullPointerException e) { System.out.println("沒有A這款產(chǎn)品,無法生產(chǎn)~"); } try { SimpleFactory.createProduct("B").show(); } catch (NullPointerException e) { System.out.println("沒有B這款產(chǎn)品,無法生產(chǎn)~"); } try { SimpleFactory.createProduct("C").show(); } catch (NullPointerException e) { System.out.println("沒有C這款產(chǎn)品,無法生產(chǎn)~"); } try { SimpleFactory.createProduct("D").show(); } catch (NullPointerException e) { System.out.println("沒有D這款產(chǎn)品,無法生產(chǎn)~"); } }}
(1)簡(jiǎn)單工廠模式弊端
操作成本高:每增加一個(gè)接口的子類,必須修改工廠類的邏輯
系統(tǒng)復(fù)雜性提高:每增加一個(gè)接口的子類,都必須向工廠類添加邏輯
這兩點(diǎn)弊端從前面的例子SimpleFactory
工廠類的實(shí)現(xiàn),可以看出簡(jiǎn)單工廠模式
中對(duì)工廠類SimpleFactory
的維護(hù)成本有點(diǎn)大,因?yàn)閷?shí)際中可能會(huì)很頻繁的去更新具體產(chǎn)品類
,每一次變更都需要去修改工廠類,此時(shí)就可以利用Java反射機(jī)制
對(duì)簡(jiǎn)單工廠模式進(jìn)行優(yōu)化~
(2)簡(jiǎn)單工廠模式的優(yōu)化思路
采用Java反射機(jī)制,通過傳入子類全局定名(包名+類名)
動(dòng)態(tài)的創(chuàng)建不同的子類對(duì)象實(shí)例
,從而使得在不增加產(chǎn)品接口子類和修改工廠類的邏輯的情況下還能實(shí)現(xiàn)了工廠類對(duì)子類實(shí)例對(duì)象的統(tǒng)一創(chuàng)建~
(3)簡(jiǎn)單工廠模式的優(yōu)化步驟
步驟1:創(chuàng)建工廠類
采用Java反射機(jī)制對(duì)工廠類進(jìn)行優(yōu)化,主要是將className
即子類全局定名(包名+類名)
作為入?yún)?,通過Class.forName
方式獲取類的java.lang.Class
實(shí)例對(duì)象,再通過Class
實(shí)例對(duì)象的getInstance
方法獲取到具體子類的實(shí)例對(duì)象~
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:調(diào)用工廠類
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這款產(chǎn)品,無法生產(chǎn)~"); } try { Product productB = Factory.getInstance("com.justin.java.lang.ProductB"); productB.show(); } catch (NullPointerException e) { System.out.println("沒有B這款產(chǎn)品,無法生產(chǎn)~"); } try { Product productC = Factory.getInstance("com.justin.java.lang.ProductC"); productC.show(); } catch (NullPointerException e) { System.out.println("沒有C這款產(chǎn)品,無法生產(chǎn)~"); } try { Product productD = Factory.getInstance("com.justin.java.lang.ProductD"); productD.show(); } catch (Exception e) { System.out.println("沒有D這款產(chǎn)品,無法生產(chǎn)~"); } }}
優(yōu)化結(jié)果:
使用
Java反射機(jī)制
優(yōu)化簡(jiǎn)單工廠模式后,可以看到,不論具體產(chǎn)品類
更新多頻繁,都不需要再修改工廠類
,從而解決了普通簡(jiǎn)單工廠模式操作成本高
和系統(tǒng)復(fù)雜性高
的問題~
(1)再次優(yōu)化背景
簡(jiǎn)單工廠模式的工廠類采用
Java反射機(jī)制
進(jìn)行優(yōu)化后,此時(shí)的仍然存在這樣一個(gè)問題,子類的全局定名(包名+類名)
是寫死的,但是實(shí)際上開發(fā)者在寫代碼時(shí)是很難提前預(yù)知所有的子類的全局定名(包名+類名)
的,因此需要進(jìn)行二次優(yōu)化~
(2)再次優(yōu)化實(shí)現(xiàn)思路
通過
配置文件
方式,統(tǒng)一定義類名對(duì)應(yīng)全局定名(包名+類名)
,將配置文件存放到資源目錄下,程序運(yùn)行時(shí)通過ClassLoader
類加載器動(dòng)態(tài)獲取到配置文件
中定義的子類的全局定名~
(3)再次優(yōu)化實(shí)現(xiàn)步驟
再次優(yōu)化步驟1:相關(guān)優(yōu)化與第一次優(yōu)化保持不變~
再次優(yōu)化步驟2:配置類名對(duì)應(yīng)全局定名(包名+類名)
創(chuàng)建屬性配置文件Product.properties
//產(chǎn)品抽象類Product相關(guān)子類的全局定名(包名+類名)定義ProductA = com.justin.java.lang.ProductAProductB = com.justin.java.lang.ProductBProductC = com.justin.java.lang.ProductC
注意:將Product.properties
需要存放在src/main/resources
資源目錄下,若資源目錄不存在則需要手動(dòng)創(chuàng)建~
再次優(yōu)化步驟3:修改調(diào)用工廠類
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這款產(chǎn)品,無法生產(chǎn)~"); } try { className = prop.getProperty("ProductB"); Product productA = Factory.getInstance(className); productA.show(); } catch (NullPointerException e) { System.out.println("沒有B這款產(chǎn)品,無法生產(chǎn)~"); } try { className = prop.getProperty("ProductC"); Product productA = Factory.getInstance(className); productA.show(); } catch (NullPointerException e) { System.out.println("沒有C這款產(chǎn)品,無法生產(chǎn)~"); } }}
運(yùn)行結(jié)果:
生產(chǎn)了產(chǎn)品A生產(chǎn)了產(chǎn)品B生產(chǎn)了產(chǎn)品C
代理(Proxy)模式
是一種設(shè)計(jì)模式
,通過代理對(duì)象
來訪問目標(biāo)對(duì)象
,還可以在不修改目標(biāo)對(duì)象
的情況下,對(duì)代理對(duì)象
進(jìn)行拓展,增強(qiáng)目標(biāo)對(duì)象
的功能~
什么?還是不太理解?
更通俗一點(diǎn)的說代理模式,就是想做某件事(
買火車票
),自己
能買(直接去火車站
買),卻委托別人去買(沒空還是代理點(diǎn)
買吧),還可以讓別人幫自己做其他事(訂好酒店)~
代理模式又分為靜態(tài)代理、動(dòng)態(tài)代理,往下介紹~
(1)
靜態(tài)代理
屬于代理模式
的一種代理方式,需要代理對(duì)象
和目標(biāo)對(duì)象
實(shí)現(xiàn)相同的接口
(2)靜態(tài)代理
的代理類是由程序員編寫源碼,編譯后即可獲取到代理類的class字節(jié)碼文件,也就是在程序運(yùn)行前
就已經(jīng)得到實(shí)際的代理類class字節(jié)碼文件了
動(dòng)態(tài)代理
(1)
動(dòng)態(tài)代理
也屬于代理模式
的一種代理方式,不過只需要目標(biāo)對(duì)象
實(shí)現(xiàn)接口,代理對(duì)象
不需要實(shí)現(xiàn)接口~
(2)動(dòng)態(tài)代理
的代理類編譯后是沒有class字節(jié)碼文件的,而是在運(yùn)行時(shí)利用Java反射機(jī)制
動(dòng)態(tài)的生成代理類的class字節(jié)碼文件~
動(dòng)態(tài)代理最常用的是JDK原生動(dòng)態(tài)代理
和cglib動(dòng)態(tài)代理
,往下介紹~
JDK 原生動(dòng)態(tài)代理
JDK 原生動(dòng)態(tài)代理,主要利用了JDK API
的java.lang.reflect.Proxy
和java.lang.relfect.InnvocationHandler
這兩個(gè)類來實(shí)現(xiàn)~
通過java.lang.reflect.Proxy
代理類的newProxyInstance
方法,傳遞3個(gè)參數(shù),分別是:目標(biāo)對(duì)象的加載器
通過MyClass.getClass().getClassLoader
方式獲取目標(biāo)對(duì)象的實(shí)現(xiàn)接口類型
通過Object.getClass().getInterfaces()
方式獲取InnvocationHandler事件處理器
通過new
實(shí)例化對(duì)象并重寫invoke
方法方式獲取
例子:
用戶接口類IUserDao
public interface IUserDao { //添加數(shù)據(jù) public void insert();}
目標(biāo)對(duì)象類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("添加數(shù)據(jù)"); }}
動(dòng)態(tài)代理類UserProxy
/** * @program: Jdk1.8Test * @description: 動(dòng)態(tài)代理類 * @author: JustinQin * @create: 2021/8/23 23:31 * @version: v1.0.0 **/public class UserProxy { private Object target; //目標(biāo)對(duì)象 public UserProxy(Object target) { this.target = target; } /** * 利用JDK API獲取到代理對(duì)象 * @return */ public Object getProxyInstance() { //目標(biāo)對(duì)象的加載器 ClassLoader loader = target.getClass().getClassLoader(); //目標(biāo)對(duì)象的實(shí)現(xiàn)接口類型 Class<?>[] interfaces = target.getClass().getInterfaces(); //InnvocationHandler事件處理器實(shí)例對(duì)象 InvocationHandler h = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("添加數(shù)據(jù)前:手動(dòng)開啟事務(wù)"); // 執(zhí)行目標(biāo)對(duì)象方法 Object value = method.invoke(target, args); System.out.println("添加數(shù)據(jù)后:手動(dòng)提交事務(wù)"); return null; } }; //傳入3個(gè)參數(shù),創(chuàng)建代理類的實(shí)例對(duì)象,并返回 return Proxy.newProxyInstance(loader, interfaces,h); }}
動(dòng)態(tài)代理單元測(cè)試類
/** * @program: 動(dòng)態(tài)代理單元測(cè)試類 * @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("目標(biāo)對(duì)象信息:" + target.getClass()); //獲取代理類實(shí)例對(duì)象 IUserDao proxy = (IUserDao) new UserProxy(target).getProxyInstance(); System.out.println("代理對(duì)象信息:" + proxy.getClass()); //執(zhí)行代理方法 proxy.insert(); }}
單元測(cè)試執(zhí)行結(jié)果
目標(biāo)對(duì)象信息:class com.justin.java.reflect.UserDao代理對(duì)象信息:class com.sun.proxy.$Proxy2添加數(shù)據(jù)前:手動(dòng)開啟事務(wù) 添加數(shù)據(jù) 添加數(shù)據(jù)后:手動(dòng)提交事務(wù)
cglib動(dòng)態(tài)代理
cglib (Code Generation Library )
是一個(gè)第三方代碼生成類庫(kù),運(yùn)行時(shí)在內(nèi)存中動(dòng)態(tài)生成一個(gè)子類對(duì)象從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象功能的擴(kuò)展。
Spring AOP
結(jié)合了cglib動(dòng)態(tài)代理
和JDK原生動(dòng)態(tài)代理
來實(shí)現(xiàn),這里不過多介紹,有興趣小伙伴可以查閱資料學(xué)習(xí)下~
JDK原生動(dòng)態(tài)代理中,獲取代理示例對(duì)象過程中,獲取目標(biāo)對(duì)象的類加載器,通過
target.getClass().getClassLoader(
獲取到目標(biāo)對(duì)象的類加載器,target.getClass()
方式獲取目標(biāo)對(duì)象的Class實(shí)例對(duì)象使用的就是Java反射機(jī)制來實(shí)現(xiàn)的~
相信很多小伙伴都知道Java JDBC連接數(shù)據(jù)庫(kù)
主要分為七大步驟,其中第一步加載JDBC驅(qū)動(dòng)
,利用Java反射機(jī)制通過傳入不同的驅(qū)動(dòng)名稱,加載不同數(shù)據(jù)庫(kù)的驅(qū)動(dòng)~
Class.forName("com.MySQL.jdbc.Driver"); //加載MySQL驅(qū)動(dòng)Class.forName("oracle.jdbc.driver.OracleDriver"); //加載Oracle驅(qū)動(dòng)
創(chuàng)建測(cè)試庫(kù)表及數(shù)據(jù)
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驅(qū)動(dòng) Class.forName("com.mysql.jdbc.Driver"); //2.獲取數(shù)據(jù)庫(kù)的連接(Connection)對(duì)象 Connection connection = DriverManager.getConnection( "jdbc:mysql://localhost/test", //mysql連接url,test表示你要連接的數(shù)據(jù)庫(kù)名 "root", //數(shù)據(jù)庫(kù)用戶名 "abc@123456"); //密碼 //3.獲取數(shù)據(jù)庫(kù)的操作(PrepareStatement)對(duì)象 PreparedStatement prepareStatement = connection.prepareStatement("select * from TEST.user where id = ?"); //4.設(shè)置傳入?yún)?shù) prepareStatement.setInt(1, 1); //5.上傳sql語句到服務(wù)器執(zhí)行(excute),并返回結(jié)果集(ResultSet) ResultSet result = prepareStatement.executeQuery(); //6.處理返回的ResultSet結(jié)果集 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.釋放相關(guān)資源:Connection對(duì)象、PrepareStatement對(duì)象、ResultSet對(duì)象。 connection.close(); prepareStatement.close(); result.close(); }
執(zhí)行結(jié)果:
1,張一,男,21
Java Oracle JDBC連接七大步驟~
public class JdbcOracleTest { public static void main(String[] args) throws ClassNotFoundException, SQLException { //1.加載JDBC驅(qū)動(dòng) Class.forName("oracle.jdbc.driver.OracleDriver"); //2.獲取數(shù)據(jù)庫(kù)的連接(Connection)對(duì)象 Connection connection = DriverManager.getConnection( "jdbc:oracle:thin:@127.0.0.1:1521:orcl", //oracle連接url "root", //數(shù)據(jù)庫(kù)用戶名 "abc@123456"); //密碼 //3.獲取數(shù)據(jù)庫(kù)的操作(PrepareStatement)對(duì)象 PreparedStatement prepareStatement = connection.prepareStatement("select * from TEST.user where id = ?"); //4.設(shè)置傳入?yún)?shù) prepareStatement.setInt(1, 1); //5.上傳sql語句到服務(wù)器執(zhí)行(excute),并返回結(jié)果集(ResultSet) ResultSet result = prepareStatement.executeQuery(); //6.處理返回的ResultSet結(jié)果集 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.釋放相關(guān)資源:Connection對(duì)象、PrepareStatement對(duì)象、ResultSet對(duì)象。 connection.close(); prepareStatement.close(); result.close(); }}
PS:上面通過Java JDBC連接數(shù)據(jù)庫(kù)并進(jìn)行操作,這里的連接是單一連接,直接通過DriverManager.getConnection這種Java原生的數(shù)據(jù)庫(kù)連接方式建立的連接,現(xiàn)在實(shí)際的Java Spring項(xiàng)目當(dāng)中,都是通過配置mybatis的數(shù)據(jù)庫(kù)連接池來實(shí)現(xiàn)的,不過原理都是一樣的,加載驅(qū)動(dòng)也是利用了
Java反射機(jī)制
指定不同的驅(qū)動(dòng)名稱,實(shí)現(xiàn)不同數(shù)據(jù)庫(kù)驅(qū)動(dòng)的加載~
數(shù)據(jù)庫(kù)連接池配置spring-mybatis.xml
<!-- 基于tomcat jdbc連接池的數(shù)據(jù)源 --> <bean id="dataSource" class="com.justin.datasource.TomcatDataSource" init-method="createPool"> <!-- 基于dbcp連接池的數(shù)據(jù)源 <bean id="dataSource" class="com.justin.datasource.DbcpDataSource" destroy-method="close"> --> <!-- 基于阿里druid連接池的數(shù)據(jù)源 <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}" /> <!-- 連接池最大數(shù)量 --> <property name="maxActive" value="${app-data-source.maxActive}" /> <!-- 連接池最大空閑 --> <property name="maxIdle" value="${app-data-source.maxIdle}" /> <!-- 連接池最小空閑 --> <property name="minIdle" value="${app-data-source.minIdle}" /> <!-- 獲取連接最大等待時(shí)間 --> <property name="maxWait" value="${app-data-source.maxWait}" /> </bean>
數(shù)據(jù)庫(kù)配置信息jdbc.propertis
#數(shù)據(jù)庫(kù)連接驅(qū)動(dòng) app-data-source.driverClassName=com.mysql.jdbc.Driver#數(shù)據(jù)庫(kù)連接url app-data-source.url=jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=UTF-8#數(shù)據(jù)庫(kù)用戶 app-data-source.username=root #數(shù)據(jù)庫(kù)用戶密碼(加密)app-data-source.password=abc@123456#連接池初始化大小 app-data-source.initialSize=10#連接池最大數(shù)量 app-data-source.maxActive=50#連接池最大空閑 app-data-source.maxIdle=20#連接池最小空閑 app-data-source.minIdle=5#獲取連接最大等待時(shí)間 app-data-source.maxWait=30000
一、Java反射機(jī)制是什么?
1、Java反射機(jī)制(Java Reflection
)是Java語言中一種動(dòng)態(tài)(運(yùn)行時(shí))訪問、檢測(cè) & 修改它本身
的能力,主要作用是動(dòng)態(tài)(運(yùn)行時(shí))獲取類的完整結(jié)構(gòu)信息 & 調(diào)用對(duì)象的方法
~
更簡(jiǎn)單點(diǎn)的說就是Java程序在運(yùn)行時(shí)(動(dòng)態(tài))通過創(chuàng)建一個(gè)類的反射對(duì)象,再對(duì)類進(jìn)行相關(guān)操作,比如:
獲取該對(duì)象的成員變量 & 賦值
調(diào)用該對(duì)象的方法(含構(gòu)造方法,有參/無參)
判斷該對(duì)象所屬的類
2、更通俗點(diǎn)的說,我們使用某個(gè)類,都會(huì)知道這個(gè)類,以及要用它來做什么,可以直接通過new
實(shí)例化創(chuàng)建對(duì)象,然后使用這個(gè)對(duì)象對(duì)類進(jìn)行操作,這個(gè)就屬于正射
~
3、而反射
則是一開始并不知道要初始化的是什么類,無法使用new
來實(shí)例化創(chuàng)建對(duì)象,主要是通過JDK提供的反射API來實(shí)現(xiàn),在運(yùn)行時(shí)才知道要操作的是什么類,并且可以獲取到類的完整構(gòu)造以及調(diào)用對(duì)應(yīng)的方法,這就是反射
~
二、Java反射機(jī)制中獲取Class的三種方式及區(qū)別?
1、獲取類的java.lang.Class
實(shí)例對(duì)象,常見的三種方式分別為:
通過MyClass.class
獲取
通過Class.forName("類的全局定名")
獲取
通過new MyClass().getClass()
獲取
2、通過MyClass.class
獲取,JVM會(huì)使用ClassLoader
類加載器將類加載到內(nèi)存中,但并不會(huì)做任何類的初始化工作,返回java.lang.Class
對(duì)象
3、通過Class.forName("類的全局定名")
獲取,同樣,類會(huì)被JVM加載到內(nèi)存中,并且會(huì)進(jìn)行類的靜態(tài)初始化工作,返回java.lang.Class
對(duì)象
4、通過new MyClass().getClass()
獲取,這種方式使用了new
進(jìn)行實(shí)例化操作,因此== 靜態(tài)初始化和非靜態(tài)初始化工作都會(huì)進(jìn)行 == ,getClass
方法屬于頂級(jí)Object
類中的方法,任何子類對(duì)象都可以調(diào)用,哪個(gè)子類調(diào)用,就返回那個(gè)子類的java.lang.Class
對(duì)象
5、這3種方式,最終在JVM堆區(qū)對(duì)應(yīng)類的java.lang.Class
對(duì)象都屬于同一個(gè),也就是內(nèi)存地址相同,進(jìn)行==
雙等號(hào)比較結(jié)果為true
,原因是JVM類加載過程中使用的是同一個(gè)ClassLoader
類加載器加載某個(gè)類,不論加載多少次,生成到堆區(qū)的java.lang.Class
對(duì)象始終只有一個(gè),除非自定義類加載器,破壞JVM的雙親委派機(jī)制,使得同一個(gè)類被不同類加載器加載,JVM才會(huì)把它當(dāng)做兩個(gè)不同的java.lang.Class
對(duì)象
三、Java反射機(jī)制的應(yīng)用場(chǎng)景有哪些?
工廠模式
中的簡(jiǎn)單工廠模式優(yōu)化
代理模式
中的動(dòng)態(tài)代理方式實(shí)現(xiàn)
Java JDBC
數(shù)據(jù)庫(kù)操作
感謝各位的閱讀,以上就是“Java反射機(jī)制原理和Class獲取方式是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Java反射機(jī)制原理和Class獲取方式是什么這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
分享題目:Java反射機(jī)制原理和Class獲取方式是什么
分享URL:http://muchs.cn/article22/gddocc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營(yíng)銷、面包屑導(dǎo)航、網(wǎng)站設(shè)計(jì)、標(biāo)簽優(yōu)化、網(wǎng)站改版、定制網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)