【零基礎 快速學Java】韓順平 零基礎30天學會Java

1、java跨平臺的原因
java文件利用javac命令編譯生成相應的class文件,利用java命令通過java虛擬機(JVM)解釋運行。JVM屏蔽了底層OS的區(qū)別,實現(xiàn)了“一次編譯,全平臺執(zhí)行”的功能。JVM包含在JDK當中,JDK版本代表著java版本。

2、JDK中有什么東西?


3、JDK安裝細節(jié)
安裝路徑不能含有中文或者特殊字符比如空格
公共JRE最好安裝,某些IDE可能會用到
4、配置環(huán)境變量path

首先,設置一個變量JAVA_HOME,對應的值設置為JDK安裝主目錄的絕對路徑。
然后,在path中,加入一個值“%JAVA_HOME%\bin”。
如果以前配置過JDK的話,需要刪除之前的緩存,見書簽https://www.cnblogs.com/lastaz3/p/14853291.html。
5、使用sublime和javac編碼問題
cmd使用的是GBK編碼,所以,編輯器sublime也需要以GBK的方式保存java文件,這樣cmd讀取的時候才不會出錯。
6、文件名和類名
一個文件一個類,文件名和類名需要完全相同,如果一個文件里的類名為hello,那么源文件就叫hello.java,編譯后的文件叫做hello.class,這點和C語言不同。

7、執(zhí)行的是類而不是文件
執(zhí)行時,輸入java hello而不是java hello.class,因為你本身運行的就是一個類,而不是class這個文件,所以不需要再加上class

8、java執(zhí)行流程



9、java helloworld的注意事項和細節(jié)說明

一個.java文件只能有一個public類,但其他類的個數(shù)不限,.java文件名和public類名必須相同。編譯后,每一個類都對應一個.class文件。
在一個文件中,寫多個類,只有一個public類

經(jīng)過編譯后,每個類生成一個.class文件,文件名和類名相同

調(diào)用非public類的main方法,如下

10、快速學習新技術或者知識點的心得分享

11、幾個比較特殊的轉(zhuǎn)移字符
println引號內(nèi)的斜杠\,也需要使用轉(zhuǎn)義字符\\,如果要打印兩個斜杠\\,則需要兩個轉(zhuǎn)義字符,共四個斜杠\\\\
英文雙引號也一樣,需要將左引號和右引號都換成轉(zhuǎn)義字符\"。中文雙引號不用。英文單引號需要使用轉(zhuǎn)義字符,中文單引號不用。

注意看上圖的代碼,在第一部分,可以看到\t為什么叫制表符,打印效果確實如同表格一般對齊了。
第二部分,則是英文引號和中文引號的區(qū)別,英文引號需要使用轉(zhuǎn)義字符,而中文不用。
12、回車與換行的區(qū)別
回車,轉(zhuǎn)義字符是\r,作用是將光標移至本行最開頭位置,后面的輸出會從該位置繼續(xù)輸出。
換行,轉(zhuǎn)義字符是\n,換到下一行最開頭。
回車+換行 = 換行,也相當于敲一下回車鍵。
13、初學java易犯錯誤

路徑問題注意相對路徑的問題
14、java代碼規(guī)范

對齊需要使用縮進,而不是空格
源文件需要使用utf8編碼
大括號的兩種風格:按照大括號的左邊的位置,分為次行風格和行尾風格(推薦)
15、第二章作業(yè)

16、java中的+號的用法
數(shù)值+數(shù)值=數(shù)值
操作數(shù)有字符串,則做拼接運算。
運算順序從左往右。
如果有多個加號,只要有一個字符串,那么從字符串往左的數(shù)值做加法,往右的做拼接。

17、java數(shù)據(jù)類型
基本數(shù)據(jù)類型8個,如下:
整型 byte[1],short[2],int[4],long[8]
浮點型 float[4],double[8]
字符型 char[2],存放單個字符,中英文均可,java采用utf16編碼,任何字符都可以用2個字節(jié)編碼
布爾型 boolean[1],true或者false
引用數(shù)據(jù)類型包括數(shù)組、類、接口
18、整型


第二點需要注意,如果直接將一個long類型的常量1L或變量long a賦值給int變量b,編譯會報錯,C則會警告

19、浮點類型



20、java類的組織形式

按照上圖結(jié)構,要找某個類有哪些方法,需要知道這個類在哪個包中,再按照包->類->方法查找。
或者下載的JDK官方文檔中有索引,可以直接查找
21、字符類型



雖然char這個類型本質(zhì)上是對應字符的unicode編碼,是一個數(shù)字,但如果不進行強制類型轉(zhuǎn)換,只會輸出unicode編碼對應的字符,而不是那個數(shù)。

如果字符遇到了加號,相當于整數(shù)遇到了加號
如果字符串遇到了加號,則是拼接操作了。

22、常用的字符編碼




23、布爾類型


24、自動類型轉(zhuǎn)換規(guī)則







注意,同類型的byte進行計算,也會先轉(zhuǎn)換成int,結(jié)果不能再賦給byte


25、強制類型轉(zhuǎn)換規(guī)則
程序員需要主動進行強制類型轉(zhuǎn)換,否則會報錯。強制類型轉(zhuǎn)換會造成溢出或者精度損失。

26、基本數(shù)據(jù)類型和字符串之間的轉(zhuǎn)換






27、關于加號的一個題目

注意兩個char相加,結(jié)果是int類型,輸出碼值
28、取模%的本質(zhì)(重要)


貌似結(jié)果的正負與左邊操作數(shù)符號相同?

29、i = i++

這個問題需要看JVM的運行過程,也就是字節(jié)碼的底層實現(xiàn)邏輯。這里可以用優(yōu)先級和棧來解釋。
根據(jù)棧的先進后出的規(guī)則,對于優(yōu)先級低的運算,其參數(shù)先壓棧,后參與計算,后出棧;優(yōu)先級高的運算,其參數(shù)后壓棧,先參與計算,先出棧。
而++的優(yōu)先級高于=,
這個表達式的具體執(zhí)行過程如下:
首先,將i的值壓棧,=的操作數(shù)入棧了,等待++完成后就出棧,
然后,自增操作將i的值+1,自增操作完成,
然后,=操作開始了,將棧頂?shù)闹蒂x給變量i,=操作的結(jié)果把自增的結(jié)果給覆蓋掉了,
所以,i = i++ 的結(jié)果是i的值不變。
i = ++i 與上述不同的是先將變量自增,然后壓棧,出棧。

