備戰金三銀四!一線互聯網公司java崗面試題整理:Java基礎+多線程+集合+JVM合集!
前言
回首來看2020年,真的是印象中過的最快的一年了,真的是時間過的飛快,還沒反應過來年就誇完了,相信大家也已經開始上班了!俗話說新年新氣象,馬上就要到了一年之中最重要的金三銀四,之前一直有粉絲要求我整理一些java崗的面試題,年前一直沒時間,這次趁着元旦節給大家整理了一些一線互聯網公司java崗面試題主要是Java基礎+多線程+集合+JVM,滿滿的乾貨放在下面了!內容有點多,大家可以挑選自己需要的部分看!
Java 語⾔有哪些特點?
- 簡單易學;
- ⾯向對象(封裝,繼承,多態);
- 平台⽆關性( Java 虛擬機實現平台⽆關性);
- 可靠性;
- 安全性;
- ⽀持多線程( C++ 語⾔沒有內置的多線程機制,因此必須調⽤操作系統的多線程功能來進⾏多線程程序設計,⽽ Java 語⾔卻提供了多線程⽀持);
⾯向對象和⾯向過程的區別
- ⾯向過程 :⾯向過程性能⽐⾯向對象⾼。 因為類調⽤時需要實例化,開銷⽐⼤,⽐消耗資源,所以當性能是最重要的考量因素的時候,⽐如單⽚機、嵌⼊式開發、Linux/Unix 等⼀般采⽤⾯向過程開發。但是,⾯向過程沒有⾯向對象易維護、易復⽤、易擴展。
- ⾯向對象 :⾯向對象易維護、易復⽤、易擴展。 因為⾯向對象有封裝、繼承、多態性的特性,所以可以設計出低耦合的系統,使系統更加靈活、更加易於維護。但是,⾯向對象性能⽐⾯向過程低。
什麼是java虛擬機
- Java 虛擬機(JVM)是運⾏ Java 位元組碼的虛擬機。JVM 有針對不同系統的特定實現(Windows,Linux,macOS),⽬的是使⽤相同的位元組碼,它們都會給出相同的結果。
什麼是位元組碼?采⽤位元組碼的好處是什麼?
- 在 Java 中,JVM 可以理解的代碼就叫做 位元組碼 (即擴展名為 .class 的⽂件),它不⾯向任何特定的處理器,只⾯向虛擬機。Java 語⾔通過位元組碼的⽅式,在⼀定程度上解決了傳統解釋型語⾔執⾏效率低的問題,同時⼜保留了解釋型語⾔可移植的特點。所以 Java 程序運⾏時⾼效,⽽且,由於位元組碼並不針對⼀種特定的機器,因此,Java 程序⽆須重新編譯便可在多種不同操作系統的計算機上運⾏。
關於JVM和位元組碼
- Java 虛擬機(JVM)是運⾏ Java 位元組碼的虛擬機。JVM 有針對不同系統的特定實現(Windows,Linux,macOS),⽬的是使⽤相同的位元組碼,它們都會給出相同的結果。位元組碼和不同系統的 JVM 實現是 Java 語⾔「⼀次編譯,隨處可以運⾏」的關鍵所在。
JDK 和 JRE
- JDK 是 Java Development Kit,它是功能⻬全的 Java SDK。它擁有 JRE 所擁有的⼀切,還有編譯器(javac)和⼯具(如 javadoc 和 jdb)。它能夠創建和編譯程序。
- JRE 是 Java 運⾏時環境。它是運⾏已編譯 Java 程序所需的所有內容的集合,包括 Java 虛擬機(JVM),Java 類庫,java 命令和其他的⼀些基礎構件。但是,它不能⽤於創建新程序。
Oracle JDK 和 OpenJDK 的對⽐
- Oracle JDK ⼤概每 6 個⽉發⼀次主要版本,⽽ OpenJDK 版本⼤概每三個⽉發佈⼀次。但這不是固定的,我覺得了解這個沒啥⽤處。
- OpenJDK 是⼀個參考模型並且是完全開源的,⽽ Oracle JDK 是 OpenJDK 的⼀個實現,並不是完全開源的;
- Oracle JDK ⽐ OpenJDK 更穩定。OpenJDK 和 Oracle JDK 的代碼⼏乎相同,但 Oracle JDK 有更多的類和⼀些錯誤修復。因此,如果您想開發企業/商業軟件,我建議您選擇 Oracle JDK,因為它經過了徹底的測試和穩定。某些情況下,有些⼈提到在使⽤ OpenJDK 可能會遇到了許多應⽤程序崩潰的問題,但是,只需切換到 Oracle JDK 就可以解決問題;
- 在響應性和 JVM 性能⽅⾯,Oracle JDK 與 OpenJDK 相⽐提供了更好的性能;
- Oracle JDK 不會為即將發佈的版本提供⻓期⽀持,⽤戶每次都必須通過更新到最新版本獲得⽀持來獲取最新版本;
- Oracle JDK 根據⼆進制代碼許可協議獲得許可,⽽ OpenJDK 根據 GPL v2 許可獲得許可。
什麼是 Java 程序的主類 應⽤程序和⼩程序的主類有何不同?
- ⼀個程序中可以有多個類,但只能有⼀個類是主類。在 Java 應⽤程序中,這個主類是指包含main()⽅法的類。⽽在 Java ⼩程序中,這個主類是⼀個繼承⾃系統類 JApplet 或 Applet 的⼦類。應⽤程序的主類不⼀定要求是 public 類,但⼩程序的主類要求必須是 public 類。主類是 Java程序執⾏的⼊⼝點。
Java 應⽤程序與⼩程序之間有哪些差別?
- 簡單說應⽤程序是從主線程啟動(也就是 main() ⽅法)。applet ⼩程序沒有 main() ⽅法,主要是嵌在瀏覽器⻚⾯上運⾏(調⽤ init() 或者 run() 來啟動),嵌⼊瀏覽器這點跟 flash 的⼩遊戲類似。
字符型常量和字符串常量的區別?
- 形式上: 字符常量是單引號引起的⼀個字符; 字符串常量是雙引號引起的若⼲個字符
- 含義上: 字符常量相當於⼀個整型值( ASCII 值),可以參加表達式運算; 字符串常量代表⼀個地址值(該字符串在內存中存放位置)
- 占內存⼤⼩ 字符常量只佔 2 個位元組; 字符串常量占若⼲個位元組 (注意: char 在 Java 中佔兩個位元組)
構造器 Constructor 是否可被 override?
- Constructor 不能被 override(重寫),但是可以 overload(重載),所以你可以看到⼀個類中有多個構造函數的情況。
重載和重寫的區別
- 重載就是同樣的⼀個⽅法能夠根據輸⼊數據的不同,做出不同的處理
- 重寫就是當⼦類繼承⾃⽗類的相同⽅法,輸⼊數據⼀樣,但要做出有別於⽗類的響應時,你就要覆蓋⽗類⽅法
說一下Java ⾯向對象編程三⼤特性: 封裝 繼承 多態
封裝
- 封裝把⼀個對象的屬性私有化,同時提供⼀些可以被外界訪問的屬性的⽅法,如果屬性不想被外界訪問,我們⼤可不必提供⽅法給外界訪問。但是如果⼀個類沒有提供給外界訪問的⽅法,那麼這個類也沒有什麼意義了。
繼承
- 繼承是使⽤已存在的類的定義作為基礎建⽴新類的技術,新類的定義可以增加新的數據或新的功能,也
可以⽤⽗類的功能,但不能選擇性地繼承⽗類。通過使⽤繼承我們能夠⾮常⽅便地復⽤以前的代碼。
關於繼承如下 3 點請記住:
- ⼦類擁有⽗類對象所有的屬性和⽅法(包括私有屬性和私有⽅法),但是⽗類中的私有屬性和⽅
法⼦類是⽆法訪問,只是擁有。 - ⼦類可以擁有⾃⼰屬性和⽅法,即⼦類可以對⽗類進⾏擴展。
- ⼦類可以⽤⾃⼰的⽅式實現⽗類的⽅法。(以後介紹)。
多態
- 所謂多態就是指程序中定義的引⽤變量所指向的具體類型和通過該引⽤變量發出的⽅法調⽤在編程時並不確定,⽽是在程序運⾏期間才確定,即⼀個引⽤變量到底會指向哪個類的實例對象,該引⽤變量發出的⽅法調⽤到底是哪個類中實現的⽅法,必須在由程序運⾏期間才能決定。在 Java 中有兩種形式可以實現多態:繼承(多個⼦類對同⼀⽅法的重寫)和接⼝(實現接⼝並覆蓋接⼝中同⼀⽅法)。
說一下⾃動裝箱與拆箱
- 裝箱:將基本類型⽤它們對應的引⽤類型包裝起來;
- 拆箱:將包裝類型轉換為基本數據類型;
在⼀個靜態⽅法內調⽤⼀個⾮靜態成員為什麼是⾮法的?
由於靜態⽅法可以不通過對象進⾏調⽤,因此在靜態⽅法⾥,不能調⽤其他⾮靜態變量,也不可以訪問⾮靜態變量成員。
在 Java 中定義⼀個不做事且沒有參數的構造⽅法的作⽤
- Java 程序在執⾏⼦類的構造⽅法之前,如果沒有⽤ super() 來調⽤⽗類特定的構造⽅法,則會調⽤⽗類中「沒有參數的構造⽅法」。因此,如果⽗類中只定義了有參數的構造⽅法,⽽在⼦類的構造⽅法中⼜沒有⽤ super() 來調⽤⽗類中特定的構造⽅法,則編譯時將發⽣錯誤,因為 Java 程序在⽗類中找不到沒有參數的構造⽅法可供執⾏。解決辦法是在⽗類⾥加上⼀個不做事且沒有參數的構造⽅法。
import java 和 javax 有什麼區別?
- 剛開始的時候 JavaAPI 所必需的包是 java 開頭的包,javax 當時只是擴展 API 包來使⽤。然⽽隨着時間的推移,javax 逐漸地擴展成為 Java API 的組成部分。但是,將擴展從 javax 包移動到 java包確實太麻煩了,最終會破壞⼀堆現有的代碼。因此,最終決定 javax 包將成為標準 API 的⼀部分。所以,實際上 java 和 javax 沒有區別。這都是⼀個名字。
接⼝和抽象類的區別是什麼?
- 接⼝的⽅法默認是 public,所有⽅法在接⼝中不能有實現(Java 8 開始接⼝⽅法可以有默認實現),⽽抽象類可以有⾮抽象的⽅法。
- 接⼝中除了 static、final 變量,不能有其他變量,⽽抽象類中則不⼀定。
- ⼀個類可以實現多個接⼝,但只能實現⼀個抽象類。接⼝⾃⼰本身可以通過 extends 關鍵字擴
展多個接⼝。 - 接⼝⽅法默認修飾符是 public,抽象⽅法可以有 public、protected 和 default 這些修飾符(抽象⽅法就是為了被重寫所以不能使⽤ private 關鍵字修飾!)。
- 從設計層⾯來說,抽象是對類的抽象,是⼀種模板設計,⽽接⼝是對⾏為的抽象,是⼀種⾏為的規範。
成員變量與局部變量的區別有哪些?
- 從語法形式上看:成員變量是屬於類的,⽽局部變量是在⽅法中定義的變量或是⽅法的參數;成
員變量可以被 public,private,static 等修飾符所修飾,⽽局部變量不能被訪問控制修飾符及
static 所修飾;但是,成員變量和局部變量都能被 final 所修飾。 - 從變量在內存中的存儲⽅式來看:如果成員變量是使⽤ static 修飾的,那麼這個成員變量是屬
於類的,如果沒有使⽤ static 修飾,這個成員變量是屬於實例的。對象存於堆內存,如果局部
變量類型為基本數據類型,那麼存儲在棧內存,如果為引⽤數據類型,那存放的是指向堆內存對
象的引⽤或者是指向常量池中的地址。 - 從變量在內存中的⽣存時間上看:成員變量是對象的⼀部分,它隨着對象的創建⽽存在,⽽局部
變量隨着⽅法的調⽤⽽⾃動消失。 - 成員變量如果沒有被賦初值:則會⾃動以類型的默認值⽽賦值(⼀種情況例外:被 final 修飾的
成員變量也必須顯式地賦值),⽽局部變量則不會⾃動賦值。
創建⼀個對象⽤什麼運算符?對象實體與對象引⽤有何不同?
- new 運算符,new 創建對象實例(對象實例在堆內存中),對象引⽤指向對象實例(對象引⽤存放在棧內存中)。⼀個對象引⽤可以指向 0 個或 1 個對象(⼀根繩⼦可以不系⽓球,也可以系⼀個⽓球);⼀個對象可以有 n 個引⽤指向它(可以⽤ n 條繩⼦系住⼀個⽓球)。
什麼是⽅法的返回值?返回值在類的⽅法⾥的作⽤是什麼?
- ⽅法的返回值是指我們獲取到的某個⽅法體中的代碼執⾏後產⽣的結果!(前提是該⽅法可能產⽣結果)。返回值的作⽤:接收出結果,使得它可以⽤於其他的操作!
⼀個類的構造⽅法的作⽤是什麼? 若⼀個類沒有聲明構造⽅法,該程序能正確執⾏嗎? 為什麼?
- 主要作⽤是完成對類對象的初始化⼯作。可以執⾏。因為⼀個類即使沒有聲明構造⽅法也會有默認的不
帶參數的構造⽅法。
構造⽅法有哪些特性?
- 名字與類名相同。
- 沒有返回值,但不能⽤ void 聲明構造函數。
- ⽣成類的對象時⾃動執⾏,⽆需調⽤。
靜態⽅法和實例⽅法有何不同
- 在外部調⽤靜態⽅法時,可以使⽤”類名.⽅法名”的⽅式,也可以使⽤”對象名.⽅法名”的⽅式。
⽽實例⽅法只有後⾯這種⽅式。也就是說,調⽤靜態⽅法可以⽆需創建對象。 - 靜態⽅法在訪問本類的成員時,只允許訪問靜態成員(即靜態成員變量和靜態⽅法),⽽不允許
訪問實例成員變量和實例⽅法;實例⽅法則⽆此限制。
對象的相等與指向他們的引⽤相等,兩者有什麼不同?
- 對象的相等,⽐的是內存中存放的內容是否相等。⽽引⽤相等,⽐的是他們指向的內存地址是否相
等。
在調⽤⼦類構造⽅法之前會先調⽤⽗類沒有參數的構造⽅法,其⽬的是?
- 幫助⼦類做初始化⼯作。
簡述線程、程序、進程的基本概念。以及他們之間關係是什麼?
-
線程與進程相似,但線程是⼀個⽐進程更⼩的執⾏單位。⼀個進程在其執⾏的過程中可以產⽣多個線程。與進程不同的是同類的多個線程共享同⼀塊內存空間和⼀組系統資源,所以系統在產⽣⼀個線程,或是在各個線程之間作切換⼯作時,負擔要⽐進程⼩得多,也正因為如此,線程也被稱為輕量級進程。
-
程序是含有指令和數據的⽂件,被存儲在磁盤或其他的數據存儲設備中,也就是說程序是靜態的代碼。
-
進程是程序的⼀次執⾏過程,是系統運⾏程序的基本單位,因此進程是動態的。系統運⾏⼀個程序即是⼀個進程從創建,運⾏到消亡的過程。簡單來說,⼀個進程就是⼀個執⾏中的程序,它在計算機中⼀個指令接着⼀個指令地執⾏着,同時,每個進程還佔有某些系統資源如 CPU 時間,內存空間,⽂件,輸⼊輸出設備的使⽤權等等。換句話說,當程序在執⾏時,將會被操作系統載⼊內存中。 線程是進程劃分成的更⼩的運⾏單位。線程和進程最⼤的不同在於基本上各進程是獨⽴的,⽽各線程則不⼀定,因為同⼀進程中的線程極有可能會相互影響。從另⼀⻆度來說,進程屬於操作系統的範疇,主要是同⼀段時間內,可以同時執⾏⼀個以上的程序,⽽線程則是在同⼀程序內⼏乎同時執⾏⼀個以上的程序段。
關於 final 關鍵字的⼀些總結
- final 關鍵字主要⽤在三個地⽅:變量、⽅法、類。
- 對於⼀個 final 變量,如果是基本數據類型的變量,則其數值⼀旦在初始化之後便不能更改;如果是引⽤類型的變量,則在對其初始化之後便不能再讓其指向另⼀個對象。
- 當⽤ final 修飾⼀個類時,表明這個類不能被繼承。final 類中的所有成員⽅法都會被隱式地指定為 final ⽅法。
- 使⽤ final ⽅法的原因有兩個。第⼀個原因是把⽅法鎖定,以防任何繼承類修改它的含義;第⼆個原因是效率。在早期的 Java 實現版本中,會將 final ⽅法轉為內嵌調⽤。但是如果⽅法過於龐⼤,可能看不到內嵌調⽤帶來的任何性能提升(現在的 Java 版本已經不需要使⽤ final⽅法進⾏這些優化了)。類中所有的 private ⽅法都隱式地指定為 final。
Java 中的異常處理
Java 異常類層次結構圖
- 在 Java 中,所有的異常都有⼀個共同的祖先 java.lang 包中的 Throwable 類。Throwable: 有兩個重要的⼦類:Exception(異常) 和 Error(錯誤) ,⼆者都是 Java 異常處理的重要⼦類,各⾃都包含⼤量⼦類。
- Error(錯誤):是程序⽆法處理的錯誤,表示運⾏應⽤程序中嚴重問題。⼤多數錯誤與代碼編寫者執⾏的操作⽆關,⽽表示代碼運⾏時 JVM(Java 虛擬機)出現的問題。例如,Java 虛擬機運⾏錯誤(Virtual MachineError),當 JVM 不再有繼續執⾏操作所需的內存資源時,將出現
- OutOfMemoryError。這些異常發⽣時,Java 虛擬機(JVM)⼀般會選擇線程終⽌。這些錯誤表示故障發⽣於虛擬機⾃身、或者發⽣在虛擬機試圖執⾏應⽤時,如 Java 虛擬機運⾏錯誤(Virtual MachineError)、類定義錯誤(NoClassDefFoundError)等。這些錯誤是不可查的,因為它們在應⽤程序的控制和處理能⼒之 外,⽽且絕⼤多數是程序運⾏時不允許出現的狀況。對於設計合理的應⽤程序來說,即使確實發⽣了錯誤,本質上也不應該試圖去處理它所引起的異常狀況。在 Java中,錯誤通過 Error 的⼦類描述。
- Exception(異常):是程序本身可以處理的異常。Exception 類有⼀個重要的⼦類RuntimeExceptionRuntimeException 異常由 Java 虛擬機拋出。NullPointerException(要訪問的變量沒有引⽤任何對象時,拋出該異常)、ArithmeticException(算術運算異常,⼀個整數除以 0時,拋出該異常)和 ArrayIndexOutOfBoundsException (下標越界異常)。
- 注意:異常和錯誤的區別:異常能被程序本身處理,錯誤是⽆法處理。
Throwable 類常⽤⽅法
- public string getMessage():返回異常發⽣時的簡要描述
- public string toString():返回異常發⽣時的詳細信息
- public string getLocalizedMessage():返回異常對象的本地化信息。使⽤ Throwable 的⼦類覆蓋這個⽅法,可以⽣成本地化信息。如果⼦類沒有覆蓋該⽅法,則該⽅法返回的信息與getMessage()返回的結果相同
- public void printStackTrace():在控制台上打印 Throwable 對象封裝的異常信息
異常處理總結
- try 塊: ⽤於捕獲異常。其後可接零個或多個 catch 塊,如果沒有 catch 塊,則必須跟⼀個finally 塊。
- catch 塊: ⽤於處理 try 捕獲到的異常。
- finally 塊: ⽆論是否捕獲或處理異常,finally 塊⾥的語句都會被執⾏。當在 try 塊或catch 塊中遇到 return 語句時,finally 語句塊將在⽅法返回之前被執⾏。
在以下 4 種特殊情況下,finally 塊不會被執⾏:
- 在 finally 語句塊第⼀⾏發⽣了異常。 因為在其他⾏,finally 塊還是會得到執⾏
- 在前⾯的代碼中⽤了 System.exit(int)已退出程序。 exit 是帶參函數 ;若該語句在異常語句之後,finally 會執⾏
- 程序所在的線程死亡。
- 關閉 CPU。
Java 序列化中如果有些字段不想進⾏序列化,怎麼辦?
- 對於不想進⾏序列化的變量,使⽤ transient 關鍵字修飾。transient 關鍵字的作⽤是:阻⽌實例中那些⽤此關鍵字修飾的的變量序列化;當對象被反序列化時,被 transient 修飾的變量值不會被持久化和恢復。transient 只能修飾變量,不能修飾類和⽅法。
獲取⽤鍵盤輸⼊常⽤的兩種⽅法
⽅法 1:通過 Scanner
⽅法 2:通過 BufferedReader
講一下Java 中 IO 流
Java 中 IO 流分為⼏種?
- 按照流的流向分,可以分為輸⼊流和輸出流;
- 按照操作單元劃分,可以劃分為位元組流和字符流;
- 按照流的⻆⾊劃分為節點流和處理流。
Java Io 流共涉及 40 多個類,這些類看上去很雜亂,但實際上很有規則,⽽且彼此之間存在⾮常緊密的聯繫, Java I0 流的 40 多個類都是從如下 4 個抽象類基類中派⽣出來的。 - InputStream/Reader: 所有的輸⼊流的基類,前者是位元組輸⼊流,後者是字符輸⼊流。
- OutputStream/Writer: 所有輸出流的基類,前者是位元組輸出流,後者是字符輸出流。
按操作⽅式分類結構圖:
按操作對象分類結構圖:
既然有了位元組流,為什麼還要有字符流?
- 字符流是由 Java 虛擬機將位元組轉換得到的,問題就出在這個過程還算是⾮常耗時,並且,如果我們不知道編碼類型就很容易出現亂碼問題。所以, I/O 流就⼲脆提供了⼀個直接操作字符的接⼝,⽅便我們平時對字符進⾏流操作。如果⾳頻⽂件、圖⽚等媒體⽂件⽤位元組流⽐好,如果涉及到字符的話使⽤字符流⽐好。
講一下深拷⻉ vs 淺拷⻉
- 淺拷⻉:對基本數據類型進⾏值傳遞,對引⽤數據類型進⾏引⽤傳遞般的拷⻉,此為淺拷⻉。
- 深拷⻉:對基本數據類型進⾏值傳遞,對引⽤數據類型,創建⼀個新的對象,並複製其內容,此
為深拷⻉。
說說List,Set,Map三者的區別?
- List(對付順序的好幫⼿): List接⼝存儲⼀組不唯⼀(可以有多個元素引⽤相同的對象),有序的對象
- Set(注重獨⼀⽆⼆的性質): 不允許重複的集合。不會有多個元素引⽤相同的對象。
- Map(⽤Key來搜索的專家): 使⽤鍵值對存儲。Map會維護與Key有關聯的值。兩個Key可以引⽤相同的對象,但Key不能重複,典型的Key是String類型,但也可以是任何對象。
HashMap 和 Hashtable 的區別
- 線程是否安全: HashMap 是⾮線程安全的,HashTable 是線程安全的;HashTable 內部的⽅法基本都經過 synchronized 修飾。(如果你要保證線程安全的話就使⽤ ConcurrentHashMap吧!);
- 效率: 因為線程安全的問題,HashMap 要⽐ HashTable 效率⾼⼀點。另外,HashTable 基本被淘汰,不要在代碼中使⽤它;
- 對Null key 和Null value的⽀持: HashMap 中,null 可以作為鍵,這樣的鍵只有⼀個,可以有⼀個或多個鍵所對應的值為 null。。但是在 HashTable 中 put 進的鍵值只要有⼀個 null,直接拋出 NullPointerException。
- 初始容量⼤⼩和每次擴充容量⼤⼩的不同 : ①創建時如果不指定容量初始值,Hashtable 默認的初始⼤⼩為11,之後每次擴充,容量變為原來的2n+1。HashMap 默認的初始化⼤⼩為16。之後每次擴充,容量變為原來的2倍。②創建時如果給定了容量初始值,那麼 Hashtable 會直接使⽤你給定的⼤⼩,⽽ HashMap 會將其擴充為2的冪次⽅⼤⼩(HashMap 中的 tableSizeFor() ⽅法保證,下⾯給出了源代碼)。也就是說 HashMap 總是使⽤2的冪作為哈希表的⼤⼩,後⾯會介紹到為什麼是2的冪次⽅。
- 底層數據結構: JDK1.8 以後的 HashMap 在解決哈希衝突時有了⼤的變化,當鏈表⻓度⼤於閾值(默認為8)時,將鏈錶轉化為紅⿊樹,以減少搜索時間。Hashtable 沒有這樣的機制。
什麼是線程和進程?
何為進程?
- 進程是程序的⼀次執⾏過程,是系統運⾏程序的基本單位,因此進程是動態的。系統運⾏⼀個程序即是⼀個進程從創建,運⾏到消亡的過程。
- 在 Java 中,當我們啟動 main 函數時其實就是啟動了⼀個 JVM 的進程,⽽ main 函數所在的線程就是這個進程中的⼀個線程,也稱主線程。
何為線程?
- 線程與進程相似,但線程是⼀個⽐進程更⼩的執⾏單位。⼀個進程在其執⾏的過程中可以產⽣多個線程。與進程不同的是同類的多個線程共享進程的堆和⽅法區資源,但每個線程有⾃⼰的程序計數器、虛擬機棧和本地⽅法棧,所以系統在產⽣⼀個線程,或是在各個線程之間作切換⼯作時,負擔要⽐進程⼩得多,也正因為如此,線程也被稱為輕量級進程。
為什麼要使⽤多線程呢?
- 從計算機底層來說: 線程可以⽐作是輕量級的進程,是程序執⾏的最⼩單位,線程間的切換和調度的成本遠遠⼩於進程。另外,多核 CPU 時代意味着多個線程可以同時運⾏,這減少了線程上下⽂切換的開銷。
- 從當代互聯⽹發展趨勢來說: 現在的系統動不動就要求百萬級甚⾄千萬級的並發量,⽽多線程並發編程正是開發⾼並發系統的基礎,利⽤好多線程機制可以⼤⼤提⾼系統整體的並發能⼒以及性能
並發編程的三個重要特性
- 原⼦性 : ⼀個的操作或者多次操作,要麼所有的操作全部都得到執⾏並且不會收到任何因素的⼲擾⽽中斷,要麼所有的操作都執⾏,要麼都不執⾏。 synchronized 可以保證代碼⽚段的原⼦性。
- 可⻅性 :當⼀個變量對共享變量進⾏了修改,那麼另外的線程都是⽴即可以看到修改後的最新值。 volatile 關鍵字可以保證共享變量的可⻅性。
- 有序性 :代碼在執⾏的過程中的先後順序,Java 在編譯器以及運⾏期間的優化,代碼的執⾏順序未必就是編寫代碼時候的順序。 volatile 關鍵字可以禁⽌指令進⾏重排序優化。
簡單的介紹⼀下強引⽤,軟引⽤,弱引⽤,虛引⽤
強引⽤(StrongReference)
- 以前我們使⽤的⼤部分引⽤實際上都是強引⽤,這是使⽤最普遍的引⽤。如果⼀個對象具有強引⽤,那就類似於必不可少的⽣活⽤品,垃圾回收器絕不會回收它。當內存空 間不⾜,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終⽌,也不會靠隨意回收具有強引⽤的對象來解決內存不⾜問題。
軟引⽤(SoftReference)
- 如果⼀個對象只具有軟引⽤,那就類似於可有可⽆的⽣活⽤品。如果內存空間⾜夠,垃圾回收器就不會回收它,如果內存空間不⾜了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使⽤。軟引⽤可⽤來實現內存敏感的⾼速緩存。軟引⽤可以和⼀個引⽤隊列(ReferenceQueue)聯合使⽤,如果軟引⽤所引⽤的對象被垃圾回收,JAVA虛擬機就會把這個軟引⽤加⼊到與之關聯的引⽤隊列中。
弱引⽤(WeakReference)
- 如果⼀個對象只具有弱引⽤,那就類似於可有可⽆的⽣活⽤品。弱引⽤與軟引⽤的區別在於:只具有弱引⽤的對象擁有更短暫的⽣命周期。在垃圾回收器線程掃描它 所管轄的內存區域的過程中,⼀旦發現了只具有弱引⽤的對象,不管當前內存空間⾜夠與否,都會回收它的內存。不過,由於垃圾回收器是⼀個優先級很低的線程, 因此不⼀定會很快發現那些只具有弱引⽤的對象。弱引⽤可以和⼀個引⽤隊列(ReferenceQueue)聯合使⽤,如果弱引⽤所引⽤的對象被垃圾回收,Java虛擬機就會把這個弱引⽤加⼊到與之關聯的引⽤隊列中。
虛引⽤(PhantomReference)
- “虛引⽤”顧名思義,就是形同虛設,與其他⼏種引⽤都不同,虛引⽤並不會決定對象的⽣命周期。如果
⼀個對象僅持有虛引⽤,那麼它就和沒有任何引⽤⼀樣,在任何時候都可能被垃圾回收。
總結
感謝你看到這裡,文章有什麼不足還請指正!由於時間關係暫時先整理了這麼多,有什麼不足的可以私信我,我都會跟進補上,希望這些能在馬上到來的金三銀四裏面對你有幫助!
另外我還為大家準備了ava核心知識點+全套架構師學習資料和視頻+一線大廠面試寶典+面試簡歷模板+阿里美團網易騰訊小米愛奇藝快手嗶哩嗶哩面試題+Spring源碼合集+Java架構實戰電子書一起免費分享給大家!
有需要的可以關注我的公眾號:前程有光回復資料即可領取!覺得文章對你有幫助的話記得給我點個贊,每天都會分享java相關技術文章或行業資訊,歡迎大家關注和轉發文章!