Java程序員開發三年,去網易社招,竟被問到這些面試題

前言

前幾天偶遇老同學,聊了聊工作;老同學和我分享了這次網易社招的面試題;文中篇幅有限,就和大家分享這麼多;更多Java後端開發面試題請見文末!

Java程序員開發三年,去網易社招,竟被問到這些面試題

面試題

1. 面向對象的特點有哪些?

①. 封裝:所謂封裝,就是將客觀事物封裝成抽象的類,並且類可以把數據和方法讓可信的類或者對象進行操作,對不可信的類或者對象進行隱藏。類就是封裝數據和操作這些數據代碼的邏輯實體。在一個類的內部,某些屬性和方法是私有的,不能被外界所訪問。通過這種方式,對象對內部數據進行了不同級別的訪問控制,就避免了程序中的無關部分的意外改變或錯誤改變了對象的私有部分。

②. 繼承:繼承有這樣一種能力,就是能使用現有的類的所有功能,並無須重新編寫原來的這些類的基礎上對這些功能進行擴展。通過繼承創建的新類稱為子類或派生類,被繼承的稱為基類。繼承有兩種,一種是實現繼承,另外一種是接口繼承。實現繼承可以直接使用基類的屬性和方法而無需額外編碼,接口繼承是指使用屬性和方法的名稱,但是子必須提供實現的能力。

③. 多態:所謂多態就是對一個實例的相同方法在不同的情形下有不同的表現形式。多態機制使得不同內部結構的對象可以共享相同的外部接口,這就意味著,雖然不同的類的內部操作不同,但可以通過一個公共的類,它們可以通過相同的方式予以調用。

④. 抽象:提取現實世界中某事物的關鍵特性,為該事物構建模型的過程。對同一事物在不同的需求下,需要提取的特性可能不一樣。得到的抽象模型中一般包含:屬性(數據)和操作(行為)。這個抽象模型我們稱之為類。對類進行實例化得到對象。

Java程序員開發三年,去網易社招,竟被問到這些面試題

2. 列舉幾個java常用的package及其作用

  • 第一個包:java.lang包。該包提供了Java語言進行程序設計的基礎類,它是默認導入的包。該包裡面的Runnable接口和Object、Math、String、StringBuffer、System、Thread以及Throwable類需要重點掌握,因為它們應用很廣。
  • 第二個包:java.util包。該包提供了包含集合框架、遺留的集合類、事件模型、日期和時間實施、國際化和各種實用工具類(字符串標記生成器、隨機數生成器和位數組)。
  • 第三個包:java.io包。該包通過文件系統、數據流和序列化提供系統的輸入與輸出。
  • 第四個包:java.net包。該包提供實現網絡應用與開發的類。
  • 第五個包:java.sql包。該包提供了使用Java語言訪問並處理存儲在數據源(通常是一個關係型數據庫)中的數據API。
  • 第六個包:java.awt包
  • 第七個包:javax.swing包。慶慶說:這兩個包提供了GUI設計與開發的類。java.awt包提供了創建界面和繪製圖形圖像的所有類,而javax.swing包提供了一組“輕量級”的組件,儘量讓這些組件在所有平臺上的工作方式相同。
  • 第八個包:java.text包。提供了與自然語言無關的方式來處理文本、日期、數字和消息的類和接口。

3. 接口和抽象類有什麼聯繫和區別

相同點

  • 接口和抽象類都不能被實例化,它們都位於繼承樹的頂端,用於被其他類實現和繼承。
  • 接口和抽象類都可以包含抽象方法,實現接口或繼承抽象類的普通子類都必須實現這些抽象方法。

不同點

  • 接口裡只能包含抽象方法,靜態方法和默認方法,不能為普通方法提供方法實現,抽象類則完全可以包含普通方法。
  • 接口裡只能定義靜態常量,不能定義普通成員變量,抽象類裡則既可以定義普通成員變量,也可以定義靜態常量。
  • 接口不能包含構造器,抽象類可以包含構造器,抽象類裡的構造器並不是用於創建對象,而是讓其子類調用這些構造器來完成屬於抽象類的初始化操作。
  • 接口裡不能包含初始化塊,但抽象類裡完全可以包含初始化塊。
  • 一個類最多隻能有一個直接父類,包括抽象類,但一個類可以直接實現多個接口,通過實現多個接口可以彌補Java單繼承不足。

4. 重載和重寫有什麼區別

override(重寫)

  • 方法名、參數、返回值相同。
  • 子類方法不能縮小父類方法的訪問權限。
  • 子類方法不能拋出比父類方法更多的異常(但子類方法可以不拋出異常)。
  • 存在於父類和子類之間。
  • 方法被定義為final不能被重寫。

overload(重載)

  • 參數類型、個數、順序至少有一個不相同。
  • 不能重載只有返回值不同的方法名。
  • 存在於父類和子類、同類中。

5. java有哪些基本數據類型?

①. 四種整數類型(byte、short、int、long)

  • byte:8 位,用於表示最小數據單位,如文件中數據,-128~127
  • short:16 位,很少用,-32768 ~ 32767 int:32 位、最常用,-231-1~231 (21 億)
  • long:64 位、次常用

②. 兩種浮點數類型(float、double)

  • float:32 位,後綴 F 或 f,1 位符號位,8 位指數,23 位有效尾數。
  • double:64 位,最常用,後綴 D 或 d,1 位符號位,11 位指數,52 位有效尾

③. 一種字符類型(char)

char:16 位,是整數類型,用單引號括起來的 1 個字符(可以是一箇中文字符),使用 Unicode 碼代表字符,0~2^16-1(65535) 。

④. 一種布爾類型(boolean):true 真 和 false 假。

⑤. 類型轉換:char-->

  • 自動轉換:byte-->short-->int-->long-->float-->double
  • 強制轉換:
  • ①會損失精度,產生誤差,小數點以後的數字全部捨棄。
  • ②容易超過取值範圍。

⑥. 記憶

  • 8位:Byte(字節型)
  • 16位:short(短整型)、char(字符型)
  • 32位:int(整型)、float(單精度型/浮點型)
  • 64位:long(長整型)、double(雙精度型)
  • 最後一個:boolean(布爾類型)

6. Java支持的數據類型有哪些?什麼是自動拆裝箱?

  • 基本數據類型:
  • 整數值型:byte,short,int,long,
  • 字符型:char
  • 浮點類型:float,double
  • 布爾型:boolean

整數默認int型,小數默認是double型。Float和long類型的必須加後綴。

首先知道String是引用類型不是基本類型,引用類型聲明的變量是指該變量在內存中實際存儲的是一個引用地址,實體在堆中。引用類型包括類、接口、數組等。String類還是final修飾的。

而包裝類就屬於引用類型,自動裝箱和拆箱就是基本類型和引用類型之間的轉換,至於為什麼要轉換,因為基本類型轉換為引用類型後,就可以new對象,從而調用包裝類中封裝好的方法進行基本類型之間的轉換或者toString(當然用類名直接調用也可以,便於一眼看出該方法是靜態的),還有就是如果集合中想存放基本類型,泛型的限定類型只能是對應的包裝類型。

7. int 和 Integer 有什麼區別

int 是基本數據類型;Integer是其包裝類,注意是一個類。為什麼要提供包裝類呢?一是為了在各種類型間轉化,通過各種方法的調用。否則 你無法直接通過變量轉化。

比如,現在int要轉為String

int a=0;
String result=Integer.toString(a);

在java中包裝類,比較多的用途是用在於各種數據類型的轉化中。

我寫幾個demo

//通過包裝類來實現轉化的
int num=Integer.valueOf("12");
int num2=Integer.parseInt("12");
double num3=Double.valueOf("12.2");
double num4=Double.parseDouble("12.2");

//其他的類似。通過基本數據類型的包裝來的valueOf和parseXX來實現String轉為XX
String a=String.valueOf("1234");//這裡括號中幾乎可以是任何類型
String b=String.valueOf(true);
String c=new Integer(12).toString();//通過包裝類的toString()也可以
String d=new Double(2.3).toString();

再舉例下。比如我現在要用泛型

List nums;

這裡<>需要類。如果你用int。它會報錯的。

8. 數組有沒有length()方法?String有沒有length()方法?

①. 數組沒有length()方法,但有length屬性

②. String類有length() 方法

如:

String[] arr = new String[]{"hello", "world"};
System.out.println(arr.length);
String str = "hello world";
System.out.println(str.length());

9. Java中符號>>和>>>有什麼區別?

  • >>:帶符號右移。正數右移高位補0,負數右移高位補1。
  • 比如:4 >> 1,結果是2;-4 >>1,結果是-2。-2 >> 1,結果是-1。
  • >>>:無符號右移。無論是正數還是負數,高位通通補0。

對於正數而言,>>和>>>沒區別。

對於負數而言,-2 >>> 1,結果是2147483647(Integer.MAX_VALUE),-1 >>> 1,結果是2147483647(Integer.MAX_VALUE)。

所以,要判斷兩個數符號是否相同時,可以這麼幹:

return ((a >> 31) ^ (b >> 31)) == 0;
Java程序員開發三年,去網易社招,竟被問到這些面試題

10. Java類的實例化順序

  • 父類靜態成員和靜態初始化塊 ,按在代碼中出現的順序依次執行
  • 子類靜態成員和靜態初始化塊 ,按在代碼中出現的順序依次執行
  • 父類實例成員和實例初始化塊 ,按在代碼中出現的順序依次執行
  • 父類構造方法
  • 子類實例成員和實例初始化塊 ,按在代碼中出現的順序依次執行
  • 子類構造方法

結論:對象初始化的順序,先靜態方法,再構造方法,每個又是先基類後子類。

11. 什麼是值傳遞和引用傳遞

值傳遞是對基本型變量而言的,傳遞的是該變量的一個副本,改變副本不影響原變量.

引用傳遞一般是對於對象型變量而言的,傳遞的是該對象地址的一個副本, 並不是原對象本身 。

一般認為,java內的傳遞都是值傳遞. java中實例對象的傳遞是引用傳遞 。

12. String能被繼承嗎?為什麼?

不可以,因為String類有final修飾符,而final修飾的類是不能被繼承的,實現細節不允許改變。平常我們定義的String str=”a”;其實和String str=new String(“a”)還是有差異的。

前者默認調用的是String.valueOf來返回String實例對象,至於調用哪個則取決於你的賦值,比如String num=1,調用的是

public static String valueOf(int i) {
return Integer.toString(i);
}

後者則是調用如下部分:

public String(String original) {
this.value = original.value;
this.hash = original.hash;
}

最後我們的變量都存儲在一個char數組中

private final char value[];

13. String和StringBuilder、StringBuffer的區別?

①. String 字符串常量(final修飾,不可被繼承),String是常量,當創建之後即不能更改。(可以通過StringBuffer和StringBuilder創建String對象(常用的兩個字符串操作類)。)

②. StringBuffer 字符串變量(線程安全),其也是final類別的,不允許被繼承,其中的絕大多數方法都進行了同步處理,包括常用的Append方法也做了同步處理(synchronized修飾)。其自jdk1.0起就已經出現。其toString方法會進行對象緩存,以減少元素複製開銷。

public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}

③. StringBuilder 字符串變量(非線程安全)其自jdk1.5起開始出現。與StringBuffer一樣都繼承和實現了同樣的接口和類,方法除了沒使用synch修飾以外基本一致,不同之處在於最後toString的時候,會直接返回一個新對象。

public String toString() {
// Create a copy, don’t share the array
return new String(value, 0, count);
}

