1、java的核心機制java
java有兩種核心機制:java虛擬機(JavaVirtual Machine)與垃圾收集機制(Garbage collection):
一、Java虛擬機:是運行全部Java程序的抽象計算機,是Java語言的運行環境,在其上面運行Java代碼編譯後的字節碼程序,java虛擬機實現了平臺無關性。
二、Java垃圾回收(Garbage Collection):自動釋放不用對象內存空間,在java程序運行過程當中自動進行,垃圾收集機制可大大縮短編程時間,保護程序的完整性,是Java語言安全性策略的一個重要部份。程序員
2、java虛擬機及其結構編程
java垃圾回收不須要程序員手動操做,咱們常常須要關注的是java虛擬機,java虛擬機承載着程序從源碼到運行的所有工做。
Java虛擬機是可運行Java代碼的假想計算機,有本身想象中的硬件,如處理器、堆棧、寄存器等,還具備相應的指令系統,能夠執行 Java 的字節碼程序。Java語言的一個很是重要的特色就是與平臺的無關性。而使用Java虛擬機是實現這一特色的關鍵。Java語言使用模式Java虛擬機屏蔽了與具體平臺相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就能夠在多種平臺上不加修改地運行。Java虛擬機在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行。
對於 JVM 的基本結構,咱們能夠從下圖能夠大體瞭解:數組
3、程序的運行過程
從源文件建立到程序運行,Java程序要通過兩大步驟:編譯,運行;一、源文件由編譯器編譯成字節碼(ByteCode) 二、字節碼由java虛擬機解釋運行。
一、 第一步(編譯): 建立完源文件以後,程序會被編譯器編譯爲.class文件。Java編譯一個類時,若是這個類所依賴的類尚未被編譯,編譯器就會先編譯這個被依賴的類,而後引用,不然直接引用。。編譯後的字節碼文件格式主要分爲兩部分:常量池和方法字節碼。
二、第二步(運行):java類運行的過程大概可分爲兩個過程:一、類的加載 二、執行。安全
4、類的加載網絡
類加載過程
java程序通過編譯後造成.class文件。經過類加載器將字節碼(.class)加載入JVM的內存中。JVM將類加載過程分紅加載,鏈接,初始化三個階段,其中鏈接階段又可分爲驗證,準備,解析三個階段。數據結構
JVM 的類加載是經過 ClassLoader 及其子類來完成的,類的層次關係和加載順序能夠由下圖來描述:分佈式
一、Bootstrap ClassLoader啓動類加載器函數
負責加載$JAVA_HOME中jre/lib/裏全部的 class(JDK 表明 JDK 的安裝目錄,下同),或被-Xbootclasspath參數指定的路徑中的,而且能被虛擬機識別的類庫(如 rt.jar,全部的java.*開頭的類均被 Bootstrap ClassLoader 加載)。啓動類加載器由 C++ 實現,不是 ClassLoader 子類。沒法被 Java 程序直接引用的。微服務
二、Extension ClassLoader擴展類加載器
該加載器由sun.misc.LauncherExtClassLoader實現,負責加載Java平臺中擴展功能的一些jar包,包括ExtClassLoader實現,負責加載Java平臺中擴展功能的一些jar包,包括JAVA_HOME中jre/lib/.jar或-Djava.ext.dirs指定目錄下的 jar 包。即JDK\jre\lib\ext目錄中,或者由 java.ext.dirs 系統變量指定的路徑中的全部類庫(如javax.開頭的類),開發者能夠直接使用擴展類加載器若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java進階羣:725219329,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。
三、App ClassLoader應用程序類加載器
該類加載器由 sun.misc.Launcher$AppClassLoader 來實現,負責記載 classpath 中指定的 jar 包及目錄中 class,開發者能夠直接使用該類加載器,若是應用程序中沒有自定義過本身的類加載器,通常狀況下這個就是程序中默認的類加載器。
啓動類加載器:它使用 C++ 實現(這裏僅限於 Hotspot,也就是 JDK1.5 以後默認的虛擬機,有不少其餘的虛擬機是用 Java 語言實現的),是虛擬機自身的一部分。
全部其餘的類加載器:這些類加載器都由 Java 語言實現,獨立於虛擬機以外,而且所有繼承自抽象類 java.lang.ClassLoader,這些類加載器須要由啓動類加載器加載到內存中以後才能去加載其餘的類。
應用程序都是由這三種類加載器互相配合進行加載的,咱們還能夠加入自定義的類加載器。
加載
加載時類加載過程的第一個階段,在加載階段,虛擬機須要完成如下三件事情:
一、經過一個類的全限定名來獲取其定義的二進制字節流。
二、將這個字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結構。
三、在 Java 堆中生成一個表明這個類的 java.lang.Class 對象,做爲對方法區中這些數據的訪問入口。
注意,這裏第 1 條中的二進制字節流並不僅是單純地從 Class 文件中獲取,好比它還能夠從 Jar 包中獲取、從網絡中獲取(最典型的應用即是 Applet)、由其餘文件生成(JSP 應用)等。
相對於類加載的其餘階段而言,加載階段(準確地說,是加載階段獲取類的二進制字節流的動做)是可控性最強的階段,由於開發人員既可使用系統提供的類加載器來完成加載,也能夠自定義本身的類加載器來完成加載。
(JVM主要在程序第一次主動使用類的時候,纔會去加載該類。也就是說,JVM並非在一開始就把一個程序就全部的類都加載到內存中,而是到用的時候才把它加載進來,並且只加載一次。)
加載過程當中會先檢查類是否被已加載,檢查順序是自底向上,從 Custom ClassLoader 到 BootStrap ClassLoader 逐層檢查,只要某個 Classloader 已加載就視爲已加載此類,保證此類只全部 ClassLoade r加載一次。而加載的順序是自頂向下,也就是由上層來逐層嘗試加載此類。
這幾種類加載器的層次關係以下圖所示:
這種層次關係稱爲類加載器的雙親委派模型。雙親委派模型的工做流程是:
若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把請求委託給父加載器去完成,依次向上,所以,全部的類加載請求最終都應該被傳遞到頂層的啓動類加載器中,只有當父加載器在它的搜索範圍中沒有找到所需的類時,即沒法完成該加載,子加載器纔會嘗試本身去加載該類。
驗證
驗證的目的是爲了確保 Class 文件中的字節流包含的信息符合當前虛擬機的要求,並且不會危害虛擬機自身的安全。不一樣的虛擬機對類驗證的實現可能會有所不一樣,但大體都會完成如下四個階段的驗證:文件格式的驗證、元數據的驗證、字節碼驗證和符號引用驗證。
準備
準備階段是正式爲類變量分配內存並設置類變量初始值的階段,這些內存都將在方法區中分配。對於該階段有如下幾點須要注意:
一、這時候進行內存分配的僅包括類變量(static),而不包括實例變量,實例變量會在對象實例化時隨着對象一塊分配在 Java 堆中。
二、這裏所設置的初始值一般狀況下是數據類型默認的零值(如 0、0L、null、false 等),而不是被在 Java 代碼中被顯式地賦予的值。
解析
解析階段是虛擬機將常量池中的符號引用轉化爲直接引用的過程。
解析動做主要針對類或接口、字段、類方法、接口方法四類符號引用進行,分別對應於常量池中的 CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info 四種常量類型。
一、類或接口的解析:判斷所要轉化成的直接引用是對數組類型,仍是普通的對象類型的引用,從而進行不一樣的解析。
二、字段解析:對字段進行解析時,會先在本類中查找是否包含有簡單名稱和字段描述符都與目標相匹配的字段,若是有,則查找結束;若是沒有,則會按照繼承關係從上往下遞歸搜索該類所實現的各個接口和它們的父接口,尚未,則按照繼承關係從上往下遞歸搜索其父類,直至查找結束。
初始化
類初始化是類加載過程的最後一個階段,到初始化階段,才真正開始執行類中的 Java 程序代碼。虛擬機規範嚴格規定了有且只有四種狀況必須當即對類進行初始化:
一、 遇到 new、getstatic、putstatic、invokestatic 這四條字節碼指令時,若是類尚未進行過初始化,則須要先觸發其初始化。生成這四條指令最多見的 Java 代碼場景是:使用 new 關鍵字實例化對象時、讀取或設置一個類的靜態字段(static)時(被 static 修飾又被 final 修飾的,已在編譯期把結果放入常量池的靜態字段除外)、以及調用一個類的靜態方法時。
二、 使用 Java.lang.refect 包的方法對類進行反射調用時,若是類尚未進行過初始化,則須要先觸發其初始化。
三、當初始化一個類的時候,若是發現其父類尚未進行初始化,則須要先觸發其父類的初始化。
四、當虛擬機啓動時,用戶須要指定一個要執行的主類,虛擬機會先執行該主類。
虛擬機規定只有這四種狀況纔會觸發類的初始化,稱爲對一個類進行主動引用,除此以外全部引用類的方式都不會觸發其初始化,稱爲被動引用。
5、靜態加載和動態加載
Java初始化一個類的時候能夠用new 操做符來初始化,也可經過Class.forName的方式來獲得一個Class類型的實例,而後經過這個Class類型的實例的newInstance來初始化.咱們把前者叫作JAVA的靜態加載,把後者叫作動態加載.。
有時候咱們說某個語言具備很強的動態性,有時候咱們會區分動態和靜態的不一樣技術與做法。咱們朗朗上口動態綁定(dynamic binding)、動態連接(dynamic linking)、動態加載(dynamic loading)等。然而「動態」一詞其實沒有絕對而廣泛適用的嚴格定義,有時候甚至像面向對象當初被導入編程領域同樣,一人一把號,各吹各的調。
通常而言,開發者社羣說到動態語言,大體認同的一個定義是:「程序運行時,容許改變程序結構或變量類型,這種語言稱爲動態語言」。從這個觀點看,Perl,Python,Ruby是動態語言,C++,Java,C#不是動態語言。若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java進階羣:725219329,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。
儘管在這樣的定義與分類下Java不是動態語言,它卻有着一個很是突出的動態相關機制:Reflection。Java程序能夠加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。
一、靜態加載的時候若是在運行環境中找不到要初始化的類,拋出的是NoClassDefFoundError,它在JAVA的異常體系中是一個Error.
二、動態態加載的時候若是在運行環境中找不到要初始化的類,拋出的是ClassNotFoundException,它在JAVA的異常體系中是一個checked異常,在寫代碼的時候就須要catch.
6、反射
JAVA反射機制:
在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
Java反射機制主要提供瞭如下功能:
在運行時判斷任意一個對象所屬的類;在運行時構造任意一個類的對象;在運行時判斷任意一個類所具備的成員變量和方法;在運行時調用任意一個對象的方法;生成動態代理。
Java有個Object 類,是全部Java 類的繼承根源,其內聲明瞭數個應該在全部Java 類中被改寫的方法:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一個Class 對象。
【Class 類十分特殊。它和通常類同樣繼承自Object,其實體用以表達Java程序運行時的classes和interfaces,也用來表達enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及關鍵詞void。當一個class被加載,或當加載器(class loader)的defineClass()被JVM調用,JVM 便自動產生一個Class 對象。】
若是您想借由「修改Java標準庫源碼」來觀察Class 對象的實際生成時機(例如在Class的constructor內添加一個println()),這樣是行不通的!由於Class並無public constructor。
Class是Reflection故事起源。針對任何您想探勘的類,惟有先爲它產生一個Class 對象,接下來才能經由後者喚起爲數十多個的Reflection APIs。Reflection機制容許程序在正在執行的過程當中,利用Reflection APIs取得任何已知名稱的類的內部信息,包括:package、 type parameters、 superclass、 implemented interfaces、 inner classes、 outer classes、 fields、 constructors、 methods、 modifiers等,並能夠在執行的過程當中,動態生成instances、變動fields內容或喚起methods。
從Class中獲取信息
Class類提供了大量的實例方法來獲取該Class對象所對應的詳細信息,Class類大體包含以下方法,其中每一個方法都包含多個重載版本,所以咱們只是作簡單的介紹,詳細請參考JDK文檔
獲取類內信息
獲取內容 方法簽名 構造器 Constructor<T> getConstructor(Class<?>... parameterTypes) 包含的方法 Method getMethod(String name, Class<?>... parameterTypes) 包含的屬性 Field getField(String name) 包含的Annotation <A extends Annotation> A getAnnotation(Class<A> annotationClass) 內部類 Class<?>[] getDeclaredClasses() 外部類 Class<?> getDeclaringClass() 所實現的接口 Class<?>[] getInterfaces() 修飾符 int getModifiers() 所在包 Package getPackage() 類名 String getName() 簡稱 String getSimpleName()
一些判斷類自己信息的方法
判斷內容 方法簽名 註解類型? boolean isAnnotation() 使用了該Annotation修飾? boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 匿名類? boolean isAnonymousClass() 數組? boolean isArray() 枚舉? boolean isEnum() 原始類型? boolean isPrimitive() 接口? boolean isInterface() obj是不是該Class的實例 boolean isInstance(Object obj)
(1)獲取class
主有三種得到class的途徑,使用時要注意區別
一、 a,類型.class 如: String.class使用類名加「.class」的方式即會返回與該類對應的Class對象。這個方法能夠直接得到與指定類關聯的Class對象,而並不須要有該類的對象存在。
二、 b,Class.forName("類名");該方法能夠根據字符串參數所指定的類名獲取與該類關聯的Class對象。若是該類尚未被裝入,該方法會將該類裝入JVM。forName方法的參數是類的完 整限定名(即包含包名)。一般用於在程序運行時根據類名動態的載入該類並得到與之對應的Class對象。
三、 c, obj.getClass();全部Java對象都具有這個方法,該方法用於返回調用該方法的對象的所屬類關聯的
(2)獲取構造方法
Class類提供了四個public方法,用於獲取某個類的構造方法:
一、Constructor getConstructor(Class[] params)根據構造函數的參數,返回一個具體的具備public屬性的構造函數
二、Constructor getConstructors() 返回全部具備public屬性的構造函數數組
三、Constructor getDeclaredConstructor(Class[] params) 根據構造函數的參數,返回一個具體的構造函數(不分public和非public屬性)
四、Constructor getDeclaredConstructors() 返回該類中全部的構造函數數組(不分public和非public屬性)
1 反射出無參的構造方法並獲得對象 * 注意: * 1 在Class.forName()中應該傳入含有包名的類全名 * 2 newInstance()方法的本質是調用類的無參Public構造方法 / String className1="cn.testreflect.Worker"; Class clazz1=Class.forName(className1); Object object1=clazz1.newInstance(); System.out.println("object1.toString()="+object1.toString()); /* * 2 反射出帶參數的構造方法並獲得對象 */ String className2="cn.testreflect.Worker"; Class clazz2=Class.forName(className2); Constructor constructor1=clazz2.getConstructor(int.class,String.class); Object object2=constructor1.newInstance(18,"小明"); System.out.println("object2.toString()="+object2.toString());
(3)獲取類的成員方法
與獲取構造方法的方式相同,存在四種獲取成員方法的方式:
一、Method getMethod(String name, Class[] params) 根據方法名和參數,返回一個具體的具備public屬性的方法
二、Method[] getMethods() 返回全部具備public屬性的方法數組
三、Method getDeclaredMethod(String name, Class[] params) 根據方法名和參數,返回一個具體的方法(不分public和非public屬性)
四、Method[] getDeclaredMethods() 返回該類中的全部的方法數組(不分public和非public屬性)
* 調用對象的帶參數的方法 */ String className5="cn.testreflect.Worker"; Class clazz5=Class.forName(className5); Method method=clazz5.getMethod("printMessage", String.class,int.class,int.class); Object object5=clazz5.newInstance(); method.invoke(object5, "周星星",50,9527); } catch (Exception e) { System.out.println(e.toString()); }
(4)獲取類的成員變量(成員屬性)
存在四種獲取成員屬性的方法
一、Field getField(String name) 根據變量名,返回一個具體的具備public屬性的成員變量
二、Field[] getFields() 返回具備public屬性的成員變量的數組若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java進階羣:725219329,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。
三、Field getDeclaredField(String name) 根據變量名,返回一個成員變量(不分public和非public屬性)
四、Field[] getDelcaredFields() 返回全部成員變量組成的數組(不分public和非public屬性)
* 1 獲取類的私有字段 * 注意: * 獲取共有字段應調用clazz3.getField(name)方法 */ String className3="cn.testreflect.Worker"; Class clazz3=Class.forName(className3); Field ageField1=clazz3.getDeclaredField("age"); System.out.println("ageField1="+ageField1); /** * 2 獲取和更改某個對象的私有字段 * 即模擬get()和set()方法 */ String className4="cn.testreflect.Worker"; Class clazz4=Class.forName(className4); Field ageField2=clazz4.getDeclaredField("age"); Object object4=constructor1.newInstance(18,"小明"); //取消訪問私有字段的合法性檢查 ageField2.setAccessible(true); //獲取對象的私有字段 Object ageObject4=ageField2.get(object4); System.out.println("ageObject4="+ageObject4); //再更改對象的私有字段的值 ageField2.set(object4, 9527); //從新得到 Object ageObject5=ageField2.get(object4); System.out.println("ageObject5="+ageObject5);