Java面試系列第一篇-基本類型與引用類型

  • 2020 年 4 月 25 日
  • 筆記

 這篇文章總結一下我認為面試中最應該掌握的關於基本類型和引用類型的面試題目。

面試題目1:值傳遞與引用傳遞

對於沒有接觸過C++這類有引用傳遞的Java程式設計師來說,很容易誤將引用類型的參數傳遞理解為引用傳遞,而基本類型的傳遞理解為值傳遞,這是錯誤的。要理解值傳遞與引用傳遞,首先要理清值傳遞、引用傳遞與指針傳遞三個概念。

值傳遞與引用傳遞最重要的就是看在傳遞的過程中,值是否發生了複製。在Java中沒有指針的概念,但是引用類型做為參數進行傳遞時,JVM將其實現為指針傳遞,那麼重點就是搞清楚指針傳遞到底是值傳遞還是引用傳遞了。指針在傳遞時也會複製,所以是值傳遞,Java中不存在引用傳遞。 

面試題目2:int類型的範圍

Java中4種基本類型表示的範圍如下圖所示。

Java中不能明確指示某個數為無符號類型,所以最高位一般為符號位。拿佔一個位元組的byte來說,由於最高位需要表示符號,所以只能用剩下的7位來表示數。所以最大可表示的數為

0111 1111(二進位)

max = (2^0+2^1+2^2+...+2^6) = 127

最小可表示數的範圍用二進位表示應該為:

1111 1111(二進位)

但是對於電腦來說,負數其實是用補碼錶示的,也就是反碼加1,所以在電腦中存儲的二進位為1000 0001(補碼),這個值才是-127。

我們要對待一種特殊情況,如下:

1000 0000(原碼)

1111 1111(反碼)

1000 0000(補碼)

如果1000 0000表示0的話,那豈不是有了0和-0之分了,所以可以用1000 0000表示-128。

由於符號位的存在,所以在許多的情況下,我們其實只是想保持二進位位的原樣,而不是十進位的值。舉個例子如下:

public static void main(String[] args) {
	byte b = -127;//10000001
	int a =  b;
	System.out.println(a);
	a =  b & 0xff;
	System.out.println(a);
}

輸出結果-127,129。

byte類型在轉換為int類型時,符號位發生擴展,所以第一次列印a的值時,十進位保持一致,二進位表示為

1111 1111 1111 1111 1111 1111 1000 0001

現在將b和&0xff做與後,十進位已經無法保持一致,因為此時的二進位表示為

0000 0000 0000 0000 0000 0000 1000 0001

這個值是129。有什麼用呢?其實在實際操作中,我們經常看到讀取位元組流時,轉換過程中要加&0xff,就是為了保持二進位的原有樣子,因為此時關注的並不是十進位值。 

面試題目3:Java中對基本類型的賦值是原子操作嗎?引用類型呢?

根據虛擬機規範:(//docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7)

For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.

Writes and reads of volatile long and double values are always atomic.

Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.

Some implementations may find it convenient to divide a single write action on a 64-bit long or double value into two write actions on adjacent 32-bit values. For efficiency's sake, this behavior is implementation-specific; an implementation of the Java Virtual Machine is free to perform writes to long and double values atomically or in two parts.

Implementations of the Java Virtual Machine are encouraged to avoid splitting 64-bit values where possible. Programmers are encouraged to declare shared 64-bit values as volatile or synchronize their programs correctly to avoid possible complications.

Java虛擬機規範表示,Java 基礎類型中,long 和 double 是 64 位長的。32 位架構 CPU 的算術邏輯單元(ALU)寬度是 32 位的,在處理大於 32 位操作時需要處理兩次。所以有可能在讀long的高32位時,低32位被另外一個並發的執行緒寫了,讀出的值可就誰也說不準了。對於引用類型來說是原子操作。

舉個例子如下:

public class P1 {
	private long b = 0;
	
	public void set1() {
		b = 0;
	}
	
	public void set2() {
		b = -1;
	}
	
	public void check() {
		System.out.println(b);	
		if (0 != b && -1 != b) {
			System.err.println("Error");
		}
	 }
}

在32位下跑這個程式,可能會列印Error,原因就是上面提到的。引起這個問題的最主要原因就是並發,所以如果要解決這個問題,可以讓執行緒序列化訪問b,也就是通過加鎖來實現;可以使用AtomicLong對長整形進行原子操作;根據虛擬機規範所說,還可以加volatile關鍵字。

這裡只所以volatile關鍵字能解決這個原子性,其實還是因為為了保證可見性而對記憶體進行了獨佔訪問,這樣在獨佔操作時,就不會有其它執行緒改寫其中的值了。 

面試題目4:關於字元串的面試題

首先來看一下下面的面試題:

另外一個高頻的問題就是字元串創建了多少個對象的問題,如下:

String str = new String("ab");

上面一行程式碼將會創建1或2個字元串。如果在字元串常量池中已經有一個字元串「ab」,那麼就只會創建一個「ab」字元串。如果字元串常量池中沒有「ab」,那麼首先會在字元串池中創建,然後才在堆記憶體中創建,這種情況就會創建2個對象。  

再來看一下下面的面試題: 

String a = "ab"; 
String b = "a" + "b"; 
a == b

如上的程式碼會對b進行常量摺疊,所以相當於如下程式:

String a = "ab"; 
String b = "ab"; 
System.out.println(a == b)

對於Java來說,==對基本類型比較的是值,而對於引用類型比較的是地址,所以要想讓a==b輸出true,那隻能a和b指向同一個對象。"ab" 屬於字元串字面量,因此編譯時期會在常量池中創建一個字元串對象,如果常量池中已經存在該字元串對象則直接引用,所以最終的結果為true。

String類型的常量池比較特殊。它的主要使用方法有兩種:

(1)直接使用雙引號聲明出來的String對象會直接存儲在常量池中。
(2)如果不是用雙引號聲明String對象,可以使用 String 提供的 intern()方法。它的作用是: 如果運行時常量池中已經包含一個等於此 String 對象內容的字元串,則返回常量池中該字元串的引用; 如果沒有,則在常量池中創建與此 String 內容相同的字元串,並返回常量池中創建的字元串的引用。

舉個例子如下:

String s1 = new String("ab");// s1保存堆中的引用
String s2 = s1.intern();     // s2保存的是字元串常量池中的引用
String s3 = "ab";            // s3保持的是字元串常量池中的引用
System.out.println(s1 == s2);// false,因為一個是堆記憶體中的String對象一個是常量池中的String對象,
System.out.println(s2 == s3);// true, s1,s2指向常量池中的」ab「

 

 

 

 

Tags: