1. 程式人生 > 實用技巧 >JavaSE部分之(1)Java基礎

JavaSE部分之(1)Java基礎

JavaSE部分之(1)Java基礎

1、為什麼重寫equals還要重寫hashcode
為了提高程式的效率才實現了hashcode方法,先進行hashcode的比較,如果不同,那就沒必要再進行equals的比較了,這樣就大大減少了equals比較的次數,在需要進行大量比較的情況下可以顯著的提高效率,一個很好的例子就是在集合中的使用;我們都知道java中的List集合是有序的、可以重複的,而set集合是無序的、不能重複的。如果單靠equals方法比較的話,假設原來集合中已經有10000個元素了,那麼放入第10001個元素時,要將前面所有的元素都進行equals比較,看看是否有重複,這個效率就會很慢,因此hashcode應運而生,java採用了hash表,利用雜湊演算法(也叫雜湊演算法),就是將物件資料根據該物件的特徵使用特定的演算法將其定義到一個地址上,那麼在後面定義進來的資料只要看對應的hashcode地址上是否有值,如果有,再用equals比較,如果沒有則直接插入,這樣就大大減少了equals的使用次數,執行效率就大大提高了。

簡單的說就是為了保證同一個物件,保證在equals相同的情況下hashcode值必定相同,如果重寫了equals而未重寫hashcode方法,可能就會出現兩個沒有關係的物件equals相同的(因為equal都是根據物件的特徵進行重寫的),但hashcode確不相同,這樣就違背了Java API中對hashCode方法的描述。
(打個比方,一個名叫張三的人去住酒店,在前臺登記完名字就去了99層100號房間,此時警察來前臺找叫張三的這個人住在哪間房,經過查詢,該酒店住宿的有50個叫張三的,需要遍歷查詢,查詢起來很不方便。
那麼就換另外一種登記方式,前臺登記時登記身份證號,警察來前臺找身份證號時發現身份證號也存在重複,經過雜湊演算法進行計算後相同的hashcode值被分到了一個房間然後產生連結串列,連結串列查詢效率非常慢,然後警察找的時候也會遇到問題。

那麼只能換第三種登記方式了,前臺登記時同時登記身份證號和名字,這樣警察來找的時候同時按照兩個條件去查,這樣就能直接鎖定要找的人在哪個房間。
在程式中:登記的前臺好比雜湊演算法,名字是對比好比 equals 方法,身份證號的對比好比 hashcode 方法只有equals 和 hashcode 都滿足的時候才能確保是同一個物件。)

當我們重寫一個類的 equals 方法時就應當連同重寫 hashcode 方法,並且兩個方法應滿足:
a:一致性,即:當兩個物件 equals 比較為 true,那麼 hashcode 值應當相等,反之亦然,因為當兩個物件hashcode 值相等,但是 equals 比較為 false,那麼在 HashMap 中會產生連結串列,影響查詢效能。
b:成對重寫,即重寫 equals 就應當重寫 hashcode。

2、說一下map的分類和常見的情況
java為資料結構中的對映定義了一個介面java.util.Map;它有四個實現類,分別是HashMap、Hashtable、LinkedHashMap和TreeMap。Map主要用於儲存健值對,不允許鍵重複(重複了會覆蓋掉),但允許值重複。

Hashmap是一個最常用的Map,它根據鍵的HashCode值儲存資料,根據鍵可以直接獲取它的值,具有很快的訪問速度,遍歷時,取得資料的順序是完全隨機的;HashMap最多隻允許一條記錄的鍵為Null,允許多條記錄的值為Null;HashMap不支援執行緒的同步,即任一時刻可以有多個執行緒同時寫HashMap,可能會導致資料的不一致;如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。

Hashtable與HashMap類似,不同的是,它不允許記錄的鍵或者值為空,支援執行緒同步,即任一時刻只有一個執行緒能寫Hashtable,因此也導致了Hashtable在寫入時會比較慢。

LinkedHashMap是HashMap的一個子類,儲存了記錄的插入順序,在遍歷LinkedHashMap時,先得到的記錄肯定是先插入的。在遍歷的時候會比HashMap慢;不過有種情況例外,當HashMap容量很大,實際資料較少時,遍歷起來可能會比LinkedHashMap慢,因為LinkedHashMap的遍歷速度只和實際資料有關,和容量無關,而HashMap的遍歷速度和他的容量有關。

TreeMap實現了SortMap介面,能夠把它儲存的記錄根據鍵排序,預設是按鍵值的升序排序,也可以指定排序比較器,遍歷得到的記錄是排過序的。

一般情況下,我們用的最多的是HashMap,在Map中插入、刪除和定位元素,HashMap是最好的選擇;但如果想要按自然順序或自定義順序遍歷鍵,那麼TreeMap會更好;如果需要輸出的順序和輸入的相同,那麼用LinkedHashMap可以實現。

