1. 程式人生 > >Java面試系列第1篇-基本型別與引用型別

Java面試系列第1篇-基本型別與引用型別

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

面試題目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中對基本型別的賦值是原子操作嗎?引用型別呢?

根據虛擬機器規範:(https://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“

面試題目5:Java中如何比較浮點數?

 

 

 

&n