1、JVM相關(guān)的內(nèi)存模型和結(jié)構(gòu)、GC,class加載過程等簡單介紹
基本概念
JDK : Java Development Kit,Java開發(fā)套件,包含java的開發(fā)和運(yùn)行環(huán)境jre
JRE : Java Runtime Environment,Java的運(yùn)行環(huán)境
JVM : Java Virtual Machine,java虛擬機(jī), Java語言的一個非常重要的特點就是與平臺的無關(guān)性。 而使用Java虛擬機(jī)是實現(xiàn)這一特點的關(guān)鍵。
GC :Garbage Collection,垃圾回收,Java虛擬機(jī)(JVM)垃圾回收器提供的一種用于在空閑時間不定時回收無任何對象引用的對象占據(jù)的內(nèi)存空間的一種機(jī)制。
對象 :對象是類的一個實例(對象不是找個女朋友),有狀態(tài)和行為。例如,一條狗是一個對象,它的狀態(tài)有:顏色、名字、品種;行為有:搖尾巴、叫、吃等。
類 :類是一個模板,它描述一類對象的行為和狀態(tài)。
JVM內(nèi)存模型和結(jié)構(gòu)
內(nèi)存劃分
java虛擬機(jī)按照運(yùn)行時內(nèi)存使用區(qū)域劃分如圖:

Java虛擬機(jī)在執(zhí)行Java程序的過程中會把它所管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域。這些區(qū)域都有各自的用途,以及創(chuàng)建和銷毀的時間,有的區(qū)域隨著虛擬機(jī)進(jìn)程的啟動而存在,有些區(qū)域則是依賴用戶線程的啟動和結(jié)束而建立和銷毀。
一、程序計數(shù)器(Program Counter Register)
????程序計數(shù)器就是記錄當(dāng)前線程執(zhí)行程序的位置,改變計數(shù)器的值來確定執(zhí)行的下一條指令,比如循環(huán)、分支、方法跳轉(zhuǎn)、異常處理,線程恢復(fù)都是依賴程序計數(shù)器來完成。
????Java虛擬機(jī)多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式實現(xiàn)的。為了線程切換能恢復(fù)到正確的位置,每個程序計數(shù)器只能記錄一個線程的行號,因此它是線程私有的。
????如果線程正在執(zhí)行的是一個Java方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;如果正在執(zhí)行的是Native方法,這個計數(shù)器值則為空(Undefined)。此內(nèi)存區(qū)域是唯一一個在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。
二、java虛擬機(jī)棧(VM Stack)
????java虛擬機(jī)棧是線程私有,生命周期與線程相同。創(chuàng)建線程的時候就會創(chuàng)建一個java虛擬機(jī)棧。
????虛擬機(jī)執(zhí)行java程序的時候,每個方法都會創(chuàng)建一個棧幀,棧幀存放在java虛擬機(jī)棧中,通過壓棧出棧的方式進(jìn)行方法調(diào)用。
????棧幀又分為一下幾個區(qū)域:局部變量表、操作數(shù)棧、動態(tài)連接、方法出口等。
????局部變量表中存儲著方法相關(guān)的局部變量,包括各種基本數(shù)據(jù)類型及對象的引用地址等,因此他有個特點:內(nèi)存空間可以在編譯期間就確定,運(yùn)行時不再改變。
????虛擬機(jī)棧定義了兩種異常類型:StackOverFlowError(棧溢出)和OutOfMemoryError(內(nèi)存溢出)。如果線程調(diào)用的棧深度大于虛擬機(jī)允許的最大深度,則拋出StackOverFlowError;不過大多數(shù)虛擬機(jī)都允許動態(tài)擴(kuò)展虛擬機(jī)棧的大小,所以線程可以一直申請棧,直到內(nèi)存不足時,拋出OutOfMemoryError。
????java的8中基本類型的局部變量的值存放在虛擬機(jī)棧的局部變量表中,如果是引用型的變量,則只存儲對象的引用地址。
注意:
- 當(dāng)用戶請求web服務(wù)器,每個請求開啟一個線程負(fù)責(zé)用戶的響應(yīng)計算(每個線程分配一個虛擬機(jī)??臻g),如果并發(fā)量大時,可能會導(dǎo)致內(nèi)存溢出(OutOfMemoneyError),可以適當(dāng)?shù)陌衙總€虛擬機(jī)棧的大小適當(dāng)調(diào)小一點,減少內(nèi)存的使用量來提高系統(tǒng)的并發(fā)量。
- 當(dāng)??臻g調(diào)小以后,又會引發(fā)方法調(diào)用深度的的問題。因為,每個方法都會生成一個棧幀,如果方法調(diào)用深度很深就意味著,棧里面存放大量的棧幀,可能導(dǎo)致棧內(nèi)存溢出(StackOverFlowError)。
三、本地方法棧(Native Method Stack)
????本地方法棧用于支持native方法的執(zhí)行,存儲了每個native方法的執(zhí)行狀態(tài)。本地方法棧和虛擬機(jī)棧他們的運(yùn)行機(jī)制一致,唯一的區(qū)別是,虛擬機(jī)棧執(zhí)行Java方法,本地方法棧執(zhí)行native方法。在很多虛擬機(jī)中(如Sun的JDK默認(rèn)的HotSpot虛擬機(jī)),會將虛擬機(jī)棧和本地方法棧一起使用。
本地方法:是非java語言實現(xiàn)的方法,例如,java調(diào)用C語言,來操作某些硬件信息。
四、堆(Heap):
????堆是被所有線程共享的區(qū)域,實在虛擬機(jī)啟動時創(chuàng)建的。堆里面存放的都是對象的實例(new 出來的對象都存在堆中)。
????我們平常所說的垃圾回收,主要回收的就是堆區(qū)。為了提升垃圾回收的性能,又把堆分成兩塊區(qū)新生代(young)和年老代(old),更細(xì)一點劃分新生代又可劃分為Eden區(qū)和2個Survivor區(qū)(From Survivor和To Survivor)。
如下圖結(jié)構(gòu):