3、Object若不重寫hashCode()的話,hashCode()如何計算出來的?
Object的hashcode方法是本地方法,也就是用c語言或c++實現的,該方法返回的是一個由記憶體地址經過雜湊得到的整數值。

4、==比較的是什麼?
==比較的是兩個物件的記憶體地址是否相同。


5、若對一個類不重寫,它的equals()方法是如何比較的?
和==比較是一樣的,都是比較兩個物件的記憶體地址是否相同。

6、java8新特性
1.Lambda表示式
2.介面的預設方法與靜態方法
3.方法引用
4.重複註解
5.擴充套件註解的支援
6.Optional類
7.Stream API
8.Date Time API
9.JavaScript引擎Nashorn
10.Base64

7、說說Lamda表示式的優缺點。
優點:
  簡潔
  非常容易平行計算
  可能代表未來的程式設計趨勢

缺點:
  可讀性不是很強
  若不用平行計算,很多時候計算速度沒有比傳統的for迴圈快
  不易除錯

參考:https://blog.csdn.net/robert_chen1988/article/details/78508322

8、一個十進位制的數在記憶體中是怎麼存的?
是以二進位制補碼形式儲存的

9、為啥有時會出現4.0-3.6=0.40000001這種現象?
在二進位制系統中無法精確地表示分數1/10,這就好像十進位制無法精確地表示分數1/3一樣。
如果在數值計算中不允許有任何舍入誤差 ,就應該使用BigDecimal類。

10、Java支援的資料型別有哪些?什麼是自動拆裝箱?
八個基本資料型別:byte,short,int,long,float,double,char,boolean;以及引用型別。

整數預設int型,小數預設是double型,float、long型別必須加字尾f、l;

自動裝箱和拆箱就是基本型別和其對應引用型別之間的轉換,
自動裝箱: 就是將基本資料型別自動轉換成對應的包裝類,
自動拆箱:就是將包裝類自動轉換成對應的基本資料型別,
基本型別轉換為引用型別後,就可以直接呼叫包裝類中封裝好的一些方法。

詳見:
https://blog.csdn.net/wufaliang003/article/details/82347077

11、什麼是值傳遞和引用傳遞?
值傳遞:方法呼叫時,實際引數把它的值傳遞給對應的形式引數,方法執行中形式引數值的改變不影響實際引數的值。

引用傳遞:也稱為傳地址。方法呼叫時,實際引數的引用被傳遞給方法中對應的形式引數,方法執行中,對形式引數的操作實際上就是對實際引數的操作,方法執行中形式引數值的改變將會影響實際引數的值。

12、陣列(Array)和列表(ArrayList)有什麼區別?什麼時候應該使用Array而不是ArrayList?
區別:
陣列的大小是固定的,列表的大小是動態變化的;
陣列在宣告的同時必須進行例項化(至少得初始化陣列的大小),列表可以只是先宣告,之後再進行例項化;
陣列可以儲存基本型別和物件型別,列表只能儲存物件型別(儲存基本型別要用包裝類);
陣列只能儲存同一型別的資料,列表可以存放不同型別的資料(在沒有宣告泛型具體型別的情況下);

使用選擇:
如果想要儲存一些在整個程式執行期間都會存在而且不變的資料,可以使用一個全域性陣列;
如果只是單純想要以陣列的形式儲存資料,而不對資料進行增加、刪除等操作,只是為了方便進行查詢的話,可以使用ArrayList,如果需要對元素進行頻繁的移動或刪除,或者是處理超大量的資料,使用ArrayList就不合適了,因為它的效率很低,可以選擇使用LinkedList。

13、你瞭解大O符號(big-O notation)麼?你能給出不同資料結構的例子麼?

大O符號描述了當資料結構裡的元素增加的時候,演算法的規模或效能在最壞的情況下有多好。

比如陣列的插入時間複雜度為O(N),空間複雜度為O(1);連結串列的插入時間複雜度為O(1),空間複雜度為O(1)。

14、String是最基本的資料型別嗎?
不是,String是引用型別;String類是final的,不能被繼承,不能被修改;

Java的基本型別只有八個:byte(1位元組)、short(2位元組)、int(4位元組)、long(8位元組)、float(4位元組)、double(8位元組)、char(2位元組)、boolean。

15、int 和 Integer 有什麼區別
int是Java的基本型別,Integer是int的包裝類,是引用型別;
int的預設值為0,Ingeter的預設值為null。

java在編譯Integer i = 128的時候,被翻譯成Integer i = Integer.valueOf(128),而valueOf()函式會對-128到127之間的數進行快取,如果在快取中,就不會新建一個物件,否則,新建一個物件;

同樣適用於Byte、Short、Integer、Long、Character,其中Character快取的是0到127之間的數;

1 public class Java8Tester {
2   public static void main(String args[]) {
3     Integer i1 = 127;
4     Integer i2 = 127;
5     System.out.println(i1==i2);
6   }
7 }//結果為:true

