banner
Violet

Violet's Blog

A Web <Developer />. Code_ for Fun.
x
github
bilibili
email

Java語言基礎

原文地址

Java 的特點#

  1. 面向對象。封裝、多態、繼承
  2. 較好的相容性(Java 虛擬機實現跨平台,一次編譯,多平台使用)
  3. 支持多線程
  4. 支持網路編程
  5. 編譯與解釋並存

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 關鍵字修飾成為常量。

字符常量和字符串常量#

  1. 形式 : 字符常量是單引號引起的一個字符,字符串常量是雙引號引起的 0 個或若干個字符。
  2. 含義 : 字符常量相當於一個整型值 (ASCII 值), 可以參加表達式運算;字符串常量代表一個地址值 (該字符串在內存中存放位置)。
  3. 佔內存大小 : 字符常量只佔 2 個字節;字符串常量佔若干個字節。

靜態方法為什麼不能調用非靜態成員#

靜態方法屬於類,在類加載時被創建。非靜態成員屬於實例,當對象被實例化時創建。

靜態方法先於非靜態成員創建。

靜態方法和實例方法區別#

調用方式:靜態方法通過類名.方法名調用。實例方法通過對象.方法名調用。

重寫和重載#

重載#

只能在同一個類中,方法名必須相同,參數類型、個數、順序不同,方法返回值,訪問修飾符可以不同。

重寫#

發生在運行時

  1. 方法名,參數列表必須相同,返回類型相同
  2. 如果父類訪問修飾符為 private/final/static 則子類就不能重寫該方法

基本數據類型#

8 種數據類型#

  • 6 種數字類型
    • 4 種整數型:int\short\byte\long
    • 2 種浮點型:float\double
  • 1 種字符類型:char
  • 1 種布爾型:boolen

默認值及所佔空間大小#

基本類型位數字節默認值取值範圍
byte810-128 ~ 127
short1620-32768 ~ 32767
int3240-2147483648 ~ 2147483647
long6480L-9223372036854775808 ~ 9223372036854775807
char162'u0000'0 ~ 65535
float3240f1.4E-45 ~ 3.4028235E38
double6480d4.9E-324 ~ 1.7976931348623157E308
boolean11falsetrue、false

long類型需要在數值後加上L,否則將作為整型解析

char a = 'h'char : 單引號,String a = "hello" : 雙引號。

基本類型和包裝類型區別#

  • 基本類型的局部變量存放在棧中,基本類型的成員變量存放在堆中。包裝類型屬於對象,存放在堆中。
  • 包裝類型默認值為 null。
  • 包裝類型可用於泛型,基本類型不可以。

包裝類型的緩存機制#

  • Byte,Short,Integer,Long 這 4 種包裝類默認創建了數值 [-128,127] 的相應類型的緩存數據
  • Character 創建了數值在 [0,127] 範圍的緩存數據
  • Boolean 直接返回 True or False
  • Float,Double 沒有緩存

如果定義新變量超出範圍則會創建新的對象

拆箱與裝箱#

  • 裝箱:將基本類型用它們對應的引用類型包裝起來;
  • 拆箱:將包裝類型轉換為基本數據類型;

為什麼浮點數運算會丟失精度#

無限循環小數在計算機中保存是會被截斷,導致小數精度丟失。

如何解決浮點數精度丟失#

使用BigDecimal

超過lang類型的數據如何表示#

使用BigInteger

面向對象基礎#

面向對象和面向過程#

  • 面向過程:將問題解決過程拆解成一個個方法,通過一個個方法的執行解決問題。
  • 面向對象:先抽出對象,用對象執行方法的方式解決問題。

對象實體和對象引用#

對象引用:對象的實例化

通過 new 創建對象實例(對象實例存放在堆中),對象引用指向對象實例(對象引用存放在棧中)。

一個對象引用可以指向 0 或 1 個對象。

一個對象可以有 n 個引用指向它。

構造方法#

完成對象的初始化,在實例化一個對象時,後面的括號說明調用的是無參構造方法。

如果沒有聲明構造方法,默認會有不帶參構造方法。如果聲明了有參構造方法,會覆蓋默認無參構造方法,需要手動再次聲明無參構造方法。

構造方法必須與類名相同,不可以重寫,但可重載。

面向對象的特性,封裝、繼承、多態#

封裝#

封裝是指將一個對象的屬性,隱藏在對象內部,不允許外部對象直接訪問對象的內部信息。但是可以通過一些允許被外部訪問的方法來操作屬性方法get(),set()來操作屬性。

繼承#

  • 子類擁有父類所有的屬性和方法,但是無法訪問父類中私有屬性和方法無法訪問。
  • 子類可以創建新的屬性和方法,對父類進行擴展。
  • 子類可以用自己的方式實現父類的方法。

多態#

  • 編譯時多態:指方法的重載
  • 運行時多態:程序中定義的對象引用所指向的具體類型在運行期間才確定

多態存在的條件:繼承、重寫、父類引用指向子類對象 (例如:Animal a = new Cat())

接口和抽象類#

相同點:

  1. 不能被實例化
  2. 可以包含抽象方法
  3. 可以有默認的實現方法(使用 default 關鍵字在接口中定義默認方法)

不同點:

  1. 單繼承,多實現
  2. 接口中成員變量只能是public static final類型的,且必須有初始值。抽象類的成員變量默認 default,可以在子類中被重新定義,賦值。

深拷貝、淺拷貝、引用拷貝#

  • 淺拷貝:會在堆上創建一個新的對象,如果原對象內部屬性是引用類型的話,淺拷貝會直接地址內部對象的引用地址,即拷貝對象和原對象共用同一個內部對象。
  • 深拷貝:完全複製整個對象,包括這個對象包含的內部對象。
  • 引用拷貝:兩個不同的引用指向同一個對象。

image

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 對象本身進行操作,而不是生成新的對象並改變對象引用。

使用:

  1. 操作少量的數據:適用 String
  2. 單線程操作字符串緩衝區下操作大量數據:適用 StringBuilder
  3. 多線程操作字符串緩衝區下操作大量數據:適用 StringBuffer

字符串拼接不能使用+#

如果使用+拼接,實際上是通過StringBuilder調用append()方法實現,拼接之後調用toString()得到String對象。在循環體內使用+拼接時,每循環一次就會創建一個StringBuilder對象,如果手動使用StringBuilder就不會有這個問題。

String.equals () 和 Object.equals ()#

  • String 中的 equals 方法是被重寫過的,比較的是 String 字符串的值是否相等。

  • Objectequals 方法是比較的對象的內存地址。

字符串常量池#

字符串常量池作用是避免重複創建 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);
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。