<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java 基礎(chǔ)常見知識點(diǎn)&面試題總結(jié)(上),2022 最新版!

          共 24722字,需瀏覽 50分鐘

           ·

          2022-07-08 14:09

          點(diǎn)擊關(guān)注公眾號,Java干貨及時送達(dá)

          基礎(chǔ)概念與常識

          Java 語言有哪些特點(diǎn)?

          1. 簡單易學(xué);
          2. 面向?qū)ο螅ǚ庋b,繼承,多態(tài));
          3. 平臺無關(guān)性( Java 虛擬機(jī)實(shí)現(xiàn)平臺無關(guān)性);
          4. 支持多線程( C++ 語言沒有內(nèi)置的多線程機(jī)制,因此必須調(diào)用操作系統(tǒng)的多線程功能來進(jìn)行多線程程序設(shè)計,而 Java 語言卻提供了多線程支持);
          5. 可靠性;
          6. 安全性;
          7. 支持網(wǎng)絡(luò)編程并且很方便( Java 語言誕生本身就是為簡化網(wǎng)絡(luò)編程設(shè)計的,因此 Java 語言不僅支持網(wǎng)絡(luò)編程而且很方便);
          8. 編譯與解釋并存;

          ?? 修正(參見:issue#544[1]:C++11 開始(2011 年的時候),C++就引入了多線程庫,在 windows、linux、macos 都可以使用std::threadstd::async來創(chuàng)建線程。參考鏈接:http://www.cplusplus.com/reference/thread/thread/?kw=thread

          ?? 拓展一下:

          “Write Once, Run Anywhere(一次編寫,隨處運(yùn)行)”這句宣傳口號,真心經(jīng)典,流傳了好多年!以至于,直到今天,依然有很多人覺得跨平臺是 Java 語言最大的優(yōu)勢。實(shí)際上,跨平臺已經(jīng)不是 Java 最大的賣點(diǎn)了,各種 JDK 新特性也不是。目前市面上虛擬化技術(shù)已經(jīng)非常成熟,比如你通過 Docker 就很容易實(shí)現(xiàn)跨平臺了。在我看來,Java 強(qiáng)大的生態(tài)才是!

          JVM vs JDK vs JRE

          JVM

          Java 虛擬機(jī)(JVM)是運(yùn)行 Java 字節(jié)碼的虛擬機(jī)。JVM 有針對不同系統(tǒng)的特定實(shí)現(xiàn)(Windows,Linux,macOS),目的是使用相同的字節(jié)碼,它們都會給出相同的結(jié)果。字節(jié)碼和不同系統(tǒng)的 JVM 實(shí)現(xiàn)是 Java 語言“一次編譯,隨處可以運(yùn)行”的關(guān)鍵所在。

          JVM 并不是只有一種!只要滿足 JVM 規(guī)范,每個公司、組織或者個人都可以開發(fā)自己的專屬 JVM。也就是說我們平時接觸到的 HotSpot VM 僅僅是是 JVM 規(guī)范的一種實(shí)現(xiàn)而已。

          除了我們平時最常用的 HotSpot VM 外,還有 J9 VM、Zing VM、JRockit VM 等 JVM 。維基百科上就有常見 JVM 的對比:Comparison of Java virtual machines[2],感興趣的可以去看看。并且,你可以在Java SE Specifications[3]上找到各個版本的 JDK 對應(yīng)的 JVM 規(guī)范。

          Java SE Specifications

          JDK 和 JRE

          JDK 是 Java Development Kit 縮寫,它是功能齊全的 Java SDK。它擁有 JRE 所擁有的一切,還有編譯器(javac)和工具(如 javadoc 和 jdb)。它能夠創(chuàng)建和編譯程序。

          JRE 是 Java 運(yùn)行時環(huán)境。它是運(yùn)行已編譯 Java 程序所需的所有內(nèi)容的集合,包括 Java 虛擬機(jī)(JVM),Java 類庫,java 命令和其他的一些基礎(chǔ)構(gòu)件。但是,它不能用于創(chuàng)建新程序。

          如果你只是為了運(yùn)行一下 Java 程序的話,那么你只需要安裝 JRE 就可以了。如果你需要進(jìn)行一些 Java 編程方面的工作,那么你就需要安裝 JDK 了。但是,這不是絕對的。有時,即使您不打算在計算機(jī)上進(jìn)行任何 Java 開發(fā),仍然需要安裝 JDK。例如,如果要使用 JSP 部署 Web 應(yīng)用程序,那么從技術(shù)上講,您只是在應(yīng)用程序服務(wù)器中運(yùn)行 Java 程序。那你為什么需要 JDK 呢?因?yàn)閼?yīng)用程序服務(wù)器會將 JSP 轉(zhuǎn)換為 Java servlet,并且需要使用 JDK 來編譯 servlet。

          什么是字節(jié)碼?采用字節(jié)碼的好處是什么?

          在 Java 中,JVM 可以理解的代碼就叫做字節(jié)碼(即擴(kuò)展名為.class的文件),它不面向任何特定的處理器,只面向虛擬機(jī)。Java 語言通過字節(jié)碼的方式,在一定程度上解決了傳統(tǒng)解釋型語言執(zhí)行效率低的問題,同時又保留了解釋型語言可移植的特點(diǎn)。所以, Java 程序運(yùn)行時相對來說還是高效的(不過,和 C++,Rust,Go 等語言還是有一定差距的),而且,由于字節(jié)碼并不針對一種特定的機(jī)器,因此,Java 程序無須重新編譯便可在多種不同操作系統(tǒng)的計算機(jī)上運(yùn)行。

          Java 程序從源代碼到運(yùn)行的過程如下圖所示:

          我們需要格外注意的是.class->機(jī)器碼這一步。在這一步 JVM 類加載器首先加載字節(jié)碼文件,然后通過解釋器逐行解釋執(zhí)行,這種方式的執(zhí)行速度會相對比較慢。而且,有些方法和代碼塊是經(jīng)常需要被調(diào)用的(也就是所謂的熱點(diǎn)代碼),所以后面引進(jìn)了 JIT(just-in-time compilation) 編譯器,而 JIT 屬于運(yùn)行時編譯。當(dāng) JIT 編譯器完成第一次編譯后,其會將字節(jié)碼對應(yīng)的機(jī)器碼保存下來,下次可以直接使用。而我們知道,機(jī)器碼的運(yùn)行效率肯定是高于 Java 解釋器的。這也解釋了我們?yōu)槭裁唇?jīng)常會說Java 是編譯與解釋共存的語言

          HotSpot 采用了惰性評估(Lazy Evaluation)的做法,根據(jù)二八定律,消耗大部分系統(tǒng)資源的只有那一小部分的代碼(熱點(diǎn)代碼),而這也就是 JIT 所需要編譯的部分。JVM 會根據(jù)代碼每次被執(zhí)行的情況收集信息并相應(yīng)地做出一些優(yōu)化,因此執(zhí)行的次數(shù)越多,它的速度就越快。JDK 9 引入了一種新的編譯模式 AOT(Ahead of Time Compilation),它是直接將字節(jié)碼編譯成機(jī)器碼,這樣就避免了 JIT 預(yù)熱等各方面的開銷。JDK 支持分層編譯和 AOT 協(xié)作使用。但是 ,AOT 編譯器的編譯質(zhì)量是肯定比不上 JIT 編譯器的。

          為什么說 Java 語言“編譯與解釋并存”?

          其實(shí)這個問題我們講字節(jié)碼的時候已經(jīng)提到過,因?yàn)楸容^重要,所以我們這里再提一下。

          我們可以將高級編程語言按照程序的執(zhí)行方式分為兩種:

          • 編譯型編譯型語言[4]會通過編譯器[5]將源代碼一次性翻譯成可被該平臺執(zhí)行的機(jī)器碼。一般情況下,編譯語言的執(zhí)行速度比較快,開發(fā)效率比較低。常見的編譯性語言有 C、C++、Go、Rust 等等。
          • 解釋型解釋型語言[6]會通過解釋器[7]一句一句的將代碼解釋(interpret)為機(jī)器代碼后再執(zhí)行。解釋型語言開發(fā)效率比較快,執(zhí)行速度比較慢。常見的解釋性語言有 Python、JavaScript、PHP 等等。
          編譯型語言和解釋型語言

          根據(jù)維基百科介紹:

          為了改善編譯語言的效率而發(fā)展出的即時編譯[8]技術(shù),已經(jīng)縮小了這兩種語言間的差距。這種技術(shù)混合了編譯語言與解釋型語言的優(yōu)點(diǎn),它像編譯語言一樣,先把程序源代碼編譯成字節(jié)碼[9]。到執(zhí)行期時,再將字節(jié)碼直譯,之后執(zhí)行。Java[10]LLVM[11]是這種技術(shù)的代表產(chǎn)物。

          相關(guān)閱讀:基本功 | Java 即時編譯器原理解析及實(shí)踐[12]

          為什么說 Java 語言“編譯與解釋并存”?

          這是因?yàn)?Java 語言既具有編譯型語言的特征,也具有解釋型語言的特征。因?yàn)?Java 程序要經(jīng)過先編譯,后解釋兩個步驟,由 Java 編寫的程序需要先經(jīng)過編譯步驟,生成字節(jié)碼(.class文件),這種字節(jié)碼必須由 Java 解釋器來解釋執(zhí)行。

          Oracle JDK vs OpenJDK

          可能在看這個問題之前很多人和我一樣并沒有接觸和使用過 OpenJDK 。那么 Oracle JDK 和 OpenJDK 之間是否存在重大差異?下面我通過收集到的一些資料,為你解答這個被很多人忽視的問題。

          對于 Java 7,沒什么關(guān)鍵的地方。OpenJDK 項目主要基于 Sun 捐贈的 HotSpot 源代碼。此外,OpenJDK 被選為 Java 7 的參考實(shí)現(xiàn),由 Oracle 工程師維護(hù)。關(guān)于 JVM,JDK,JRE 和 OpenJDK 之間的區(qū)別,Oracle 博客帖子在 2012 年有一個更詳細(xì)的答案:

          問:OpenJDK 存儲庫中的源代碼與用于構(gòu)建 Oracle JDK 的代碼之間有什么區(qū)別?

          答:非常接近 - 我們的 Oracle JDK 版本構(gòu)建過程基于 OpenJDK 7 構(gòu)建,只添加了幾個部分,例如部署代碼,其中包括 Oracle 的 Java 插件和 Java WebStart 的實(shí)現(xiàn),以及一些閉源的第三方組件,如圖形光柵化器,一些開源的第三方組件,如 Rhino,以及一些零碎的東西,如附加文檔或第三方字體。展望未來,我們的目的是開源 Oracle JDK 的所有部分,除了我們考慮商業(yè)功能的部分。

          總結(jié):(提示:下面括號內(nèi)的內(nèi)容是基于原文補(bǔ)充說明的,因?yàn)樵奶^于晦澀難懂,用人話重新解釋了下,如果你看得懂里面的術(shù)語,可以忽略括號解釋的內(nèi)容)

          1. Oracle JDK 大概每 6 個月發(fā)一次主要版本(從 2014 年 3 月 JDK 8 LTS 發(fā)布到 2017 年 9 月 JDK 9 發(fā)布經(jīng)歷了長達(dá) 3 年多的時間,所以并不總是 6 個月),而 OpenJDK 版本大概每三個月發(fā)布一次。但這不是固定的,我覺得了解這個沒啥用處。詳情參見:https://blogs.oracle.com/java-platform-group/update-and-faq-on-the-java-se-release-cadence[13]

          2. OpenJDK 是一個參考模型并且是完全開源的,而 Oracle JDK 是 OpenJDK 的一個實(shí)現(xiàn),并不是完全開源的;(個人觀點(diǎn):眾所周知,JDK 原來是 SUN 公司開發(fā)的,后來 SUN 公司又賣給了 Oracle 公司,Oracle 公司以 Oracle 數(shù)據(jù)庫而著名,而 Oracle 數(shù)據(jù)庫又是閉源的,這個時候 Oracle 公司就不想完全開源了,但是原來的 SUN 公司又把 JDK 給開源了,如果這個時候 Oracle 收購回來之后就把他給閉源,必然會引其很多 Java 開發(fā)者的不滿,導(dǎo)致大家對 Java 失去信心,那 Oracle 公司收購回來不就把 Java 爛在手里了嗎!然后,Oracle 公司就想了個騷操作,這樣吧,我把一部分核心代碼開源出來給你們玩,并且我要和你們自己搞的 JDK 區(qū)分下,你們叫 OpenJDK,我叫 Oracle JDK,我發(fā)布我的,你們繼續(xù)玩你們的,要是你們搞出來什么好玩的東西,我后續(xù)發(fā)布 Oracle JDK 也會拿來用一下,一舉兩得!)OpenJDK 開源項目:https://github.com/openjdk/jdk[14]

          3. Oracle JDK 比 OpenJDK 更穩(wěn)定(肯定啦,Oracle JDK 由 Oracle 內(nèi)部團(tuán)隊進(jìn)行單獨(dú)研發(fā)的,而且發(fā)布時間不 OpenJDK 更長,質(zhì)量更有保障)。OpenJDK 和 Oracle JDK 的代碼幾乎相同(OpenJDK 的代碼是從 Oracle JDK 代碼派生出來的,可以理解為在 Oracle JDK 分支上拉了一條新的分支叫 OpenJDK,所以大部分代碼相同),但 Oracle JDK 有更多的類和一些錯誤修復(fù)。因此,如果您想開發(fā)企業(yè)/商業(yè)軟件,我建議您選擇 Oracle JDK,因?yàn)樗?jīng)過了徹底的測試和穩(wěn)定。某些情況下,有些人提到在使用 OpenJDK 可能會遇到了許多應(yīng)用程序崩潰的問題,但是,只需切換到 Oracle JDK 就可以解決問題;

          4. 在響應(yīng)性和 JVM 性能方面,Oracle JDK 與 OpenJDK 相比提供了更好的性能;

          5. Oracle JDK 不會為即將發(fā)布的版本提供長期支持(如果是 LTS 長期支持版本的話也會,比如 JDK 8,但并不是每個版本都是 LTS 版本),用戶每次都必須通過更新到最新版本獲得支持來獲取最新版本;

          6. Oracle JDK 使用 BCL/OTN 協(xié)議獲得許可,而 OpenJDK 根據(jù) GPL v2 許可獲得許可。

          既然 Oracle JDK 這么好,那為什么還要有 OpenJDK?

          答:

          1. OpenJDK 是開源的,開源意味著你可以對它根據(jù)你自己的需要進(jìn)行修改、優(yōu)化,比如 Alibaba 基于 OpenJDK 開發(fā)了 Dragonwell8:https://github.com/alibaba/dragonwell8[15]

          2. OpenJDK 是商業(yè)免費(fèi)的(這也是為什么通過 yum 包管理器上默認(rèn)安裝的 JDK 是 OpenJDK 而不是 Oracle JDK)。雖然 Oracle JDK 也是商業(yè)免費(fèi)(比如 JDK 8),但并不是所有版本都是免費(fèi)的。

          3. OpenJDK 更新頻率更快。Oracle JDK 一般是每 6 個月發(fā)布一個新版本,而 OpenJDK 一般是每 3 個月發(fā)布一個新版本。(現(xiàn)在你知道為啥 Oracle JDK 更穩(wěn)定了吧,先在 OpenJDK 試試水,把大部分問題都解決掉了才在 Oracle JDK 上發(fā)布)

          基于以上這些原因,OpenJDK 還是有存在的必要的!

          oracle jdk release cadence

          ?? 拓展一下:

          • BCL 協(xié)議(Oracle Binary Code License Agreement): 可以使用 JDK(支持商用),但是不能進(jìn)行修改。
          • OTN 協(xié)議(Oracle Technology Network License Agreement): 11 及之后新發(fā)布的 JDK 用的都是這個協(xié)議,可以自己私下用,但是商用需要付費(fèi)。

          相關(guān)閱讀 ??:《Differences Between Oracle JDK and OpenJDK》[16]

          Java 和 C++ 的區(qū)別?

          我知道很多人沒學(xué)過 C++,但是面試官就是沒事喜歡拿咱們 Java 和 C++ 比呀!沒辦法!!!就算沒學(xué)過 C++,也要記下來。

          雖然,Java 和 C++ 都是面向?qū)ο蟮恼Z言,都支持封裝、繼承和多態(tài),但是,它們還是有挺多不相同的地方:

          • Java 不提供指針來直接訪問內(nèi)存,程序內(nèi)存更加安全
          • Java 的類是單繼承的,C++ 支持多重繼承;雖然 Java 的類不可以多繼承,但是接口可以多繼承。
          • Java 有自動內(nèi)存管理垃圾回收機(jī)制(GC),不需要程序員手動釋放無用內(nèi)存。
          • C ++同時支持方法重載和操作符重載,但是 Java 只支持方法重載(操作符重載增加了復(fù)雜性,這與 Java 最初的設(shè)計思想不符)。
          • ......

          基本語法

          字符型常量和字符串常量的區(qū)別?

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

          (注意:char在 Java 中占兩個字節(jié))

          注釋有哪幾種形式?

          Java 中的注釋有三種:

          1. 單行注釋

          2. 多行注釋

          3. 文檔注釋。

          在我們編寫代碼的時候,如果代碼量比較少,我們自己或者團(tuán)隊其他成員還可以很輕易地看懂代碼,但是當(dāng)項目結(jié)構(gòu)一旦復(fù)雜起來,我們就需要用到注釋了。注釋并不會執(zhí)行(編譯器在編譯代碼之前會把代碼中的所有注釋抹掉,字節(jié)碼中不保留注釋),是我們程序員寫給自己看的,注釋是你的代碼說明書,能夠幫助看代碼的人快速地理清代碼之間的邏輯關(guān)系。因此,在寫程序的時候隨手加上注釋是一個非常好的習(xí)慣。

          《Clean Code》這本書明確指出:

          代碼的注釋不是越詳細(xì)越好。實(shí)際上好的代碼本身就是注釋,我們要盡量規(guī)范和美化自己的代碼來減少不必要的注釋。

          若編程語言足夠有表達(dá)力,就不需要注釋,盡量通過代碼來闡述。

          舉個例子:

          去掉下面復(fù)雜的注釋,只需要創(chuàng)建一個與注釋所言同一事物的函數(shù)即可

          // check to see if the employee is eligible for full benefits
          if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))

          應(yīng)替換為

          if (employee.isEligibleForFullBenefits())

          標(biāo)識符和關(guān)鍵字的區(qū)別是什么?

          在我們編寫程序的時候,需要大量地為程序、類、變量、方法等取名字,于是就有了標(biāo)識符。簡單來說,標(biāo)識符就是一個名字

          有一些標(biāo)識符,Java 語言已經(jīng)賦予了其特殊的含義,只能用于特定的地方,這些特殊的標(biāo)識符就是關(guān)鍵字。簡單來說,關(guān)鍵字是被賦予特殊含義的標(biāo)識符 。比如,在我們的日常生活中,如果我們想要開一家店,則要給這個店起一個名字,起的這個“名字”就叫標(biāo)識符。但是我們店的名字不能叫“警察局”,因?yàn)椤熬炀帧边@個名字已經(jīng)被賦予了特殊的含義,而“警察局”就是我們?nèi)粘I钪械年P(guān)鍵字。

          Java 語言關(guān)鍵字有哪些?

          分類關(guān)鍵字





          訪問控制privateprotectedpublic



          類,方法和變量修飾符abstractclassextendsfinalimplementsinterfacenative

          newstaticstrictfpsynchronizedtransientvolatileenum
          程序控制breakcontinuereturndowhileifelse

          forinstanceofswitchcasedefaultassert
          錯誤處理trycatchthrowthrowsfinally

          包相關(guān)importpackage




          基本類型booleanbytechardoublefloatintlong

          short





          變量引用superthisvoid



          保留字gotoconst




          Tips:所有的關(guān)鍵字都是小寫的,在 IDE 中會以特殊顏色顯示。

          default這個關(guān)鍵字很特殊,既屬于程序控制,也屬于類,方法和變量修飾符,還屬于訪問控制。

          • 在程序控制中,當(dāng)在switch中匹配不到任何情況時,可以使用default來編寫默認(rèn)匹配的情況。
          • 在類,方法和變量修飾符中,從 JDK8 開始引入了默認(rèn)方法,可以使用default關(guān)鍵字來定義一個方法的默認(rèn)實(shí)現(xiàn)。
          • 在訪問控制中,如果一個方法前沒有任何修飾符,則默認(rèn)會有一個修飾符default,但是這個修飾符加上了就會報錯。

          ?? 注意 :雖然true,false, 和null看起來像關(guān)鍵字但實(shí)際上他們是字面值,同時你也不可以作為標(biāo)識符來使用。

          官方文檔:https://docs.oracle.com/javase/tutorial/java/nutsandbolts/\_keywords.html[17]

          自增自減運(yùn)算符

          在寫代碼的過程中,常見的一種情況是需要某個整數(shù)類型變量增加 1 或減少 1,Java 提供了一種特殊的運(yùn)算符,用于這種表達(dá)式,叫做自增運(yùn)算符(++)和自減運(yùn)算符(--)。

          ++ 和 -- 運(yùn)算符可以放在變量之前,也可以放在變量之后,當(dāng)運(yùn)算符放在變量之前時(前綴),先自增/減,再賦值;當(dāng)運(yùn)算符放在變量之后時(后綴),先賦值,再自增/減。例如,當(dāng)b = ++a時,先自增(自己增加 1),再賦值(賦值給 b);當(dāng)b = a++時,先賦值(賦值給 b),再自增(自己增加 1)。也就是,++a 輸出的是 a+1 的值,a++輸出的是 a 值。用一句口訣就是:“符號在前就先加/減,符號在后就后加/減”。

          continue、break 和 return 的區(qū)別是什么?

          在循環(huán)結(jié)構(gòu)中,當(dāng)循環(huán)條件不滿足或者循環(huán)次數(shù)達(dá)到要求時,循環(huán)會正常結(jié)束。但是,有時候可能需要在循環(huán)的過程中,當(dāng)發(fā)生了某種條件之后 ,提前終止循環(huán),這就需要用到下面幾個關(guān)鍵詞:

          1. continue:指跳出當(dāng)前的這一次循環(huán),繼續(xù)下一次循環(huán)。
          2. break:指跳出整個循環(huán)體,繼續(xù)執(zhí)行循環(huán)下面的語句。

          return用于跳出所在方法,結(jié)束該方法的運(yùn)行。return 一般有兩種用法:

          1. return;:直接使用 return 結(jié)束方法執(zhí)行,用于沒有返回值函數(shù)的方法
          2. return value;:return 一個特定值,用于有返回值函數(shù)的方法

          思考一下:下列語句的運(yùn)行結(jié)果是什么?

              public static void main(String[] args) {
                  boolean flag = false;
                  for (int i = 0; i <= 3; i++) {
                      if (i == 0) {
                          System.out.println("0");
                      } else if (i == 1) {
                          System.out.println("1");
                          continue;
                      } else if (i == 2) {
                          System.out.println("2");
                          flag = true;
                      } else if (i == 3) {
                          System.out.println("3");
                          break;
                      } else if (i == 4) {
                          System.out.println("4");
                      }
                      System.out.println("xixi");
                  }
                  if (flag) {
                      System.out.println("haha");
                      return;
                  }
                  System.out.println("heihei");
              }

          運(yùn)行結(jié)果:

          0
          xixi
          1
          2
          xixi
          3
          haha

          方法

          什么是方法的返回值?方法有哪幾種類型?

          方法的返回值是指我們獲取到的某個方法體中的代碼執(zhí)行后產(chǎn)生的結(jié)果!(前提是該方法可能產(chǎn)生結(jié)果)。返回值的作用是接收出結(jié)果,使得它可以用于其他的操作!

          我們可以按照方法的返回值和參數(shù)類型將方法分為下面這幾種:

          1.無參數(shù)無返回值的方法

          public void f1() {
              //......
          }
          // 下面這個方法也沒有返回值,雖然用到了 return
          public void f(int a) {
              if (...) {
                  // 表示結(jié)束方法的執(zhí)行,下方的輸出語句不會執(zhí)行
                  return;
              }
              System.out.println(a);
          }

          2.有參數(shù)無返回值的方法

          public void f2(Parameter 1, ..., Parameter n) {
              //......
          }

          3.有返回值無參數(shù)的方法

          public int f3() {
              //......
              return x;
          }

          4.有返回值有參數(shù)的方法

          public int f4(int a, int b) {
              return a * b;
          }

          靜態(tài)方法為什么不能調(diào)用非靜態(tài)成員?

          這個需要結(jié)合 JVM 的相關(guān)知識,主要原因如下:

          1. 靜態(tài)方法是屬于類的,在類加載的時候就會分配內(nèi)存,可以通過類名直接訪問。而非靜態(tài)成員屬于實(shí)例對象,只有在對象實(shí)例化之后才存在,需要通過類的實(shí)例對象去訪問。
          2. 在類的非靜態(tài)成員不存在的時候靜態(tài)成員就已經(jīng)存在了,此時調(diào)用在內(nèi)存中還不存在的非靜態(tài)成員,屬于非法操作。

          靜態(tài)方法和實(shí)例方法有何不同?

          1、調(diào)用方式

          在外部調(diào)用靜態(tài)方法時,可以使用類名.方法名的方式,也可以使用對象.方法名的方式,而實(shí)例方法只有后面這種方式。也就是說,調(diào)用靜態(tài)方法可以無需創(chuàng)建對象

          不過,需要注意的是一般不建議使用對象.方法名的方式來調(diào)用靜態(tài)方法。這種方式非常容易造成混淆,靜態(tài)方法不屬于類的某個對象而是屬于這個類。

          因此,一般建議使用類名.方法名的方式來調(diào)用靜態(tài)方法。

          public class Person {
              public void method() {
                //......
              }

              public static void staicMethod(){
                //......
              }
              public static void main(String[] args) {
                  Person person = new Person();
                  // 調(diào)用實(shí)例方法
                  person.method();
                  // 調(diào)用靜態(tài)方法
                  Person.staicMethod()
              }
          }

          2、訪問類成員是否存在限制

          靜態(tài)方法在訪問本類的成員時,只允許訪問靜態(tài)成員(即靜態(tài)成員變量和靜態(tài)方法),不允許訪問實(shí)例成員(即實(shí)例成員變量和實(shí)例方法),而實(shí)例方法不存在這個限制。

          重載和重寫的區(qū)別

          重載就是同樣的一個方法能夠根據(jù)輸入數(shù)據(jù)的不同,做出不同的處理

          重寫就是當(dāng)子類繼承自父類的相同方法,輸入數(shù)據(jù)一樣,但要做出有別于父類的響應(yīng)時,你就要覆蓋父類方法

          重載

          發(fā)生在同一個類中(或者父類和子類之間),方法名必須相同,參數(shù)類型不同、個數(shù)不同、順序不同,方法返回值和訪問修飾符可以不同。

          《Java 核心技術(shù)》這本書是這樣介紹重載的:

          如果多個方法(比如StringBuilder的構(gòu)造方法)有相同的名字、不同的參數(shù), 便產(chǎn)生了重載。

          StringBuilder sb = new StringBuilder();
          StringBuilder sb2 = new StringBuilder("HelloWorld");

          編譯器必須挑選出具體執(zhí)行哪個方法,它通過用各個方法給出的參數(shù)類型與特定方法調(diào)用所使用的值類型進(jìn)行匹配來挑選出相應(yīng)的方法。 如果編譯器找不到匹配的參數(shù), 就會產(chǎn)生編譯時錯誤, 因?yàn)楦静淮嬖谄ヅ洌?或者沒有一個比其他的更好(這個過程被稱為重載解析(overloading resolution))。

          Java 允許重載任何方法, 而不只是構(gòu)造器方法。

          綜上:重載就是同一個類中多個同名方法根據(jù)不同的傳參來執(zhí)行不同的邏輯處理。

          重寫

          重寫發(fā)生在運(yùn)行期,是子類對父類的允許訪問的方法的實(shí)現(xiàn)過程進(jìn)行重新編寫。

          1. 方法名、參數(shù)列表必須相同,子類方法返回值類型應(yīng)比父類方法返回值類型更小或相等,拋出的異常范圍小于等于父類,訪問修飾符范圍大于等于父類。
          2. 如果父類方法訪問修飾符為private/final/static則子類就不能重寫該方法,但是被static修飾的方法能夠被再次聲明。
          3. 構(gòu)造方法無法被重寫

          綜上:重寫就是子類對父類方法的重新改造,外部樣子不能改變,內(nèi)部邏輯可以改變。

          區(qū)別點(diǎn)重載方法重寫方法
          發(fā)生范圍同一個類子類
          參數(shù)列表必須修改一定不能修改
          返回類型可修改子類方法返回值類型應(yīng)比父類方法返回值類型更小或相等
          異常可修改子類方法聲明拋出的異常類應(yīng)比父類方法聲明拋出的異常類更小或相等;
          訪問修飾符可修改一定不能做更嚴(yán)格的限制(可以降低限制)
          發(fā)生階段編譯期運(yùn)行期

          方法的重寫要遵循“兩同兩小一大”(以下內(nèi)容摘錄自《瘋狂 Java 講義》,issue#892[18]):

          • “兩同”即方法名相同、形參列表相同;
          • “兩小”指的是子類方法返回值類型應(yīng)比父類方法返回值類型更小或相等,子類方法聲明拋出的異常類應(yīng)比父類方法聲明拋出的異常類更小或相等;
          • “一大”指的是子類方法的訪問權(quán)限應(yīng)比父類方法的訪問權(quán)限更大或相等。

          ?? 關(guān)于重寫的返回值類型這里需要額外多說明一下,上面的表述不太清晰準(zhǔn)確:如果方法的返回類型是 void 和基本數(shù)據(jù)類型,則返回值重寫時不可修改。但是如果方法的返回值是引用類型,重寫時是可以返回該引用類型的子類的。

          public class Hero {
              public String name() {
                  return "超級英雄";
              }
          }
          public class SuperMan extends Hero{
              @Override
              public String name() {
                  return "超人";
              }
              public Hero hero() {
                  return new Hero();
              }
          }

          public class SuperSuperMan extends SuperMan {
              public String name() {
                  return "超級超級英雄";
              }

              @Override
              public SuperMan hero() {
                  return new SuperMan();
              }
          }

          什么是可變長參數(shù)?

          從 Java5 開始,Java 支持定義可變長參數(shù),所謂可變長參數(shù)就是允許在調(diào)用方法時傳入不定長度的參數(shù)。就比如下面的這個printVariable方法就可以接受 0 個或者多個參數(shù)。

          public static void method1(String... args) {
             //......
          }

          另外,可變參數(shù)只能作為函數(shù)的最后一個參數(shù),但其前面可以有也可以沒有任何其他參數(shù)。

          public static void method2(String arg1, String... args) {
             //......
          }

          遇到方法重載的情況怎么辦呢?會優(yōu)先匹配固定參數(shù)還是可變參數(shù)的方法呢?

          答案是會優(yōu)先匹配固定參數(shù)的方法,因?yàn)楣潭▍?shù)的方法匹配度更高。

          我們通過下面這個例子來證明一下。

          /**
           * 微信搜 JavaGuide 回復(fù)"面試突擊"即可免費(fèi)領(lǐng)取個人原創(chuàng)的 Java 面試手冊
           *
           * @author Guide哥
           * @date 2021/12/13 16:52
           **/

          public class VariableLengthArgument {

              public static void printVariable(String... args) {
                  for (String s : args) {
                      System.out.println(s);
                  }
              }

              public static void printVariable(String arg1, String arg2) {
                  System.out.println(arg1 + arg2);
              }

              public static void main(String[] args) {
                  printVariable("a""b");
                  printVariable("a""b""c""d");
              }
          }

          輸出:

          ab
          a
          b
          c
          d

          另外,Java 的可變參數(shù)編譯后實(shí)際會被轉(zhuǎn)換成一個數(shù)組,我們看編譯后生成的class文件就可以看出來了。

          public class VariableLengthArgument {

              public static void printVariable(String... args) {
                  String[] var1 = args;
                  int var2 = args.length;

                  for(int var3 = 0; var3 < var2; ++var3) {
                      String s = var1[var3];
                      System.out.println(s);
                  }

              }
              // ......
          }

          基本數(shù)據(jù)類型

          Java 中的幾種基本數(shù)據(jù)類型了解么?

          Java 中有 8 種基本數(shù)據(jù)類型,分別為:

          • 6 種數(shù)字類型:
            • 4 種整數(shù)型:byteshortintlong
            • 2 種浮點(diǎn)型:floatdouble
          • 1 種字符類型:char
          • 1 種布爾型:boolean

          這 8 種基本數(shù)據(jù)類型的默認(rèn)值以及所占空間的大小如下:

          基本類型位數(shù)字節(jié)默認(rèn)值取值范圍
          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
          boolean1
          falsetrue、false

          對于boolean,官方文檔未明確定義,它依賴于 JVM 廠商的具體實(shí)現(xiàn)。邏輯上理解是占用 1 位,但是實(shí)際中會考慮計算機(jī)高效存儲因素。

          另外,Java 的每種基本類型所占存儲空間的大小不會像其他大多數(shù)語言那樣隨機(jī)器硬件架構(gòu)的變化而變化。這種所占存儲空間大小的不變性是 Java 程序比用其他大多數(shù)語言編寫的程序更具可移植性的原因之一(《Java 編程思想》2.2 節(jié)有提到)。

          注意:

          1. Java 里使用long類型的數(shù)據(jù)一定要在數(shù)值后面加上L,否則將作為整型解析。
          2. char a = 'h'char :單引號,String a = "hello":雙引號。

          這八種基本類型都有對應(yīng)的包裝類分別為:ByteShortIntegerLongFloatDoubleCharacterBoolean

          基本類型和包裝類型的區(qū)別?

          • 成員變量包裝類型不賦值就是null,而基本類型有默認(rèn)值且不是null
          • 包裝類型可用于泛型,而基本類型不可以。
          • 基本數(shù)據(jù)類型的局部變量存放在 Java 虛擬機(jī)棧中的局部變量表中,基本數(shù)據(jù)類型的成員變量(未被static修飾 )存放在 Java 虛擬機(jī)的堆中。包裝類型屬于對象類型,我們知道幾乎所有對象實(shí)例都存在于堆中。
          • 相比于對象類型, 基本數(shù)據(jù)類型占用的空間非常小。

          為什么說是幾乎所有對象實(shí)例呢?這是因?yàn)?HotSpot 虛擬機(jī)引入了 JIT 優(yōu)化之后,會對對象進(jìn)行逃逸分析,如果發(fā)現(xiàn)某一個對象并沒有逃逸到方法外部,那么就可能通過標(biāo)量替換來實(shí)現(xiàn)棧上分配,而避免堆上分配內(nèi)存

          ?? 注意 :基本數(shù)據(jù)類型存放在棧中是一個常見的誤區(qū)!基本數(shù)據(jù)類型的成員變量如果沒有被static修飾的話(不建議這么使用,應(yīng)該要使用基本數(shù)據(jù)類型對應(yīng)的包裝類型),就存放在堆中。

          class BasicTypeVar{
            private int x;
          }

          包裝類型的緩存機(jī)制了解么?

          Java 基本數(shù)據(jù)類型的包裝類型的大部分都用到了緩存機(jī)制來提升性能。

          Byte,Short,Integer,Long這 4 種包裝類默認(rèn)創(chuàng)建了數(shù)值[-128,127]的相應(yīng)類型的緩存數(shù)據(jù),Character創(chuàng)建了數(shù)值在[0,127]范圍的緩存數(shù)據(jù),Boolean直接返回TrueorFalse

          Integer 緩存源碼:

          public static Integer valueOf(int i) {
              if (i >= IntegerCache.low && i <= IntegerCache.high)
                  return IntegerCache.cache[i + (-IntegerCache.low)];
              return new Integer(i);
          }
          private static class IntegerCache {
              static final int low = -128;
              static final int high;
              static {
                  // high value may be configured by property
                  int h = 127;
              }
          }

          Character緩存源碼:

          public static Character valueOf(char c) {
              if (c <= 127) { // must cache
                return CharacterCache.cache[(int)c];
              }
              return new Character(c);
          }

          private static class CharacterCache {
              private CharacterCache(){}
              static final Character cache[] = new Character[127 + 1];
              static {
                  for (int i = 0; i < cache.length; i++)
                      cache[i] = new Character((char)i);
              }

          }

          Boolean緩存源碼:

          public static Boolean valueOf(boolean b) {
              return (b ? TRUE : FALSE);
          }

          如果超出對應(yīng)范圍仍然會去創(chuàng)建新的對象,緩存的范圍區(qū)間的大小只是在性能和資源之間的權(quán)衡。

          兩種浮點(diǎn)數(shù)類型的包裝類Float,Double并沒有實(shí)現(xiàn)緩存機(jī)制。

          Integer i1 = 33;
          Integer i2 = 33;
          System.out.println(i1 == i2);// 輸出 true

          Float i11 = 333f;
          Float i22 = 333f;
          System.out.println(i11 == i22);// 輸出 false

          Double i3 = 1.2;
          Double i4 = 1.2;
          System.out.println(i3 == i4);// 輸出 false

          下面我們來看一下問題。下面的代碼的輸出結(jié)果是true還是false呢?

          Integer i1 = 40;
          Integer i2 = new Integer(40);
          System.out.println(i1==i2);

          Integer i1=40這一行代碼會發(fā)生裝箱,也就是說這行代碼等價于Integer i1=Integer.valueOf(40)。因此,i1直接使用的是緩存中的對象。而Integer i2 = new Integer(40)會直接創(chuàng)建新的對象。

          因此,答案是false。你答對了嗎?

          記住:所有整型包裝類對象之間值的比較,全部使用 equals 方法比較

          自動裝箱與拆箱了解嗎?原理是什么?

          什么是自動拆裝箱?

          • 裝箱:將基本類型用它們對應(yīng)的引用類型包裝起來;
          • 拆箱:將包裝類型轉(zhuǎn)換為基本數(shù)據(jù)類型;

          舉例:

          Integer i = 10;  //裝箱
          int n = i;   //拆箱

          上面這兩行代碼對應(yīng)的字節(jié)碼為:

             L1

              LINENUMBER 8 L1

              ALOAD 0

              BIPUSH 10

              INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;

              PUTFIELD AutoBoxTest.i : Ljava/lang/Integer;

             L2

              LINENUMBER 9 L2

              ALOAD 0

              ALOAD 0

              GETFIELD AutoBoxTest.i : Ljava/lang/Integer;

              INVOKEVIRTUAL java/lang/Integer.intValue ()I

              PUTFIELD AutoBoxTest.n : I

              RETURN

          從字節(jié)碼中,我們發(fā)現(xiàn)裝箱其實(shí)就是調(diào)用了 包裝類的valueOf()方法,拆箱其實(shí)就是調(diào)用了xxxValue()方法。

          因此,

          • Integer i = 10等價于Integer i = Integer.valueOf(10)
          • int n = i等價于int n = i.intValue();

          注意:如果頻繁拆裝箱的話,也會嚴(yán)重影響系統(tǒng)的性能。我們應(yīng)該盡量避免不必要的拆裝箱操作。

          private static long sum() {
              // 應(yīng)該使用 long 而不是 Long
              Long sum = 0L;
              for (long i = 0; i <= Integer.MAX_VALUE; i++)
                  sum += i;
              return sum;
          }

          參考

          • https://stackoverflow.com/questions/1906445/what-is-the-difference-between-jdk-and-jre
          • https://www.educba.com/oracle-vs-openjdk/
          • https://stackoverflow.com/questions/22358071/differences-between-oracle-jdk-and-openjdk

          參考資料

          [1]

          issue#544:https://github.com/Snailclimb/JavaGuide/issues/544

          [2]

          Comparison of Java virtual machines:https://en.wikipedia.org/wiki/Comparison_of_Java_virtual_machines

          [3]

          Java SE Specifications:https://docs.oracle.com/javase/specs/index.html

          [4]

          編譯型語言:https://zh.wikipedia.org/wiki/%E7%B7%A8%E8%AD%AF%E8%AA%9E%E8%A8%80

          [5]

          編譯器:https://zh.wikipedia.org/wiki/%E7%B7%A8%E8%AD%AF%E5%99%A8

          [6]

          解釋型語言:https://zh.wikipedia.org/wiki/%E7%9B%B4%E8%AD%AF%E8%AA%9E%E8%A8%80

          [7]

          解釋器:https://zh.wikipedia.org/wiki/直譯器

          [8]

          即時編譯:https://zh.wikipedia.org/wiki/即時編譯

          [9]

          字節(jié)碼:https://zh.wikipedia.org/wiki/字節(jié)碼

          [10]

          Java:https://zh.wikipedia.org/wiki/Java

          [11]

          LLVM:https://zh.wikipedia.org/wiki/LLVM

          [12]

          基本功 | Java 即時編譯器原理解析及實(shí)踐:https://tech.meituan.com/2020/10/22/java-jit-practice-in-meituan.html

          [13]

          https://blogs.oracle.com/java-platform-group/update-and-faq-on-the-java-se-release-cadence:https://blogs.oracle.com/java-platform-group/update-and-faq-on-the-java-se-release-cadence

          [14]

          https://github.com/openjdk/jdk:https://github.com/openjdk/jdk

          [15]

          https://github.com/alibaba/dragonwell8:https://github.com/alibaba/dragonwell8

          [16]

          《Differences Between Oracle JDK and OpenJDK》:https://www.baeldung.com/oracle-jdk-vs-openjdk

          [17]

          https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html:https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html

          [18]

          issue#892:https://github.com/Snailclimb/JavaGuide/issues/892

              

          1、拖動文件就能觸發(fā)7-Zip安全漏洞,波及所有版本

          2、進(jìn)程切換的本質(zhì)是什么?

          3、一次 SQL 查詢優(yōu)化原理分析:900W+ 數(shù)據(jù),從 17s 到 300ms

          4、Redis數(shù)據(jù)結(jié)構(gòu)為什么既省內(nèi)存又高效?

          5、IntelliJ IDEA快捷鍵大全 + 動圖演示

          6、全球第三瀏覽器,封殺中國用戶這種操作!(文末送書)

          點(diǎn)

          點(diǎn)

          點(diǎn)點(diǎn)

          點(diǎn)

          瀏覽 46
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美色图视频一区 | 亚洲一级黄片免费观看视频洲免费观看 | 中国成人无码 | 在线视频一区二区 | 69性爱网 |