上面的結(jié)果???怎樣用棧來實現(xiàn)算術符號優(yōu)先級???編譯原理???
30、關系運算符 instanceof
檢查是否有一個實例,是一個類的對象。會在OOP中講解
31、關系運算符的細節(jié)說明

32、邏輯運算符(重要)


C語言中,
&&和||,這兩個叫做邏輯與、邏輯或,這兩個運算符常用來對兩個操作符整體的邏輯進行運算,操作數(shù)一般是關系表達式,比如a<b這種。存在短路效應。
&和|,這兩個叫做按位與、按位或,常用于位運算,操作數(shù)一般是兩個真實的變量,比如a&b、a|b,結(jié)果一般是一個數(shù)
與C不同,java中不可以用0或者非0的數(shù)來表示true或者false。
所以,||和&&的操作數(shù)必須是boolean類型的常量或者變量,最后結(jié)果也必須是boolean。
但|和&的操作數(shù)可以是boolean,也可以是整形常量或變量。前一種情況和||、&&的運算一樣,結(jié)果一樣,后一種情況則是二進制按位運算,結(jié)果是個整數(shù)。
總結(jié)成這個表格

取反!a的操作數(shù)只能是boolean,不能用于按位取反,按位取反為~a
異或a^b的操作數(shù)既可以是boolean,也可以是整數(shù)
33、一道關于邏輯運算符的練習題

34、復合賦值運算符會進行類型轉(zhuǎn)換

35、運算符的優(yōu)先級

36、標識符的命名規(guī)則和規(guī)范(重要)


37、保留字

38、四種進制

39、原碼、反碼、補碼(重點)

注意,補碼是計算機“看”的數(shù),原碼是人“看”的數(shù)。我們看到的數(shù)(屏幕上打印的結(jié)果)都是原碼,但我們分析計算過程時,需要將原碼轉(zhuǎn)換成補碼再進行分析,這時得到的結(jié)果是補碼,需要再轉(zhuǎn)換成原碼,才能給人看。
注意,java沒有無符號數(shù),全都是有符號數(shù)?。?!這點與C不同?。?!
40、位運算符
計算過程:
原碼變補碼
按位操作
補碼變原碼,原碼就是結(jié)果
注意,按位與、按位或、按位取反、按位異或,都需要對補碼的符號位進行操作。

41、第三章 運算符 作業(yè)


(第二題不考慮了)


42、switch用法

注意,switch()里面不能放boolean?。。?/p>
case后面跟著的多條操作不需要使用大括號

43、編程的兩大思想
化繁為簡:將需求分解,逐步實現(xiàn),將復雜功能轉(zhuǎn)換成基本功能+過濾器
先死后活:為了方便程序后續(xù)的修改,將常量用變量表示(封裝、宏都是這種思想)
44、do……while
do……while 先執(zhí)行一次操作,再進行條件判斷,操作至少執(zhí)行一次;
while和for都是先判斷,后操作,不一定會執(zhí)行操作。
但是,循環(huán)條件是一樣的!
do{}while(表達式1),
while(表達式2),
for(;表達式3;),
同一需求對應的條件表達式是一樣的?。。?/p>
45、break的用法

break + 標簽的用法,與C不同
46、continue用法

有這個用法,但不推薦使用。
47、數(shù)組的初始化
動態(tài)方式:int[] a = new int[5];
int[] a; a = new int[5];(先聲明再new)
靜態(tài)方式:int[] a = new int[]{1,3,7,4,……};
靜態(tài)省略方式:int[] a = {1,3,7,4,……};
動態(tài)方式根據(jù)[]內(nèi)的數(shù)字為數(shù)組動態(tài)分配元素個數(shù),
(一個很重要的區(qū)別:[ ]內(nèi)的數(shù)字可以是變量,C中只能是常量)
靜態(tài)方式根據(jù){}內(nèi)的元素個數(shù)確定元素個數(shù),[]中不能寫數(shù)字,
靜態(tài)方式和靜態(tài)省略方式創(chuàng)建數(shù)組不能先聲明后賦值,只能聲明的同時直接賦值。
數(shù)組的長度(元素個數(shù))可以通過 數(shù)組名.length 來得到。
java中的數(shù)組下標從0開始,和C一樣。
“int[] a = new int[5];” 也可以寫成
"int a[] = new int[5];",前者比較清晰,后面學到二維數(shù)組、字符數(shù)組可能會更清楚一點。
48、數(shù)組的使用細節(jié)

49、數(shù)組的賦值方式

java的數(shù)組直接可以用等號賦值,是引用賦值,實際上是將arr1的地址賦給了arr2。arr1和arr2都相當于指向數(shù)字內(nèi)存地址的兩個符號鏈接。(arr2沒有new,沒有開辟新的空間)
注意值傳遞和引用傳遞的區(qū)別,聯(lián)想到C的函數(shù)傳參(傳值or傳地址)

注意?。?!java中的數(shù)組是在堆中開辟空間存儲的?。?!棧中只存放指向?qū)獌?nèi)存的地址鏈接。

https://www.cnblogs.com/wangyage/p/7434703.html
傳值的方式則需要重新new一個數(shù)組,用for循環(huán)完成數(shù)組拷貝
50、數(shù)組擴容和縮小
重新new一個數(shù)組,用for循環(huán)進行數(shù)組拷貝,將原數(shù)組指向新數(shù)組。但這樣操作效率太低,可以考慮鏈表。
51、二維數(shù)組的初始化

法一:int arr[][] = new int[2][3];(或者 int[][] arr = new int[2][3];)
法二:int[][] arr; arr = new int[2][3];
法三:如下圖

法四:靜態(tài)初始化
int[][] arr = {{1,2}, {1}, {1,2,3}}; //一維數(shù)組的元素數(shù)可以不同


52、類與對象簡介

由于單獨變量和數(shù)組有以上缺點,為滿足新的需求,創(chuàng)造了對象這個概念。對象的主要內(nèi)容包括屬性和行為。
類與對象的關系:類就像變量類型,是模板,是共性;對象就像是具體的變量,是實例。
屬性,也叫成員變量 / 字段 / field
53、對象在內(nèi)存中的存在形式



創(chuàng)建對象時,
首先,會加載類的信息到方法區(qū),主要是屬性和行為信息。
然后,為對象分配空間。在堆中,為基本類型的變量分配空間,存儲變量的值;在方法區(qū)的常量池中,為字符串分配空間,存儲字符串的值,相應地會在堆中存儲常量池中字符串的地址。
最后,棧中存儲著堆中對象空間的地址。
(有點一二級指針的意思)
54、創(chuàng)建對象的兩種方式
先聲明再創(chuàng)建
Cat cat;
cat = new Cat();
直接創(chuàng)建
Cat cat = new Cat();
55、對象的引用傳遞

56、null
null表示空指針,目前出現(xiàn)在
String已定義未賦值時,默認值為null
對象已聲明未分配空間時,默認值為null
57、成員方法的介紹


58、二維數(shù)組作參數(shù)

59、成員方法的注意事項和使用細節(jié)


需要注意的點就是返回類型和參數(shù)類型都可以是類(具體方法?)或者數(shù)組,數(shù)組不需要像C考慮指針的問題了,比較方柏霓
同一個類中的方法不能嵌套定義。

同類方法調(diào)用:同一類中的一個方法可以直接調(diào)用該類的另一個方法。
跨類方法調(diào)用:調(diào)用發(fā)起方需要創(chuàng)建被調(diào)用類的對象,然后再通過 “對象.方法” 進行調(diào)用。
60、成員方法的傳參機制
基本數(shù)據(jù)類型的傳參和修改,不會影響調(diào)用者中原變量的值
引用數(shù)據(jù)類型(數(shù)組和對象)傳遞的是地址,可以通過形參影響實參。
放一個例子

(上面的調(diào)用其實有點別扭,使用成員方法時不需要將對象傳參,下面比較正常)

比較上面兩個成員方法及調(diào)用過程,得出使用成員方法的時候,發(fā)起調(diào)用的對象已經(jīng)隱藏地成為了形參之一,無需聲明,可以直接對成員變量進行修改。
61、遞歸的規(guī)則

62、方法重載
方法名必須相同,形參(類型或者數(shù)量)必須不同,返回值類型無所謂
方法名和參數(shù)列表共同構成了方法簽名。通過方法名和參數(shù)列表可以唯一確定一個方法,所以同一個類中,沒有哪個方法的方法名和參數(shù)列表是完全一樣的。

println()就是重載的方法,后面的形參可以是不同類型的變量,這樣就可以將多個不同操作的方法集合到一個方法名之下。將相似有微小區(qū)別的功能集成,方便使用。
63、可變參數(shù)

可變參數(shù)也可以用方法重載實現(xiàn),但可變參數(shù)更簡潔。

注意,可變參數(shù)可以和固定參數(shù)一起出現(xiàn)在參數(shù)列表中。但是必須固定參數(shù)在前,可變參數(shù)在后,且只能有一個可變參數(shù)。
可變參數(shù)對應的實參可以是一個個單個的變量,也可以是一個該類型的數(shù)組。

可變參數(shù)的本質(zhì)是數(shù)組。
64、作用域



65、構造方法/構造器
構造方法又叫構造器(constructor),是類的一種特殊的方法,主要作用是完成對新對象的初始化,在創(chuàng)建對象時,系統(tǒng)會自動調(diào)用該類的構造器完成對象的初始化。

構造器可以重載。
構造器只是完成對象的初始化,而不是創(chuàng)建對象,對象空間已經(jīng)分配好了。對象沒有自定義的構造器也可以。

未定義構造器時,會執(zhí)行默認構造器,Person() {}。
有構造器時,也可以顯式地寫出該默認構造器,相當于構造器重載,不會引起方法重復的沖突。
但是,有自定義構造器而沒有顯式定義默認構造器時,new的成員必須要帶自定義構造器的參數(shù),不能無參,否則報錯。這時候類中就沒有默認構造器了,這就是“覆蓋”的含義。(如下圖)



顯式定義默認構造器的時候,可以寫方法體。

66、對象創(chuàng)建流程分析
首先,new創(chuàng)建對象,
將類信息加載到方法區(qū),
在堆中為對象開辟空間,存儲基本變量類型的值,存默認值,
數(shù)組的創(chuàng)建流程不變,只是最后數(shù)組的內(nèi)存地址會放入堆中對象分配給數(shù)組的那個內(nèi)存空間中,
將字符串類型的數(shù)據(jù)放入方法區(qū)的常量池中(有的話),將對應地址放入堆中對象非陪給字符串的內(nèi)存空間,字符串沒初始化的該位置置為null,
最后,將堆中的對象內(nèi)存地址放入棧中,
對象創(chuàng)建完成。
之后,通過構造器進行成員變量的初始化
67、this
this應用場景:
全局變量和局部變量同名且同時出現(xiàn)在局部變量的作用域內(nèi),用this.name來指代全局變量,用name表示局部變量。解決了一個作用域內(nèi)變量不能重名的問題。
當然,不管是否變量重名,都可以使用this。
this的使用范圍也不光限定在構造器。
對this的解釋:
this相當于一個對象的隱藏屬性,存儲著對象的內(nèi)存地址,指向?qū)ο笞约骸?/p>
哪個對象調(diào)用,this就指代那個對象。看到this,自動轉(zhuǎn)換成對象名就行。

this注意事項:


this的一個典型用法:涉及兩個對象,用this指代調(diào)用者。

68、一個關于作用域的錯誤

69、IDEA快捷鍵



模板快捷鍵:
main
fori
sout
70、包
包的本質(zhì):
通過創(chuàng)建不同的文件夾,保存不同的類 。
包的簡單使用:
屬于包的java文件需要在開頭聲明所在包: package 本包名。
一個類只能有一個package語句。
調(diào)用包中類的兩種情況:
不屬于包的java文件,開頭通過import調(diào)用其他包里的類。
沒有用import聲明的,在new的時候類名前面必須帶上包名
舉個例子:

有兩個包,com.lyl 和 com.shy,
有兩個包中類com.lyl.TestPackageLyl 和 com.lyl.TestPackageShy,
兩個類都有一個名為pout的方法。


在java文件中,各創(chuàng)建這兩個類的一個對象,并調(diào)用pout() 方法。
一個是import的,一個是沒有import的

可以看到,經(jīng)過import的包,可以直接用類名,
沒有import的包,必須在類名前加上報名,要寫完整。
71、包的命名
命名規(guī)則:
與變量命名類似,但包可以用小圓點區(qū)分上下級目錄,類似文件路徑的/
命名規(guī)范:

72、常用的包

73、包的使用細節(jié)


