前言
前幾天偶遇老同學(xué),聊了聊工作;老同學(xué)和我分享了這次網(wǎng)易社招的面試題;文中篇幅有限,就和大家分享這么多;更多Java后端開發(fā)面試題請(qǐng)見文末!

1. 面向?qū)ο蟮奶攸c(diǎn)有哪些?
①. 封裝:所謂封裝,就是將客觀事物封裝成抽象的類,并且類可以把數(shù)據(jù)和方法讓可信的類或者對(duì)象進(jìn)行操作,對(duì)不可信的類或者對(duì)象進(jìn)行隱藏。類就是封裝數(shù)據(jù)和操作這些數(shù)據(jù)代碼的邏輯實(shí)體。在一個(gè)類的內(nèi)部,某些屬性和方法是私有的,不能被外界所訪問。通過這種方式,對(duì)象對(duì)內(nèi)部數(shù)據(jù)進(jìn)行了不同級(jí)別的訪問控制,就避免了程序中的無關(guān)部分的意外改變或錯(cuò)誤改變了對(duì)象的私有部分。
②. 繼承:繼承有這樣一種能力,就是能使用現(xiàn)有的類的所有功能,并無須重新編寫原來的這些類的基礎(chǔ)上對(duì)這些功能進(jìn)行擴(kuò)展。通過繼承創(chuàng)建的新類稱為子類或派生類,被繼承的稱為基類。繼承有兩種,一種是實(shí)現(xiàn)繼承,另外一種是接口繼承。實(shí)現(xiàn)繼承可以直接使用基類的屬性和方法而無需額外編碼,接口繼承是指使用屬性和方法的名稱,但是子必須提供實(shí)現(xiàn)的能力。
③. 多態(tài):所謂多態(tài)就是對(duì)一個(gè)實(shí)例的相同方法在不同的情形下有不同的表現(xiàn)形式。多態(tài)機(jī)制使得不同內(nèi)部結(jié)構(gòu)的對(duì)象可以共享相同的外部接口,這就意味著,雖然不同的類的內(nèi)部操作不同,但可以通過一個(gè)公共的類,它們可以通過相同的方式予以調(diào)用。
④. 抽象:提取現(xiàn)實(shí)世界中某事物的關(guān)鍵特性,為該事物構(gòu)建模型的過程。對(duì)同一事物在不同的需求下,需要提取的特性可能不一樣。得到的抽象模型中一般包含:屬性(數(shù)據(jù))和操作(行為)。這個(gè)抽象模型我們稱之為類。對(duì)類進(jìn)行實(shí)例化得到對(duì)象。
2. 列舉幾個(gè)java常用的package及其作用
- 第一個(gè)包:java.lang包。該包提供了Java語言進(jìn)行程序設(shè)計(jì)的基礎(chǔ)類,它是默認(rèn)導(dǎo)入的包。該包里面的Runnable接口和Object、Math、String、StringBuffer、System、Thread以及Throwable類需要重點(diǎn)掌握,因?yàn)樗鼈儜?yīng)用很廣。
- 第二個(gè)包:java.util包。該包提供了包含集合框架、遺留的集合類、事件模型、日期和時(shí)間實(shí)施、國際化和各種實(shí)用工具類(字符串標(biāo)記生成器、隨機(jī)數(shù)生成器和位數(shù)組)。
- 第三個(gè)包:java.io包。該包通過文件系統(tǒng)、數(shù)據(jù)流和序列化提供系統(tǒng)的輸入與輸出。
- 第四個(gè)包:java.net包。該包提供實(shí)現(xiàn)網(wǎng)絡(luò)應(yīng)用與開發(fā)的類。
- 第五個(gè)包:java.sql包。該包提供了使用Java語言訪問并處理存儲(chǔ)在數(shù)據(jù)源(通常是一個(gè)關(guān)系型數(shù)據(jù)庫)中的數(shù)據(jù)API。
- 第六個(gè)包:java.awt包
- 第七個(gè)包:javax.swing包。慶慶說:這兩個(gè)包提供了GUI設(shè)計(jì)與開發(fā)的類。java.awt包提供了創(chuàng)建界面和繪制圖形圖像的所有類,而javax.swing包提供了一組“輕量級(jí)”的組件,盡量讓這些組件在所有平臺(tái)上的工作方式相同。
- 第八個(gè)包:java.text包。提供了與自然語言無關(guān)的方式來處理文本、日期、數(shù)字和消息的類和接口。
3. 接口和抽象類有什么聯(lián)系和區(qū)別
相同點(diǎn):
- 接口和抽象類都不能被實(shí)例化,它們都位于繼承樹的頂端,用于被其他類實(shí)現(xiàn)和繼承。
- 接口和抽象類都可以包含抽象方法,實(shí)現(xiàn)接口或繼承抽象類的普通子類都必須實(shí)現(xiàn)這些抽象方法。
不同點(diǎn)
- 接口里只能包含抽象方法,靜態(tài)方法和默認(rèn)方法,不能為普通方法提供方法實(shí)現(xiàn),抽象類則完全可以包含普通方法。
- 接口里只能定義靜態(tài)常量,不能定義普通成員變量,抽象類里則既可以定義普通成員變量,也可以定義靜態(tài)常量。
- 接口不能包含構(gòu)造器,抽象類可以包含構(gòu)造器,抽象類里的構(gòu)造器并不是用于創(chuàng)建對(duì)象,而是讓其子類調(diào)用這些構(gòu)造器來完成屬于抽象類的初始化操作。
- 接口里不能包含初始化塊,但抽象類里完全可以包含初始化塊。
- 一個(gè)類最多只能有一個(gè)直接父類,包括抽象類,但一個(gè)類可以直接實(shí)現(xiàn)多個(gè)接口,通過實(shí)現(xiàn)多個(gè)接口可以彌補(bǔ)Java單繼承不足。
4. 重載和重寫有什么區(qū)別
override(重寫)
- 方法名、參數(shù)、返回值相同。
- 子類方法不能縮小父類方法的訪問權(quán)限。
- 子類方法不能拋出比父類方法更多的異常(但子類方法可以不拋出異常)。
- 存在于父類和子類之間。
- 方法被定義為final不能被重寫。
overload(重載)
- 參數(shù)類型、個(gè)數(shù)、順序至少有一個(gè)不相同。
- 不能重載只有返回值不同的方法名。
- 存在于父類和子類、同類中。
5. java有哪些基本數(shù)據(jù)類型?
①. 四種整數(shù)類型(byte、short、int、long):
- byte:8 位,用于表示最小數(shù)據(jù)單位,如文件中數(shù)據(jù),-128~127
- short:16 位,很少用,-32768 ~ 32767 int:32 位、最常用,-231-1~231 (21 億)
- long:64 位、次常用
②. 兩種浮點(diǎn)數(shù)類型(float、double):
- float:32 位,后綴 F 或 f,1 位符號(hào)位,8 位指數(shù),23 位有效尾數(shù)。
- double:64 位,最常用,后綴 D 或 d,1 位符號(hào)位,11 位指數(shù),52 位有效尾
③. 一種字符類型(char):
char:16 位,是整數(shù)類型,用單引號(hào)括起來的 1 個(gè)字符(可以是一個(gè)中文字符),使用 Unicode 碼代表字符,0~2^16-1(65535) 。
④. 一種布爾類型(boolean):true 真 和 false 假。
⑤. 類型轉(zhuǎn)換:char-->
- 自動(dòng)轉(zhuǎn)換:byte-->short-->int-->long-->float-->double
- 強(qiáng)制轉(zhuǎn)換:
- ①會(huì)損失精度,產(chǎn)生誤差,小數(shù)點(diǎn)以后的數(shù)字全部舍棄。
- ②容易超過取值范圍。
⑥. 記憶:
- 8位:Byte(字節(jié)型)
- 16位:short(短整型)、char(字符型)
- 32位:int(整型)、float(單精度型/浮點(diǎn)型)
- 64位:long(長整型)、double(雙精度型)
- 最后一個(gè):boolean(布爾類型)
6. Java支持的數(shù)據(jù)類型有哪些?什么是自動(dòng)拆裝箱?
- 基本數(shù)據(jù)類型:
- 整數(shù)值型:byte,short,int,long,
- 字符型:char
- 浮點(diǎn)類型:float,double
- 布爾型:boolean
整數(shù)默認(rèn)int型,小數(shù)默認(rèn)是double型。Float和long類型的必須加后綴。
首先知道String是引用類型不是基本類型,引用類型聲明的變量是指該變量在內(nèi)存中實(shí)際存儲(chǔ)的是一個(gè)引用地址,實(shí)體在堆中。引用類型包括類、接口、數(shù)組等。String類還是final修飾的。
而包裝類就屬于引用類型,自動(dòng)裝箱和拆箱就是基本類型和引用類型之間的轉(zhuǎn)換,至于為什么要轉(zhuǎn)換,因?yàn)榛绢愋娃D(zhuǎn)換為引用類型后,就可以new對(duì)象,從而調(diào)用包裝類中封裝好的方法進(jìn)行基本類型之間的轉(zhuǎn)換或者toString(當(dāng)然用類名直接調(diào)用也可以,便于一眼看出該方法是靜態(tài)的),還有就是如果集合中想存放基本類型,泛型的限定類型只能是對(duì)應(yīng)的包裝類型。
7. int 和 Integer 有什么區(qū)別
int 是基本數(shù)據(jù)類型;Integer是其包裝類,注意是一個(gè)類。為什么要提供包裝類呢?一是為了在各種類型間轉(zhuǎn)化,通過各種方法的調(diào)用。否則 你無法直接通過變量轉(zhuǎn)化。
比如,現(xiàn)在int要轉(zhuǎn)為String
int a=0;
String result=Integer.toString(a);
在java中包裝類,比較多的用途是用在于各種數(shù)據(jù)類型的轉(zhuǎn)化中。
我寫幾個(gè)demo
//通過包裝類來實(shí)現(xiàn)轉(zhuǎn)化的
int num=Integer.valueOf("12");
int num2=Integer.parseInt("12");
double num3=Double.valueOf("12.2");
double num4=Double.parseDouble("12.2");
//其他的類似。通過基本數(shù)據(jù)類型的包裝來的valueOf和parseXX來實(shí)現(xiàn)String轉(zhuǎn)為XX
String a=String.valueOf("1234");//這里括號(hào)中幾乎可以是任何類型
String b=String.valueOf(true);
String c=new Integer(12).toString();//通過包裝類的toString()也可以
String d=new Double(2.3).toString();
再舉例下。比如我現(xiàn)在要用泛型
List nums;
這里<>需要類。如果你用int。它會(huì)報(bào)錯(cuò)的。
8. 數(shù)組有沒有l(wèi)ength()方法?String有沒有l(wèi)ength()方法?
①. 數(shù)組沒有l(wèi)ength()方法,但有l(wèi)ength屬性
②. String類有l(wèi)ength() 方法
如:
String[] arr = new String[]{"hello", "world"};
System.out.println(arr.length);
String str = "hello world";
System.out.println(str.length());
9. Java中符號(hào)>>和>>>有什么區(qū)別?
-
>>:帶符號(hào)右移。正數(shù)右移高位補(bǔ)0,負(fù)數(shù)右移高位補(bǔ)1。- 比如:
4 >> 1,結(jié)果是2;-4 >>1,結(jié)果是-2。-2 >> 1,結(jié)果是-1。
- 比如:
-
>>>:無符號(hào)右移。無論是正數(shù)還是負(fù)數(shù),高位通通補(bǔ)0。
對(duì)于正數(shù)而言,>>和>>>沒區(qū)別。
對(duì)于負(fù)數(shù)而言,-2 >>> 1,結(jié)果是2147483647(Integer.MAX_VALUE),-1 >>> 1,結(jié)果是2147483647(Integer.MAX_VALUE)。
所以,要判斷兩個(gè)數(shù)符號(hào)是否相同時(shí),可以這么干:
return ((a >> 31) ^ (b >> 31)) == 0;
10. Java類的實(shí)例化順序
- 父類靜態(tài)成員和靜態(tài)初始化塊 ,按在代碼中出現(xiàn)的順序依次執(zhí)行
- 子類靜態(tài)成員和靜態(tài)初始化塊 ,按在代碼中出現(xiàn)的順序依次執(zhí)行
- 父類實(shí)例成員和實(shí)例初始化塊 ,按在代碼中出現(xiàn)的順序依次執(zhí)行
- 父類構(gòu)造方法
- 子類實(shí)例成員和實(shí)例初始化塊 ,按在代碼中出現(xiàn)的順序依次執(zhí)行
- 子類構(gòu)造方法
結(jié)論:對(duì)象初始化的順序,先靜態(tài)方法,再構(gòu)造方法,每個(gè)又是先基類后子類。
11. 什么是值傳遞和引用傳遞
值傳遞是對(duì)基本型變量而言的,傳遞的是該變量的一個(gè)副本,改變副本不影響原變量.
引用傳遞一般是對(duì)于對(duì)象型變量而言的,傳遞的是該對(duì)象地址的一個(gè)副本, 并不是原對(duì)象本身 。
一般認(rèn)為,java內(nèi)的傳遞都是值傳遞. java中實(shí)例對(duì)象的傳遞是引用傳遞 。
12. String能被繼承嗎?為什么?
不可以,因?yàn)镾tring類有final修飾符,而final修飾的類是不能被繼承的,實(shí)現(xiàn)細(xì)節(jié)不允許改變。平常我們定義的String str=”a”;其實(shí)和String str=new String(“a”)還是有差異的。
前者默認(rèn)調(diào)用的是String.valueOf來返回String實(shí)例對(duì)象,至于調(diào)用哪個(gè)則取決于你的賦值,比如String num=1,調(diào)用的是
public static String valueOf(int i) {
return Integer.toString(i);
}
后者則是調(diào)用如下部分:
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
最后我們的變量都存儲(chǔ)在一個(gè)char數(shù)組中
private final char value[];
13. String和StringBuilder、StringBuffer的區(qū)別?
①. String 字符串常量(final修飾,不可被繼承),String是常量,當(dāng)創(chuàng)建之后即不能更改。(可以通過StringBuffer和StringBuilder創(chuàng)建String對(duì)象(常用的兩個(gè)字符串操作類)。)
②. StringBuffer 字符串變量(線程安全),其也是final類別的,不允許被繼承,其中的絕大多數(shù)方法都進(jìn)行了同步處理,包括常用的Append方法也做了同步處理(synchronized修飾)。其自jdk1.0起就已經(jīng)出現(xiàn)。其toString方法會(huì)進(jìn)行對(duì)象緩存,以減少元素復(fù)制開銷。
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
③. StringBuilder 字符串變量(非線程安全)其自jdk1.5起開始出現(xiàn)。與StringBuffer一樣都繼承和實(shí)現(xiàn)了同樣的接口和類,方法除了沒使用synch修飾以外基本一致,不同之處在于最后toString的時(shí)候,會(huì)直接返回一個(gè)新對(duì)象。
public String toString() {
// Create a copy, don’t share the array
return new String(value, 0, count);
}
14. Java集合框架的基礎(chǔ)接口有哪些?
Collection:為集合層級(jí)的根接口。一個(gè)集合代表一組對(duì)象,這些對(duì)象即為它的元素。Java平臺(tái)不提供這個(gè)接口任何直接的實(shí)現(xiàn)。
Set:是一個(gè)不能包含重復(fù)元素的集合。這個(gè)接口對(duì)數(shù)學(xué)集合抽象進(jìn)行建模,被用來代表集合,就如一副牌。
List:是一個(gè)有序集合,可以包含重復(fù)元素。你可以通過它的索引來訪問任何元素。List更像長度動(dòng)態(tài)變換的數(shù)組。
Map:是一個(gè)將key映射到value的對(duì)象.一個(gè)Map不能包含重復(fù)的key:每個(gè)key最多只能映射一個(gè)value。
一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。
15. Java集合框架是什么?說出一些集合框架的優(yōu)點(diǎn)?
每種編程語言中都有集合,最初的Java版本包含幾種集合類:Vector、Stack、HashTable和Array。隨著集合的廣泛使用,Java1.2提出了囊括所有集合接口、實(shí)現(xiàn)和算法的集合框架。在保證線程安全的情況下使用泛型和并發(fā)集合類,Java已經(jīng)經(jīng)歷了很久。它還包括在Java并發(fā)包中,阻塞接口以及它們的實(shí)現(xiàn)。
集合框架的部分優(yōu)點(diǎn)如下:
- 使用核心集合類降低開發(fā)成本,而非實(shí)現(xiàn)我們自己的集合類。
- 隨著使用經(jīng)過嚴(yán)格測試的集合框架類,代碼質(zhì)量會(huì)得到提高。
- 通過使用JDK附帶的集合類,可以降低代碼維護(hù)成本。
- 復(fù)用性和可操作性。
16. HashMap 與HashTable有什么區(qū)別
HashTable
- 底層數(shù)組+鏈表實(shí)現(xiàn),無論key還是value都不能為null,線程安全,實(shí)現(xiàn)線程安全的方式是在修改數(shù)據(jù)時(shí)鎖住整個(gè)HashTable,效率低,ConcurrentHashMap做了相關(guān)優(yōu)化
- 初始size為11,擴(kuò)容:newsize = olesize*2+1
- 計(jì)算index的方法:index = (hash & 0x7FFFFFFF) % tab.length
HashMap
- 底層數(shù)組+鏈表實(shí)現(xiàn),可以存儲(chǔ)null鍵和null值,線程不安全
- 初始size為16,擴(kuò)容:newsize = oldsize*2,size一定為2的n次冪
- 擴(kuò)容針對(duì)整個(gè)Map,每次擴(kuò)容時(shí),原來數(shù)組中的元素依次重新計(jì)算存放位置,并重新插入
- 插入元素后才判斷該不該擴(kuò)容,有可能無效擴(kuò)容(插入后如果擴(kuò)容,如果沒有再次插入,就會(huì)產(chǎn)生無效擴(kuò)容)
- 當(dāng)Map中元素總數(shù)超過Entry數(shù)組的75%,觸發(fā)擴(kuò)容操作,為了減少鏈表長度,元素分配更均勻
- 計(jì)算index方法:index = hash & (tab.length – 1)
17. ArrayList 和 LinkedList 有什么區(qū)別?
ArrayList和LinkedList都實(shí)現(xiàn)了List接口,有以下的不同點(diǎn):
- ArrayList是基于索引的數(shù)據(jù)接口,它的底層是數(shù)組。它可以以O(shè)(1)時(shí)間復(fù)雜度對(duì)元素進(jìn)行隨機(jī)訪問。與此對(duì)應(yīng),LinkedList是以元素列表的形式存儲(chǔ)它的數(shù)據(jù),每一個(gè)元素都和它的前一個(gè)和后一個(gè)元素鏈接在一起,在這種情況下,查找某個(gè)元素的時(shí)間復(fù)雜度是O(n)。
- 相對(duì)于ArrayList,LinkedList的插入,添加,刪除操作速度更快,因?yàn)楫?dāng)元素被添加到集合任意位置的時(shí)候,不需要像數(shù)組那樣重新計(jì)算大小或者是更新索引。
- LinkedList比ArrayList更占內(nèi)存,因?yàn)長inkedList為每一個(gè)節(jié)點(diǎn)存儲(chǔ)了兩個(gè)引用,一個(gè)指向前一個(gè)元素,一個(gè)指向下一個(gè)元素。
18. 簡單介紹Java異??蚣?/h2>
Java對(duì)異常進(jìn)行了分類,不同類型的異常分別用不同的Java類表示,所有異常的根類為java.lang.Throwable,Throwable下面又派生了兩個(gè)子類:Error和Exception。
Error 表示應(yīng)用程序本身無法克服和恢復(fù)的一種嚴(yán)重問題,程序只有死的份了,例如,說內(nèi)存溢出和線程死鎖等系統(tǒng)問題。
Exception表示程序還能夠克服和恢復(fù)的問題,其中又分為系統(tǒng)異常和普通異常,系統(tǒng)異常是軟件本身缺陷所導(dǎo)致的問題,也就是軟件開發(fā)人員考慮不周所導(dǎo)致的問題,軟件使用者無法克服和恢復(fù)這種問題,但在這種問題下還可以讓軟件系統(tǒng)繼續(xù)運(yùn)行或者讓軟件死掉,例如,數(shù)組腳本越界(ArrayIndexOutOfBoundsException),空指針異常(NullPointerException)、類轉(zhuǎn)換異常(ClassCastException);普通異常是運(yùn)行環(huán)境的變化或異常所導(dǎo)致的問題,是用戶能夠克服的問題,例如,網(wǎng)絡(luò)斷線,硬盤空間不夠,發(fā)生這樣的異常后,程序不應(yīng)該死掉。
java為系統(tǒng)異常和普通異常提供了不同的解決方案,編譯器強(qiáng)制普通異常必須try..catch處理或用throws聲明繼續(xù)拋給上層調(diào)用方法處理,所以普通異常也稱為checked異常,而系統(tǒng)異常可以處理也可以不處理,所以,編譯器不強(qiáng)制用try..catch處理或用throws聲明,所以系統(tǒng)異常也稱為unchecked異常。
提示答題者:就按照三個(gè)級(jí)別去思考:虛擬機(jī)必須宕機(jī)的錯(cuò)誤,程序可以死掉也可以不死掉的錯(cuò)誤,程序不應(yīng)該死掉的錯(cuò)誤;
19. java中的throw 和 throws關(guān)鍵字有什么區(qū)別?
參考例子:
public class Test {
public static void main(String[] args) {
try {
test(args);
}
catch (Exception e) {
e.printStackTrace();
}
}
public static void test(String[] args) throws Exception {
if (args == null || args.length != 1 || !"true".equals(args[0])) {
throw new IllegalArgumentException("Invalid argument!");
}
}
}
區(qū)別:
throw是語句拋出一個(gè)異常,一般是在代碼塊的內(nèi)部,當(dāng)程序出現(xiàn)某種邏輯錯(cuò)誤時(shí)由程序員主動(dòng)拋出某種特定類型的異常
throws是方法可能拋出異常的聲明。(用在聲明方法時(shí),表示該方法可能要拋出異常)
20. 列舉幾個(gè)常見的運(yùn)行時(shí)異常?
常用的RuntimeException如:
- java.lang.ClassNotFoundException 類不存在異常
- java.lang.NullPointerException 空指針異常
- java.lang.IndexOutOfBoundsException 數(shù)據(jù)下標(biāo)越界
- java.lang.IllegalArgumentException 參數(shù)異常
- java.io.FileNotFoundException 文件不存在異常
其它如:
- ClassCastException - 類型強(qiáng)制轉(zhuǎn)換異常。
- ArithmeticException - 算術(shù)運(yùn)算異常
- ArrayStoreException - 向數(shù)組中存放與聲明類型不兼容對(duì)象異常
- NegativeArraySizeException - 創(chuàng)建一個(gè)大小為負(fù)數(shù)的數(shù)組錯(cuò)誤異常
- NumberFormatException - 數(shù)字格式異常
- SecurityException - 安全異常
- UnsupportedOperationException - 不支持的操作異常
21. final, finally, finalize有什么區(qū)別
- final:修飾符(關(guān)鍵字)如果一個(gè)類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個(gè)類不能既被聲明為 abstract的,又被聲明為final的。將變量或方法聲明為final,可以保證它們在使用中不被改變。被聲明為final的變量必須在聲明時(shí)給定初值,而在以后的引用中只能讀取,不可修改。被聲明為final的方法也同樣只能使用,不能重載 。
- finally:在異常處理時(shí)提供 finally 塊來執(zhí)行任何清除操作。如果拋出一個(gè)異常,那么相匹配的 catch 子句就會(huì)執(zhí)行,然后控制就會(huì)進(jìn)入 finally 塊(如果有的話)。
- finalize:方法名。Java 技術(shù)允許使用 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)用的。
22. 描述Java內(nèi)存模型
Java虛擬機(jī)規(guī)范中將Java運(yùn)行時(shí)數(shù)據(jù)分為六種:
①. 程序計(jì)數(shù)器:是一個(gè)數(shù)據(jù)結(jié)構(gòu),用于保存當(dāng)前正常執(zhí)行的程序的內(nèi)存地址。Java虛擬機(jī)的多線程就是通過線程輪流切換并分配處理器時(shí)間來實(shí)現(xiàn)的,為了線程切換后能恢復(fù)到正確的位置,每條線程都需要一個(gè)獨(dú)立的程序計(jì)數(shù)器,互不影響,該區(qū)域?yàn)椤熬€程私有”。
②. Java虛擬機(jī)棧:線程私有的,與線程生命周期相同,用于存儲(chǔ)局部變量表,操作棧,方法返回值。局部變量表放著基本數(shù)據(jù)類型,還有對(duì)象的引用。
③. 本地方法棧:跟虛擬機(jī)棧很像,不過它是為虛擬機(jī)使用到的Native方法服務(wù)。
④. Java堆:所有線程共享的一塊內(nèi)存區(qū)域,對(duì)象實(shí)例幾乎都在這分配內(nèi)存。
⑤. 方法區(qū):各個(gè)線程共享的區(qū)域,儲(chǔ)存虛擬機(jī)加載的類信息,常量,靜態(tài)變量,編譯后的代碼。
⑥. 運(yùn)行時(shí)常量池:代表運(yùn)行時(shí)每個(gè)class文件中的常量表。包括幾種常量:編譯時(shí)的數(shù)字常量、方法或者域的引用。
23. java中垃圾收集的方法有哪些?
①. 標(biāo)記-清除:這是垃圾收集算法中最基礎(chǔ)的,根據(jù)名字就可以知道,它的思想就是標(biāo)記哪些要被回收的對(duì)象,然后統(tǒng)一回收。這種方法很簡單,但是會(huì)有兩個(gè)主要問題:1.效率不高,標(biāo)記和清除的效率都很低;2.會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,導(dǎo)致以后程序在分配較大的對(duì)象時(shí),由于沒有充足的連續(xù)內(nèi)存而提前觸發(fā)一次GC動(dòng)作。
②. 復(fù)制算法:為了解決效率問題,復(fù)制算法將可用內(nèi)存按容量劃分為相等的兩部分,然后每次只使用其中的一塊,當(dāng)一塊內(nèi)存用完時(shí),就將還存活的對(duì)象復(fù)制到第二塊內(nèi)存上,然后一次性清楚完第一塊內(nèi)存,再將第二塊上的對(duì)象復(fù)制到第一塊。但是這種方式,內(nèi)存的代價(jià)太高,每次基本上都要浪費(fèi)一般的內(nèi)存。 于是將該算法進(jìn)行了改進(jìn),內(nèi)存區(qū)域不再是按照1:1去劃分,而是將內(nèi)存劃分為8:1:1三部分,較大那份內(nèi)存交Eden區(qū),其余是兩塊較小的內(nèi)存區(qū)叫Survior區(qū)。每次都會(huì)優(yōu)先使用Eden區(qū),若Eden區(qū)滿,就將對(duì)象復(fù)制到第二塊內(nèi)存區(qū)上,然后清除Eden區(qū),如果此時(shí)存活的對(duì)象太多,以至于Survivor不夠時(shí),會(huì)將這些對(duì)象通過分配擔(dān)保機(jī)制復(fù)制到老年代中。(java堆又分為新生代和老年代)
③. 標(biāo)記-整理:該算法主要是為了解決標(biāo)記-清除,產(chǎn)生大量內(nèi)存碎片的問題;當(dāng)對(duì)象存活率較高時(shí),也解決了復(fù)制算法的效率問題。它的不同之處就是在清除對(duì)象的時(shí)候現(xiàn)將可回收對(duì)象移動(dòng)到一端,然后清除掉端邊界以外的對(duì)象,這樣就不會(huì)產(chǎn)生內(nèi)存碎片了。
④. 分代收集:現(xiàn)在的虛擬機(jī)垃圾收集大多采用這種方式,它根據(jù)對(duì)象的生存周期,將堆分為新生代和老年代。在新生代中,由于對(duì)象生存期短,每次回收都會(huì)有大量對(duì)象死去,那么這時(shí)就采用復(fù)制算法。老年代里的對(duì)象存活率較高,沒有額外的空間進(jìn)行分配擔(dān)保,所以可以使用標(biāo)記-整理 或者 標(biāo)記-清除。
24. 常見的垃圾收集算法
主要分為三種算法:
①. 標(biāo)記-清除(Mark-Sweep)算法:
首先進(jìn)行標(biāo)記工作,標(biāo)識(shí)出所有要回收的對(duì)象,標(biāo)記完成后統(tǒng)一進(jìn)行清除。缺點(diǎn)有效率低,標(biāo)記完成后會(huì)產(chǎn)生大量的內(nèi)存碎片??臻g內(nèi)存太多可能會(huì)導(dǎo)致下一次分配較大對(duì)象時(shí)沒有連續(xù)內(nèi)存。
②. 復(fù)制算法:
將可用的內(nèi)存容量分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另外一塊上面,然后再把已使用過的內(nèi)存一次性清理掉。這樣每次只對(duì)一個(gè)半?yún)^(qū)進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)不用考慮內(nèi)存碎片等問題。代價(jià)是只使用一半的內(nèi)存空間。
現(xiàn)在大部分的新生代GC都是基于復(fù)制算法。新生代大部分的對(duì)象都是朝生夕死的。將內(nèi)存分為一塊較大的Eden區(qū)和兩塊較小的Survivor區(qū)域。每次使用Eden和一個(gè)Survivor.每次清理時(shí)把Eden和Survivor中還活著的區(qū)域復(fù)制到另外一塊Survivor,最后清理掉剛才使用的Eden和Survivor區(qū)域。
③. 標(biāo)記-整理(Mark-Compact)算法:
類似于標(biāo)記-清除(Mark-Sweep)算法,但為了避免內(nèi)存碎片化,它會(huì)在清理過程中將對(duì)象移動(dòng),以確保移動(dòng)后的對(duì)象占用連續(xù)的內(nèi)存空間
25. 在JVM GC中如何判斷一個(gè)對(duì)象是否可以回收?
解析:
①. 引用計(jì)數(shù)算法
顧名思義,為對(duì)象添加一個(gè)引用計(jì)數(shù),用于記錄對(duì)象被引用的情況。每當(dāng)有一個(gè)地方引用它,計(jì)數(shù)器加1.引用失效,計(jì)數(shù)器-1。如果計(jì)數(shù)為0則表示對(duì)象可回收。優(yōu)點(diǎn):簡單高效 缺點(diǎn): 很難解決對(duì)象之間相互循環(huán)。
②. 引用的問題
可達(dá)性分析。通過一系列的稱為GC Roots的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所走過的路徑稱為引用鏈。如果一個(gè)對(duì)象到GC Roots沒有任何引用鏈(圖論的角度表示為不可達(dá)),則該對(duì)象可回收
寫在最后
篇幅有限,文中只寫出部分的面試題,其實(shí)老同學(xué)在復(fù)習(xí)的時(shí)候,就準(zhǔn)備了一份完整的面試題;于是我厚著臉皮的找他要了一份,包含了Kafka、Mysql、Tomcat、Docker、Spring、MyBatis、Nginx、Netty、Dubbo、Redis、Netty、Spring cloud、分布式、高并發(fā)、性能調(diào)優(yōu)、微服務(wù)等架構(gòu)技術(shù);
需要的朋友請(qǐng)請(qǐng)點(diǎn)擊下方傳送門;即可免費(fèi)領(lǐng)取完整的面試專題文件
傳送門
部分面試題截圖:
