1、hashCode 和 equals
- Object 中沒有重寫 hashCode 方法,默認使用對象的 jvm 內存地址進行 hashCode;重寫了 equals 方法,直接判斷的是兩個對象的地址是否相等
- 如果 hashCode 相等,equals 不一定相等;如果 equals 相等,那麼 hashCode 一定相等。也就是說使用 equals 比較是絕對可靠的, 但是爲了效率,可以先比較 hashCode,如果相等,再比較 equals 方法;如果 hashCode 不相等,那麼肯定不想動。
- 若重寫 equals 方法,一定要重寫 hashCode 方法
package com.vim.test;
public class User{
private String name;
public User(String name){
this.name = name;
}
// 重寫 equals 方法
public boolean equals(Object arg0){
if(!(arg0 instanceof User)){
return false;
}
User user = (User)arg0;
//如果名字相同,則表示屬於同一個對象。
if (user.getName().equals(this.getName())){
return true;
}else{
return false;
}
}
// 重寫 hashCode 方法,返回名字的哈希碼
public int hashCode() {
return name.hashCode();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
User u1 = new User("張三");
User u2 = new User("李四");
User u3 = new User("張三");
System.out.println(u1.equals(u3));
}
}
該圖爲 Set 集合放入元素時判斷重複的邏輯流程。
2、ceil、floor、round
- floor 返回不大於的最大整數;ceil 則是不小於他的最小整數
- round 則是4舍5入的計算,如果是負數,可以先取絕對值;正數小數點後大於5則進位;負數小數點後小於以及等於5都捨去,大於5的則進位
3、StringBuffer、StringBuilder
- 兩種都是 final 修飾的類,底層都是採用字符數組來實現的;減少了字符串拼接的開銷
- StringBuffer 是線程安全的
4、抽象類特點
- 抽象類不一定非要有抽象方法,還可以有構造函數和main方法,並且可以運行
- 抽象類不能直接實例化
- 抽象類不能使用 final 修飾
- 抽象類中的方法可以使用任意修飾符
5、集合知識點
- Collection 和 Map 兩大類,Collection 下又分爲 List 和 Set;常用的集合類有 ArrayList、LinkedList、HashSet、LinkedHashSet、TreeSet 和 HashMap、LinkedHashMap、TreeMap
- List、Set 主要區別在於重不重複和是否有序,List:有序、可重複; Set:無序(也可有序),重複
- HashMap 和 Hashtable 區別,兩者都是在內部通過單鏈表解決衝突問題,容量不足(超過了閥值)時,會自動增長;而 HashMap 是線程不安全的,而且可以存儲 null 值;基於 hash 算法存儲元素,當計算出的 hash 相同(衝突)時,採用鏈表和紅黑樹的方式進行存儲
- ArrayList 和 LinkedList 兩種實現的方式不同:前一個使用數組結構,後一個使用雙向鏈表結構;所以隨機訪問效率、增加和刪除操作,兩種各有優勢
6、線程有哪些狀態
- 新建(new)、就緒(Runnable)、運行(run)、阻塞(blocked)、死亡(dead)
- 新建:實現 Runnable 接口或繼承 Thread 的類,使用 new 關鍵字創建
- 就緒:調用 start 方法、sleep 結束、join 結束、時間片用完、調用 yield
- 運行:線程調度程序從可運行池中選擇一個線程作爲當前線程時線程所處的狀態
- 阻塞:調用 sleep 方法
7、wait 和 notify 流程原理
- 調用了 wait 方法的線程會進入等待隊列
- 調用了 notify 方法的線程,會從等待隊列中選擇一個線程進入鎖池;調用了 notifyAll 方法的線程,會從等待隊列中選擇所有的線程進入鎖池,然後共同去競爭對象鎖
8、sleep 和 wait 的區別
- sleep 來自於 Thread 類,wait 屬於 Object 類
- wait 會釋放鎖,如果sleep放在同步方法中,並不會釋放鎖,只是會讓出 cpu
- wait 必須運行在同步方法中,sleep不需要
9、ThreadLocal
- 爲每一個線程提供了獨立的變量副本,如 數據庫連接、session 管理
10、synchronized 和 volatile 的區別
- synchronized 會創建一個內存屏障,內存屏障指令保證了所有CPU操作結果都會直接刷到主存中,從而保證了操作的內存可見性; volatile 關鍵字解決的是內存可見性的問題,會使得所有對
volatile
變量的讀寫都會直接刷到主存,即保證了變量的可見性 - volatile 僅能夠使用在變量級別,而 synchronized 可以使用在變量、方法、類上面
- volatile 不會造成線程的阻塞,但是 synchronized 會造成線程的阻塞
11、深拷貝和淺拷貝
- 將一個對象複製給另一個對象,有三種方式: 直接複製、淺拷貝、深拷貝,其中直接複製,如果是引用對象,會導致兩者一起變化
- 淺拷貝:使用 Object 類的 clone 方法,創建一個新對象,將當前對象的非靜態字段複製到新對象,如果字段是基本類型,直接複製,如果是引用類型,複製引用而不復制引用的對象
public class Resume implements Cloneable{
private String name; //姓名
private String sex; //性別
public Resume(String name, String sex) {
this.name = name;
this.sex = sex;
}
public Object clone() {
try {
return (Resume)super.clone();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
- 深拷貝示例,使用該工具類的對象必須要實現Serializable接口
public class CloneUtils {
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj){
T cloneObj = null;
try {
//寫入字節流
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream obs = new ObjectOutputStream(out);
obs.writeObject(obj);
obs.close();
//分配內存,寫入原始對象,生成新對象
ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream ois = new ObjectInputStream(ios);
//返回生成的新對象
cloneObj = (T) ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return cloneObj;
}
}
12、session 和 cookie 的區別
- 存儲位置:session 在服務端,cookie 在客戶端
- cookie 出現的原因是爲了解決 http 協議的無狀態問題,通過在客戶端記錄信息來確定用戶的信息,而 session 則是通過在服務端記錄信息來解決這個問題
- session 攜帶的 sessionId 需要通過 cookie 機制保存到客戶端瀏覽器中;也可以將 sessionId 放入鏈接參數的方式來使用
13、常見的異常類有哪些
- NullPointerException、ClassNotFoundException、IndexOutOfBoundsException、ClassCastException
- NumberFormatException、FileNotFoundException、IOException
14、tcp 和 udp
- tcp 是面向連接的,udp 是面向無連接的
- tcp 是面向字節流的,udp 是面向數據包的;由於緩衝區的存在,tcp 的讀寫不需要一一匹配,可以分多次讀取和寫入,而 udp 針對報文原樣發送,不會拆分,也不會合並。
- tcp 保證數據正確性,udp 可能丟包
- 三次握手:保證通信雙方全都有來有回
初始階段,客戶端和服務端都處於 CLOSED 階段,服務端監聽某個端口,處於 LISTEN 狀態
客戶端主動發送 SYN 連接請求,並置發送序號爲 x,之後處於 SYN-SENT 狀態
服務端收到連接請求之後,返回返回SYN + ACK,並且發送序號爲 y,並確認序號爲 x+1
客戶端收到服務端的 SYN+ACK 之後,進入 ESTABLISHED 狀態,然後回覆 ACK,並置發送序號爲 x+1,確認序號 y+1
服務端收到 ACK 之後處於 ESTABLISHED 狀態,
5、四次揮手:
客戶端發送 FIN 數據包來主動斷開,進入 FIN-WAIT-1 狀態(此時可以接受、應答數據,無法發送數據)
服務端收到 FIN 後,進入 CLOSED-WAIT 狀態,並回復 ACK
客戶端收到 ACK 之後,進入 FIN-WAIT-2 狀態,該狀態是等待對方的 FIN,默認60s,超過就本地關閉,不會進入下一個狀態
服務端發送 FIN+ACK 給客戶端,進入 LAST-ACK 階段
當客戶端收到了服務端發來的 FIN 之後,回覆 ACK ,進入 TIME-WAIT 狀態,此時內核會設定一個時間長度爲 2MSL 的定時器,當到時間後,內核會將該連接關閉,MSL即報文最大生存時間
15、粘包產生原因
- 發送方:發送端需要等緩衝區滿才發送出去
- 接收方:不及時接收緩存區數據,造成多個包接收
16、事務介紹
- 四大特徵:原子性、一致性、隔離性、持久性(ACID)
- 原子性:事務包含的所有操作要麼全部成功,要麼全部失敗; 一致性:事務操作必須使數據庫從一個一致性狀態切換到另一個一致性狀態,如轉賬前後,兩個用戶錢的總和不變;隔離性:多個用戶併發訪問數據庫,爲每一個用戶開啓的事務不能被其他事務干擾;持久性:一個事務一旦被提交了,那麼對數據的改變就是永久性的。
- 隔離級別:讀未提交、讀已提交、可重複讀、可串行化
- 讀未提交:事務A未提交的數據,事務B可以讀到,此時讀到的是髒數據;
- 讀已提交:事務A提交的數據,事務B可以立即讀到,可以避免髒讀,但是不可重複讀;
- 可重複讀(mysql默認級別):事務A和事務B開啓事務之後,事務A更新了數據,進行事務提交後,事務B讀取的仍然是之前的數據,在事務B提交之前,這條數據都是可重複讀的;
- 串行化:事務A在操作數據時,事務B只能等待。
17、Mybatis 中 #{} 和 ${} 的區別
#{} 是預編譯處理,${} 是字符替換; #{} 會將SQL 中的 #{} 替換爲 ?,然後配合 PreparedStatement 的 Set 方法進行賦值,這樣可以有效地防止 SQL 注入,保證安全。
18、Mybatis 的一級和二級緩存