最最最常見的Java面試題總結——第二週

SnailClimb發表於2018-08-16

String和StringBuffer、StringBuilder的區別是什麼?String為什麼是不可變的?

String和StringBuffer、StringBuilder的區別

可變性   String類中使用字元陣列:private final char value[]儲存字串,所以String物件是不可變的。StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中也是使用字元陣列儲存字串,char[]value,這兩種物件都是可變的。   

執行緒安全性

String中的物件是不可變的,也就可以理解為常量,執行緒安全。AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer對方法加了同步鎖或者對呼叫的方法加了同步鎖,所以是執行緒安全的。StringBuilder並沒有對方法進行加同步鎖,所以是非執行緒安全的。   

效能

每次對String 型別進行改變的時候,都會生成一個新的String物件,然後將指標指向新的String 物件。StringBuffer每次都會對StringBuffer物件本身進行操作,而不是生成新的物件並改變物件引用。相同情況下使用StirngBuilder 相比使用StringBuffer 僅能獲得10%~15% 左右的效能提升,但卻要冒多執行緒不安全的風險。

對於三者使用的總結:

  • 如果要操作少量的資料用 = String
  • 單執行緒操作字串緩衝區 下操作大量資料 = StringBuilder
  • 多執行緒操作字串緩衝區 下操作大量資料 = StringBuffer

String為什麼是不可變的嗎?

簡單來說就是String類利用了final修飾的char型別陣列儲存字元,原始碼如下圖所以:

    /** The value is used for character storage. */
    private final char value[];
複製程式碼

String真的是不可變的嗎?

我覺得如果別人問這個問題的話,回答不可變就可以了。 下面只是給大家看兩個有代表性的例子:

1) String不可變但不代表引用不可以變

		String str = "Hello";
		str = str + " World";
		System.out.println("str=" + str);
複製程式碼

結果:

str=Hello World
複製程式碼

解析:

實際上,原來String的內容是不變的,只是str由原來指向"Hello"的記憶體地址轉為指向"Hello World"的記憶體地址而已,也就是說多開闢了一塊記憶體區域給"Hello World"字串。

2) 通過反射是可以修改所謂的“不可變”物件

		// 建立字串"Hello World", 並賦給引用s
		String s = "Hello World";

		System.out.println("s = " + s); // Hello World

		// 獲取String類中的value欄位
		Field valueFieldOfString = String.class.getDeclaredField("value");

		// 改變value屬性的訪問許可權
		valueFieldOfString.setAccessible(true);

		// 獲取s物件上的value屬性的值
		char[] value = (char[]) valueFieldOfString.get(s);

		// 改變value所引用的陣列中的第5個字元
		value[5] = '_';

		System.out.println("s = " + s); // Hello_World
複製程式碼

結果:

s = Hello World
s = Hello_World
複製程式碼

解析:

用反射可以訪問私有成員, 然後反射出String物件中的value屬性, 進而改變通過獲得的value引用改變陣列的結構。但是一般我們不會這麼做,這裡只是簡單提一下有這個東西。

什麼是反射機制?反射機制的應用場景有哪些?

反射機制介紹

JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。

靜態編譯和動態編譯

  • **靜態編譯:**在編譯時確定型別,繫結物件
  • **動態編譯:**執行時確定型別,繫結物件

反射機制優缺點

  • 優點: 執行期型別的判斷,動態載入類,提高程式碼靈活度。
  • 缺點: 效能瓶頸:反射相當於一系列解釋操作,通知 JVM 要做的事情,效能比直接的java程式碼要慢很多。

反射的應用場景

反射是框架設計的靈魂。

在我們平時的專案開發過程中,基本上很少會直接使用到反射機制,但這不能說明反射機制沒有用,實際上有很多設計、開發都與反射機制有關,例如模組化的開發,通過反射去呼叫對應的位元組碼;動態代理設計模式也採用了反射機制,還有我們日常使用的 Spring/Hibernate 等框架也大量使用到了反射機制。

舉例:①我們在使用JDBC連線資料庫時使用Class.forName()通過反射載入資料庫的驅動程式;②Spring框架也用到很多反射機制,最經典的就是xml的配置模式。Spring 通過 XML 配置模式裝載 Bean 的過程:1) 將程式內所有 XML 或 Properties 配置檔案載入入記憶體中; 2)Java類裡面解析xml或properties裡面的內容,得到對應實體類的位元組碼字串以及相關的屬性資訊; 3)使用反射機制,根據這個字串獲得某個類的Class例項; 4)動態配置例項的屬性

推薦閱讀:

什麼是JDK?什麼是JRE?什麼是JVM?三者之間的聯絡與區別

這幾個是Java中很基本很基本的東西,但是我相信一定還有很多人搞不清楚!為什麼呢?因為我們大多數時候在使用現成的編譯工具以及環境的時候,並沒有去考慮這些東西。

JDK: 顧名思義它是給開發者提供的開發工具箱,是給程式開發者用的。它除了包括完整的JRE(Java Runtime Environment),Java執行環境,還包含了其他供開發者使用的工具包。

JRE: 普通使用者而只需要安裝JRE(Java Runtime Environment)來執行Java程式。而程式開發者必須安裝JDK來編譯、除錯程式。

JVM: 當我們執行一個程式時,JVM負責將位元組碼轉換為特定機器程式碼,JVM提供了記憶體管理/垃圾回收和安全機制等。這種獨立於硬體和作業系統,正是java程式可以一次編寫多處執行的原因。