順序為:package、import、class
74、import用法
1、import語句用來完成導入其它類,同一個包下的類不需要導入,不在同一個包下需要手動導入。
2、import語法格式:
import 類名;
import 包名.*;
3、import語句需要編寫到package語句之下,class語句之上。
4、java.lang.*:lang:language語言包,是java語言的核心類。不需要手動引入,系統(tǒng)自動引入。
5、什么時候需要import?
不是非java.lang包下,并且不在同一個包下的時候,需要使用import進行引入。
75、訪問修飾符

注:子類指的是不同包中的子類,同包的子類算在同包中


只有默認、public可以修飾類,四種訪問修飾符都可以修飾對象的成員和方法,規(guī)則相同。
76、封裝
只能通過對象的方法對對象的屬性進行操作,而不可以直接對屬性進行修改,對屬性的修改行為僅限于提供的public方法。

getter setter快速創(chuàng)建的快捷鍵:alt + insert
77、將構造器與setter結(jié)合:
上述的散步封裝還有一個漏洞:如果調(diào)用者在new的時候直接調(diào)用構造器,那么就可以繞過setter方法的驗證邏輯,進行不合法的操作;
將構造器中的復制操作也調(diào)用setter操作即可。
78、繼承
將多個類中共同的屬性和方法提取出來,作為父類。子類通過extends關鍵字繼承父類的共性,只需要添加自己的特有屬性和方法。提高代碼的復用性。
子類也叫派生類,父類也叫基類、超類。

繼承的基本語法:
class 子類 extends 父類 {}
79、繼承的細節(jié)問題
(1)子類繼承了所有的屬性和方法,非私有屬性和方法可以在子類直接訪問,但私有屬性和方法不能在子類直接訪問,要通過父類提供的非私有的方法去訪問。
(2)子類必須先調(diào)用父類的構造器,完成公共部分(父類)的初始化。然后調(diào)用子類的構造器,完成子類特有屬性的初始化。子類中隱藏了super(); 這句指令,由編譯器負責。
(3)創(chuàng)建子類時,不管調(diào)用子類的哪個構造器,默認都先會調(diào)用父類的無參構造器。如果父類沒有提供無參構造器(只有有參構造器,沒有顯式定義無參構造器),則必須在子類的每一個構造器中都使用 super(父類構造器的參數(shù)) 指定父類的有參構造器,否則編譯報錯。
(4)如果希望顯式地調(diào)用父類的某個構造器,則需要在子類構造器中指定。命令:super(父類相應構造器的參數(shù))
(5)super在使用的時候,必須放在子類構造器的第一行
(6)super(參數(shù)列表) 和 this(參數(shù)列表) (this調(diào)用構造器時的用法)都只能放在構造器第一行,兩者不能存在于一個構造器。
(7)java所有類都是Object類的子類,Object類是所有類的父類
(8)父類構造器的調(diào)用不限于直接父類,將一直往上追溯直到Object類的構造器。
(9)子類最多只能繼承一個父類(直接繼承)。如何讓A類繼承B類和C類?A繼承B,B繼承C
(10)不能濫用繼承,子類和父類必須滿足is-a的邏輯關系。
80、繼承的本質(zhì)
new子類的過程中,構造器的調(diào)用順序是從最高父類Object類開始調(diào)用構造器,然后一級級父類調(diào)用各自的構造器,最后子類調(diào)用自己的構造器。
但是,訪問屬性和方法時,則是從子類開始,向更高級的父類搜索,直到Object類。類似于就近原則。

如果子類和父類有相同名稱的屬性,那么會就近選擇子類的屬性。如果屬性
不可訪問,那么會報錯,不會考慮上一級的相同屬性。
子類的方法和父類的方法可以完全相同(同方法名、同參數(shù)列表),和屬性一樣,也是就近原則。如果不可訪問,則會報錯,不會考慮上一級的相同的方法。
注意,更高級父類的同名屬性不會被直接訪問到(除非通過公開的方法間接訪問),但是仍然存在,在內(nèi)存中分配了空間。有沒有和是否可以訪問是兩回事。
81、this、super調(diào)用構造器
兩者調(diào)用構造器的語法均只能出現(xiàn)在構造器的第一行:其他方法不行,其他行不行。
并且,只能在第一行有一個this或者super,不能有第二個。
區(qū)別在于this是調(diào)用this所指代的類的對應構造器,super是調(diào)用父類的對應構造器。
82、繼承的例題
題目:

答案:

先調(diào)用B()。
由于B()第一行有this,所以不會有super,this調(diào)用B(String name)。
B(String name) 第一行沒有this,且B為子類,所以隱藏著super(),調(diào)用A()。
A為父類,不會有隱藏的super(),所以這就是最開始的語句了。
輸出順序:A() --> B(String name) --> B()
83、super關鍵字
super代表父類的引用,用于訪問父類的屬性、方法、構造器

針對1和2,子類在不考慮屬性名、方法名相同的情況下,不用super也可以訪問父類的非private屬性和方法。
但如果父類和子類的屬性、方法重名,那么只能通過super來訪問父類。


84、方法重寫/覆蓋(override)
子類通過重寫父類的方法,實現(xiàn)功能的替換。
要求:
子類和父類重寫的方法的方法名、參數(shù)列表完全一樣,否則就不是重寫的關系
子類和父類的返回值類型可以相同(基本數(shù)據(jù)類型),或者子類的返回類型是父類返回類型的子類(對象)
子類方法不能縮小父類方法的訪問權限。
85、重寫和重載的比較

86、數(shù)組的length 和字符串的length()
比較方便地理解,數(shù)組一旦創(chuàng)建,它的長度是固定的,所以length可以看成是數(shù)組的一個屬性。
而字符串背后是由char數(shù)組實現(xiàn)的,char數(shù)組已經(jīng)具有了length的屬性,字符串也有l(wèi)ength,但經(jīng)過封裝不能直接訪問,所以用公開的length()獲得。實際上,length()也是return length
87、多態(tài)
(1)方法的多態(tài)
重寫和重載就體現(xiàn)多態(tài)
(2)對象的多態(tài)

編譯類型:來自對象名前的類名,來自定義
運行類型:變量實際指向的對象類型,來自new
這里類似于指針,指針的地址空間就存儲著一個地址,指針本身的類型是不會變的,但是指針卻可以指向不同類型的變量(類型不同可以使用強轉(zhuǎn))。
有點類似于不同類之間的強轉(zhuǎn)。
對象通過不斷修改運行類型,可以調(diào)用運行類型對象的方法。
編譯類型和運行類型的提出,使得方法傳參時實參和形參的對象類型不必嚴格相同,提高方法的復用性。