- Eden:新創(chuàng)建的對象存放在Eden區(qū)
- From Survivor和To Survivor:保存新生代gc后還存活的對象。(使用復(fù)制算法,導(dǎo)致有一個Survivor空間浪費(fèi))Hotspot虛擬機(jī)新生代Eden和Survivor的大小比值為4:1,因為有兩個Survivor,所以Eden:From Survivor:To Survivor比值為8:1:1。
- 老年代:對象存活時間比較長(經(jīng)過多次新生代的垃圾收集,默認(rèn)是15次)的對象則進(jìn)入老年的。
當(dāng)堆中分配的對象實例過多,且大部分對象都在使用,就會報內(nèi)存溢出異常(OutOfMemoneyError)。
五、方法區(qū)(Method Area)
????方法區(qū)存放了要加載的類的信息(如類名、修飾符等)、靜態(tài)變量、構(gòu)造函數(shù)、final定義的常量、類中的字段和方法等信息。方法區(qū)是全局共享的,在一定條件下也會被GC。當(dāng)方法區(qū)超過它允許的大小時,就會拋出OutOfMemory:PermGen Space異常。
????在Hotspot虛擬機(jī)中,這塊區(qū)域?qū)?yīng)持久代(Permanent Generation),一般來說,方法區(qū)上執(zhí)行GC的情況很少,因此方法區(qū)被稱為持久代的原因之一,但這并不代表方法區(qū)上完全沒有GC,其上的GC主要針對常量池的回收和已加載類的卸載。在方法區(qū)上進(jìn)行GC,條件相當(dāng)苛刻而且困難.
????運(yùn)行時常量池(Runtime Constant Pool)是方法區(qū)的一部分,用于存儲編譯器生成的常量和引用。一般來說,常量的分配在編譯時就能確定,但也不全是,也可以存儲在運(yùn)行時期產(chǎn)生的常量。比如String類的intern()方法,作用是String類維護(hù)了一個常量池,如果調(diào)用的字符"hello"已經(jīng)在常量池中,則直接返回常量池中的地址,否則新建一個常量加入池中,并返回地址。
java8中已經(jīng)沒有方法區(qū)了,取而代之的是元空間(Metaspace)。
六:直接內(nèi)存
????直接內(nèi)存(Direct Memory)并不是虛擬機(jī)運(yùn)行時數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域,但是這部分內(nèi)存也被頻繁地使用,而且也可能導(dǎo)致OutOfMemoryError異常出現(xiàn)。
????JDK1.4加的NIO中,ByteBuffer有個方法是allocateDirect(int capacity) ,這是一種基于通道(Channel)與緩沖區(qū)(Buffer)的I/O方式,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存,然后通過一個存儲在Java堆里面的DirectByteBuffer對象作為這塊內(nèi)存的引用進(jìn)行操作。這樣能在一些場景中顯著提高性能,因為避免了在Java堆和Native堆中來回復(fù)制數(shù)據(jù)。
參考鏈接:http://www.itdecent.cn/p/a60d6ef0771b
GC 機(jī)制
隨著程序的運(yùn)行,內(nèi)存中的實例對象、變量等占據(jù)的內(nèi)存越來越多,如果不及時進(jìn)行回收,會降低程序運(yùn)行效率,甚至引發(fā)系統(tǒng)異常。
????在上面介紹的五個內(nèi)存區(qū)域中,有3個是不需要進(jìn)行垃圾回收的:本地方法棧、程序計數(shù)器、虛擬機(jī)棧。因為他們的生命周期是和線程同步的,隨著線程的銷毀,他們占用的內(nèi)存會自動釋放。所以,只有方法區(qū)和堆區(qū)需要進(jìn)行垃圾回收,回收的對象就是那些不存在任何引用的對象。
GC機(jī)制的基本算法是:分代收集,這個不用贅述。下面闡述每個分代的收集方法。
年輕代:
在新生代中,使用“停止-復(fù)制”算法進(jìn)行清理,將新生代內(nèi)存分為2部分,1部分 Eden區(qū)較大,1部分Survivor比較小,并被劃分為兩個等量的部分。每次進(jìn)行清理時,將Eden區(qū)和一個Survivor中仍然存活的對象拷貝到 另一個Survivor中,然后清理掉Eden和剛才的Survivor。
這里也可以發(fā)現(xiàn),停止復(fù)制算法中,用來復(fù)制的兩部分并不總是相等的(傳統(tǒng)的停止復(fù)制算法兩部分內(nèi)存相等,但新生代中使用1個大的Eden區(qū)和2個小的Survivor區(qū)來避免這個問題)
由于絕大部分的對象都是短命的,甚至存活不到Survivor中,所以,Eden區(qū)與Survivor的比例較大,HotSpot默認(rèn)是 8:1,即分別占新生代的80%,10%,10%。如果一次回收中,Survivor+Eden中存活下來的內(nèi)存超過了10%,則需要將一部分對象分配到 老年代。用-XX:SurvivorRatio參數(shù)來配置Eden區(qū)域Survivor區(qū)的容量比值,默認(rèn)是8,代表Eden:Survivor1:Survivor2=8:1:1.
老年代:
老年代存儲的對象比年輕代多得多,而且不乏大對象,對老年代進(jìn)行內(nèi)存清理時,如果使用停止-復(fù)制算法,則相當(dāng)?shù)托?。一般,老年代用的算法是?biāo)記-整理算法,即:標(biāo)記出仍然存活的對象(存在引用的),將所有存活的對象向一端移動,以保證內(nèi)存的連續(xù)。
在發(fā)生Minor GC時,虛擬機(jī)會檢查每次晉升進(jìn)入老年代的大小是否大于老年代的剩余空間大小,如果大于,則直接觸發(fā)一次Full GC,否則,就查看是否設(shè)置了-XX:+HandlePromotionFailure(允許擔(dān)保失敗),如果允許,則只會進(jìn)行MinorGC,此時可以容忍內(nèi)存分配失敗;如果不允許,則仍然進(jìn)行Full GC(這代表著如果設(shè)置-XX:+Handle PromotionFailure,則觸發(fā)MinorGC就會同時觸發(fā)Full GC,哪怕老年代還有很多內(nèi)存,所以,最好不要這樣做)。
方法區(qū)(永久代):
永久代的回收有兩種:常量池中的常量,無用的類信息,常量的回收很簡單,沒有引用了就可以被回收。對于無用的類進(jìn)行回收,必須保證3點:
- 類的所有實例都已經(jīng)被回收
- 加載類的ClassLoader已經(jīng)被回收
- 類對象的Class對象沒有被引用(即沒有通過反射引用該類的地方)
永久代的回收并不是必須的,可以通過參數(shù)來設(shè)置是否對類進(jìn)行回收。HotSpot提供-Xnoclassgc進(jìn)行控制
使用-verbose,-XX:+TraceClassLoading、-XX:+TraceClassUnLoading可以查看類加載和卸載信息
-verbose、-XX:+TraceClassLoading可以在Product版HotSpot中使用;
-XX:+TraceClassUnLoading需要fastdebug版HotSpot支持
類加載機(jī)制
如下圖所示,JVM類加載機(jī)制分為五個部分:加載,驗證,準(zhǔn)備,解析,初始化,下面我們就分別來看一下這五個過程。

1、加載
加載是類加載過程中的一個階段,這個階段會在內(nèi)存中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的入口。注意這里不一定非得要從一個Class文件獲取,這里既可以從ZIP包中讀?。ū热鐝膉ar包和war包中讀?。部梢栽谶\(yùn)行時計算生成(動態(tài)代理),也可以由其它文件生成(比如將JSP文件轉(zhuǎn)換成對應(yīng)的Class類)。
站在Java開發(fā)人員的角度來看,類加載器可以大致劃分為以下三類:
-
啟動類加載器:
Bootstrap ClassLoader,跟上面相同。它負(fù)責(zé)加載存放在JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下,或被-Xbootclasspath參數(shù)指定的路徑中的,并且能被虛擬機(jī)識別的類庫(如rt.jar,所有的java.*開頭的類均被Bootstrap ClassLoader加載)。啟動類加載器是無法被Java程序直接引用的。 -
擴(kuò)展類加載器:
Extension ClassLoader,該加載器由sun.misc.Launcher$ExtClassLoader實現(xiàn),它負(fù)責(zé)加載JDK\jre\lib\ext目錄中,或者由java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫(如javax.*開頭的類),開發(fā)者可以直接使用擴(kuò)展類加載器 -
應(yīng)用程序類加載器:
Application ClassLoader,該類加載器由sun.misc.Launcher$AppClassLoader來實現(xiàn),它負(fù)責(zé)加載用戶類路徑(ClassPath)所指定的類,開發(fā)者可以直接使用該類加載器,如果應(yīng)用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認(rèn)的類加載器。
image.png
JVM通過雙親委派模型進(jìn)行類的加載,當(dāng)然我們也可以通過繼承java.lang.ClassLoader實現(xiàn)自定義的類加載器。
當(dāng)一個類加載器收到類加載任務(wù),會先交給其父類加載器去完成,因此最終加載任務(wù)都會傳遞到頂層的啟動類加載器,只有當(dāng)父類加載器無法完成加載任務(wù)時,才會嘗試執(zhí)行加載任務(wù)。
采用雙親委派的一個好處是比如加載位于rt.jar包中的類java.lang.Object,不管是哪個加載器加載這個類,最終都是委托給頂層的啟動類加載器進(jìn)行加載,這樣就保證了使用不同的類加載器最終得到的都是同樣一個Object對象。
2、驗證
驗證的目的是為了確保Class文件中的字節(jié)流包含的信息符合當(dāng)前虛擬機(jī)的要求,而且不會危害虛擬機(jī)自身的安全。不同的虛擬機(jī)對類驗證的實現(xiàn)可能會有所不同,但大致都會完成以下四個階段的驗證:文件格式的驗證、元數(shù)據(jù)的驗證、字節(jié)碼驗證和符號引用驗證。
-
文件格式的驗證:
驗證字節(jié)流是否符合Class文件格式的規(guī)范,并且能被當(dāng)前版本的虛擬機(jī)處理,該驗證的主要目的是保證輸入的字節(jié)流能正確地解析并存儲于方法區(qū)之內(nèi)。經(jīng)過該階段的驗證后,字節(jié)流才會進(jìn)入內(nèi)存的方法區(qū)中進(jìn)行存儲,后面的三個驗證都是基于方法區(qū)的存儲結(jié)構(gòu)進(jìn)行的。 -
元數(shù)據(jù)驗證:
對類的元數(shù)據(jù)信息進(jìn)行語義校驗(其實就是對類中的各數(shù)據(jù)類型進(jìn)行語法校驗),保證不存在不符合Java語法規(guī)范的元數(shù)據(jù)信息。 -
字節(jié)碼驗證:
該階段驗證的主要工作是進(jìn)行數(shù)據(jù)流和控制流分析,對類的方法體進(jìn)行校驗分析,以保證被校驗的類的方法在運(yùn)行時不會做出危害虛擬機(jī)安全的行為。 -
符號引用驗證:
這是最后一個階段的驗證,它發(fā)生在虛擬機(jī)將符號引用轉(zhuǎn)化為直接引用的時候(解析階段中發(fā)生該轉(zhuǎn)化,后面會有講解),主要是對類自身以外的信息(常量池中的各種符號引用)進(jìn)行匹配性的校驗。
3、準(zhǔn)備
準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些內(nèi)存都將在方法區(qū)中分配。對于該階段有以下幾點需要注意:
- 這時候進(jìn)行內(nèi)存分配的僅包括類變量(static),而不包括實例變量,實例變量會在對象實例化時隨著對象一塊分配在Java堆中。
- 這里所設(shè)置的初始值通常情況下是數(shù)據(jù)類型默認(rèn)的零值(如0、0L、null、false等),而不是被在Java代碼中被顯式地賦予的值。
4、解析
解析階段是指虛擬機(jī)將常量池中的符號引用替換為直接引用的過程。
解釋一下符號引用和直接引用的概念:
- 符號引用與虛擬機(jī)實現(xiàn)的布局無關(guān),引用的目標(biāo)并不一定要已經(jīng)加載到內(nèi)存中。各種虛擬機(jī)實現(xiàn)的內(nèi)存布局可以各不相同,但是它們能接受的符號引用必須是一致的,因為符號引用的字面量形式明確定義在Java虛擬機(jī)規(guī)范的Class文件格式中。
- 直接引用可以是指向目標(biāo)的指針,相對偏移量或是一個能間接定位到目標(biāo)的句柄。如果有了直接引用,那引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在。
5、初始化
初始化是類加載過程的最后一步,到了此階段,才真正開始執(zhí)行類中定義的Java程序代碼。在準(zhǔn)備階段,類變量已經(jīng)被賦過一次系統(tǒng)要求的初始值,而在初始化階段,則是根據(jù)程序員通過程序指定的主觀計劃去初始化類變量和其他資源,或者可以從另一個角度來表達(dá):初始化階段是執(zhí)行類構(gòu)造器<clinit>()方法的過程。
參考鏈接:http://www.importnew.com/25295.html
https://blog.csdn.net/ns_code/article/details/17881581
數(shù)據(jù)類型
一、基本數(shù)據(jù)類型
Java基本類型共有八種,基本類型可以分為四類,字符類型char,布爾類型boolean以及數(shù)值類型byte、short、int、long、float、double。數(shù)值類型又可以分為整數(shù)類型byte、short、int、long和浮點數(shù)類型float、double。JAVA中的數(shù)值類型不存在無符號的,它們的取值范圍是固定的,不會隨著機(jī)器硬件環(huán)境或者操作系統(tǒng)的改變而改變。
基本類型的分類以及他們對應(yīng)的包裝類:
- 整數(shù)型: byte (java.lang.Byte) 、short (java.lang.Short)、 int (java.lang.Integer)、 long (java.lang.Long)
- 浮點型: float (java.lang.Float)、 double (java.lang.Double)
- 布爾型: boolean (java.lang.Boolean)
- 字符型: char (java.lang.Character)
| 序號 | 數(shù)據(jù)類型 | 大小/位 | 封裝類 | 默認(rèn)值 | 可表示數(shù)據(jù)范圍 |
|---|---|---|---|---|---|
| 1 | byte(位) | 8 | Byte | 0 | -128~127 |
| 2 | short(短整數(shù)) | 16 | Short | 0 | -32768~32767 |
| 3 | int(整數(shù)) | 32 | Integer | 0 | -2147483648~2147483647 |
| 4 | long(長整數(shù)) | 64 | Long | 0L | -9223372036854775808~9223372036854775807 |
| 5 | float(單精度) | 32 | Float | 0.0F | 1.4E-45~3.4028235E38 |
| 6 | double(雙精度) | 64 | Double | 0.0D | 4.9E-324~1.7976931348623157E308 |
| 7 | char(字符) | 16 | Character | 空 | 0~65535 |
| 8 | boolean | 8 | Boolean | flase | true或false |
boolean t = true;
boolean f = false;
char c = '1';
char d; //局部變量需要初始化值,否則會編譯報錯
System.out.println(c + 0); //輸出49
對于數(shù)值類型的基本類型的取值范圍,我們無需強(qiáng)制去記憶,因為它們的值都已經(jīng)以常量的形式定義在對應(yīng)的包裝類中了。
基本類型存儲在棧中,因此它們的存取速度要快于存儲在堆中的對應(yīng)包裝類的實例對象
所有基本類型(包括void)的包裝類都使用了final修飾,因此我們無法繼承它們擴(kuò)展新的類,也無法重寫它們的任何方法。
- 基本類型的優(yōu)勢:數(shù)據(jù)存儲相對簡單,運(yùn)算效率比較高。
- 包裝類的優(yōu)勢:自帶方法豐富,集合的元素必須是對象類型,體現(xiàn)了Java一切皆是對象的思想。
public static void main(String[] args) {
Integer a = 1;
Integer b = 1;
System.out.println(a==b); //結(jié)果為true
Integer c = 200;
Integer d = 200;
System.out.println(c==d); //結(jié)果為false
}
拆箱
將包裝類變?yōu)榛緮?shù)據(jù)類型,例如
Integer i = 2;
int j = i;
參考連接:https://blog.csdn.net/bingduanlbd/article/details/27790287
https://blog.csdn.net/thebigdipperbdx/article/details/81047288
二、String是基本的數(shù)據(jù)類型嗎?
String不是基本數(shù)據(jù)類型,而是一個類(class),是Java編程語言中的字符串。String對象是char的有序集合,并且該值是不可變的。因為java.lang.String類是final類型的,因此不可以繼承這個類、不能修改這個類。為了提高效率節(jié)省空間,我們應(yīng)該用StringBuffer類(線程安全),或者StringBuilder(非線程安全)。
java 中String 是個對象,是引用類型
- 基礎(chǔ)類型與引用類型的區(qū)別是,基礎(chǔ)類型只表示簡單的字符或數(shù)字,引用類型可以是任何復(fù)雜的數(shù)據(jù)結(jié)構(gòu)
- 基本類型僅表示簡單的數(shù)據(jù)類型,引用類型可以表示復(fù)雜的數(shù)據(jù)類型,還可以操作這種數(shù)據(jù)類型的行為
- java虛擬機(jī)處理基礎(chǔ)類型與引用類型的方式是不一樣的,對于基本類型,java虛擬機(jī)會為其分配數(shù)據(jù)類型實際占用的內(nèi)存空間,而對于引用類型變量,他僅僅是一個指向堆區(qū)中某個實例的指針。
不能用“==”去判斷兩個String是否相等
特點一:String常量也是對象,在加載期就被產(chǎn)生,放到數(shù)據(jù)段的字符串常量池當(dāng)中。
特點二:String對象一旦產(chǎn)生,內(nèi)容不可更改;每次改變都是產(chǎn)生了一個新的對象,這個特點導(dǎo)致了String的效率不高(做字符串拼接的時候,每次拼接都要產(chǎn)生新的String對象)
String s1 = "hello";
String s2 = new String("hello");
System.out.println(s1 == s2); //結(jié)果是false
System.out.println(s1.equals(s2)); //結(jié)果是true
1,S1首先查看字符串 abc 是否存在字符串常量池中,如果存在則直接指向,不存在,則創(chuàng)建一個
2,S2 查看常量池中 abc 是否存在,結(jié)果已經(jīng)存在了,就直接指向引用了
區(qū)別:
- 前者s1 創(chuàng)建了兩個對象 堆內(nèi)存中的new String 和 字符串常量區(qū)的 abc
- s2只創(chuàng)建了一個對象 abc 如果存在 abc 那就一個對象也沒創(chuàng)建
同時 String s = "abc";
字符串 abc作為一個對象也可以調(diào)用String類的方法
訪問控制
public:
具有最大的訪問權(quán)限,可以訪問任何一個在classpath下的類、接口、異常等。它往往用于對外的情況,也就是對象或類對外的一種接口的形式。
protected:
主要的作用就是用來保護(hù)子類的。它的含義在于子類可以用它修飾的成員,其他的不可以,它相當(dāng)于傳遞給子類的一種繼承的東西
default:
有時候也稱為friendly,它是針對本包訪問而設(shè)計的,任何處于本包下的類、接口、異常等,都可以相互訪問,即使是父類沒有用protected修飾的成員也可以。
private:
訪問權(quán)限僅限于類的內(nèi)部,是一種封裝的體現(xiàn),例如,大多數(shù)成員變量都是修飾符為private的,它們不希望被其他任何外部的類訪問。