區別與聯絡:

  1. JDK用於開發,JRE用於執行java程式 ;
  2. JDK和JRE中都包含JVM ;
  3. JVM是java程式語言的核心並且具有平臺獨立性。

什麼是位元組碼?採用位元組碼的最大好處是什麼?

先看下java中的編譯器和直譯器:   

Java中引入了虛擬機器的概念,即在機器和編譯程式之間加入了一層抽象的虛擬的機器。這臺虛擬的機器在任何平臺上都提供給編譯程式一個的共同的介面。編譯程式只需要面向虛擬機器,生成虛擬機器能夠理解的程式碼,然後由直譯器來將虛擬機器程式碼轉換為特定系統的機器碼執行。在Java中,這種供虛擬機器理解的程式碼叫做位元組碼(即副檔名為.class的檔案),它不面向任何特定的處理器,只面向虛擬機器。每一種平臺的直譯器是不同的,但是實現的虛擬機器是相同的。Java源程式經過編譯器編譯後變成位元組碼,位元組碼由虛擬機器解釋執行,虛擬機器將每一條要執行的位元組碼送給直譯器,直譯器將其翻譯成特定機器上的機器碼,然後在特定的機器上執行。這也就是解釋了Java的編譯與解釋並存的特點。

Java原始碼---->編譯器---->jvm可執行的Java位元組碼(即虛擬指令)---->jvm---->jvm中直譯器----->機器可執行的二進位制機器碼---->程式執行。

採用位元組碼的好處:   

Java語言通過位元組碼的方式,在一定程度上解決了傳統解釋型語言執行效率低的問題,同時又保留了解釋型語言可移植的特點。所以Java程式執行時比較高效,而且,由於位元組碼並不專對一種特定的機器,因此,Java程式無須重新編譯便可在多種不同的計算機上執行。

Java和C++的區別

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

  • 都是物件導向的語言,都支援封裝、繼承和多型
  • Java不提供指標來直接訪問記憶體,程式記憶體更加安全
  • Java的類是單繼承的,C++支援多重繼承;雖然Java的類不可以多繼承,但是介面可以多繼承。
  • Java有自動記憶體管理機制,不需要程式設計師手動釋放無用記憶體

介面和抽象類的區別是什麼?

  1. 介面的方法預設是public,所有方法在介面中不能有實現,抽象類可以有非抽象的方法
  2. 介面中的例項變數預設是final型別的,而抽象類中則不一定
  3. 一個類可以實現多個介面,但最多隻能實現一個抽象類
  4. 一個類實現介面的話要實現介面的所有方法,而抽象類不一定
  5. 介面不能用new例項化,但可以宣告,但是必須引用一個實現該介面的物件 從設計層面來說,抽象是對類的抽象,是一種模板設計,介面是行為的抽象,是一種行為的規範。

成員變數與區域性變數的區別有那些?

  1. 從語法形式上,看成員變數是屬於類的,而區域性變數是在方法中定義的變數或是方法的引數;成員變數可以被public,private,static等修飾符所修飾,而區域性變數不能被訪問控制修飾符及static所修飾;但是,成員變數和區域性變數都能被final所修飾;
  2. 從變數在記憶體中的儲存方式來看,成員變數是物件的一部分,而物件存在於堆記憶體,區域性變數存在於棧記憶體
  3. 從變數在記憶體中的生存時間上看,成員變數是物件的一部分,它隨著物件的建立而存在,而區域性變數隨著方法的呼叫而自動消失。
  4. 成員變數如果沒有被賦初值,則會自動以型別的預設值而賦值(一種情況例外被final修飾但沒有被static修飾的成員變數必須顯示地賦值);而區域性變數則不會自動賦值。

過載和重寫的區別

過載: 發生在同一個類中,方法名必須相同,引數型別不同、個數不同、順序不同,方法返回值和訪問修飾符可以不同,發生在編譯時。   

重寫: 發生在父子類中,方法名、引數列表必須相同,返回值範圍小於等於父類,丟擲的異常範圍小於等於父類,訪問修飾符範圍大於等於父類;如果父類方法訪問修飾符為private則子類就不能重寫該方法。

字元型常量和字串常量的區別

  1. 形式上: 字元常量是單引號引起的一個字元 字串常量是雙引號引起的若干個字元
  2. 含義上: 字元常量相當於一個整形值(ASCII值),可以參加表示式運算 字串常量代表一個地址值(該字串在記憶體中存放位置)
  3. 佔記憶體大小 字元常量只佔一個位元組 字串常量佔若干個位元組(至少一個字元結束標誌)

寫在最後

開源文件推薦

Java-Guide: Java面試通關手冊(Java學習指南)Java Interview Customs Manual (Java Study Guide)。star:1.9k。

Github地址:github.com/Snailclimb/…

文件定位:一個專門為Java後端工程師準備的開源文件,相信不論你是Java新手還是已經成為一名Java工程師都能從這份文件中收穫到一些東西。

參考:

segmentfault.com/a/119000001…

blog.csdn.net/sinat_38259…

你若盛開,清風自來。 歡迎關注我的微信公眾號:“Java面試通關手冊”,一個有溫度的微信公眾號。公眾號有大量資料,回覆關鍵字“1”你可能看到想要的東西哦!

最最最常見的Java面試題總結——第二週

相關文章