這兩張圖中,Dog和Cat都是Animal的子類。
在feed方法中,定義的形參是Animal,而調(diào)用時傳入的實參卻是Dog和Cat這兩個子類。
將形參定義成編譯類型,實參寫成運行類型,利用對象的多態(tài)提高了方法的復用性。
對象多態(tài)的前提:兩個對象(類)存在繼承關系
(3)多態(tài)的實質(zhì)
方法的訪問順序,先子類后父類

88、向上轉(zhuǎn)型

向上轉(zhuǎn)型的父類可以是直接父類,也可以是更高級的父類(比如Object類)
在編譯階段,能調(diào)用哪些成員,哪些方法,是由編譯類型決定的。
向上轉(zhuǎn)型時,編譯時是父類,父類不能調(diào)用子類特有的方法和變量。
運行時就是子類了,調(diào)用父類的成員需要遵守訪問權限,同時運行時還是遵循重寫規(guī)則的那一套,同名變量或方法從子類到父類逐級搜索,就近采用。
總結(jié),向上轉(zhuǎn)型的調(diào)用規(guī)則:
(下面的私有指的是本類可調(diào)用,其他類不可調(diào)用,而不僅僅private)
父類中的
非私有方法和變量可以直接調(diào)用
私有方法不可以調(diào)用,私有變量通過非私有方法間接調(diào)用
子類中的
特有(父類中沒有的,不是重寫的)方法和變量不可以調(diào)用,與父類重名的方法和變量可以調(diào)用
子類的變量、方法與父類的變量、方法重名時
遵循重寫、繼承的規(guī)則進行訪問(就近原則)
注意,通過這條規(guī)則,可以訪問到子類中和父類重名的非私有變量和方法
89、向下轉(zhuǎn)型

向下轉(zhuǎn)型的目標是經(jīng)過向上轉(zhuǎn)型的父類引用(即指向子類對象的父類引用)
向上轉(zhuǎn)型是子類的對象當父類用。
向下轉(zhuǎn)型是對向上轉(zhuǎn)型的補充,向下轉(zhuǎn)型是將向上轉(zhuǎn)型的父類引用轉(zhuǎn)換成原本的子類引用,當子類用。
注意,只能將父類引用轉(zhuǎn)換成原本的子類對象的引用,而不能是新的子類對象的引用。因為新的子類對象沒創(chuàng)建。
問題是這么麻煩的過程,實際工作時會經(jīng)常用嗎?很離譜的規(guī)則
90、屬性重寫
屬性沒有重寫之說,重復屬性選擇哪個取決于編譯類型
91、instanceof 操作符
用法:aa instanceof BB
作用:判斷對象aa的運行類型是否為BB類型或者BB類型的子類型,返回true或者false
92、 多態(tài)例題1

考察基本類型的強轉(zhuǎn)和對象的向上向下轉(zhuǎn)型的命令格式
注意Object obj = "Hello";
這里不是常見的new創(chuàng)建對象,因為是String類型,所以直接賦值了,但實際也是創(chuàng)建了對象,并使用了一個父類的引用指向子類的對象,所以是向上轉(zhuǎn)型。
93、多態(tài)例題2

注意最后一個,b的運行類型是Sub,所以會先從子類中找方法。
注意,如果子類沒有display(),父類有,那么會輸出count=10,調(diào)用父類的重名屬性。子類是繼承而不是復制父類的成員和方法,繼承的實質(zhì)還是引用。子類調(diào)用父類方法時,相當于在父類屬性的作用域當中,同名首選父類屬性。
94、多態(tài)例題3
class A {??
??public String show(D obj){??
??????return ("A and D");??
??}??
??public String show(A obj){??
??????return ("A and A");??
??}??
}??
class B extends A{??
??public String show(B obj){??
??????return ("B and B");??
??}??
??public String show(A obj){??
??????return ("B and A");??
??}??
}??
class C extends B{}??
class D extends B{}??
public class Testduotai {
public static void main(String[] args) {
??A a1 = new A();??
??A a2 = new B();??
??B b = new B();??
??C c = new C();??
??D d = new D();??
??System.out.println("1"+a1.show(b));???
??System.out.println("2"+a1.show(c));??
??System.out.println("3"+a1.show(d));??
??System.out.println("4"+a2.show(b));???
??System.out.println("5"+a2.show(c));??
??System.out.println("6"+a2.show(d));??
??System.out.println("7"+b.show(b));???
??System.out.println("8"+b.show(c));???
??System.out.println("9"+b.show(d));??
??System.out.println("10"+d.show(a1));?
??System.out.println("11"+d.show(a2));?
??System.out.println("11"+d.show(b));?
??System.out.println("12"+d.show(c));?
??
}
??
}
運行結(jié)果:
1A and A
2A and A
3A and D
4B and A
5B and A
6A and D
7B and B
8B and B
9A and D
10B and A
11B and A
11B and B
12B and B
結(jié)果解析:
https://www.cnblogs.com/lylhome/p/15795075.html
95、動態(tài)綁定機制
當調(diào)用對象方法時,該方法會和該對象的內(nèi)存地址/運行類型綁定
當調(diào)用對象屬性時,沒有動態(tài)綁定機制,哪里聲明(在哪個變量的作用域內(nèi))哪里使用
調(diào)用屬性的規(guī)則,常見的有兩種情景,
直接調(diào)屬性,要看對象的編譯類型,
調(diào)用的方法中調(diào)用了屬性,這就要看方法屬于哪個對象了,要調(diào)相應對象的屬性(涉及到作用域)
96、多態(tài)數(shù)組
創(chuàng)建一個父類類型的數(shù)組,這個數(shù)組可以存放父類類型和子類類型(向上轉(zhuǎn)型)對象。先創(chuàng)建一個父類類型的數(shù)組,即一組指向父類類型的引用。然后對里面的每個元素創(chuàng)建一個對象,并指向該對象。這里面有向上轉(zhuǎn)型的使用。
注意這兩個new的作用。

調(diào)用父類方法時,for循環(huán)即可

調(diào)用子類方法時,for循環(huán)里面if+instanceof+向下轉(zhuǎn)型即可