public class PublicTest {
//public 標(biāo)記了這個方法可以被開放調(diào)用,不受地點限制
//static 標(biāo)記了這個方法為static靜態(tài)的,隨著class被初始化,可以通過類直接調(diào)用
//void 標(biāo)記了這個方法的返回為void或者不需要手動返回執(zhí)行結(jié)果
public static void launchNuclearBomb(String operateParam) {
System.out.println("BOMB !");
}
protected static void sayHello(String name) {
System.out.println("hello , " + name);
}
//String 標(biāo)記這個方法執(zhí)行完了需要給調(diào)用方返回一個類型為String 的對象
private String getDate(){
return new Date().toString();
}
}
流程控制
if else
Integer a = 2;
if(a > 1){
System.out.println("a > 1");
}else{
System.out.println("a < 1");
}
for
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
whlie
int count = 0;
while (count < 10){
System.out.println(count);
count ++; //count = count + 1
}
switch case
public class Test {
public static void main(String args[]){
//char grade = args[0].charAt(0);
char grade = 'C';
switch(grade)
{
case 'A' :
System.out.println("優(yōu)秀");
break;
case 'B' :
case 'C' :
System.out.println("良好");
break;
case 'D' :
System.out.println("及格");
break;
case 'F' :
System.out.println("你需要再努力努力");
break;
default :
System.out.println("未知等級");
}
System.out.println("你的等級是 " + grade);
}
}