14. Java集合框架的基礎接口有哪些?

  • Collection:為集合層級的根接口。一個集合代表一組對象,這些對象即為它的元素。Java平臺不提供這個接口任何直接的實現。
  • Set:是一個不能包含重複元素的集合。這個接口對數學集合抽象進行建模,被用來代表集合,就如一副牌。
  • List
    :是一個有序集合,可以包含重複元素。你可以通過它的索引來訪問任何元素。List更像長度動態變換的數組。
  • Map:是一個將key映射到value的對象.一個Map不能包含重複的key:每個key最多隻能映射一個value。

一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。

Java程序員開發三年,去網易社招,竟被問到這些面試題

15. Java集合框架是什麼?說出一些集合框架的優點?

每種編程語言中都有集合,最初的Java版本包含幾種集合類:Vector、Stack、HashTable和Array。隨著集合的廣泛使用,Java1.2提出了囊括所有集合接口、實現和算法的集合框架。在保證線程安全的情況下使用泛型和併發集合類,Java已經經歷了很久。它還包括在Java併發包中,阻塞接口以及它們的實現。

集合框架的部分優點如下:

  • 使用核心集合類降低開發成本,而非實現我們自己的集合類。
  • 隨著使用經過嚴格測試的集合框架類,代碼質量會得到提高。
  • 通過使用JDK附帶的集合類,可以降低代碼維護成本。
  • 複用性和可操作性。

16. HashMap 與HashTable有什麼區別

HashTable

  • 底層數組+鏈表實現,無論key還是value都不能為null,線程安全,實現線程安全的方式是在修改數據時鎖住整個HashTable,效率低,ConcurrentHashMap做了相關優化
  • 初始size為11,擴容:newsize = olesize*2+1
  • 計算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

HashMap

  • 底層數組+鏈表實現,可以存儲null鍵和null值,線程不安全
  • 初始size為16,擴容:newsize = oldsize*2,size一定為2的n次冪
  • 擴容針對整個Map,每次擴容時,原來數組中的元素依次重新計算存放位置,並重新插入
  • 插入元素後才判斷該不該擴容,有可能無效擴容(插入後如果擴容,如果沒有再次插入,就會產生無效擴容)
  • 當Map中元素總數超過Entry數組的75%,觸發擴容操作,為了減少鏈表長度,元素分配更均勻
  • 計算index方法:index = hash & (tab.length – 1)

17. ArrayList 和 LinkedList 有什麼區別?

ArrayList和LinkedList都實現了List接口,有以下的不同點:

  • ArrayList是基於索引的數據接口,它的底層是數組。它可以以O(1)時間複雜度對元素進行隨機訪問。與此對應,LinkedList是以元素列表的形式存儲它的數據,每一個元素都和它的前一個和後一個元素鏈接在一起,在這種情況下,查找某個元素的時間複雜度是O(n)。
  • 相對於ArrayList,LinkedList的插入,添加,刪除操作速度更快,因為當元素被添加到集合任意位置的時候,不需要像數組那樣重新計算大小或者是更新索引。
  • LinkedList比ArrayList更佔內存,因為LinkedList為每一個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。

18. 簡單介紹Java異常框架

Java對異常進行了分類,不同類型的異常分別用不同的Java類表示,所有異常的根類為java.lang.Throwable,Throwable下面又派生了兩個子類:Error和Exception。

Error 表示應用程序本身無法克服和恢復的一種嚴重問題,程序只有死的份了,例如,說內存溢出和線程死鎖等系統問題。

Exception表示程序還能夠克服和恢復的問題,其中又分為系統異常和普通異常,系統異常是軟件本身缺陷所導致的問題,也就是軟件開發人員考慮不周所導致的問題,軟件使用者無法克服和恢復這種問題,但在這種問題下還可以讓軟件系統繼續運行或者讓軟件死掉,例如,數組腳本越界(ArrayIndexOutOfBoundsException),空指針異常(NullPointerException)、類轉換異常(ClassCastException);普通異常是運行環境的變化或異常所導致的問題,是用戶能夠克服的問題,例如,網絡斷線,硬盤空間不夠,發生這樣的異常後,程序不應該死掉。

