Java 的特點#
- 面向對象。封裝、多態、繼承
- 較好的相容性(Java 虛擬機實現跨平台,一次編譯,多平台使用)
- 支持多線程
- 支持網路編程
- 編譯與解釋並存
Write Once, Run Anywhere
JVM#
Java 虛擬機,是運行 Java 字節碼的虛擬機。JVM 有針對不同系統的特定實現,目的是使用相同的字節碼,得到相同的運行結果,是一次編譯,處處運行的體現。
JRE 和 JDK#
JDK(Java Development Kit),是全功能的 Java SDK。除了包含 JRE 之外,還有編譯器 javac,工具 javadoc,jdb。
JRE 是 Java 程序運行時的環境,是運行已編譯 Java 程序所需內容的集合。包含 JVM,Java 類庫,Java 命令和一些基礎構件。正常情況下只需要運行 Java 程序使用 JRE 足夠,如果需要開發或者編譯 Java 程序則必須使用 JDK。
如果使用 JSP 部署 WEB 程序,則需要 JDK。因為應用程序伺服器會將 JSP 轉換為 Java Servlet,需要 JDK 來編譯 Servlet。
字節碼#
JVM 可以理解的代碼叫做字節碼,擴展名為.class 的文件。
字節碼解決了傳統解釋型語言執行效率低的問題,同時保留了解釋型語言可以值的提點。
編譯與解釋並存#
編譯型:通過編譯器一次性將所有代碼翻譯成機器碼。執行速度快但開發效率低。如 C、C++、Go、Rust。
解釋型:通過解釋器一句一句將代碼解釋成機器碼再執行。開發快但執行速度慢。如 Python,PHP,JS。
Java 語言即具有編譯型語言的特徵,也具有解釋型語言的特徵。Java 程序需要經過先編譯,再解釋兩個過程。
通過編譯生成字節碼.class 文件,再由 Java 解釋器解釋執行。
基礎語法#
continue、break 和 return#
continue:跳出當前這一次循環,繼續執行下一次循環。
break:跳出整個循環體,繼續執行循環體下面的代碼。
return:退出當前方法,結束該方法的執行。
成員變量和局部變量#
- 語法形式:成員變量屬於類,局部變量屬於方法。成員變量可以被 public、private、static 修飾符修飾,局部變量不行。兩者都能被 final 修飾。
- 存儲方式:如果成員變量被 static 修飾,這個成員變量屬於類,如果沒有被 static 修飾,成員變量屬於實例。對象存儲於堆中,局部變量存儲在棧中。
- 生存時間;成員變量是對象的一部分,隨著對象的創建而創建。局部變量隨著方法調用生成,隨方法效用結束而結束。
- 默認值:成員變量如果沒有被賦初始值,會自動以類型的默認值賦值(例如 boolen 默認 false,int 默認 0,string 默認 null)。局部變量不會自動賦值。
靜態變量#
靜態變量可以被類的所有實例共享。無論一個類創建了多少個對象,它們都共享同一份靜態變量。
通常情況下,靜態變量會被 final
關鍵字修飾成為常量。
字符常量和字符串常量#
- 形式 : 字符常量是單引號引起的一個字符,字符串常量是雙引號引起的 0 個或若干個字符。
- 含義 : 字符常量相當於一個整型值 (ASCII 值), 可以參加表達式運算;字符串常量代表一個地址值 (該字符串在內存中存放位置)。
- 佔內存大小 : 字符常量只佔 2 個字節;字符串常量佔若干個字節。
靜態方法為什麼不能調用非靜態成員#
靜態方法屬於類,在類加載時被創建。非靜態成員屬於實例,當對象被實例化時創建。
靜態方法先於非靜態成員創建。
靜態方法和實例方法區別#
調用方式:靜態方法通過類名.方法名
調用。實例方法通過對象.方法名
調用。
重寫和重載#
重載#
只能在同一個類中,方法名必須相同,參數類型、個數、順序不同,方法返回值,訪問修飾符可以不同。
重寫#
發生在運行時
- 方法名,參數列表必須相同,返回類型相同
- 如果父類訪問修飾符為
private/final/static
則子類就不能重寫該方法
基本數據類型#
8 種數據類型#
- 6 種數字類型
- 4 種整數型:
int\short\byte\long
- 2 種浮點型:
float\double
- 4 種整數型:
- 1 種字符類型:
char
- 1 種布爾型:
boolen
默認值及所佔空間大小#
基本類型 | 位數 | 字節 | 默認值 | 取值範圍 |
---|---|---|---|---|
byte | 8 | 1 | 0 | -128 ~ 127 |
short | 16 | 2 | 0 | -32768 ~ 32767 |
int | 32 | 4 | 0 | -2147483648 ~ 2147483647 |
long | 64 | 8 | 0L | -9223372036854775808 ~ 9223372036854775807 |
char | 16 | 2 | 'u0000' | 0 ~ 65535 |
float | 32 | 4 | 0f | 1.4E-45 ~ 3.4028235E38 |
double | 64 | 8 | 0d | 4.9E-324 ~ 1.7976931348623157E308 |
boolean | 1 | 1 | false | true、false |
long
類型需要在數值後加上L
,否則將作為整型解析
char a = 'h'
char : 單引號,String a = "hello"
: 雙引號。
基本類型和包裝類型區別#
- 基本類型的局部變量存放在棧中,基本類型的成員變量存放在堆中。包裝類型屬於對象,存放在堆中。
- 包裝類型默認值為 null。
- 包裝類型可用於泛型,基本類型不可以。
包裝類型的緩存機制#
Byte
,Short
,Integer
,Long
這 4 種包裝類默認創建了數值 [-128,127] 的相應類型的緩存數據Character
創建了數值在 [0,127] 範圍的緩存數據Boolean
直接返回True
orFalse
Float
,Double
沒有緩存
如果定義新變量超出範圍則會創建新的對象
拆箱與裝箱#
- 裝箱:將基本類型用它們對應的引用類型包裝起來;
- 拆箱:將包裝類型轉換為基本數據類型;
為什麼浮點數運算會丟失精度#
無限循環小數在計算機中保存是會被截斷,導致小數精度丟失。
如何解決浮點數精度丟失#
使用BigDecimal
超過lang
類型的數據如何表示#
使用BigInteger
面向對象基礎#
面向對象和面向過程#
- 面向過程:將問題解決過程拆解成一個個方法,通過一個個方法的執行解決問題。
- 面向對象:先抽出對象,用對象執行方法的方式解決問題。
對象實體和對象引用#
對象引用:對象的實例化
通過 new 創建對象實例(對象實例存放在堆中),對象引用指向對象實例(對象引用存放在棧中)。
一個對象引用可以指向 0 或 1 個對象。
一個對象可以有 n 個引用指向它。
構造方法#
完成對象的初始化,在實例化一個對象時,後面的括號說明調用的是無參構造方法。
如果沒有聲明構造方法,默認會有不帶參構造方法。如果聲明了有參構造方法,會覆蓋默認無參構造方法,需要手動再次聲明無參構造方法。
構造方法必須與類名相同,不可以重寫,但可重載。
面向對象的特性,封裝、繼承、多態#
封裝#
封裝是指將一個對象的屬性,隱藏在對象內部,不允許外部對象直接訪問對象的內部信息。但是可以通過一些允許被外部訪問的方法來操作屬性方法get(),set()
來操作屬性。
繼承#
- 子類擁有父類所有的屬性和方法,但是無法訪問父類中私有屬性和方法無法訪問。
- 子類可以創建新的屬性和方法,對父類進行擴展。
- 子類可以用自己的方式實現父類的方法。
多態#
- 編譯時多態:指方法的重載
- 運行時多態:程序中定義的對象引用所指向的具體類型在運行期間才確定
多態存在的條件:繼承、重寫、父類引用指向子類對象 (例如:Animal a = new Cat()
)
接口和抽象類#
相同點:
- 不能被實例化
- 可以包含抽象方法
- 可以有默認的實現方法(使用 default 關鍵字在接口中定義默認方法)
不同點:
- 單繼承,多實現
- 接口中成員變量只能是
public static final
類型的,且必須有初始值。抽象類的成員變量默認 default,可以在子類中被重新定義,賦值。
深拷貝、淺拷貝、引用拷貝#
- 淺拷貝:會在堆上創建一個新的對象,如果原對象內部屬性是引用類型的話,淺拷貝會直接地址內部對象的引用地址,即拷貝對象和原對象共用同一個內部對象。
- 深拷貝:完全複製整個對象,包括這個對象包含的內部對象。
- 引用拷貝:兩個不同的引用指向同一個對象。
String Object#
== 和 equals () 區別#
- == 判斷引用是否指向堆內存的同一塊地址
- equals 比較堆中的內容是否相同,不比較地址
重寫 hashCode () 必須重寫 equals ()#
因為不同對象的 hashCode 可能相同,但是 hashCode 不同的對象一定不相同
如果只重寫一個,可能會出現 equals 相同但是 hashCode 不相同
使用 HashSet 時,先判斷 HashCode 是否相同,如果相同再使用 equals 方法判斷是否相同。
String、StringBuffer、StringBuilder#
StringBuffer
對方法加了同步鎖或者對調用的方法加了同步鎖,所以是線程安全。StringBuilder
沒有對方法進行加同步鎖,所以線程不安全。String
不可變(保存字符串的數組被final
修飾且為私有的),每次對String
類型進行改變的時候,會生成一個新的String
對象,然後將指針指向新的String
對象。StringBuffer
每次都會對StringBuffer
對象本身進行操作,而不是生成新的對象並改變對象引用。
使用:
- 操作少量的數據:適用
String
- 單線程操作字符串緩衝區下操作大量數據:適用
StringBuilder
- 多線程操作字符串緩衝區下操作大量數據:適用
StringBuffer
字符串拼接不能使用+
#
如果使用+
拼接,實際上是通過StringBuilder
調用append()
方法實現,拼接之後調用toString()
得到String
對象。在循環體內使用+
拼接時,每循環一次就會創建一個StringBuilder
對象,如果手動使用StringBuilder
就不會有這個問題。
String.equals () 和 Object.equals ()#
-
String
中的equals
方法是被重寫過的,比較的是 String 字符串的值是否相等。 -
Object
的equals
方法是比較的對象的內存地址。
字符串常量池#
字符串常量池作用是避免重複創建 String 類導致內存消耗專門開闢的區域。
當創建一個 String 類型對象時,會將對象的內容保存到堆中,將對象的引用保存到常量池。再次創建相同的 String 對象時,如果堆中已經有相同的對象,則直接返回常量池中保存的引用。
String s1 = new String ("abc") 會創建幾個字符串對象#
如果字符串 "abc" 之前已經創建過了,則會創建 1 個字符串對象。如果字符串 "abc" 之前未創建,則會創建 2 個字符串對象
intern 方法#
intern 方法將指定的字符串對象的引用保存在字符串常量池
- 如果字符串常量池中保存了對應的字符串對象的引用,就直接返回該引用。
- 如果字符串常量池中沒有保存了對應的字符串對象的引用,那就在常量池中創建一個指向該字符串對象的引用並返回。
String 類型的變量和常量做 “+” 運算#
- 如果是編譯期可以確定值的字符串,JVM 會將拼接後的字符串引用放入常量池。
例如:String str3 = "str" + "ing"
會優化成 String str3 = "string";
- 如果是編譯期間無法確定的字符串,
StringBuilder
調用append()
之後在調用toString()
得到一個String
異常#
Exception 和 Error#
Java 中主要由兩種異常 Exception 和 Error。
- Exception: 程序本身可以處理的異常,可以通過 catch 捕獲。Exception 有分為 Checked Exception受檢查異常,必須處理 和 Unchecked Exception 不受檢查異常,可以不處理
- Error:程序無法處理的異常,出現 Error 會導致線程終止。例如 Java 虛擬機運行異常,內存溢出,堆棧溢出等。
Checked Exception 和 Unchecked Exception#
- Checked Exception 受檢查異常,在編譯過程中,如果檢查出受檢查異常沒有被catch或者throws,編譯將無法通過。
- 除了 RuntimeException 及其子類外,其他 Exception 都屬於受檢查異常。
- Unchecked Exception 不受檢查異常,在編譯過程中可以不處理。
- RuntimeException 及其子類都是不受檢查異常,常見的有:空指針、數組越界等
Throwable 類常用方法#
getMessage()
返回異常的簡要描述toString()
返回異常詳細信息getLocalizedMessage()
返回異常對象本地化信息printStackTrace()
在控制台打印出 Throwable 對象封裝的異常信息
try-catch-finally 使用#
- try:用戶捕獲異常,後必須要接 catch 或者 finally 中一個
- catch:處理 try 捕獲到的異常
- finally:無論是否捕獲到或處理異常,finally 都會被執行。如果 try 或者 catch 中有 return,finally 會在方法返回之前執行。
finally 是否一定會執行#
不一定,如果 finally 之前 JVM 終止運行,finally 就不會執行,後者程序所在的線程死亡,或者 cpu 關閉
泛型#
泛型使用的方式#
- 泛型類
- 泛型接口
- 泛型方法
反射#
什麼是反射#
在程序運行過程中,動態獲取一個對象所屬的類,獲取類的成員變量和方法,調用類的屬性和方法
什麼地方用到了反射#
spring 通過註解 @Component 獲取到類的信息,屬性,參數等。
如何使用反射獲取一個類#
-
透過
類名.class
Class alunbarClass = TargetObject.class;
-
透過
Class.forName(“類名”)
Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");
-
透過方法
Object.getClass()
TargetObject o = new TargetObject(); Class alunbarClass2 = o.getClass();
-
透過類加載器
xxxClassLoader.loadClass()
傳入類路徑獲取:ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");
註解#
什麼是註解#
可以修飾類、方法、變量,用於提供信息給程序編譯或運行期間使用
註解解析的方式#
- 編譯期間掃描
- 運行期間通過反射處理
引用類型#
通過引用類型來判斷對象是否可以被垃圾回收器回收♻️
強引用#
通過new
關鍵字創建的對象,是強引用,強引用的對象不會被 GC 回收。
軟引用#
軟引用對象,會在內存不足時被回收。
通過SoftReference
關鍵字可以創建軟引用對象
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
弱引用#
弱引用對象會被直接回收,創建的弱引用對象只能存活到下次 GC 發生之前。
可以通過WeakReference
關鍵字創建弱引用對象
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
虛引用#
不能通過虛引用獲取一個對象,虛引用不會對對象產生任何影響。
虛引用的作用是:虛引用的對象被 GC 回收時可以收到一個通知。
可以通過PhantomReference
關鍵字創建虛引用對象。
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);