1 public class Java8Tester {
2   public static void main(String args[]) {
3     Integer i1 = 128;
4     Integer i2 = 128;
5     System.out.println(i1==i2);
6   }
7 }//結果為:false


int型別和Integer型別比較,會把Integer自動拆箱為int再去比,所以,只要值相同,兩者就是相同的。

1 public class Java8Tester {
2   public static void main(String args[]) {
3     Integer i1 = 99999;
4     int i2 = 99999;
5     System.out.println(i1==i2);
6   }
7 }//結果為:true


16、String 和StringBuffer的區別
String類是final的,不可變(這就導致每次對String的操作都會生成新的String物件,不僅效率低下,而且大量浪費有限的記憶體空間),
StringBuilder和StringBuffer(執行緒安全的字串操作類,任何對它指向的字串的操作都不會產生新的物件。 每個StringBuffer物件都有一定的緩衝區容量,當字串大小沒有超過容量時,不會分配新的容量,當字串大小超過容量時,會自動增加容量)可變;
大部分情況下的執行效率:StringBuilder > StringBuffer > String,但是類似String str = "abc"+"def"這種情況下,String的效率最高;
String是執行緒安全的,因為String類是final的,StringBuffer也是執行緒安全的,StringBuilder不是執行緒安全的。

總結一下:
String適用於少量字串操作的情況;
StringBuilder適用於單執行緒下進行大量操作的情況;
StringBuffer適用於多執行緒下進行大量操作的情況。

StringBuilder執行緒不安全驗證:
https://blog.csdn.net/weixin_34062469/article/details/86825792

17、我們在web應用開發過程中經常遇到輸出某種編碼的字元,如iso8859-1等,如何輸出一個某種編碼的字串?

 1 public class Java8Tester {
 2   public String translate(String str) {
 3     String tempStr = "";
 4     try {
 5       //按照ISO-8859-1進行解碼
 6       byte[] b = str.getBytes("ISO-8859-1");
 7       //按照GBK重新進行編碼 
 8       tempStr = new String(b, "GBK");
 9       tempStr = tempStr.trim();
10     } catch (UnsupportedEncodingException e) {
11       e.printStackTrace();
12     }
13     return tempStr;
14   }
15 }

18、&和&&的區別?
相同點:
&和&&都可以用作邏輯與的運算子,表示邏輯與

不同點:
&不具備短路功能,&兩邊都要計算,而&&具有短路功能,即&&左邊為false結果就為false,右邊不再進行計算;
&還可以表示按位與操作,而&&不行。

19、在Java中,如何跳出當前的多重巢狀迴圈?
java中使用帶有標號的break語句跳出多重迴圈,即在外面的迴圈語句前定義一個標號,然後在裡層迴圈體的程式碼中使用帶有標號的break語句,即可跳出。

20、你能比較一下Java和JavaSciprt嗎?
java:面向物件,需要編譯,再進行執行,強型別;

javascript:基於物件和事件驅動,解釋型語言,弱型別。

21、簡述正則表示式及其用途。
在編寫處理字串的程式時,經常會有查詢符合某些複雜規則的字串的需要。正則表示式就是用於描述這些規則的工具。換句話說,正則表示式就是記錄文字規則的程式碼。

22、Java中是如何支援正則表示式操作的?
Java中的String類提供了支援正則表示式操作的方法,包括:matches()、replaceAll()、replaceFirst()、split()。此外,Java中可以用Pattern類表示正則表示式物件,它提供了豐富的API進行各種正則表示式操作。


擴充套件:
字串方法replace()、replaceAll()、replaceFirst()的區別和使用方法?

1 String s = "my.test.txt";
2 System.out.println(s.replace(".", "#"));
3 System.out.println(s.replaceAll(".", "#"));
4 System.out.println(s.replaceFirst(".", "#"));

執行結果:
my#test#txt
###########
#y.test.txt

解析:
“.”是正則表示式的元字元,匹配除換行符以外的任意字元,所以replaceAll()、replaceFirst()才出現了意想不到的結果。
而replace()沒有用到正則表示式,但會把所有“.”替換掉,很多人可能會誤解replace()是替換單個,而replaceAll是替換全部,
其實這是錯的(我以前也是這麼想的- -)。replace()只是沒有用到正則表示式,但會替換所有匹配的字串。
到這裡一些不懂正則表示式的小夥伴可能就要喊坑爹了,“那我不想用正則表示式去替換第一個字串腫麼辦?”
其實也很簡單,只要將元字串轉義就行了。

1 String s = "my.test.txt";
2 System.out.println(s.replace(".", "#"));
3 System.out.println(s.replaceAll("\\.", "#"));
4 System.out.println(s.replaceFirst("\\.", "#"));

執行結果:
my#test#txt
my#test#txt
my#test.txt

23、請你說說Java和PHP的區別?

Java是純面向物件的語言,是編譯型強型別語言;

PHP既可面向物件,又可面向過程,是解釋型弱型別語言。