java為系統異常和普通異常提供了不同的解決方案,編譯器強制普通異常必須try..catch處理或用throws聲明繼續拋給上層調用方法處理,所以普通異常也稱為checked異常,而系統異常可以處理也可以不處理,所以,編譯器不強制用try..catch處理或用throws聲明,所以系統異常也稱為unchecked異常。

提示答題者:就按照三個級別去思考:虛擬機必須宕機的錯誤,程序可以死掉也可以不死掉的錯誤,程序不應該死掉的錯誤;

19. java中的throw 和 throws關鍵字有什麼區別?

參考例子:

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!");
}
}
}

區別

throw是語句拋出一個異常,一般是在代碼塊的內部,當程序出現某種邏輯錯誤時由程序員主動拋出某種特定類型的異常

throws是方法可能拋出異常的聲明。(用在聲明方法時,表示該方法可能要拋出異常)

20. 列舉幾個常見的運行時異常?

常用的RuntimeException如

  • java.lang.ClassNotFoundException 類不存在異常
  • java.lang.NullPointerException 空指針異常
  • java.lang.IndexOutOfBoundsException 數據下標越界
  • java.lang.IllegalArgumentException 參數異常
  • java.io.FileNotFoundException 文件不存在異常

其它如

  • ClassCastException - 類型強制轉換異常。
  • ArithmeticException - 算術運算異常
  • ArrayStoreException - 向數組中存放與聲明類型不兼容對象異常
  • NegativeArraySizeException - 創建一個大小為負數的數組錯誤異常
  • NumberFormatException - 數字格式異常
  • SecurityException - 安全異常
  • UnsupportedOperationException - 不支持的操作異常

21. final, finally, finalize有什麼區別

  • final:修飾符(關鍵字)如果一個類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個類不能既被聲明為 abstract的,又被聲明為final的。將變量或方法聲明為final,可以保證它們在使用中不被改變。被聲明為final的變量必須在聲明時給定初值,而在以後的引用中只能讀取,不可修改。被聲明為final的方法也同樣只能使用,不能重載 。
  • finally:在異常處理時提供 finally 塊來執行任何清除操作。如果拋出一個異常,那麼相匹配的 catch 子句就會執行,然後控制就會進入 finally 塊(如果有的話)。
  • finalize:方法名。Java 技術允許使用 finalize() 方法在垃圾收集器將對象從內存中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在確定這個對象沒有被引用時對這個對象調用的。它是在 Object類中定義的,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統資源或者執行其他清理工作。finalize() 方法是在垃圾收集器刪除對象之前對這個對象調用的。

22. 描述Java內存模型

Java虛擬機規範中將Java運行時數據分為六種:

①. 程序計數器:是一個數據結構,用於保存當前正常執行的程序的內存地址。Java虛擬機的多線程就是通過線程輪流切換並分配處理器時間來實現的,為了線程切換後能恢復到正確的位置,每條線程都需要一個獨立的程序計數器,互不影響,該區域為“線程私有”。

②. Java虛擬機棧:線程私有的,與線程生命週期相同,用於存儲局部變量表,操作棧,方法返回值。局部變量表放著基本數據類型,還有對象的引用。

③. 本地方法棧:跟虛擬機棧很像,不過它是為虛擬機使用到的Native方法服務。

④. Java堆:所有線程共享的一塊內存區域,對象實例幾乎都在這分配內存。

⑤. 方法區:各個線程共享的區域,儲存虛擬機加載的類信息,常量,靜態變量,編譯後的代碼。

⑥. 運行時常量池:代表運行時每個class文件中的常量表。包括幾種常量:編譯時的數字常量、方法或者域的引用。

23. java中垃圾收集的方法有哪些?

①. 標記-清除:這是垃圾收集算法中最基礎的,根據名字就可以知道,它的思想就是標記哪些要被回收的對象,然後統一回收。這種方法很簡單,但是會有兩個主要問題:1.效率不高,標記和清除的效率都很低;2.會產生大量不連續的內存碎片,導致以後程序在分配較大的對象時,由於沒有充足的連續內存而提前觸發一次GC動作。

