Java面試中的各種問題
阿新 • • 發佈:2018-11-08
1,String、StringBuffer、StringBuilder的區別
解答:
- String的物件是字串常量,如果做大量的字串拼接效率會很低下。因為Java中對String物件進行的操作實際上是一個不斷建立新物件並且把舊物件回收的一個過程,虛擬機器需要不斷的將物件引用指向新的地址。
- 而StringBuffer和StringBuilder的物件是字串變數,StringBuilder速度比StringBuffer更快。線上程安全上,StringBuffer中很多方法帶有synchronized關鍵字,可以保證執行緒是安全的;而StringBuilder中的方法沒有synchronized關鍵字,執行緒是不安全的。
使用場景小結:
- String:適用於少量的字串操作的情況;
- StringBuilder:適用於單執行緒下在字元緩衝區進行大量操作的情況;
- StringBuffer:適用多執行緒下在字元緩衝區進行大量操作的情況;
- 一般方法內的私有變數推薦使用StringBuilder,如果是多執行緒需要同步的話就選用SringBuffer。
2,對引數為做空驗證,就做判斷值相等
下面的寫法將常量放到方法左邊,能防止NPE。
解答:
- null的值呼叫equals方法與其他值做比較的時候,會導致丟擲空指標異常。因為null值,並不是一個String物件,自然不能呼叫String的例項方法equals
- 在原始碼中如果anobject為空的時候,呼叫equals方法只會返回false,並不會出現NPE
public boolean equals(Object anObject) { //首先,先去判斷兩個物件是否具有相同的地址(是否同一個物件的引用) if (this == anObject) { return true; } //如果地址不一樣,則證明不是引用同一個物件,接下來就是挨個去比較兩個字串物件的內容是否一致 if (anObject instanceof String) { String anotherString = (String)anObject; int n = count; if (n == anotherString.count) { char v1[] = value; char v2[] = anotherString.value; int i = offset; int j = anotherString.offset; while (n-- != 0) { if (v1[i++] != v2[j++]) return false; } return true; } } return false; }
小例子:
假設兩個字串
- String a = "hello" 這裡Java的機制首先在常量池裡面建立這個hello字串,然後在記憶體裡面建立一個地址,引用常量池裡面的這個hello值
- String b = null; 這裡或者 String b; 這裡兩行申請 Java在記憶體裡面都申請了一個地址,但是不指向任何引用地址,或者說他沒有內容'
小結:
- s.equals(s1),s必須非null,否則會空指標異常;
- s1可以為null,在確保s1不為空時,可以用s1.equals(s)來判斷;
- 無法確定s或s1不為null時,解決辦法:
- s != null && s.equals(s1)
3,Integer型別正整數在小於128時和大於128時
問題:
列印結果為false,而下面結果為true為啥
解答:
- Integer型別當正整數小於128時是在記憶體棧中建立值的,並將物件指向這個值,這樣當比較兩個棧引用時因為是同一地址引用兩者則相等。
- 當大於127時將會呼叫new Integer(),兩個整數物件地址引用不相等了。
- 這就是為什麼當值為128時不相等,當值為100時相等了。
小結(==與equals的區別):
- == 只針對變數的值;equals只針對物件的內容
- 引用型別的變數值是所指物件的記憶體地址
4,將變數和物件作為引數傳遞的區別
問題:將變數作為引數傳遞,在方法中改變引數值,變數的值改變了麼?下圖total值到底是幾?
解答:
- 將一個私有變數作為形參傳遞賦值並不會改變引數原有的值
- 但是如果將一個物件作為引數傳遞改變屬性,物件的屬性值就會隨著改變
- 因此total的值仍然為0。
將一個物件作為引數傳遞時:
public class AtWill {
public static void main(String[] args) {
NumObject nb = new NumObject(10, "法拉利");
paramCheck(nb);
System.out.println(nb.toString());
}
private static void paramCheck(NumObject nb) {
nb.setName("賓利");
}
}
result
NumObject [total=10, name=賓利]
5,陣列中內部定義的一種資料結構型別
問題:由陣列轉換的list,只能迴圈遍歷,而不能看長度,增加元素,刪除元素,這是為何?下圖程式碼執行竟然出錯!
解答:
- 因為將陣列轉換的列表其實不是我們經常使用的arrayList
- 這只是陣列中內部定義的一種資料結構型別,本質還是原陣列而並非列表
- 因此當向列表新增元素就會出現錯誤
小結:
對於Arrays.asList()方法需要注意以下幾點:
- 該方法返回的是基於陣列的List檢視(List view)。所以,這種方式是將陣列轉換為List的最快的方式。因為返回的只是檢視,不需要多餘的記憶體來建立新的List以及複製操作。
- 該方法返回的List是長度是固定的(fixed),不是隻讀的。所以我們不能進行刪除、新增操作,而可以使用set()方法進行修改元素操作。如果你對返回的List執行add()新增新元素,會返回UnsupportedOperationException。(原因:Arrays.asList()方法返回的是個內部的ArrayList,而這種ArrayList根本就沒有add()和remove()方法,但有set()方法)
- 因為該方法返回的是基於原陣列的List檢視,所以,當我們使用set方法修改了List中的元素的時候,那麼原來的陣列也會跟著改變(這是檢視的特性)。
- 從java 5開始,該方法支援泛型,所以我們可以從陣列中得到型別安全ArrayList。
注意:
- 如果我們想讓轉換為只讀的List,可以使用Collections.unmodifiableList()方法來將陣列轉換為指定List。
- 如果想返回的方法能夠進行新增、刪除元素操作,則可以使用new ArrayList(Arrays.asList(array)) ,這樣就會建立一個物件型別的ArrayList,並將陣列的內容拷貝過去。
6,遍歷過程中移除元素,導致的越界錯誤
問題:將列表中李明的名字移除掉,下圖實現有無問題?
解答:
- 在列表中移除最後一個元素按說應該沒有問題的,但是這個演算法還是出現了錯誤
- 主要是這種寫法的列表迴圈(加強for迴圈)遵循下表索引查詢,當移除某個元素時,上次計算出來的長度超過了當前列表長度,故而會出現越界錯誤。
另一種迴圈方法(可以看見remove方法後,列表的長度實時的減一了):
public class AtWill {
public static void main(String[] args) {
List<String> list = new ArrayList();
String removName = "李明";
list.add("張三");
list.add("李四");
list.add("李明");
list.add("李明");
list.add("趙六");
System.out.println(list);
System.out.println("list的長度"+list.size());
for(int i=0; i<list.size(); i++) {
if(removName.equals(list.get(i))) {
list.remove(list.get(i));
//索引往回一個單位
i--;
//列表的長度實時的減一了
System.out.println("list當前的長度"+list.size());
}
}
System.out.println(list);
}
}
7,mkdir建立目錄
問題:在指定目錄下建立檔案目錄,到底使用哪一種呢,兩個方法都沒報錯,為何第一次沒建立目錄而第二次建立?
解答:
- mkdir()只會建立一級的資料夾
- mkdirs()可以建立多級資料夾
8,float型別資料相減會丟失精度
解答:
- 兩個float型別資料相減會丟失精度,尾部帶著常常的一串數字。
- 如果實際場景要做計算有這麼兩種思路:
- 第一先將單位做成整數再做除法;
- 第二可以用bigdecimal來計算(用來對超過16位有效位的數進行精確的運算)(點選傳送到BigDecimal )。
public class AtWill {
public static void main(String[] args) {
Float totalMoney = 200000.8f;
Float ownMoney = 170000.5f;
//不能直接相減,會損失精度,錯誤X
// Float leftMoney = totalMoney - ownMoney;
//第一種合理的做法,先將單位做成整數再做除法(思路)
Float leftMoney = 0f;
process(totalMoney,ownMoney);
System.out.println("還剩餘:"+leftMoney);
}
public static Float process(Float totalMoney, Float ownMoney) {
int change = (int)(totalMoney*10) - (int)(ownMoney*10);
int remainder = change%10;
return (float)(change/10) + (float)remainder/10;
}
}
9,建立執行緒的方式(點選傳送到Java中建立執行緒的三種方式以及區別)
問題:
面試者:繼承Thread類和實現runnable介面!
面試官:除此以外還有方法麼?
面試者:就這兩種實現啊?!
面試官:我們聊點別的,呵呵。
解答:
- 其實 實現多執行緒還可以實現Callable介面,利用task來接受非同步執行緒的執行結果。
- 希望後面再被面試官問到這道題可千萬別再入坑,並且能回答這第三種方式跟前兩種不同的地方(可以獲取執行結果)。
參考來源於:
https://www.cnblogs.com/TTTTT/p/5905440.html
https://zhidao.baidu.com/question/326494195772278445.html
https://blog.csdn.net/darxin/article/details/5247391