首先,通過if + instanceof + 向下轉(zhuǎn)型,實現(xiàn)了訪問子類方法的需求。通過 if + instanceof 判斷對象的類型,并調(diào)用對應子類的方法。instanceof判斷的是對象的運行類型(實際指向?qū)ο蟮念愋停?/p>
而向下轉(zhuǎn)型恰好是將編譯類型強轉(zhuǎn)成運行類型,根據(jù)上面的結(jié)果可以就可以確定運行類型了。
其次,需要先判斷元素是否是子類類型,然后再判斷是否是父類類型,越小的子類越先判斷。
判斷的先后順序不能錯,因為instanceof只要是目標類或其子類,就會返回true。為了區(qū)分到底是目標類還是子類,需要先判斷子類,如果不是,再判斷目標類。
97、多態(tài)參數(shù)
方法的形參類型為父類,傳入的實參類型是子類,是向上傳參的一種情況。
98、== 與 equals方法
==是一個比較運算符
==既可以判斷基本類型,也可以判斷引用類型
用于基本類型時,判斷兩邊的值是否相同
用于引用類型時,判斷引用的是否是同一個對象(引用的對象地址是否相同)
注意,兩個對象的類型不同時(不是同類也不是父子類),不能使用==比較,編譯會報錯
記一個錯誤例子 6 == 6.0 true
equals()是Object類的一個方法
只能對引用類型使用
默認判斷的是地址是否相等。這和==作用相同。
但是子類往往重寫該方法,用于判斷屬性的值是否相等。
String類中的equals(),被重寫為比較字符串的內(nèi)容是否相同,如下圖

Integer類中的equals(),被重寫為比較兩數(shù)是否相同,如下例子

equals()重寫例子


99、查看JDK源碼
100、hashCode方法
簡單了解,后面講集合類會講底層原理并重寫

101、toString方法
Object類中的toString() 會返回字符串:
對象的全類名(報名+類名)+ @ +hashCode的十六進制
子類往往會重寫toString方法,用于返回對象的屬性信息。
重寫例子如下,IDEA可以自動生成了

sout一個對象的時候,會默認調(diào)用toString方法,最終輸出toString方法返回的內(nèi)容
102、finalize方法
該方法目前不推薦使用了,簡單了解即可,實際開發(fā)幾乎不會使用
https://blog.csdn.net/shanghui815/article/details/6787855?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-4.no_search_link&spm=1001.2101.3001.4242.3&utm_relevant_index=7

子類中重寫的finalize方法,IDEA有模板


103、IDEA斷點調(diào)試
注意,在調(diào)試過程中,是運行狀態(tài),是以對象的運行類型來執(zhí)行的。
快捷鍵:
點擊行號 下斷點
F7 步入
F8 步過
shift + F8 跳出
F9 繼續(xù)執(zhí)行至下一斷點

調(diào)試過程中查看JDK源碼 ,如果F7無法跳入源碼,看下圖方法

104、零錢通小項目
(1)實現(xiàn)功能:
基本功能:
面向過程
顯示菜單并進行選擇
顯示明細
記錄收益
記錄消費
退出
改進功能:
退出確認
金額校驗
面向?qū)ο?/p>
(2)先完成顯示菜單及進行選擇
做菜單一般使用do while ,因為菜單最少要顯示一次
循環(huán)里面有顯示菜單的內(nèi)容,還有接受輸入的scanner
根據(jù)接收的數(shù)字key,繼續(xù)不同的功能,使用switch
(3)完成收支明細
一條收支記錄,有以下三種常見的數(shù)據(jù)類型存儲:
數(shù)組,但大小確定,不能動態(tài)增長
對象
字符串拼接,實現(xiàn)最簡單
(4)確認退出機制
要求:用戶輸入4之后,再次確認用戶是否確認退出 y/n,
y退出,n不退出,其他輸入忽略,繼續(xù)詢問是否確認退出 y/n
一段代碼完成一個功能,不要用一段長代碼混在一起完成多個功能。
這樣代碼結(jié)構清晰,方便功能的增加和修改。
要充分了解需求,對需求進行分解,方便之后的維護
本例中,
不建議使用while{if……else if……else}這種結(jié)構,

建議使用while{}先判斷輸入是否是y/n,再根據(jù)輸入用 if 修改退出的標志。

(5)判斷用戶輸入的合法性
兩種思路:
每個if判斷一種不正確的輸入情況,進行相應的錯誤處理。如果過了所有if,則執(zhí)行正確輸入的代碼。
直接用if 判斷是否是正確的情況,如果是,則運行正確輸入的代碼,如果不是,再判斷是哪種錯誤輸入,進行相應的錯誤處理。
老師推薦第一種,因為第一種方便之后加if應對更多的錯誤輸入,而第二種的正確情況需要將正確代碼用{}括起來,可讀性差,結(jié)構復雜。
但是,我覺得,看具體情況吧。第一種如果錯誤情況羅列不全,那就可能有安全問題,第二種則更安全,就是白名單和黑名單的區(qū)別。
這畢竟不是真正的上線項目,有合法性檢查的意識就行了,更多的問題交給測試。
(6)修改成面向?qū)ο?/p>
視頻中的拆分,主要是將整個的方法這份成了幾個方法,只用了一個對象,更像是函數(shù)的拆分。面向?qū)ο蟮娜筇攸c沒體現(xiàn),不過面向CV編程是真得香~~
105、對OOP的一點理解
面向過程的編程,信息的傳遞是通過各個函數(shù)來傳遞的,信息是一份的,
面向?qū)ο蟮木幊?,還多了子類和父類的繼承,通過封裝、繼承、多態(tài),規(guī)定好了子類和父類的訪問規(guī)則,信息是雙份的,這樣的使用更加靈活多變。
學習封裝繼承多態(tài)時,讓我想起了C語言的跨源文件時修飾符、作用域的相關知識,感覺有點像,但后者比不上前者方便。前者只需要一個繼承關系,就可以輕松調(diào)用父類的屬性和方法,而C語言中信息的傳遞只能依靠變量挨個傳遞。
學完OOP,才知道C的繁瑣。只能說jvm是個好東西啊,機器替人干了好多工作。
另外再復習一下多態(tài)和動態(tài)綁定機制

