《面试宝典》:检验是否为合格的初中级程序员的面试知识点,你都知道了吗?查漏补缺
歡迎關(guān)注文章系列,一起學(xué)習(xí)
《提升能力,漲薪可待篇》
《面試知識(shí),工作可待篇》
《實(shí)戰(zhàn)演練,拒絕996篇》
也歡迎關(guān)注公 眾 號(hào)【Ccww技術(shù)博客】,原創(chuàng)技術(shù)文章第一時(shí)間推出
如果此文對(duì)你有幫助、喜歡的話,那就點(diǎn)個(gè)贊唄,點(diǎn)個(gè)關(guān)注唄!
《面試知識(shí),工作可待篇》-Java筆試面試基礎(chǔ)知識(shí)大全
前言
是不是感覺找工作面試是那么難呢?
在找工作面試應(yīng)在學(xué)習(xí)的基礎(chǔ)進(jìn)行總結(jié)面試知識(shí)點(diǎn),工作也指日可待,歡迎一起學(xué)習(xí)【面試知識(shí),工作可待】系列
《面試知識(shí),工作可待篇》
- 《面試知識(shí),工作可待:集合篇》-java集合面試知識(shí)大全
- 《面試知識(shí),工作可待:多線程篇》java多線程并發(fā)系列–基礎(chǔ)知識(shí)點(diǎn)(筆試、面試必備)
- 《面試知識(shí),工作可待:JVM篇》JVM虛擬機(jī)(內(nèi)存、垃圾回收、性能優(yōu)化)(筆試、面試必備)
- …
1. Java環(huán)境
1. 1 Java 和 C++ 的區(qū)別?
- 都是面向?qū)ο蟮恼Z(yǔ)言,都支持封裝、繼承和多態(tài)。
- Java 不提供指針來直接訪問內(nèi)存,程序內(nèi)存更加安全。
- Java 的類是單繼承的,C++ 支持多重繼承;雖然 Java 的類不可以多繼承,但是接口可以多繼承。
【重要】Java 有自動(dòng)內(nèi)存管理機(jī)制,不需要程序員手動(dòng)釋放無用內(nèi)存。
1.2 JDK、JRE、JVM
1.2.1 JDK
JDK 即為 Java 開發(fā)工具包,包含編寫 Java 程序所必須的編譯、運(yùn)行等開發(fā)工具以及 JRE。開發(fā)工具如:
- 用于編譯 Java 程序的 javac 命令。
- 用于啟動(dòng) JVM 運(yùn)行 Java 程序的 Java 命令。
- 用于生成文檔的 Javadoc 命令。
- 用于打包的 jar 命令等等。
1.2…2 JRE
JRE 即為 Java 運(yùn)行環(huán)境,提供了運(yùn)行 Java 應(yīng)用程序所必須的軟件環(huán)境,包含有 Java 虛擬機(jī)(JVM)和豐富的系統(tǒng)類庫(kù)。系統(tǒng)類庫(kù)即為 Java 提前封裝好的功能類,只需拿來直接使用即可,可以大大的提高開發(fā)效率。
1.2…3 JVM
JVM 即為 Java 虛擬機(jī),提供了字節(jié)碼文件(.class)的運(yùn)行環(huán)境支持。
1.2…4 三者關(guān)系
- JDK > JRE > JVM
1.2 為什么 Java 被稱作是“平臺(tái)無關(guān)的編程語(yǔ)言”?
- Java 虛擬機(jī)是一個(gè)可以執(zhí)行 Java 字節(jié)碼的虛擬機(jī)進(jìn)程。
- Java 源文件( .java )被編譯成能被 Java 虛擬機(jī)執(zhí)行的字節(jié)碼文件( .class )。
- Java 被設(shè)計(jì)成允許應(yīng)用程序可以運(yùn)行在任意的平臺(tái),而不需要程序員為每一個(gè)平臺(tái)單獨(dú)重寫或者是重新編譯。Java 虛擬機(jī)讓這個(gè)變?yōu)榭赡?#xff0c;因?yàn)樗赖讓佑布脚_(tái)的指令長(zhǎng)度和其他特性。
1.3 什么是字節(jié)碼?
這個(gè)問題,面試官可以衍生提問,Java 是編譯執(zhí)行的語(yǔ)言,還是解釋執(zhí)行的語(yǔ)言。
Java 中引入了虛擬機(jī)的概念,即在機(jī)器和編譯程序之間加入了一層抽象的虛擬的機(jī)器。這臺(tái)虛擬的機(jī)器在任何平臺(tái)上都提供給編譯程序一個(gè)的共同的接口。
編譯程序只需要面向虛擬機(jī),生成虛擬機(jī)能夠理解的代碼,然后由解釋器來將虛擬機(jī)代碼轉(zhuǎn)換為特定系統(tǒng)的機(jī)器碼執(zhí)行。在 Java 中,這種供虛擬機(jī)理解的代碼叫做字節(jié)碼(即擴(kuò)展名為 .class 的文件),它不面向任何特定的處理器,只面向虛擬機(jī)。
每一種平臺(tái)的解釋器是不同的,但是實(shí)現(xiàn)的虛擬機(jī)是相同的。Java 源程序經(jīng)過編譯器編譯后變成字節(jié)碼,字節(jié)碼由虛擬機(jī)解釋執(zhí)行,虛擬機(jī)將每一條要執(zhí)行的字節(jié)碼送給解釋器,解釋器將其翻譯成特定機(jī)器上的機(jī)器碼,然后在特定的機(jī)器上運(yùn)行。這也就是解釋了 Java 的編譯與解釋并存的特點(diǎn)。
1.4 Java 源代碼
=> 編譯器 => JVM 可執(zhí)行的 Java 字節(jié)碼(即虛擬指令)=> JVM => JVM 中解釋器 => 機(jī)器可執(zhí)行的二進(jìn)制機(jī)器碼 => 程序運(yùn)行
1.5 采用字節(jié)碼的好處?
Java 語(yǔ)言通過字節(jié)碼的方式,在一定程度上解決了傳統(tǒng)解釋型語(yǔ)言執(zhí)行效率低的問題,同時(shí)又保留了解釋型語(yǔ)言可移植的特點(diǎn)。所以 Java 程序運(yùn)行時(shí)比較高效,而且,由于字節(jié)碼并不專對(duì)一種特定的機(jī)器,因此,Java程序無須重新編譯便可在多種不同的計(jì)算機(jī)上運(yùn)行。
解釋型語(yǔ)言:解釋型語(yǔ)言,是在運(yùn)行的時(shí)候?qū)⒊绦蚍g成機(jī)器語(yǔ)言。解釋型語(yǔ)言的程序不需要在運(yùn)行前編譯,在運(yùn)行程序的時(shí)候才翻譯,專門的解釋器負(fù)責(zé)在每個(gè)語(yǔ)句執(zhí)行的時(shí)候解釋程序代碼。這樣解釋型語(yǔ)言每執(zhí)行一次就要翻譯一次,效率比較低
例如:Python、PHP 。
2. 面向?qū)ο蠛兔嫦蜻^程
2.1 什么是面向?qū)ο?#xff1f;
面向?qū)ο笫且环N思想,萬事萬物抽象成一個(gè)對(duì)象,這里只討論面向?qū)ο缶幊?#xff08;OOP),Java 是一個(gè)支持并發(fā)、基于類和面向?qū)ο蟮挠?jì)算機(jī)編程語(yǔ)言。
2.1.1 類class
類是抽象的概念,是萬事萬物的抽象,是一類事物的共同特征的集合。
用計(jì)算機(jī)語(yǔ)言來描述類,是屬性和方法的集合。
2.1.2 對(duì)象instance、object
對(duì)象是類的具象,是一個(gè)實(shí)體。
對(duì)于我們每個(gè)人這個(gè)個(gè)體,都是抽象概念人 類 的不同的 實(shí)體 。
面向?qū)ο筌浖_發(fā)具有以下優(yōu)點(diǎn):
- 代碼開發(fā)模塊化,更易維護(hù)和修改。
- 代碼復(fù)用性強(qiáng)。
- 增強(qiáng)代碼的可靠性和靈活性。
- 增加代碼的可讀性。
2.2 面向?qū)ο蟮奶卣?/h3>
2.1 封裝
封裝,給對(duì)象提供了隱藏內(nèi)部特性和行為的能力。對(duì)象提供一些能被其他對(duì)象訪問的方法來改變它內(nèi)部的數(shù)據(jù)。
在 Java 當(dāng)中,有 4 種修飾符: default、public、private 和 protected 。每一種修飾符給其他的位于同一個(gè)包或者不同包下面對(duì)象賦予了不同的訪問權(quán)限,權(quán)限如下:
| public | ∨ | ∨ | ∨ | ∨ |
| protect | ∨ | ∨ | ∨ | × |
| default | ∨ | ∨ | × | × |
| private | ∨ | × | × | × |
封裝好處:
- 通過隱藏對(duì)象的屬性來保護(hù)對(duì)象內(nèi)部的狀態(tài)。
- 提高了代碼的可用性和可維護(hù)性,因?yàn)閷?duì)象的行為可以被單獨(dú)的改變或者是擴(kuò)展。
- 禁止對(duì)象之間的不良交互提高模塊化。
2.2 繼承
繼承,使對(duì)象基于基類字段和方法,新增自定義的的方法和屬性。繼承提供了代碼的重用行,也可以在不修改類的情況下給現(xiàn)存的類添加新特性。
繼承屬性:
- 子類擁有父類非 private 的屬性和方法。
- 子類可以擁有自己屬性和方法,即子類可以對(duì)父類進(jìn)行擴(kuò)展。
- 子類可以用自己的方式實(shí)現(xiàn)父類的方法
2.3 多態(tài)
多態(tài),程序中定義的引用變量所指向的具體類型和通過該引用變量發(fā)出的方法調(diào)用在編程時(shí)并不確定,而是在程序運(yùn)行期間才確定,即一個(gè)引用變量到底會(huì)指向哪個(gè)類的實(shí)例對(duì)象,該引用變量發(fā)出的方法調(diào)用到底是哪個(gè)類中實(shí)現(xiàn)的方法,必須在由程序運(yùn)行期間才能決定。
Java中有兩種形式可以實(shí)現(xiàn)多態(tài):
- 繼承(多個(gè)子類對(duì)同一方法的重寫)
- 接口(實(shí)現(xiàn)接口并覆蓋接口中同一方法)
2.3 面向?qū)ο蠛兔嫦蜻^程的區(qū)別?
面向過程:就是分析出解決問題所需要的步驟,然后用函數(shù)把這些步驟一步一步實(shí)現(xiàn),使用的時(shí)候一個(gè)一個(gè)依次調(diào)用就可以了,
面向?qū)ο?/strong>:是把構(gòu)成問題事務(wù)分解成各個(gè)對(duì)象,建立對(duì)象的目的不是為了完成一個(gè)步驟,而是為了描敘某個(gè)事物在整個(gè)解決問題的步驟中的行為。
3.1 面向過程
- 優(yōu)點(diǎn):性能比面向?qū)ο蟾?#xff0c;因?yàn)轭愓{(diào)用時(shí)需要實(shí)例化,開銷比較大,比較消耗資源。比如,單片機(jī)、嵌入式開發(fā)、Linux/Unix 等一般采用面向過程開發(fā),性能是最重要的因素。
- 缺點(diǎn):沒有面向?qū)ο笠拙S護(hù)、易復(fù)用、易擴(kuò)展。
3.2 面向?qū)ο?/h4>
- 優(yōu)點(diǎn):易維護(hù)、易復(fù)用、易擴(kuò)展,由于面向?qū)ο笥蟹庋b、繼承、多態(tài)性的特性,可以設(shè)計(jì)出低耦合的系統(tǒng),使系統(tǒng)更加靈活、更加易于維護(hù)。
- 缺點(diǎn):性能比面向過程低。
3. Java數(shù)據(jù)類型(基本數(shù)據(jù)類型和引用類型)
3. 1 基本數(shù)據(jù)類型如下:
- 整數(shù)值型:byte、short、int、long
- 字符型:char
- 浮點(diǎn)類型:float、double
- 布爾型:boolean
整數(shù)型:默認(rèn) int 型,小數(shù)默認(rèn)是 double 型。Float 和 Long 類型的必須加后綴。
比如:float f = 100f 。
引用類型聲明的變量是指該變量在內(nèi)存中實(shí)際存儲(chǔ)的是一個(gè)引用地址,實(shí)體在堆中。
3.2 引用類型
引用類型指向一個(gè)對(duì)象,不是原始值,指向?qū)ο蟮淖兞渴且米兞?/p>
在java里面除去基本數(shù)據(jù)類型的其他類型都是引用類型,自己定義的class類都是引用類型,可以像基本類型一樣使用。
引用類型常見的有:String、StringBuffer、ArrayList、HashSet、HashMap等
特別注意,String 是引用類型不是基本類型。
3.3 引用類型簡(jiǎn)介
| 強(qiáng)引用 | 可以 | 從不回收 | 普遍對(duì)象的狀態(tài) |
| 軟引用 | 可以 | 內(nèi)存不足時(shí) | 內(nèi)存敏感的高速緩存 |
| 弱引用 | 可以 | 下一次GC | 對(duì)象緩存 |
| 虛引用 | 不可以 | 下一次GC | 一般用于追蹤垃圾收集器的回收動(dòng)作 |
?
3. 4 什么是值傳遞和引用傳遞?
-
值傳遞,是對(duì)基本型變量而言的,傳遞的是該變量的一個(gè)副本,改變副本不影響原變量。
-
引用傳遞,一般是對(duì)于對(duì)象型變量而言的,傳遞的是該對(duì)象地址的一個(gè)副本,并不是原對(duì)象本身。
一般認(rèn)為,Java 內(nèi)的傳遞都是值傳遞,Java 中實(shí)例對(duì)象的傳遞是引用傳遞。
3. 5 char 型變量中能不能存貯一個(gè)中文漢字?為什么?
- C 語(yǔ)言中,char 類型占 1 個(gè)字節(jié),而漢字占 2 個(gè)字節(jié),所以不能存儲(chǔ)。
- Java 語(yǔ)言中,char 類型占 2 個(gè)字節(jié),而且 Java 默認(rèn)采用 Unicode 編碼,一個(gè) Unicode 碼是 16 位,所以一個(gè) Unicode 碼占兩個(gè)字節(jié),Java 中無論漢字還是英文字母,都是用 Unicode 編碼來表示的。所以,在 Java 中,char 類型變量可以存儲(chǔ)一個(gè)中文漢字。
3.6 equals 與 == 的區(qū)別?
- 值類型(int,char,long,boolean 等)都是用 == 判斷相等性。
- 對(duì)象引用的話
- == 判斷引用所指的對(duì)象是否是同一個(gè)。
- equals 方法,是 Object 的成員函數(shù),有些類會(huì)覆蓋(override) 這個(gè)方法,用于判斷對(duì)象的等價(jià)性。
例如 String 類,兩個(gè)引用所指向的 String 都是 “abc” ,但可能出現(xiàn)他們實(shí)際對(duì)應(yīng)的對(duì)象并不是同一個(gè)(和 JVM 實(shí)現(xiàn)方式有關(guān)),因此用 == 判斷他們可能不相等,但用 equals 方法判斷一定是相等的。
4. Java類Class
類是對(duì)事物的抽象,抽象類是對(duì)類的抽象,接口是對(duì)抽象類的抽象。
4.1 Java 對(duì)象(Class)創(chuàng)建的方式?
- 使用 new 關(guān)鍵字創(chuàng)建對(duì)象。
- 使用 Class 類的 newInstance 方法(反射機(jī)制)。
- 使用 Constructor 類的 newInstance 方法(反射機(jī)制)。
- 使用 clone 方法創(chuàng)建對(duì)象。
- 使用(反)序列化機(jī)制創(chuàng)建對(duì)象。
4.2 抽象類與接口
4.2.1 抽象類
從面向?qū)ο蟮慕嵌葋碇v,我們知道所有的對(duì)象都是通過類來描繪的,但是反過來卻不是這樣,并不是 所有的類都是用來描繪對(duì)象的,如果一個(gè)類中沒有包含足夠的信息來描繪一個(gè)具體的對(duì)象,這樣的類就可以認(rèn)為是抽象類。
抽象類除了不能實(shí)例化對(duì)象之外,類的其它功能依然存在,成員變量、成員方法和構(gòu)造方法的訪問方式和普通類一樣。由于抽象類不能實(shí)例化對(duì)象,所以抽象類必須被繼承,才能被使用。
4.2.2 接口
接口,在JAVA編程語(yǔ)言中是一個(gè)抽象類型,主要是抽象方法的集合,接口中的變量定義必須為public static final類型。接口通常以interface來聲明。
4.2.3 抽象類與接口的對(duì)比
| 默認(rèn)的方法實(shí)現(xiàn) | 它可以有默認(rèn)的方法實(shí)現(xiàn) | 接口完全是抽象的。它根本不存在方法的實(shí)現(xiàn) |
| 實(shí)現(xiàn) | 子類使用extends關(guān)鍵字來繼承抽象類。如果子類不是抽象類的話,它需要提供抽象類中所有聲明的方法的實(shí)現(xiàn)。 | 子類使用關(guān)鍵字implements來實(shí)現(xiàn)接口。它需要提供接口中所有聲明的方法的實(shí)現(xiàn) |
| 構(gòu)造器 | 抽象類可以有構(gòu)造器 | 接口不能有構(gòu)造器 |
| 與正常Java類的區(qū)別 | 除了你不能實(shí)例化抽象類之外,它和普通Java類沒有任何區(qū)別 | 接口是完全不同的類型 |
| 訪問修飾符 | 抽象方法可以有public、protected和default這些修飾符 | 接口方法默認(rèn)修飾符是public。你不可以使用其它修飾符。 |
| main方法 | 抽象方法可以有main方法并且我們可以運(yùn)行它 | 接口沒有main方法,因此我們不能運(yùn)行它。(java8以后接口可以有default和static方法,所以可以運(yùn)行main方法) |
| 多繼承 | 抽象方法可以繼承一個(gè)類和實(shí)現(xiàn)多個(gè)接口 | 接口只可以繼承一個(gè)或多個(gè)其它接口 |
| 速度 | 它比接口速度要快 | 接口是稍微有點(diǎn)慢的,因?yàn)樗枰獣r(shí)間去尋找在類中實(shí)現(xiàn)的方法。 |
| 添加新方法 | 如果你往抽象類中添加新的方法,你可以給它提供默認(rèn)的實(shí)現(xiàn)。因此你不需要改變你現(xiàn)在的代碼 | 如果你往接口中添加方法,那么你必須改變實(shí)現(xiàn)該接口的類。 |
4.3 講講類的實(shí)例化順序
初始化順序如下:
->父類靜態(tài)變量
->父類靜態(tài)代碼塊
->子類靜態(tài)變量、
->子類靜態(tài)代碼塊
->父類非靜態(tài)變量(父類實(shí)例成員變量)
->父類構(gòu)造函數(shù)
->子類非靜態(tài)變量(子類實(shí)例成員變量)
->子類構(gòu)造函數(shù)
4.4 內(nèi)部類
簡(jiǎn)單的說,就是在一個(gè)類、接口或者方法的內(nèi)部創(chuàng)建另一個(gè)類。這樣理解的話,創(chuàng)建內(nèi)部類的方法就很明確了。當(dāng)然,詳細(xì)的可以看看 《Java 內(nèi)部類總結(jié)(吐血之作)》 文章。
4.4.1 內(nèi)部類的作用是什么?
-
內(nèi)部類可以很好的實(shí)現(xiàn)隱藏(一般的非內(nèi)部類,是不允許有 private 與protected權(quán)限的,但內(nèi)部類可以)
-
內(nèi)部類擁有外圍類的所有元素的訪問權(quán)限
-
可是實(shí)現(xiàn)多重繼承
-
可以避免修改接口而實(shí)現(xiàn)同一個(gè)類中兩種同名方法的調(diào)用。
4.5 Anonymous Inner Class(匿名內(nèi)部類)是否可以繼承其它類?是否可以實(shí)現(xiàn)接口?
可以繼承其他類或?qū)崿F(xiàn)其他接口,在 Java 集合的流式操作中,我們常常這么干。
4.6 內(nèi)部類可以引用它的包含類(外部類)的成員嗎?有沒有什么限制?
一個(gè)內(nèi)部類對(duì)象可以訪問創(chuàng)建它的外部類對(duì)象的成員,包括私有成員。
如果你把靜態(tài)嵌套類當(dāng)作內(nèi)部類的一種特例,那在這種情況下不可以訪問外部類的普通成員變量,而只能訪問外部類中的靜態(tài)成員,例如:
class Outer {static int x;static class Inner {void test() {syso(x);}} }4.7 構(gòu)造方法、構(gòu)造方法重載
4.7.1 構(gòu)造方法
當(dāng)新對(duì)象被創(chuàng)建的時(shí)候,構(gòu)造方法會(huì)被調(diào)用。每一個(gè)類都有構(gòu)造方法。在程序員沒有給類提供構(gòu)造方法的情況下,Java 編譯器會(huì)為這個(gè)類創(chuàng)建一個(gè)默認(rèn)的構(gòu)造方法。
4.7.2 構(gòu)造方法重載
Java 中構(gòu)造方法重載和方法重載很相似。可以為一個(gè)類創(chuàng)建多個(gè)構(gòu)造方法。每一個(gè)構(gòu)造方法必須有它自己唯一的參數(shù)列表。
4. 8 重載和重寫的區(qū)別?
4.8.1 重寫 override
- 方法名、參數(shù)、返回值相同。
- 子類方法不能縮小父類方法的訪問權(quán)限。
- 子類方法不能拋出比父類方法更多的異常(但子類方法可以不拋出異常)。
- 存在于父類和子類之間。
- 方法被定義為 final 不能被重寫。
4.8.2 重載 overload
- 參數(shù)類型、個(gè)數(shù)、順序至少有一個(gè)不相同。
- 不能重載只有返回值不同的方法名。
- 存在于父類和子類、同類中。
4.9 hashCode() 以及equals()
4.9.1 為什么需要子類實(shí)現(xiàn)這兩個(gè)方法?
父類的 equals ,一般情況下是無法滿足子類的 equals 的需求。
比如所有的對(duì)象都繼承 Object ,默認(rèn)使用的是 Object 的 equals 方法,在比較兩個(gè)對(duì)象的時(shí)候,是看他們是否指向同一個(gè)地址。但是我們的需求是對(duì)象的某個(gè)屬性相同,就相等了,而默認(rèn)的 equals 方法滿足不了當(dāng)前的需求,所以我們要重寫 equals 方法。
如果重寫了 equals 方法,就必須重寫 hashCode 方法,否則就會(huì)降低 Map 等集合的索引速度。
4.9.2 說一說你對(duì) java.lang.Object 對(duì)象中 hashCode 和 equals 方法的理解。在什么場(chǎng)景下需要重新實(shí)現(xiàn)這兩個(gè)方法?
理解答案與4.8.1差不多,
equals 方法,用于比較對(duì)象的內(nèi)容是否相等。
當(dāng)覆蓋了 equals 方法時(shí),比較對(duì)象是否相等將通過覆蓋后的 equals 方法進(jìn)行比較(判斷對(duì)象的內(nèi)容是否相等)。
hashCode 方法,大多在集合中用到。
將對(duì)象放入到集合中時(shí),首先判斷要放入對(duì)象的 hashCode 值與集合中的任意一個(gè)元素的 hashCode 值是否相等,如果不相等直接將該對(duì)象放入集合中。
如果 hashCode 值相等,然后再通過 equals 方法判斷要放入對(duì)象與集合中的任意一個(gè)對(duì)象是否相等,如果 equals 判斷不相等,直接將該元素放入到集合中,否則不放入。
4.9.3 有沒有可能 2 個(gè)不相等的對(duì)象有相同的 hashCode?
可能會(huì)發(fā)生,這個(gè)被稱為哈希碰撞。當(dāng)然,相等的對(duì)象,即我們重寫了 equals 方法,一定也要重寫 hashCode 方法,否則將出現(xiàn)我們?cè)?HashMap 中,相等的對(duì)象作為 key ,將找不到對(duì)應(yīng)的 value 。
4.9.4 equals 和 hashCode 的關(guān)系
-
equals 不相等,hashCode 可能相等。
-
equals 相等,請(qǐng)重寫 hashCode 方法,保證 hashCode 相等。
一般來說,hashCode 方法的重寫,可以看看 《科普:為什么 String hashCode 方法選擇數(shù)字31作為乘子》 方法。
5. 常用類
5.1 String、StringBuffer、StringBuilder
5.1.1 String、StringBuffer、StringBuilder 的區(qū)別?
可變性:
String 類中使用 final 關(guān)鍵字字符數(shù)組保存字符串,代碼:
private?final?char?value[],所以string對(duì)象是不可變的。
StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在 AbstractStringBuilder 中也是使用字符數(shù)組保存字符串 char[] value ,但是沒有用 final 關(guān)鍵字修飾,代碼:
char[]value這兩種對(duì)象都是可變的。
線程安全性:
- String中的對(duì)象是不可變的,也就可以理解為常量,線程安全。
- StringBuffer對(duì)方法加了同步鎖或者對(duì)調(diào)用的方法加了同步鎖,所以是線程安全的。
- StringBuilder并沒有對(duì)方法進(jìn)行加同步鎖,所以是非線程安全的。
AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
性能:
每次對(duì)String 類型進(jìn)行改變的時(shí)候,都會(huì)生成一個(gè)新的String對(duì)象,然后將指針指向新的String 對(duì)象。
StringBuffer每次都會(huì)對(duì)StringBuffer對(duì)象本身進(jìn)行操作,而不是生成新的對(duì)象并改變對(duì)象引用。相同情況下使用StirngBuilder 相比使用StringBuffer 僅能獲得10%~15% 左右的性能提升,但卻要冒多線程不安全的風(fēng)險(xiǎn)。
5.1.2對(duì)于三者使用的總結(jié)
- 操作少量的數(shù)據(jù)用 = String
- 單線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) = StringBuilder,甚至有時(shí),我們?yōu)榱吮苊饷總€(gè)線程重復(fù)創(chuàng)建 StringBuilder 對(duì)象,會(huì)通過 ThreadLocal + StringBuilder 的方式,進(jìn)行對(duì) StringBuilder 的重用
- 多線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) = StringBuffer
5.1.3 String s = new String(“xyz”) 會(huì)創(chuàng)建幾個(gè)對(duì)象?
首先,在 String 池內(nèi)找,找到 “xyz” 字符串,不創(chuàng)建 “xyz” 對(duì)應(yīng)的 String 對(duì)象,否則創(chuàng)建一個(gè)對(duì)象。
然后,遇到 new 關(guān)鍵字,在內(nèi)存上創(chuàng)建 String 對(duì)象,并將其返回給 s ,又一個(gè)對(duì)象。
所以,總共是 1 個(gè)或者 2 個(gè)對(duì)象
5.1.4 StringTokenizer 是什么?
StringTokenizer ,是一個(gè)用來分割字符串的工具類。
示例代碼如下:
StringTokenizer st = new StringTokenizer(”Hello World”); while (st.hasMoreTokens()) {System.out.println(st.nextToken()); }輸出如下:
Hello
World
5.2 什么是自動(dòng)拆裝箱?
自動(dòng)裝箱和拆箱,就是基本類型和引用類型之間的轉(zhuǎn)換。
5.2.1 什么要轉(zhuǎn)換?
如果你在 Java5 下進(jìn)行過編程的話,你一定不會(huì)陌生這一點(diǎn),你不能直接地向集合( Collection )中放入原始類型值,因?yàn)榧现唤邮諏?duì)象。
通常這種情況下你的做法是,將這些原始類型的值轉(zhuǎn)換成對(duì)象,然后將這些轉(zhuǎn)換的對(duì)象放入集合中。使用 Integer、Double、Boolean 等這些類,我們可以將原始類型值轉(zhuǎn)換成對(duì)應(yīng)的對(duì)象,但是從某些程度可能使得代碼不是那么簡(jiǎn)潔精煉。
為了讓代碼簡(jiǎn)練,Java5 引入了具有在原始類型和對(duì)象類型自動(dòng)轉(zhuǎn)換的裝箱和拆箱機(jī)制。
但是自動(dòng)裝箱和拆箱并非完美,在使用時(shí)需要有一些注意事項(xiàng),如果沒有搞明白自動(dòng)裝箱和拆箱,可能會(huì)引起難以察覺的 Bug 。
5.3 int 和 Integer 有什么區(qū)別?
-
int 是基本數(shù)據(jù)類型。
-
Integer 是其包裝類,注意是一個(gè)類。
需要注意下 Integer 的緩存策略
5.2.3 理解Java Integer 的緩存策略
6. 關(guān)鍵字
6.1 final、finally、finalize
6.1.1 final
final ,是修飾符關(guān)鍵字。
-
Class 類:如果一個(gè)類被聲明為 final ,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個(gè)類不能既被聲明為 abstract 的,又被聲明為 final 的。
-
變量或方法聲明為 final ,可以保證它們?cè)谑褂弥胁槐桓淖儭1宦暶鳛?final 的變量必須在聲明時(shí)給定初值,而在以后的引用中只能讀取,不可修改。被聲明為 final 的方法也同樣只能使用,不能重寫。
另外,在早期的 Java 實(shí)現(xiàn)版本中,會(huì)將 final 方法轉(zhuǎn)為內(nèi)嵌調(diào)用。但是如果方法過于龐大,可能看不到內(nèi)嵌調(diào)用帶來的任何性能提升(現(xiàn)在的 Java 版本已經(jīng)不需要使用 final 方法進(jìn)行這些優(yōu)化了)。類中所有的private 方法都隱式地指定為 final 。
6.1.2 finally
在異常處理時(shí)提供 finally 塊來執(zhí)行任何清除操作。如果拋出一個(gè)異常,那么相匹配的 catch 子句就會(huì)執(zhí)行,然后控制就會(huì)進(jìn)入 finally 塊(如果有的話)。
在以下 4 種特殊情況下,finally塊不會(huì)被執(zhí)行:
-
在 finally 語(yǔ)句塊中發(fā)生了異常。
-
在前面的代碼中用了 System.exit() 退出程序。
-
程序所在的線程死亡。
-
關(guān)閉 CPU 。
6.1.3 finalize
finalize ,是方法名。
Java 允許使用 #finalize() 方法,在垃圾收集器將對(duì)象從內(nèi)存中清除出去之前做必要的清理工作。這個(gè)方法是由垃圾收集器在確定這個(gè)對(duì)象沒有被引用時(shí)對(duì)這個(gè)對(duì)象調(diào)用的。
它是在 Object 類中定義的,因此所有的類都繼承了它。
子類覆蓋 finalize() 方法,以整理系統(tǒng)資源或者執(zhí)行其他清理工作。
finalize() 方法,是在垃圾收集器刪除對(duì)象之前對(duì)這個(gè)對(duì)象調(diào)用的。
一般情況下,我們?cè)跇I(yè)務(wù)中不會(huì)自己實(shí)現(xiàn)這個(gè)方法,更多是在一些框架中使用。
6.1.4 String 類能被繼承嗎,為什么?
不能,因?yàn)?String 是 final 修飾。
6.2 static
6.2.1 static特點(diǎn)
- static是一個(gè)修飾符,用于修飾成員。(成員變量,成員函數(shù))static修飾的成員變量 稱之為靜態(tài)變量或類變量。
- static修飾的成員被所有的對(duì)象共享。
- static優(yōu)先于對(duì)象存在,因?yàn)閟tatic的成員隨著類的加載就已經(jīng)存在。
- static修飾的成員多了一種調(diào)用方式,可以直接被類名所調(diào)用,(類名.靜態(tài)成員)。
- static修飾的數(shù)據(jù)是共享數(shù)據(jù),對(duì)象中的存儲(chǔ)的是特有的數(shù)據(jù)
6.2.2 是否可以在 static方法中訪問非 static 變量?
static 變量在 Java 中是屬于類的,它在所有的實(shí)例中的值是一樣的。當(dāng)類被 Java 虛擬機(jī)載入的時(shí)候,會(huì)對(duì) static 變量進(jìn)行初始化。如果你的代碼嘗試不用實(shí)例來訪問非 static 的變量,編譯器會(huì)報(bào)錯(cuò),因?yàn)檫@些變量還沒有被創(chuàng)建出來,還沒有跟任何實(shí)例關(guān)聯(lián)上。
由于靜態(tài)方法可以不通過對(duì)象進(jìn)行調(diào)用,因此在靜態(tài)方法里,不能調(diào)用其他非靜態(tài)變量,也不可以訪問非靜態(tài)變量成員。
如果你的代碼嘗試不用實(shí)例來訪問非 static 的變量,編譯器會(huì)報(bào)錯(cuò),因?yàn)檫@些變量還沒有被創(chuàng)建出來,還沒有跟任何實(shí)例關(guān)聯(lián)上。
6.2.3 成員變量和靜態(tài)變量的區(qū)別:
-
生命周期的不同:
- 成員變量隨著對(duì)象的創(chuàng)建而存在隨著對(duì)象的回收而釋放。
- 靜態(tài)變量隨著類的加載而存在隨著類的消失而消失。
-
調(diào)用方式不同:
- 成員變量只能被對(duì)象調(diào)用。
- 靜態(tài)變量可以被對(duì)象調(diào)用,也可以用類名調(diào)用。(推薦用類名調(diào)用)
-
別名不同:
- 成員變量也稱為實(shí)例變量。
- 靜態(tài)變量稱為類變量。
-
數(shù)據(jù)存儲(chǔ)位置不同:
-
成員變量數(shù)據(jù)存儲(chǔ)在堆內(nèi)存的對(duì)象中,所以也叫對(duì)象的特有數(shù)據(jù)。
-
靜態(tài)變量數(shù)據(jù)存儲(chǔ)在方法區(qū)(共享數(shù)據(jù)區(qū))的靜態(tài)區(qū),所以也叫對(duì)象的共享數(shù)據(jù)。
-
6.2.4 static 關(guān)鍵字修飾的加載順序
->父類靜態(tài)變量
? ->父類靜態(tài)代碼塊
? ->子類靜態(tài)變量
? ->子類靜態(tài)代碼塊
? ->父類普通變量
? ->父類普通代碼塊
? ->父類構(gòu)造函數(shù)
? ->子類普通變量
? ->子類普通代碼塊
? ->子類構(gòu)造函數(shù)
6.3 transient 關(guān)鍵字
transient聲明一個(gè)實(shí)例變量,當(dāng)對(duì)象存儲(chǔ)時(shí),它的值不需要維持。換句話來說就是,用transient關(guān)鍵字標(biāo)記的成員變量不參與序列化過程,
transient 只能修飾變量,不能修飾類和方法。
6.3.1 Java 序列話中,如果有些字段不想進(jìn)行序列化怎么辦?
對(duì)于不想進(jìn)行序列化的變量,使用 transient 關(guān)鍵字修飾,
- 當(dāng)對(duì)象被序列化時(shí),阻止實(shí)例中那些用此關(guān)鍵字修飾的的變量序列化。
- 當(dāng)對(duì)象被反序列化時(shí),被 transient 修飾的變量值不會(huì)被持久化和恢復(fù)。
6.4 volatile關(guān)鍵詞
volatile 關(guān)鍵字用在多線程同步中,可保證讀取的可見性,JVM只是保證從主內(nèi)存加載到線程工作內(nèi)存的值是最新的讀取值,而非 cache 中
6.4.1 volatile關(guān)鍵字是否能保證線程安全?
不能 , 多個(gè)線程對(duì) volatile 的寫操作,無法保證線程安全。例如假如線程 1,線程 2 在進(jìn)行 read,load 操作中,發(fā)現(xiàn)主內(nèi)存中 count 的值都是 5,那么都會(huì)加載這個(gè)最新的值,在線程 1 堆 count 進(jìn)行修改之后,會(huì) write 到主內(nèi)存中,主內(nèi)存中的 count 變量就會(huì)變?yōu)?6;線程 2 由于已經(jīng)進(jìn)行 read,load 操作,在進(jìn)行運(yùn)算之后,也會(huì)更新主內(nèi)存 count 的變量值為 6;導(dǎo)致兩個(gè)線程及時(shí)用 volatile 關(guān)鍵字修改之后,還是會(huì)存在并發(fā)的情況
7. Java IO
7.1 Java IO 相關(guān)的類
Java IO 相關(guān)的類,在 java.io 包下,具體操作分成面向字節(jié)(Byte)和面向字符(Character)兩種方式。如下圖所示:
7.2 什么是 Java 序列化?
序列化就是一種用來處理對(duì)象流的機(jī)制,所謂對(duì)象流也就是將對(duì)象的內(nèi)容進(jìn)行流化。
可以對(duì)流化后的對(duì)象進(jìn)行讀寫操作,也可將流化后的對(duì)象傳輸于網(wǎng)絡(luò)之間。
序列化是為了解決在對(duì)對(duì)象流進(jìn)行讀寫操作時(shí)所引發(fā)的問題。
反序列化的過程,則是和序列化相反的過程。
我們不能將序列化局限在 Java 對(duì)象轉(zhuǎn)換成二進(jìn)制數(shù)組,比如,將一個(gè) Java 對(duì)象轉(zhuǎn)換成 JSON 字符串等,這也可以理解為是序列化。
7.2.1如何實(shí)現(xiàn) Java 序列化?
將需要被序列化的類,實(shí)現(xiàn) Serializable 接口,該接口沒有需要實(shí)現(xiàn)的方法,implements Serializable 只是為了標(biāo)注該對(duì)象是可被序列化的。
-
序列化
- 首先使用一個(gè)輸出流(如:FileOutputStream)來構(gòu)造一個(gè) ObjectOutputStream(對(duì)象流)對(duì)象
- 接著,使用 ObjectOutputStream 對(duì)象的 #writeObject(Object obj) 方法,就可以將參數(shù)為 obj 的對(duì)象寫出(即保存其狀態(tài))。
-
反序列化
- 要恢復(fù)的話則用輸入流。
7.3 如何實(shí)現(xiàn)對(duì)象克隆(淺克隆和深克隆)?
- 實(shí)現(xiàn) Cloneable 接口,并重寫 Object 類中的 #clone() 方法。可以實(shí)現(xiàn)淺克隆,也可以實(shí)現(xiàn)深克隆。
- 實(shí)現(xiàn) Serializable 接口,通過對(duì)象的序列化和反序列化實(shí)現(xiàn)克隆。可以實(shí)現(xiàn)真正的深克隆。
實(shí)際場(chǎng)景下,我們使用的克隆比較少,更多是對(duì)象之間的屬性克隆。例如說,將 DO 的屬性復(fù)制到 DTO 中,又或者將 DTO 的屬性復(fù)制到 VO 中。此時(shí),我們一般使用 BeanUtils 工具類。
8.異常
8.1 異常機(jī)制的概述
? 異常機(jī)制是指當(dāng)程序出現(xiàn)錯(cuò)誤后,程序如何處理。具體來說,異常機(jī)制提供了程序退出的安全通道。當(dāng)出現(xiàn)錯(cuò)誤后,程序執(zhí)行的流程發(fā)生改變,程序的控制權(quán)轉(zhuǎn)移到異常處理器。
程序錯(cuò)誤分為三種:
- 編譯錯(cuò)誤:因?yàn)槌绦驔]有遵循語(yǔ)法規(guī)則,編譯程序能夠自己發(fā)現(xiàn)并且提示我們錯(cuò)誤的原因和位置,這個(gè)也是大家在剛接觸編程語(yǔ)言最常遇到的問題。
- 運(yùn)行時(shí)錯(cuò)誤:因?yàn)槌绦蛟趫?zhí)行時(shí),運(yùn)行環(huán)境發(fā)現(xiàn)了不能執(zhí)行的操作。
- 邏輯錯(cuò)誤:因?yàn)槌绦驔]有按照預(yù)期的邏輯順序執(zhí)行。異常也就是指程序運(yùn)行時(shí)發(fā)生錯(cuò)誤,而異常處理就是對(duì)這些錯(cuò)誤進(jìn)行處理和控制。
8.2 Throwable
Throwable 類圖
Throwable有兩個(gè)重要的子類 :
- Exception(異常)
- Error(錯(cuò)誤)
二者都是 Java 異常處理的重要子類,各自都包含大量子類
8.2.1 Exception(異常)和 Error(錯(cuò)誤)
-
Error(錯(cuò)誤),表示系統(tǒng)級(jí)的錯(cuò)誤和程序不必處理的異常,是 Java 運(yùn)行環(huán)境中的內(nèi)部錯(cuò)誤或者硬件問題。
- 例如:內(nèi)存資源不足等。
- 對(duì)于這種錯(cuò)誤,程序基本無能為力,除了退出運(yùn)行外別無選擇,它是由 Java 虛擬機(jī)拋出的
-
Exception(異常),表示需要捕捉或者需要程序進(jìn)行處理的異常,它處理的是因?yàn)槌绦蛟O(shè)計(jì)的瑕疵而引起的問題或者在外的輸入等引起的一般性問題,是程序必須處理的。Exception 又分為運(yùn)行時(shí)異常,受檢查異常。
- RuntimeException(運(yùn)行時(shí)異常),表示無法讓程序恢復(fù)的異常,導(dǎo)致的原因通常是因?yàn)閳?zhí)行了錯(cuò)誤的操作,建議終止邏輯,因此,編譯器不檢查這些異常。
- CheckedException(受檢查異常),是表示程序可以處理的異常,也即表示程序可以修復(fù)(由程序自己接受異常并且做出處理),所以稱之為受檢查異常
8.3 error 和 exception 有什么區(qū)別?
-
Error:Error類對(duì)象由 Java 虛擬機(jī)生成并拋出,大多數(shù)錯(cuò)誤與代碼編寫者所執(zhí)行的操作無關(guān) 。比如:
- OutOfMemoryError
- NoClassDefFoundError
- LinkageError
-
Exception : 在Exception分支中有一個(gè)重要的子類RuntimeException(運(yùn)行時(shí)異常),該類型的異常自動(dòng)為你所編寫的程序定義 異常,比如:
-
ArrayIndexOutOfBoundsException(數(shù)組下標(biāo)越界)
-
NullPointerException(空指針異常)
-
ArithmeticException(算術(shù)異常)
-
MissingResourceException(丟失資源)
-
ClassNotFoundException(找不到類)
-
BufferOverflowException
-
ClassCastException
8.4 CheckedException 和 RuntimeException 有什么區(qū)別?
- RuntimeException運(yùn)行異常:表示程序運(yùn)行過程中可能出現(xiàn)的非正常狀態(tài),運(yùn)行時(shí)異常表示虛擬機(jī)的通常操作中可能遇到的異常,是一種常見運(yùn)行錯(cuò)誤,只要程序設(shè)計(jì)得沒有問題通常就不會(huì)發(fā)生。
- CheckedException受檢異常:跟程序運(yùn)行的上下文環(huán)境有關(guān),即使程序設(shè)計(jì)無誤,仍然可能因使用的問題而引發(fā)。Java編譯器要求方法必須聲明拋出可能發(fā)生的受檢異常,但是并不要求必須聲明拋出未被捕獲的運(yùn)行時(shí)異常
Effective Java中對(duì)異常的使用給出了以下指導(dǎo)原則 :
不要將異常處理用于正常的控制流(設(shè)計(jì)良好的API不應(yīng)該強(qiáng)迫它的調(diào)用者為了正常的控制流而使用異常)
對(duì)可以恢復(fù)的情況使用受檢異常,對(duì)編程錯(cuò)誤使用運(yùn)行時(shí)異常
避免不必要的使用受檢異常(可以通過一些狀態(tài)檢測(cè)手段來避免異常的發(fā)生)
優(yōu)先使用標(biāo)準(zhǔn)的異常
每個(gè)方法拋出的異常都要有文檔
保持異常的原子性
不要在catch中忽略掉捕獲到的異常
8.5 Throwable 類常用方法?
- getMessage() 方法:返回異常發(fā)生時(shí)的詳細(xì)信息。
- getCause() 方法:獲得導(dǎo)致當(dāng)前 Throwable 異常的 Throwable 異常。
- getStackTrace() 方法:獲得 Throwable 對(duì)象封裝的異常信息。
- printStackTrace() 方法:在控制臺(tái)上打印。
8.6 throw 與 throws 的區(qū)別 ?
- throw ,用于在程序中顯式地拋出一個(gè)異常。
- throws ,用于指出在該方法中沒有處理的異常。每個(gè)方法必須顯式指明哪些異常沒有處理,以便該方法的調(diào)用者可以預(yù)防可能發(fā)生的異常。最后,多個(gè)異常用逗號(hào)分隔。
8.7 異常處理中 finally 語(yǔ)句塊的重要性?
不管程序是否發(fā)生了異常, finally 語(yǔ)句塊都會(huì)被執(zhí)行,甚至當(dāng)沒有catch 聲明但拋出了一個(gè)異常時(shí), finally 語(yǔ)句塊也會(huì)被執(zhí)行。
finally 語(yǔ)句塊通常用于釋放資源, 如 I/O 緩沖區(qū), 數(shù)據(jù)庫(kù)連接等等。
8.8 UnsupportedOperationException 是什么?
UnsupportedOperationException ,是用于表明操作不支持的異常。
在 JDK 類中已被大量運(yùn)用,在集合框架java.util.Collections.UnmodifiableCollection 將會(huì)在所有 add 和 remove 操作中拋出這個(gè)異常。
9.反射
9.1 反射簡(jiǎn)介
當(dāng)程序運(yùn)行時(shí),允許改變程序結(jié)構(gòu)或變量類型,這種語(yǔ)言稱為動(dòng)態(tài)語(yǔ)言。我們認(rèn)為 Java 并不是動(dòng)態(tài)語(yǔ)言,但是它卻又一個(gè)非常突出的動(dòng)態(tài)相關(guān)的機(jī)制
9.2 反射的用途及實(shí)現(xiàn)?
Java 反射機(jī)制主要提供了以下功能:
- 在運(yùn)行時(shí)構(gòu)造一個(gè)類的對(duì)象。
- 判斷一個(gè)類所具有的成員變量和方法。
- 調(diào)用一個(gè)對(duì)象的方法。
- 生成動(dòng)態(tài)代理。
反射的主要用途, 開發(fā)各種通用框架 :
- Spring 框架的 IoC 基于反射創(chuàng)建對(duì)象和設(shè)置依賴屬性。
- Spring MVC 的請(qǐng)求調(diào)用對(duì)應(yīng)方法,也是通過反射。
- JDBC 的 Class#forName(String className) 方法,也是使用反射。
9.3 反射中,Class.forName 和 ClassLoader 區(qū)別?
- Class#forName(…) 方法,除了將類的 .class 文件加載到JVM 中之外,還會(huì)對(duì)類進(jìn)行解釋,執(zhí)行類中的 static 塊。
- ClassLoader 只干一件事情,就是將 .class 文件加載到 JVM 中,不會(huì)執(zhí)行 static 中的內(nèi)容,只有在 newInstance 才會(huì)去執(zhí)行 static 塊。
- Class#forName(name, initialize, loader) 方法,帶參函數(shù)也可控制是否加載 static 塊,并且只有調(diào)用了newInstance 方法采用調(diào)用構(gòu)造函數(shù),創(chuàng)建類的對(duì)象。
9.4 什么時(shí)候用斷言(assert)?
斷言,在軟件開發(fā)中是一種常用的調(diào)試方式,很多開發(fā)語(yǔ)言中都支持這種機(jī)制。
一般來說,斷言用于保證程序最基本、關(guān)鍵的正確性。斷言檢查通常在開發(fā)和測(cè)試時(shí)開啟。為了保證程序的執(zhí)行效率,在軟件發(fā)布后斷言檢查通常是關(guān)閉的。
斷言是一個(gè)包含布爾表達(dá)式的語(yǔ)句,在執(zhí)行這個(gè)語(yǔ)句時(shí)假定該表達(dá)式為true;如果表達(dá)式的值為 false ,那么系統(tǒng)會(huì)報(bào)告一個(gè)AssertionError 錯(cuò)誤。斷言的使用如下面的代碼所示:
斷言可以有兩種形式:
assert Expression1; 。
assert Expression1 : Expression2;
Expression1 應(yīng)該總是產(chǎn)生一個(gè)布爾值。
Expression2 可以是得出一個(gè)值的任意表達(dá)式;這個(gè)值用于生成顯示更多調(diào)試信息的字符串消息。
要在運(yùn)行時(shí)啟用斷言,可以在啟動(dòng) JVM 時(shí)使用 -enableassertions 或者 -ea 標(biāo)記。要在運(yùn)行時(shí)選擇禁用斷言,可以在啟動(dòng) JVM 時(shí)使用 -da 或者 -disableassertions 標(biāo)記。要在系統(tǒng)類中啟用或禁用斷言,可使用 -esa 或 -dsa 標(biāo)記。還可以在包的基礎(chǔ)上啟用或者禁用斷言。
也歡迎關(guān)注公 眾 號(hào)【Ccww技術(shù)博客】,原創(chuàng)技術(shù)文章第一時(shí)間推出
如果此文對(duì)你有幫助、喜歡的話,那就點(diǎn)個(gè)贊唄,點(diǎn)個(gè)關(guān)注唄!
總結(jié)
以上是生活随笔為你收集整理的《面试宝典》:检验是否为合格的初中级程序员的面试知识点,你都知道了吗?查漏补缺的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CEF | CEF浏览器客户端能扩展:实
- 下一篇: 利用css 使div上下左右移动