Java jvm 類加載 反射
點擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時間送達(dá)
? 作者?|??吳楠予
來源 |? urlify.cn/E3euIb
Java 底層
jvm,類加載,反射
Java語言是跨平臺語言,一段java代碼,經(jīng)過編譯成class文件后,能夠在不同系統(tǒng)的服務(wù)器上運(yùn)行;因為java語言中有虛擬機(jī)jvm,才有了跨平臺,java為了實現(xiàn)跨平臺,在jvm上投入了很大的研發(fā)開發(fā)資源。jvm是java的底層,本文學(xué)習(xí)探討下java的jvm及關(guān)聯(lián)的類加載和反射知識
JVM
JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫,JVM是一種用于計算設(shè)備的規(guī)范,它是一個虛構(gòu)出來的計算機(jī),是通過在實際的計算機(jī)上仿真模擬各種計算機(jī)功能來實現(xiàn)的。
Java語言的一個非常重要的特點就是與平臺的無關(guān)性。而使用Java虛擬機(jī)是實現(xiàn)這一特點的關(guān)鍵。一般的高級語言如果要在不同的平臺上運(yùn)行,至少需要編譯成不同的目標(biāo)代碼。而引入Java語言虛擬機(jī)后,Java語言在不同平臺上運(yùn)行時不需要重新編譯。Java語言使用模式Java虛擬機(jī)屏蔽了與具體平臺相關(guān)的信息,使得Java語言編譯程序只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼),就可以在多種平臺上不加修改地運(yùn)行。Java虛擬機(jī)在執(zhí)行字節(jié)碼時,把字節(jié)碼解釋成具體平臺上的機(jī)器指令執(zhí)行。[1]
jvm的構(gòu)成
jvm周期:是在java程序執(zhí)行時運(yùn)行,程序結(jié)束時停止
jvm的基本結(jié)構(gòu)有:類加載子系統(tǒng)、本地方法棧、Java棧、方法區(qū)、Java堆、pc寄存器,垃圾回收,執(zhí)行引擎
類加載子系統(tǒng)
java是面向?qū)ο笳Z言,邏輯代碼中的類文件執(zhí)行邏輯前,是需要jvm讀取class文件并校驗初始化后才能使用的,包括變量,方法,構(gòu)造。
類加載系統(tǒng)可以認(rèn)為是在使用到j(luò)ava對像時(抽象),對java對象字節(jié)碼的讀取加載預(yù)編譯(具體),之后不再加載(讀取校驗一次)。
Java棧
棧是先進(jìn)后出的結(jié)構(gòu),java棧時一塊線程私有的內(nèi)存空間,可以理解為一個java線程對應(yīng)一個java棧,棧和線程密切關(guān)聯(lián),棧包含線程運(yùn)行的實時信息,如當(dāng)前運(yùn)行方法地址,方法中的瞬時變量等信息
方法區(qū)
在一個jvm實例的內(nèi)部,類型信息被存儲在一個稱為方法區(qū)的內(nèi)存邏輯區(qū)中。類型信息是由類加載器在類加載時從類文件中提取出來的。類(靜態(tài))變量也存儲在方法區(qū)中。
Java堆
java堆是和應(yīng)用程序關(guān)系最為密切的內(nèi)存空間,幾乎所有的對象都存放在堆上。并且java堆是完全自動化管理的,通過垃圾回收機(jī)制,垃圾對象會被自動清理,而不需要顯示的釋放。
pc寄存器
存放計算機(jī)下一步要執(zhí)行的指令的地址,
垃圾回收
因為程序運(yùn)行沒創(chuàng)建一個對象都需要使用硬件的內(nèi)存資源,不能無限使用,jvm的垃圾回收能夠自動回收不再使用的java對象,使內(nèi)存空間有效利用。垃圾回收線程是后臺執(zhí)行的,不需要認(rèn)為回收內(nèi)存垃圾,即使有垃圾回收方法調(diào)用,但并不能控制jvm如何去將一個對象失效回收。
執(zhí)行引擎
Java 字節(jié)碼指令指向特定邏輯得本地機(jī)器碼,而JVM 解釋執(zhí)行Java字節(jié)碼指令時,會直接調(diào)用字節(jié)碼指向得本地機(jī)器碼;
java底層由C語言編寫,執(zhí)行java程序時,jvm每讀取一個字節(jié)碼指令動作,執(zhí)行引擎就解析解釋執(zhí)行本地系統(tǒng)對應(yīng)的本地機(jī)器碼。
類加載
虛擬機(jī)把描述類的數(shù)據(jù)從 Class 文件加載到內(nèi)存,并對數(shù)據(jù)進(jìn)行校驗、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的 Java 類型,這就是虛擬機(jī)的類加載機(jī)制。
在Java語言里面,類型的加載、連接和初始化過程都是在程序運(yùn)行期間完成的
雙親委派機(jī)制
作為軟件開發(fā)語言,java在安全方面也有很高的要求,所以類加載是有一套規(guī)則的,jre是java運(yùn)行時的環(huán)境,包括很多基本類,如java.lang.String 是字符串類,這個類很基礎(chǔ)也很重要,那在加載的時候不能允許加載的String類被篡改,java保證類加載安全,首先看是否已經(jīng)加載,如果沒有查看核心庫是否有此類,沒有此類才會去擴(kuò)展環(huán)境找類文件加載,這種機(jī)制保證了類在加載時的唯一性和安全性。
java類加載一般來說是詢問自己的parentClassLoader 加載,如果沒有加載成功,才自己加載,加載順序是自上而下
java.lang.ClassLoader 加載類方法
protected?Class>?loadClass(String?name,?boolean?resolve)
????????throws?ClassNotFoundException
????{
????????synchronized?(getClassLoadingLock(name))?{
????????????//?First,?check?if?the?class?has?already?been?loaded
????????????Class>?c?=?findLoadedClass(name);?//0.是否已加載
????????????if?(c?==?null)?{
????????????????long?t0?=?System.nanoTime();
????????????????try?{
????????????????????if?(parent?!=?null)?{
????????????????????????c?=?parent.loadClass(name,?false);?//1.沒有加載,首先通過父類加載器加載
????????????????????}?else?{
????????????????????????c?=?findBootstrapClassOrNull(name);?//1.沒有父類加載器時加載方式
????????????????????}
????????????????}?catch?(ClassNotFoundException?e)?{
????????????????????//?ClassNotFoundException?thrown?if?class?not?found
????????????????????//?from?the?non-null?parent?class?loader
????????????????}
????????????????if?(c?==?null)?{?
????????????????????//?If?still?not?found,?then?invoke?findClass?in?order
????????????????????//?to?find?the?class.
????????????????????long?t1?=?System.nanoTime();
????????????????????c?=?findClass(name);
?????//如果父類沒有加載到類,則使用findClass方法去加載類(這個方法可以重寫自定義)
????????????????????//?this?is?the?defining?class?loader;?record?the?stats
????????????????????sun.misc.PerfCounter.getParentDelegationTime().addTime(t1?-?t0);
????????????????????sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
????????????????????sun.misc.PerfCounter.getFindClasses().increment();
????????????????}
????????????}
????????????if?(resolve)?{
????????????????resolveClass(c);
????????????}
????????????return?c;
????????}
????}
反射
反射是Java重要的技術(shù)點,在框架開發(fā),AOP切面編程代理等方面都需要反射方面的技術(shù)去實現(xiàn)。
Java反射機(jī)制主要提供了以下功能:
在運(yùn)行時判斷任意一個對象所屬的類。
在運(yùn)行時構(gòu)造任意一個類的對象。
在運(yùn)行時判斷任意一個類所具有的成員變量和方法。
在運(yùn)行時調(diào)用任意一個對象的方法。
生成動態(tài)代理。
反射相關(guān)的類
Class 類的字節(jié)碼對象
Field 類的屬性
Method 類的方法
Constructor 類的構(gòu)造方法
Annotation 類(方法字段)的注解
反射的使用
一般使用
模擬事務(wù)的注解
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public?@interface?defTransaction?{
}
普通封裝對象
public?interface?People?{
?String?getName();
?void?setName(String?name);
?Integer?getAge();
?void?setAge(Integer?age);
?BigDecimal?getMoney();
?void?setMoney(BigDecimal?money);
?@defTransaction
?void?addMoney(BigDecimal?addNum);
?@defTransaction
?void?subTractMoney(BigDecimal?subNum);
?
}
public?class?TestPeople?implements?People{
?
?//?姓名
?public?String?name;
?
?//?年齡
?private?Integer?age;
?
?//?錢
?private?BigDecimal?money;
?
?public?String?getName()?{
??return?name;
?}
?
?public?void?setName(String?name)?{
??this.name?=?name;
?}
?public?Integer?getAge()?{
??return?age;
?}
?public?void?setAge(Integer?age)?{
??this.age?=?age;
?}
?public?BigDecimal?getMoney()?{
??return?money;
?}
?public?void?setMoney(BigDecimal?money)?{
??this.money?=?money;
?}
?@Override
?public?void?addMoney(BigDecimal?addNum)?{
??this.money?=?this.money.add(addNum);
??
?}
?@Override
?public?void?subTractMoney(BigDecimal?subNum)?{
??this.money?=?this.money.subtract(subNum);
?}??
}
反射測試類
public?class?ReflectTest?{
?public?static?void?main(String[]?args)?{
??//?普通對象創(chuàng)建?使用new
??People?testPeople?=?new?TestPeople();
??testPeople.setName("Frank");
??testPeople.setAge(18);
??testPeople.setMoney(new?BigDecimal(10));
??System.out.println("json:"?+?JsonUtil.objectToJson(testPeople));
??
??//?反射創(chuàng)建對象?class.newInstance()
??ClassLoader?contextClassLoader?=?Thread.currentThread().getContextClassLoader();
??try?{
???Class>?clazz?=?contextClassLoader.loadClass("com.domoment.leaves.common.util.reflect.TestPeople");
???if(clazz?!=?null)?{
?????Object?people?=?clazz.newInstance();
?????System.out.println("newInstance?start?json:"?+?JsonUtil.objectToJson(people));
?????
?????//?通過反射執(zhí)行方法
?????Method?setName?=?clazz.getMethod("setName",?String.class);
?????setName.invoke(people,?"inoverFrank");
?????
?????
?????System.out.println("newInstance?end?json:"?+?JsonUtil.objectToJson(people));
?????
???}
??}?catch?(Exception?e)?{
???e.printStackTrace();
??}
?}
}
使用反射實現(xiàn)代理
代理類DefProxy (People是被代理類)
public?class?DefProxy?implements?InvocationHandler{
??//?這個就是我們要代理的真實對象
????private?Object?subject;
????
????//????構(gòu)造方法,給我們要代理的真實對象賦初值
????public?DefProxy(Object?subject){
????????this.subject?=?subject;
????}
????
?@Override
?public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
??Annotation[]?annotations?=?method.getDeclaredAnnotations();
??boolean?transactionOpen?=?false;
??for?(Annotation?annotation?:?annotations)?{
???if(annotation?instanceof?defTransaction)?{
????transactionOpen?=?true;
????break;
???}
??}
??if(transactionOpen)?{?//當(dāng)方法上有?defTransaction?注解時,執(zhí)行方法前開啟事務(wù)
???System.out.println("open?Transaction");
??}
??System.out.println("proxy:"?+?method.getName());
??Object?result?=?method.invoke(subject,?args);
??
??if(transactionOpen)?{?//當(dāng)方法上有?defTransaction?注解時,執(zhí)行方法后關(guān)閉事務(wù)
???System.out.println("close?Transaction");
??}
??return?result;
?}
}
代理測試代碼
public?class?ReflectTest?{
?public?static?void?main(String[]?args)?{
??//?反射創(chuàng)建對象
??ClassLoader?contextClassLoader?=?Thread.currentThread().getContextClassLoader();
??try?{
???Class>?clazz?=?contextClassLoader.loadClass("com.domoment.leaves.common.util.reflect.TestPeople");
???if(clazz?!=?null)?{
?????Object?people?=?clazz.newInstance();
?????System.out.println("newInstance?start?json:"?+?JsonUtil.objectToJson(people));
?????
?????
?????Method?setName?=?clazz.getMethod("setName",?String.class);
?????setName.invoke(people,?"inoverFrank");
?????
?????System.out.println("newInstance?end?json:"?+?JsonUtil.objectToJson(people));
?????
?????InvocationHandler?handler?=?new?DefProxy(people);
?????
?????????????????//?構(gòu)造代理對象
?????People?proxyPeople?=?(People)Proxy.
???????newProxyInstance(handler.getClass().getClassLoader(),?people.getClass().getInterfaces(),?handler);
?????proxyPeople.setName("proxySetFrank");
?????proxyPeople.setAge(20);
?????proxyPeople.setMoney(new?BigDecimal(999));
?????System.out.println("proxyPeople?end?json:"?+?JsonUtil.objectToJson(people));
?????proxyPeople.addMoney(new?BigDecimal(20));
?????System.out.println("proxyPeople?add?json:"?+?JsonUtil.objectToJson(people));
?????proxyPeople.subTractMoney(new?BigDecimal(17));
?????System.out.println("proxyPeople?end?json:"?+?JsonUtil.objectToJson(people));
?????
???}
??}?catch?(Exception?e)?{
???e.printStackTrace();
??}
?}
}
控制臺打印
newInstance?start?json:{}
newInstance?end?json:{"name":"inoverFrank"}
proxy:setName
proxy:setAge
proxy:setMoney
proxyPeople?end?json:{"name":"proxySetFrank","age":20,"money":999}
open?Transaction
proxy:addMoney
close?Transaction
proxyPeople?add?json:{"name":"proxySetFrank","age":20,"money":1019}
open?Transaction
proxy:subTractMoney
close?Transaction
proxyPeople?end?json:{"name":"proxySetFrank","age":20,"money":1002}
可以看到方法上有 defTransaction 注解的時候,
方法執(zhí)行前 打印 open Transaction
方法執(zhí)行后 打印 close Transaction
這是模擬,真實場景就可以將打印改為代理時擴(kuò)展方法,如數(shù)據(jù)庫操作時候,開啟關(guān)閉事務(wù)
粉絲福利:實戰(zhàn)springboot+CAS單點登錄系統(tǒng)視頻教程免費領(lǐng)取
???
?長按上方微信二維碼?2 秒 即可獲取資料
感謝點贊支持下哈?