106、房屋出租系統(tǒng)
(1)需求分析
主菜單及選項選擇
顯示房屋信息
添加房屋信息
刪除房屋信息
修改房屋信息
退出系統(tǒng)
與零錢通不同的是,刪除及修改這兩個操作。
修改要求的是查找。
選擇怎樣的數(shù)據(jù)結(jié)構來適應查找的要求?
刪除要求的是查找,刪除操作。(刪除是一種特殊的修改)
選擇怎樣的數(shù)據(jù)結(jié)構來適應查找的要求?
其對應的刪除操作怎么做?
刪除后造成的碎片怎么辦?
怎樣將前后兩部分拼接到一起?
這兩個問題導致我們不能再選擇和零錢通一樣的字符串作為信息存儲的數(shù)據(jù)結(jié)構了,那么還有兩種選擇:數(shù)組、對象。
(2)設計程序框架圖(非常重要)
采用簡單的分層模式

分為界面、業(yè)務層、數(shù)據(jù)層

(3)根據(jù)框架圖,創(chuàng)建相應文件

為每一層建立一個子包,每個子包中存放每層需要的多個java文件。
這個項目比較簡單,每個子包只有一個java文件。
但要有這個意識,做好以后擴展的準備,并且同包與不同包差別很大。
(4)工具類的使用
先寫工具類,造輪子

static,靜態(tài)方法,可以通過 類名.方法名 調(diào)用

這里主要包括了接收并處理輸入用戶輸入的方法、循環(huán)確認Y/N的方法。
(5)顯示菜單與退出功能
以此為例,分析完成一個功能的思路和過程

(6)顯示房屋信息
這里注意每一層的作用
業(yè)務層向界面返回數(shù)據(jù)(這里不是直接打印數(shù)據(jù))
界面調(diào)用業(yè)務層的方法,接收數(shù)據(jù),并打印
各司其職

(7)添加房屋信息
界面接收用戶輸入的信息,創(chuàng)建House對象,id=1,發(fā)送給業(yè)務層
業(yè)務層接收House對象,修改其房屋id,加入數(shù)組
從這里開始設置了主鍵,即id。主鍵是唯一且自增的,用戶無法指定主鍵。這樣,主鍵就可以作為區(qū)分房屋信息的”指紋“了。后面用到的查找、刪除、修改都以查找為基礎,都需要使用主鍵查找。
要注意數(shù)組下標(房屋數(shù))和已分配id的關系。
單純的考慮添加功能,下標和id是有確定關系的,因為每增加一行房屋信息,下標和id都會+1。
但是,如果考慮刪除功能,那么數(shù)組中間的元素可能會被刪除,這時id不變,而下標(房屋數(shù))-1,這樣兩者關系就亂了。
所以需要設置變量分別記錄房屋數(shù)和已分配id,在增刪的操作中對兩者進行不同的操作。
(8)查找房屋信息
界面接收用戶輸入的id,發(fā)送給業(yè)務層
業(yè)務層接收id,根據(jù)查找的id,順序遍歷,將查找到的對象返回給界面(未找到則為null)
由于id是唯一的,這里就可以根據(jù)id查找房屋信息了。
設計的數(shù)組是id從小到大的(因為刪除某個id后,后面元素依次前移)所以可以考慮二分查找
(9)刪除房屋信息
界面接收用戶輸入的id,發(fā)送給業(yè)務層
業(yè)務層接收id,對數(shù)組進行查找,找到則刪除,返回true,沒找到則返回false。
刪除操作,因為id是房屋信息的唯一標識,所以房屋信息被刪除后,該id也被刪除并棄用。
對于刪除產(chǎn)生的空洞,將后面的房屋信息依次前移一位,始終保持數(shù)組前部是連續(xù)的房屋信息,后部全是null。
這樣操作雖然刪除后修補麻煩,但查找簡單。這樣的操作保證了數(shù)組下標(非null)始終等于房屋數(shù)-1,遍歷時的i上界為房屋數(shù)-2,且順序遍歷遇不到空指針,二分查找id還是遞增的。
(10)修改房屋信息
界面接收用戶輸入的id,發(fā)送給業(yè)務層
業(yè)務層接收id,對數(shù)組進行查找,找到則返回House對象,沒找到則返回null
界面顯示查找結(jié)果,如果查找到了對應id,則繼續(xù)接收用戶輸入的修改信息,根據(jù)這些信息創(chuàng)建House對象,再發(fā)送給業(yè)務層
業(yè)務層接收對象,將指向舊House對象的引用指向新的,完成修改。
注意先返回查找結(jié)果,再進行修改。不是一開始就獲取所有信息進行修改。在輸入修改信息時有舊信息參照,也方便修改。這里查找并返回結(jié)果的邏輯不能省。
(11)總結(jié)
我在做項目之前在(1)中考慮了這些問題:
與零錢通不同的是,刪除及修改這兩個操作。
修改要求的是查找。
選擇怎樣的數(shù)據(jù)結(jié)構來適應查找的要求?
刪除要求的是查找,刪除操作。(刪除是一種特殊的修改)
選擇怎樣的數(shù)據(jù)結(jié)構來適應查找的要求?
其對應的刪除操作怎么做?
刪除后造成的碎片怎么辦?
怎樣將前后兩部分拼接到一起?
總結(jié)起來就是考慮查找效率、刪除后的元素移動、擴容這幾個的問題。
在這個項目中,選擇了數(shù)組來存放房屋信息,
刪除后,數(shù)組后半段集體前移,保證前面都是房屋信息,后面都是null,不會出現(xiàn)null和有效信息交錯的情況。雖然移動的操作麻煩,但查找就簡單了。
查找可以用順序查找。由于引入了id,元素有序,并且刪除后保證數(shù)組前段都是有效信息,可以二分查找
擴容,添加信息之前會判斷數(shù)組是否有空位。在發(fā)現(xiàn)數(shù)組滿了之后,創(chuàng)建2倍長度的數(shù)組,對原數(shù)組內(nèi)容進行復制,在新數(shù)組中進行添加操作。
這里突然想到了,如果不斷刪除,應該也要縮容。
引入id和始終保持數(shù)組有序緊致,這兩點是我在操作過程中沒有想到的。
另外,最大的收獲因該是嘗試在項目中應用分層模式。
分析需求,確定有哪幾個要完成的需求功能。
分層,確定每一層的層級功能是什么(需求功能和層級功能是兩個方向上的,類似橫向和縱向),針對層級功能,確定每一層需要哪幾個類。
將一個需求功能分解到不同層。針對這一個需求功能,每一層需要干什么。并確定相應的變量和方法。
具體實現(xiàn)。再考慮每一層的方法的具體實現(xiàn)。
將需求功能一個一個完成,最終實現(xiàn)所有需求。
107、類變量/靜態(tài)變量
定義語法:訪問修飾符 static 數(shù)據(jù)類型 變量名;
(在class 類名{}中定義)
調(diào)用語法:類名.變量名
注意!
對靜態(tài)變量的訪問也需要符合訪問修飾符的訪問權限及范圍
?即使沒有創(chuàng)建對象實例,也可以直接訪問
用static修飾的變量,被叫做靜態(tài)變量。靜態(tài)變量在內(nèi)存中是獨一份的,共享的。
108、靜態(tài)變量的位置
首先,需要知道類信息被存儲在方法區(qū)中,并在堆中創(chuàng)建一個Class實例,每個類只有一個,相當于該類的一個模板,創(chuàng)建對象時依照每個類的class實例創(chuàng)建對象。
其次,靜態(tài)變量的位置與JDK版本有關。JDK8之前,靜態(tài)變量放在方法區(qū)中,JDK8之后,靜態(tài)變量放在堆中的對應類的Class實例中。但不管哪個版本,靜態(tài)變量都是唯一且被該類的對象共享的。
然后,靜態(tài)變量是在類加載的時候就生成的,早于實例對象的創(chuàng)建。即使沒有創(chuàng)建對象實例,也可以直接調(diào)用。
最后,靜態(tài)變量的生命周期與類相同。隨類的加載而開始,隨類的消亡而銷毀。
109、靜態(tài)方法/類方法
定義語法:訪問修飾符 static 數(shù)據(jù)返回類型 方法名() {}
(在class 類名{}中定義)
調(diào)用語法:類名.方法名
注意!
對靜態(tài)方法的訪問也需要符合訪問修飾符的訪問權限及范圍
?即使沒有創(chuàng)建對象實例,也可以直接訪問
110、類方法的使用
經(jīng)典使用場景:工具類

實際上,如果方法中不涉及任何對象的成員,也就是說,該方法涉及的數(shù)據(jù)類型是基本數(shù)據(jù)類型。這就更像C語言中的函數(shù)了。
注意事項:


this.靜態(tài)變量名(x)
super.靜態(tài)變量名(x)
類名.靜態(tài)變量名(√)當在構造器中出現(xiàn)靜態(tài)變量時,為防止與參數(shù)同名,就可以用這種方法表示
靜態(tài)方法如果想訪問本類的非靜態(tài)成員或方法,那么需要先創(chuàng)建對象,再調(diào)用即可
111、理解main方法的語法

注意,java中的命令行參數(shù)與C語言不同,args[ ]默認為空,而不會自動將exe的絕對路徑存儲在args[0]中。

112、代碼塊


代碼塊在加載類和創(chuàng)建對象(無論哪個構造器)時都會調(diào)用。
代碼塊的順序是在構造器之前,但類中的多個代碼塊和變量定義語句之間是順序執(zhí)行的關系,誰在前誰先執(zhí)行,誰在后誰后執(zhí)行。
113、代碼塊的使用細節(jié)
什么時候調(diào)用?
1)static代碼塊只有第一次類加載的時候才會被執(zhí)行。
普通代碼塊每創(chuàng)建一個對象,就會執(zhí)行一次。

補充:加載子類時,父類也會被加載,并且父類先加載。
舉例如下:

創(chuàng)建兩個對象

結(jié)果如下:

static代碼塊在類加載的時候被執(zhí)行,且只執(zhí)行一次:
創(chuàng)建了兩個對象,DD類加載了兩次,但是static代碼塊只執(zhí)行了一次。
普通代碼塊每創(chuàng)建一個對象,就會執(zhí)行一次:
創(chuàng)建了兩個對象實例,則執(zhí)行兩次普通代碼塊。

這一點和上面說的一個意思,使用靜態(tài)成員會引起類加載,但只有類加載情況之一的創(chuàng)建對象才能引起普通代碼塊執(zhí)行,使用靜態(tài)成員則不會。
調(diào)用順序是什么?
單個類中調(diào)用順序如下:

如果有繼承關系時,調(diào)用順序如下:

原理如下:
先加載類,創(chuàng)建子類對象時,肯定會先加載父類和子類,先父類后子類,所以先調(diào)用父類的static代碼塊,后調(diào)用子類的static代碼塊
然后創(chuàng)建對象,由于構造器默認首行包含super(); 會先調(diào)用父類構造器,而父類的普通代碼塊的執(zhí)行順序又在父類構造器之前,子類的普通代碼塊的執(zhí)行順序在子類構造器之前,所以,順序依次為父類普通代碼塊、父類構造器、子類普通代碼塊、子類構造器。
實際上在子類構造器中,第一行是默認的suoer();,后面則是默認隱藏的子類的普通代碼塊,然后才是子類構造器的語句。

完整的例子如下:




調(diào)用順序總結(jié)如下:


訪問權限及范圍

114、代碼塊的一個例題

這個題的執(zhí)行順序每什么好說的,但是Test類中有一個成員變量是靜態(tài)對象sam。sam創(chuàng)建對象時,需要通過new來調(diào)用構造器,那么構造器是靜態(tài)方法嗎?
首先,構造方法通過new操作符在靜態(tài)方法中被調(diào)用。靜態(tài)方法可以在未創(chuàng)建對象的時候調(diào)用,但是構造方法也可以在未創(chuàng)建對象的時候調(diào)用。從這個角度來看,構造方法算是個靜態(tài)方法。其次,靜態(tài)方法不能調(diào)用實例成員,而構造方法內(nèi)部就可以調(diào)用實例變量和方法。這么看,構造方法又是個非靜態(tài)方法。總之,這個問題的答案很難說,目前記住構造方法可以在靜態(tài)方法中調(diào)用即可。
115、單例設計模式


適用于那些核心的、非常消耗資源、但又只需一個實例的類。
餓漢式單例模式


比較神奇的一點是,在類的內(nèi)部存在該類的對象作為成員,有點套娃的感覺,想起了C語言中,鏈表節(jié)點(結(jié)構體套結(jié)構體指針)的形式。
懶漢式單例模式
單例模式的對象通常是重量級的對象,很耗資源,如果先創(chuàng)建而不用,是對資源的一種浪費。所以改進為使用時現(xiàn)場創(chuàng)建。

懶漢式的getInstance是被調(diào)用時,現(xiàn)場new對象的。