②. 複製算法:為了解決效率問題,複製算法將可用內存按容量劃分為相等的兩部分,然後每次只使用其中的一塊,當一塊內存用完時,就將還存活的對象複製到第二塊內存上,然後一次性清楚完第一塊內存,再將第二塊上的對象複製到第一塊。但是這種方式,內存的代價太高,每次基本上都要浪費一般的內存。 於是將該算法進行了改進,內存區域不再是按照1:1去劃分,而是將內存劃分為8:1:1三部分,較大那份內存交Eden區,其餘是兩塊較小的內存區叫Survior區。每次都會優先使用Eden區,若Eden區滿,就將對象複製到第二塊內存區上,然後清除Eden區,如果此時存活的對象太多,以至於Survivor不夠時,會將這些對象通過分配擔保機制複製到老年代中。(java堆又分為新生代和老年代)

③. 標記-整理:該算法主要是為了解決標記-清除,產生大量內存碎片的問題;當對象存活率較高時,也解決了複製算法的效率問題。它的不同之處就是在清除對象的時候現將可回收對象移動到一端,然後清除掉端邊界以外的對象,這樣就不會產生內存碎片了。

④. 分代收集:現在的虛擬機垃圾收集大多采用這種方式,它根據對象的生存週期,將堆分為新生代和老年代。在新生代中,由於對象生存期短,每次回收都會有大量對象死去,那麼這時就採用複製算法。老年代裡的對象存活率較高,沒有額外的空間進行分配擔保,所以可以使用標記-整理 或者 標記-清除。

24. 常見的垃圾收集算法

主要分為三種算法

①. 標記-清除(Mark-Sweep)算法

首先進行標記工作,標識出所有要回收的對象,標記完成後統一進行清除。缺點有效率低,標記完成後會產生大量的內存碎片。空間內存太多可能會導致下一次分配較大對象時沒有連續內存。

②. 複製算法

將可用的內存容量分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活著的對象複製到另外一塊上面,然後再把已使用過的內存一次性清理掉。這樣每次只對一個半區進行內存回收,內存分配時不用考慮內存碎片等問題。代價是隻使用一半的內存空間。

現在大部分的新生代GC都是基於複製算法。新生代大部分的對象都是朝生夕死的。將內存分為一塊較大的Eden區和兩塊較小的Survivor區域。每次使用Eden和一個Survivor.每次清理時把Eden和Survivor中還活著的區域複製到另外一塊Survivor,最後清理掉剛才使用的Eden和Survivor區域。

③. 標記-整理(Mark-Compact)算法:

類似於標記-清除(Mark-Sweep)算法,但為了避免內存碎片化,它會在清理過程中將對象移動,以確保移動後的對象佔用連續的內存空間

25. 在JVM GC中如何判斷一個對象是否可以回收?

解析

①. 引用計數算法

顧名思義,為對象添加一個引用計數,用於記錄對象被引用的情況。每當有一個地方引用它,計數器加1.引用失效,計數器-1。如果計數為0則表示對象可回收。優點:簡單高效 缺點: 很難解決對象之間相互循環。

②. 引用的問題

可達性分析。通過一系列的稱為GC Roots的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈。如果一個對象到GC Roots沒有任何引用鏈(圖論的角度表示為不可達),則該對象可回收

寫在最後

篇幅有限,文中只寫出部分的面試題,其實老同學在複習的時候,就準備了一份完整的面試題;於是我厚著臉皮的找他要了一份,包含了Kafka、Mysql、Tomcat、Docker、Spring、MyBatis、Nginx、Netty、Dubbo、Redis、Netty、Spring cloud、分佈式、高併發、性能調優、微服務等架構技術;

需要的朋友請幫忙轉發一下本文,然後私信我 【學習】 即可免費領取!

需要的朋友請幫忙轉發一下本文,然後私信我 【學習】 即可免費領取!

需要的朋友請幫忙轉發一下本文,然後私信我 【學習】 即可免費領取!

部分面試題截圖:

Java程序員開發三年,去網易社招,竟被問到這些面試題


分享到:


相關文章: