1. 程式人生 > >整理面試題,以及部分答案,2018

整理面試題,以及部分答案,2018

個人總結

個人瞎寫,僅供參考;部分轉載,逐漸完善。

  • Java基礎
  • Java 併發
  • Spring
  • Netty
  • 分散式相關
  • 資料庫
  • 快取
  • JVM
  • 系統優化
  • 手寫程式碼

Java基礎

1. List、Set、Map的區別

list和set是實現了collection介面,map不是collection的子介面或者實現類,Map是一個介面。

List:
1.可以允許重複的物件。
2.可以插入多個null元素。
3.是一個有序容器,保持了每個元素的插入順序,輸出的順序就是插入的順序。
4.常用的實現類有 ArrayList、LinkedList 和 Vector。ArrayList 最為流行,它提供了使用索引的隨意訪問,而 LinkedList 則對於經常需要從 List 中新增或刪除元素的場合更為合適。

Set:
1.不允許重複物件
2. 無序容器,你無法保證每個元素的儲存順序,TreeSet通過 Comparator  或者 Comparable 維護了一個排序順序。
3. 只允許一個 null 元素
4.Set 介面最流行的幾個實現類是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基於 HashMap 實現的 HashSet;TreeSet 還實現了 SortedSet 介面,因此 TreeSet 是一個根據其 compare() 和 compareTo() 的定義進行排序的有序容器。

Map:
1.Map不是collection的子介面或者實現類。Map是一個介面。
2.Map 的 每個 Entry 都持有兩個物件,也就是一個鍵一個值,Map 可能會持有相同的值物件但鍵物件必須是唯一的。
3. TreeMap 也通過 Comparator  或者 Comparable 維護了一個排序順序。
4. Map 裡你可以擁有隨意個 null 值但最多隻能有一個 null 鍵。
5.Map 介面最流行的幾個實現類是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)

2. HashSet 是如何保證不重複的

首先會呼叫Object的hashCode方法判hashCode是否已經存在,如不存在則直接插入元素;
如果已存在則呼叫Object物件的equals方法判斷是否返回true,如果為true則說明元素已經存在,如為false則插入元素。

3.HashMap 是執行緒安全的嗎,為什麼不是執行緒安全的(最好畫圖說明多執行緒環境下不安全)?

不是執行緒安全的,當A、B兩個執行緒,將資料同時put到同一個HashMap中,會引起碰撞,造成資料被覆蓋。

4.HashMap 的擴容過程

5.JDK 1.7 與 1.8 的 區別,說明 1.8 做了哪些優化,如何優化的?

switch支援String型別;
int、short、long、byte 可以用二進位制表示;
新增Lambda表示式;
HashMap中,1.7中當HashCode值相同時,會將資料排成連結串列,查詢時間複雜度比較高,需要遍歷連結串列;1.8中當連結串列過長時,會將連結串列擴充套件成紅黑樹,使查詢效率變高。

6.final finally finalize

final關鍵字,可以修飾類、方法、變數。修飾類,這個類不能被繼承;修飾方法,這個方法不能被重寫;修飾變數,需要在宣告變數時,賦初始值,之後不能修改。
finally是在異常處理時提供finally塊來執行任何清除操作。不管有沒有異常被丟擲、捕獲,finally塊都會被執行。
finalize是方法名。finalize()方法是在垃圾收集器刪除物件之前對這個物件呼叫的。

7.強引用 、軟引用、 弱引用、虛引用

強引用實際使用中大多數是強引用,String str = “abc”; list.add(str);
軟引用,jvm在記憶體中有快取記憶體,將物件放到裡面,讀取資料時,提高執行效率;

8.Java反射

Java利用現有物件方法,獲取當前物件的某些屬性,例如:Object類的getClass()方法

9.Arrays.sort 實現原理和 Collection 實現原理

10.LinkedHashMap的應用

LinkedHashMap是在HashMap基礎上新增了雙向連結串列,用於儲存迭代順序。

12、異常分類以及處理機制

Error(錯誤):是程式無法處理的錯誤,表示執行應用程式中較嚴重問題。

runtimeException子類:
    java.lang.ArrayIndexOutOfBoundsException陣列索引越界異常。當對陣列的索引值為負數或大於等於陣列大小時丟擲。
    java.lang.NullPointerException空指標異常。當應用試圖在要求使用物件的地方使用了null時,丟擲該異常。譬如:呼叫null物件的例項方法、訪問null物件的屬性、計算null物件的長度、使用throw語句丟擲null等等
    java.lang.ClassNotFoundException找不到類異常。當應用試圖根據字串形式的類名構造類,而在遍歷CLASSPAH之後找不到對應名稱的class檔案時,丟擲該異常。
IOException子類:
    IOException:操作輸入流和輸出流時可能出現的異常。
    FileNotFoundException   檔案未找到異常
其他類:
    SQLException:操作資料庫異常類
    StringIndexOutOfBoundsException 字串索引超出範圍丟擲的異常

13、wait和sleep的區別

wait()方法屬於Object類;sleep()方法屬於Thread類;
呼叫wait()方法的過程中,執行緒會釋放物件鎖;呼叫sleep()方法的過程中,執行緒不會釋放物件鎖。

14、陣列在記憶體中如何分配

//定義第一個陣列
int[] arr=new int[3];
arr[0]=10;
arr[1]=20;
arr[2]=70;

第一步:棧儲存區域性變數(在方法定義中或方法宣告上的變數),所以int[] arr 存放在了棧中;
第二步:new出的變數放在堆中,所以new int【3】在堆中。
第三步:每一個new出來的東西都有地址值(系統隨機分配),所以new int【3】的地址值為0x001;把0x001賦給arr,在棧中的陣列通過地址值找到堆中相應的地址。用陣列名和編號的配合就可以找到陣列中指定編號的元素,這種方法就叫做索引。
第四步:int型別的資料預設值為0
第五步:給陣列中的每個元素賦值,把原先的預設值0幹掉。

延伸:
當加上static修飾,變成 static int[] arr=new int[3];
arr 會被放在方法區(持久代/非堆)中。

Java 併發

1、synchronized 的實現原理以及鎖優化?

2、volatile 的實現原理?

4、synchronized 在靜態方法和普通方法的區別?

修飾靜態方法,保證多個執行緒執行時,進行加鎖操作,使之相互不干擾;
修飾普通方法,沒有用處,因為普通方法,儲存在單獨的 本地方法棧中,是私有的。

5、怎麼實現執行緒順序執行?

閉鎖CountDownLatch
閉鎖是典型的等待事件發生的同步工具類,將閉鎖的初始值設定1,所有執行緒呼叫await方法等待,當事件發生時呼叫countDown將閉鎖值減為0,則所有await等待閉鎖的執行緒得以繼續執行。

join()方法,保證執行緒執行順序。

7、synchronized 和 lock 有什麼區別?

synchronized是java關鍵字;Lock是一個類;
synchronized不需要使用者去手動釋放鎖,當synchronized方法或者synchronized程式碼塊執行完之後,系統會自動讓執行緒釋放對鎖的佔用;而Lock則必須要使用者去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象;
多個執行緒進行讀操作,用synchronized鎖,當一個執行緒在進行讀操作時,其他執行緒只能等待無法進行讀操作;用lock鎖,執行緒之間不會發生衝突,可以進行讀操作;
通過Lock可以知道執行緒有沒有成功獲取到鎖,synchronized沒辦法知道;

9、HashMap 的併發問題?

HashMap在高併發下可能引起死迴圈,造成cpu佔用過高。
假如有兩個執行緒P1、P2,以及連結串列 a=》b=》null
    P1先執行,執行完"Entry<K,V> next=e.next;"程式碼後發生堵塞,或者其它情況不再執行下去,此時e=a。next=b
    而P2已經執行完整段程式碼,於是當前的新連結串列newTable[i]為b=》a=》null
    P1又繼續執行"Entry<K,V> next=e.next;
    "之後的程式碼,則執行完"e=next;"後,newTable[i]為a《=》b。則造成迴路,while(e!=null)一直死迴圈

個人理解翻譯下:
口->a->b->c->null
A執行緒執行remove(a)方法,將a節點拆除,將b節點拼接到根節點上;
B執行緒執行put(a)方法,將a節點put到c節點後,這個時候,結構變為:口->b->c->a->b->c->a....;//後面造成迴圈結構
get()方法;當查詢時,進行連結串列迴圈,由於是迴圈結構,所以會一直查詢,造成死迴圈。

10、ConcurrenHashMap 介紹?1.8 中為什麼要用紅黑樹?

ConcurrenHashMap鎖方式是稍微細粒度的,內部採用分離鎖(分段鎖)的設計。它預設將Hash表分為16個分段,get,put,remove等常用操作只鎖當前需要用到的分段;
JDK1.8相較1.7,當連結串列長度大於8時,會將連結串列結構轉成紅黑樹,便於大資料量的查詢,使時間複雜度由O(n)變成O(logN)

12、如何檢測死鎖?怎麼預防死鎖?

分析程式碼;
用jstack工具查詢,jstack是java虛擬機器自帶的一種堆疊跟蹤工具;
加鎖順序(執行緒按照一定的順序加鎖)
加鎖時限(執行緒嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,並釋放自己佔有的鎖)

13、Java 記憶體模型?

JVM記憶體模型

14、如何保證多執行緒下 i++ 結果正確?

使用鎖機制,實現i++原子操作:
public static ReentrantLock lock = new ReentrantLock();
或者
synchronized (SynchronizedTest.class) {
    count++;
}

15、執行緒池的種類,區別和使用場景?

newCachedThreadPool:執行很多短期非同步的小程式或者負載較輕的伺服器
newFixedThreadPool:執行長期的任務,效能好很多
newSingleThreadExecutor:一個任務一個任務執行的場景
NewScheduledThreadPool:週期性執行任務的場景

16、分析執行緒池的實現原理和執行緒的排程過程?

17、執行緒池如何調優,最大數目如何確認?

N為CPU核心數
CPU密集型:N + 1
IO密集型:2N + 1

18、ThreadLocal原理,用的時候需要注意什麼?

19、CountDownLatch 和 CyclicBarrier 的用法,以及相互之間的差別?

23、分段鎖的原理,鎖力度減小的思考

Spring

1、BeanFactory 和 FactoryBean?

2、Spring IOC 的理解,其初始化過程?

3、BeanFactory 和 ApplicationContext?

4、Spring Bean 的生命週期,如何被管理的?

5、Spring Bean 的載入過程是怎樣的?

Netty

1、BIO、NIO和AIO

BIO:同步阻塞I/O    1.4之前只有BIO  適用於連線數目比較小且固定的架構,這種方式對伺服器資源要求比較高,併發侷限於應用中
NIO:同步非阻塞I/O  1.4開始有NIO    適用於連線數目多且連線比較短(輕操作)的架構,比如聊天伺服器
AIO:非同步非阻塞I/O  1.7開始有AIO    適用於連線數目多且連線比較長(重操作)的架構,比如相簿伺服器

分散式相關

1、Dubbo的底層實現原理和機制

2、描述一個服務從釋出到被消費的詳細過程

3、分散式系統怎麼做服務治理

4、介面的冪等性的概念

5、訊息中介軟體如何解決訊息丟失問題

6、Dubbo的服務請求失敗怎麼處理

7、重連機制會不會造成錯誤

8、對分散式事務的理解

9、如何實現負載均衡,有哪些演算法可以實現?

10、Zookeeper的用途,選舉的原理是什麼?

11、資料的垂直拆分水平拆分。

12、zookeeper原理和適用場景

13、zookeeper watch機制

14、redis/zk節點宕機如何處理

15、分散式叢集下如何做到唯一序列號

16、如何做一個分散式鎖

17、用過哪些MQ,怎麼用的,和其他mq比較有什麼優缺點,MQ的連線是執行緒安全的嗎

18、MQ系統的資料如何保證不丟失

19、列舉出你能想到的資料庫分庫分表策略;分庫分表後,如何解決全表查詢的問題

20、zookeeper的選舉策略

21、全域性ID

資料庫

1、mysql分頁有什麼優化

2、悲觀鎖、樂觀鎖

3、組合索引,最左原則

4、mysql 的表鎖、行鎖

5、mysql 效能優化

6、mysql的索引分類:B+,hash;什麼情況用什麼索引

7、事務的特性和隔離級別

原子性;一致性;隔離性;永續性;
Serializable (序列化):可避免髒讀、不可重複讀、幻讀的發生。
Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。
Read committed (讀已提交):可避免髒讀的發生。
Read uncommitted (讀未提交):最低級別,任何情況都無法保證。

快取

1、Redis用過哪些資料型別,以及Redis底層怎麼實現

字串物件:SDS簡單動態字串
列表物件:壓縮連結串列(ziplist)、雙向連結串列(linkedlist)
雜湊物件:ziplist或者hashtable
集合物件:intset或者hashtable
有序集合物件:ziplist或者tiaoyueb

2、Redis快取穿透,快取雪崩

快取穿透解決方案:
方案1、使用互斥鎖排隊,分散式環境中要使用分散式鎖,單機的話用普通的鎖(synchronized、Lock)
方案2、布隆過濾器,就類似於一個hash set,用於快速判某個元素是否存在於集合中
快取雪崩解決方案:
方案1、也是像解決快取穿透一樣加鎖排隊,實現同上;
方案2、建立備份快取,快取A和快取B,A設定超時時間,B不設值超時時間,先從A讀快取,A沒有讀B,並且更新A快取    和B快取;
方案3、設定快取超時時間的時候加上一個隨機的時間長度,比如這個快取key的超時時間是固定的5分鐘加上隨機的2分鐘,醬紫可從一定程度上避免雪崩問題;

3、布隆過濾器

在這裡插入圖片描述

每個布隆過濾器對應到 Redis 的資料結構裡面就是一個大型的位陣列和幾個不一樣的無偏 hash 函式。所謂無偏就是能夠把元素的 hash 值算得比較均勻。
向布隆過濾器中新增 key 時,會使用多個 hash 函式對 key 進行 hash 算得一個整數索引值然後對位陣列長度進行取模運算得到一個位置,每個 hash 函式都會算得一個不同的位置。再把位陣列的這幾個位置都置為 1 就完成了 add 操作。
向布隆過濾器詢問 key 是否存在時,跟 add 一樣,也會把 hash 的幾個位置都算出來,看看位陣列中這幾個位置是否都為 1,只要有一個位為 0,那麼說明布隆過濾器中這個 key 不存在。如果都是 1,這並不能說明這個 key 就一定存在,只是極有可能存在,因為這些位被置為 1 可能是因為其它的 key 存在所致。如果這個位陣列比較稀疏,判斷正確的概率就會很大,如果這個位陣列比較擁擠,判斷正確的概率就會降低。具體的概率計算公式比較複雜,感興趣可以閱讀擴充套件閱讀,非常燒腦,不建議讀者細看。
使用時不要讓實際元素遠大於初始化大小,當實際元素開始超出初始化大小時,應該對布隆過濾器進行重建,重新分配一個 size 更大的過濾器,再將所有的歷史元素批量 add 進去 (這就要求我們在其它的儲存器中記錄所有的歷史元素)。因為 error_rate 不會因為數量超出就急劇增加,這就給我們重建過濾器提供了較為寬鬆的時間。...

3、如何使用Redis來實現分散式鎖

加鎖程式碼:
public class RedisTool {

private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";

    /**
     * 嘗試獲取分散式鎖
     * @param jedis Redis客戶端
     * @param lockKey 鎖
     * @param requestId 請求標識
     * @param expireTime 超期時間
     * @return 是否獲取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {

        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }
}

解鎖程式碼:
public class RedisTool {

    private static final Long RELEASE_SUCCESS = 1L;

    /**
     * 釋放分散式鎖
     * @param jedis Redis客戶端
     * @param lockKey 鎖
     * @param requestId 請求標識
     * @return 是否釋放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;

    }

}

4、Redis的併發競爭問題如何解決

redis自帶的incr命令
在程式碼裡要對redis操作的時候,針對同一key的資源,就先進行加鎖(java裡的synchronized或lock)
利用redis的setnx實現內建的鎖

5、Redis持久化的幾種方式,優缺點是什麼,怎麼實現的

RDB持久化可以在指定的時間間隔內生成資料集的時間點快照。
    優點:適合備份;適用於災難恢復;可以最大化 Redis 的效能,父程序只需分出一個子程序進行備份;在恢復大資料集時的速度比 AOF 的恢復速度要快
    缺點:在伺服器故障時可能丟失資料;當資料集比較大時,分出子執行緒比較耗時,甚至會影響客戶端使用
AOF持久化記錄伺服器執行的所有寫操作命令
    優點:AOF 持久化會讓 Redis 變得非常耐久;Redis 可以在 AOF 檔案體積變得過大時,自動地在後臺對 AOF 進行重寫,重寫後的新 AOF 檔案包含了恢復當前資料集所需的最小命令集合,使檔案體積縮小;
    缺點:對於相同的資料集來說,AOF 檔案的體積通常要大於 RDB 檔案的體積;AOF 的速度可能會慢於 RDB;

6、Redis的快取失效策略

定時刪除、惰性刪除、定期刪除
目前常用的策略是 惰性刪除 + 定期刪除

7、Redis叢集,高可用,原理

8. Redis有哪些資料結構?

字串String、字典Hash、列表List、集合Set、有序集合SortedSet。 
如果你是Redis中高階使用者,還需要加上下面幾種資料結構HyperLogLog、Geo、Pub/Sub。 
如果你說還玩過Redis Module,像BloomFilter,RedisSearch,Redis-ML,面試官得眼睛就開始發亮了。

9. 使用過Redis分散式鎖麼,它是什麼回事?

高版本的Redis的Set命令支援多個引數,jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
這是加鎖,執行一條命令,使之進行原子化加鎖。
解鎖命令利用Lua指令碼,再結合eval命令,一次性地進行原子解鎖。

9. Redis裡面有1億個key,其中有10w個key是以某個固定的已知的字首開頭的,如何將它們全部找出來?

使用keys指令可以掃出指定模式的key列表。 
對方接著追問:如果這個redis正在給線上的業務提供服務,那使用keys指令會有什麼問題? 
這個時候你要回答redis關鍵的一個特性:redis的單執行緒的。keys指令會導致執行緒阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。這個時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重複概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長。

9. 如果有大量的key需要設定同一時間過期,一般需要注意什麼?

如果大量的key過期時間設定的過於集中,到過期的那個時間點,redis可能會出現短暫的卡頓現象。一般需要在時間上加一個隨機值,使得過期時間分散一些。

9. Redis如何做持久化的?

bgsave做映象全量持久化,aof做增量持久化。因為bgsave會耗費較長時間,不夠實時,在停機的時候會導致大量丟失資料,所以需要aof來配合使用。在redis例項重啟時,優先使用aof來恢復記憶體的狀態,如果沒有aof日誌,就會使用rdb檔案來恢復。 
如果再問aof檔案過大恢復時間過長怎麼辦?你告訴面試官,Redis會定期做aof重寫,壓縮aof檔案日誌大小。如果面試官不夠滿意,再拿出殺手鐗答案,Redis4.0之後有了混合持久化的功能,將bgsave的全量和aof的增量做了融合處理,這樣既保證了恢復的效率又兼顧了資料的安全性。這個功能甚至很多面試官都不知道,他們肯定會對你刮目相看。 
如果對方追問那如果突然機器掉電會怎樣?取決於aof日誌sync屬性的配置,如果不要求效能,在每條寫指令時都sync一下磁碟,就不會丟失資料。但是在高效能的要求下每次都sync是不現實的,一般都使用定時sync,比如1s1次,這個時候最多就會丟失1s的資料。

10、Redis為什麼不支援事物回滾?為什麼 Redis 先執行指令再記錄aof日誌而不是像其它儲存引擎一樣反過來呢?

不支援回滾操作是因為redis是先執行指令然後做日誌,所以即使發生異常,沒有可以用來執行回滾操作的日誌。
傳統的資料庫都是先做日誌然後再做操作,這樣可以用於事物回滾。

11、主從要求redis版本一致嗎?

要求從庫至少和主庫一樣新,否則主庫的新指令同步過去從庫不能識別,同步就會出錯,所以升級版本時應該先升級從庫,再升級主庫。

12、sentinel(哨兵模式)、codis、cluster 的區別?

sentinel : 主從同步; codis:國人寫的叢集中介軟體; cluster:官方的叢集方案

JVM

1、詳細jvm記憶體模型

2、講講什麼情況下回出現記憶體溢位,記憶體洩漏?

物件記憶體過大;
資源釋放;
static關鍵字的使用。

4、JVM 年輕代到年老代的晉升過程的判斷條件是什麼呢?

年輕代分三個區。一個Eden區,兩個 Survivor區(一般而言)。大部分物件在Eden區中生成。當Eden區滿時,還存活的物件將被複制到Survivor區(兩個中的一個),當這個 Survivor區滿時,此區的存活物件將被複制到另外一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區複製過來的並且此時還存活的物件,將被複制 到年老區。

5、JVM 出現 fullGC 很頻繁,怎麼去線上排查問題?

引起原因:年輕代空間不足;永久代空間滿了;
可以通過kill -3 檢視記憶體快照,進行排查。

6、類載入為什麼要使用雙親委派模式,有沒有什麼場景是打破了這個模式?

A類繼承B類,當類載入器載入A類時,需要先載入B類,再載入到B類時,由於已經載入過了,所以不再載入。自己定義新的類載入器可以不使用雙親委派模式。

7、類的例項化順序

父類靜態成員和靜態初始化塊 ,按在程式碼中出現的順序依次執行
子類靜態成員和靜態初始化塊 ,按在程式碼中出現的順序依次執行
父類例項成員和例項初始化塊 ,按在程式碼中出現的順序依次執行
父類構造方法
子類例項成員和例項初始化塊 ,按在程式碼中出現的順序依次執行
子類構造方法

初始化的順序,先靜態方法,再構造方法,每個又是先基類後子類。

8、JVM垃圾回收機制,何時觸發MinorGC等操作

Minor GC觸發條件:(複製-清除)
    當Eden區滿時,觸發Minor GC;
Full GC觸發條件:(標記-清除)
    呼叫System.gc時,系統建議執行Full GC,但是不必然執行;
    老年代空間不足;
    方法區空間不足;

9、如何優化(重構)現有系統

優化SQL
資料庫配置低,加配置
資料庫負載高,加機器做叢集
將資料放到快取層,redis、es
程式碼優化,例:迴圈查詢資料庫
靜態資源放到專門的OSS伺服器,並用CDN加速
合理減少中間服務的遠端呼叫

10、java類載入機制

載入.class檔案的方式
– 從本地系統中直接載入
– 通過網路下載.class檔案
– 從zip,jar等歸檔檔案中載入.class檔案
– 從專有資料庫中提取.class檔案
– 將Java原始檔動態編譯為.class檔案

11、類的生命週期

在這裡插入圖片描述

	類載入的過程包括了載入、驗證、準備、解析、初始化五個階段。在這五個階段中,載入、驗證、準備和初始化這四個階段發生的順序是確定的,而解析階段則不一定,它在某些情況下可以在初始化階段之後開始,這是為了支援Java語言的執行時繫結(也成為動態繫結或晚期繫結)。另外注意這裡的幾個階段是按順序開始,而不是按順序進行或完成,因為這些階段通常都是互相交叉地混合進行的,通常在一個階段執行的過程中呼叫或啟用另一個階段。

載入:查詢並載入類的二進位制資料

載入時類載入過程的第一個階段,在載入階段,虛擬機器需要完成以下三件事情:

1、通過一個類的全限定名來獲取其定義的二進位制位元組流。

2、將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構。

3、在Java堆中生成一個代表這個類的java.lang.Class物件,作為對方法區中這些資料的訪問入口。

相對於類載入的其他階段而言,載入階段(準確地說,是載入階段獲取類的二進位制位元組流的動作)是可控性最強的階段,因為開發人員既可以使用系統提供的類載入器來完成載入,也可以自定義自己的類載入器來完成載入。

載入階段完成後,虛擬機器外部的 二進位制位元組流就按照虛擬機器所需的格式儲存在方法區之中,而且在Java堆中也建立一個java.lang.Class類的物件,這樣便可以通過該物件訪問方法區中的這些資料。

•連線

– 驗證:確保被載入的類的正確性

驗證是連線階段的第一步,這一階段的目的是為了確保Class檔案的位元組流中包含的資訊符合當前虛擬機器的要求,並且不會危害虛擬機器自身的安全。驗證階段大致會完成4個階段的檢驗動作:

檔案格式驗證:驗證位元組流是否符合Class檔案格式的規範;例如:是否以0xCAFEBABE開頭、主次版本號是否在當前虛擬機器的處理範圍之內、常量池中的常量是否有不被支援的型別。

元資料驗證:對位元組碼描述的資訊進行語義分析(注意:對比javac編譯階段的語義分析),以保證其描述的資訊符合Java語言規範的要求;例如:這個類是否有父類,除了java.lang.Object之外。

位元組碼驗證:通過資料流和控制流分析,確定程式語義是合法的、符合邏輯的。

符號引用驗證:確保解析動作能正確執行。

驗證階段是非常重要的,但不是必須的,它對程式執行期沒有影響,如果所引用的類經過反覆驗證,那麼可以考慮採用-Xverifynone引數來關閉大部分的類驗證措施,以縮短虛擬機器類載入的時間。

– 準備:為類的靜態變數分配記憶體,並將其初始化為預設值

準備階段是正式為類變數分配記憶體並設定類變數初始值的階段,這些記憶體都將在方法區中分配。對於該階段有以下幾點需要注意:

1、這時候進行記憶體分配的僅包括類變數(static),而不包括例項變數,例項變數會在物件例項化時隨著物件一塊分配在Java堆中。

2、這裡所設定的初始值通常情況下是資料型別預設的零值(如0、0L、null、false等),而不是被在Java程式碼中被顯式地賦予的值。

假設一個類變數的定義為:public static int value = 3;

那麼變數value在準備階段過後的初始值為0,而不是3,因為這時候尚未開始執行任何Java方法,而把value賦值為3的putstatic指令是在程式編譯後,存放於類構造器()方法之中的,所以把value賦值為3的動作將在初始化階段才會執行。

複製程式碼
· 這裡還需要注意如下幾點:
· 對基本資料型別來說,對於類變數(static)和全域性變數,如果不顯式地對其賦值而直接使用,則系統會為其賦予預設的零值,而對於區域性變數來說,在使用前必須顯式地為其賦值,否則編譯時不通過。
· 對於同時被static和final修飾的常量,必須在宣告的時候就為其顯式地賦值,否則編譯時不通過;而只被final修飾的常量則既可以在宣告時顯式地為其賦值,也可以在類初始化時顯式地為其賦值,總之,在使用前必須為其顯式地賦值,系統不會為其賦予預設零值。
· 對於引用資料型別reference來說,如陣列引用、物件引用等,如果沒有對其進行顯式地賦值而直接使用,系統都會為其賦予預設的零值,即null。
· 如果在陣列初始化時沒有對陣列中的各元素賦值,那麼其中的元素將根據對應的資料型別而被賦予預設的零值。
複製程式碼
3、如果類欄位的欄位屬性表中存在ConstantValue屬性,即同時被final和static修飾,那麼在準備階段變數value就會被初始化為ConstValue屬性所指定的值。

假設上面的類變數value被定義為: public static final int value = 3;

編譯時Javac將會為value生成ConstantValue屬性,在準備階段虛擬機器就會根據ConstantValue的設定將value賦值為3。回憶上一篇博文中物件被動引用的第2個例子,便是這種情況。我們可以理解為static final常量在編譯期就將其結果放入了呼叫它的類的常量池中

– 解析:把類中的符號引用轉換為直接引用

解析階段是虛擬機器將常量池內的符號引用替換為直接引用的過程,解析動作主要針對類或介面、欄位、類方法、介面方法、方法型別、方法控制代碼和呼叫點限定符7類符號引用進行。符號引用就是一組符號來描述目標,可以是任何字面量。

直接引用就是直接指向目標的指標、相對偏移量或一個間接定位到目標的控制代碼。

•初始化
初始化,為類的靜態變數賦予正確的初始值,JVM負責對類進行初始化,主要對類變數進行初始化。在Java中對類變數進行初始值設定有兩種方式:

①宣告類變數是指定初始值

②使用靜態程式碼塊為類變數指定初始值

JVM初始化步驟

1、假如這個類還沒有被載入和連線,則程式先載入並連線該類

2、假如該類的直接父類還沒有被初始化,則先初始化其直接父類

3、假如類中有初始化語句,則系統依次執行這些初始化語句

類初始化時機:只有當對類的主動使用的時候才會導致類的初始化,類的主動使用包括以下六種:

– 建立類的例項,也就是new的方式

– 訪問某個類或介面的靜態變數,或者對該靜態變數賦值

– 呼叫類的靜態方法

– 反射(如Class.forName(“com.shengsiyuan.Test”))

– 初始化某個類的子類,則其父類也會被初始化

– Java虛擬機器啟動時被標明為啟動類的類(Java Test),直接使用java.exe命令來執行某個主類

結束生命週期

•在如下幾種情況下,Java虛擬機器將結束生命週期

– 執行了System.exit()方法

– 程式正常執行結束

– 程式在執行過程中遇到了異常或錯誤而異常終止

– 由於作業系統出現錯誤而導致Java虛擬機器程序終止

12、類載入器的層次關係

在這裡插入圖片描述

站在Java開發人員的角度來看,類載入器可以大致劃分為以下三類:
	啟動類載入器:Bootstrap ClassLoader,負責載入存放在JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下,或被-Xbootclasspath引數指定的路徑中的,並且能被虛擬機器識別的類庫(如rt.jar,所有的java.*開頭的類均被Bootstrap ClassLoader載入)。啟動類載入器是無法被Java程式直接引用的。
	擴充套件類載入器:Extension ClassLoader,該載入器由sun.misc.Launcher$ExtClassLoader實現,它負責載入DK\jre\lib\ext目錄中,或者由java.ext.dirs系統變數指定的路徑中的所有類庫(如javax.*開頭的類),開發者可以直接使用擴充套件類載入器。
	應用程式類載入器:Application ClassLoader,該類載入器由sun.misc.Launcher$AppClassLoader來實現,它負責載入使用者類路徑(ClassPath)所指定的類,開發者可以直接使用該類載入器,如果應用程式中沒有自定義過自己的類載入器,一般情況下這個就是程式中預設的類載入器。
	應用程式都是由這三種類載入器互相配合進行載入的,如果有必要,我們還可以加入自定義的類載入器。因為JVM自帶的ClassLoader只是懂得從本地檔案系統載入標準的java class檔案,因此如果編寫了自己的ClassLoader,便可以做到如下幾點:
		1)在執行非置信程式碼之前,自動驗證數字簽名。
		2)動態地建立符合使用者特定需要的定製化構建類。
		3)從特定的場所取得java class,例如資料庫中和網路中。

13、JVM類載入機制

	•全盤負責,當一個類載入器負責載入某個Class時,該Class所依賴的和引用的其他Class也將由該類載入器負責載入,除非顯示使用另外一個類載入器來載入
	•父類委託,先讓父類載入器試圖載入該類,只有在父類載入器無法載入該類時才嘗試從自己的類路徑中載入該類
	•快取機制,快取機制將會保證所有載入過的Class都會被快取,當程式中需要使用某個Class時,類載入器先從快取區尋找該Class,只有快取區不存在,系統才會讀取該類對應的二進位制資料,並將其轉換成Class物件,存入快取區。這就是為什麼修改了Class後,必須重啟JVM,程式的修改才會生效

14、雙親委派模型

	雙親委派模型的工作流程是:如果一個類載入器收到了類載入的請求,它首先不會自己去嘗試載入這個類,而是把請求委託給父載入器去完成,依次向上,因此,所有的類載入請求最終都應該被傳遞到頂層的啟動類載入器中,只有當父載入器在它的搜尋範圍中沒有找到所需的類時,即無法完成該載入,子載入器才會嘗試自己去載入該類。
	雙親委派機制:
		1、當AppClassLoader載入一個class時,它首先不會自己去嘗試載入這個類,而是把類載入請求委派給父類載入器ExtClassLoader去完成。
		2、當ExtClassLoader載入一個class時,它首先也不會自己去嘗試載入這個類,而是把類載入請求委派給BootStrapClassLoader去完成。
		3、如果BootStrapClassLoader載入失敗(例如在$JAVA_HOME/jre/lib裡未查詢到該class),會使用ExtClassLoader來嘗試載入;
		4、若ExtClassLoader也載入失敗,則會使用AppClassLoader來載入,如果AppClassLoader也載入失敗,則會報出異常ClassNotFoundException。

15、雙親委派模型意義:

-系統類防止記憶體中出現多份同樣的位元組碼
-保證Java程式安全穩定執行

16、ES和Sola的區別

在這裡插入圖片描述

10、手寫程式碼

//快速排序程式碼
public class QuickSort {  
    public static void main(String[] args) {  
        int[] a = {1, 2, 4, 5, 7, 4, 5 ,3 ,9 ,0};  
        System.out.println(Arrays.toString(a));  
        quickSort(a);  
        System.out.println(Arrays.toString(a));  
    }  
  
    public static void quickSort(int[] a) {  
        if(a.length>0) {  
            quickSort(a, 0 , a.length-1);  
        }  
    }  
  
    private static void quickSort(int[] a, int low, int high) {  
        //1,找到遞迴演算法的出口  
        if( low > high) {  
            return;  
        }  
        //2, 存  
        int i = low;  
        int j = high;  
        //3,key  
        int key = a[ low ];  
        //4,完成一趟排序  
        while( i< j) {  
            //4.1 ,從右往左找到第一個小於key的數  
            while(i<j && a[j] > key){  
                j--;  
            }  
            // 4.2 從左往右找到第一個大於key的數  
            while( i<j && a[i] <= key) {  
                i++;  
            }  
            //4.3 交換  
            if(i<j) {  
                int p = a[i];  
                a[i] = a[j];  
                a[j] = p;  
            }  
        }  
        // 4.4,調整key的位置  
        int p = a[i];  
        a[i] = a[low];  
        a[low] = p;  
        //5, 對key左邊的數快排  
        quickSort(a, low, i-1 );  
        //6, 對key右邊的數快排  
        quickSort(a, i+1, high);  
    }  
}  

//氣泡排序程式碼
public class Sort {
    public static void main(String[] args){
        int[] arr = {6,3,2,1,7};
        for(int i = 0;i<arr.length-1;i++){//外層迴圈n-1
            for(int j = 0;j<arr.length-i-1;j++){//內層迴圈n-i-1
                if(arr[j]>arr[j+1]){//從第一個開始,往後兩兩比較大小,如果前面的比後面的大,交換位置
                    int tmp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = tmp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }
}

//單例模式(餓漢)
public class Singleton {
   	private final static Singleton INSTANCE = new Singleton();
   	private Singleton(){}
   	public static Singleton getInstance(){
       	return INSTANCE;
   	}
}

//單例模式(懶漢)
public class Singleton {
	private static Singleton singleton;
	private Singleton() {}
	public static Singleton getInstance() {
       	if (singleton == null) {
           	singleton = new Singleton();
       	}
       	return singleton;
   	}
}

//工廠模式

相關推薦

整理試題以及部分答案2018

個人總結 個人瞎寫,僅供參考;部分轉載,逐漸完善。 Java基礎 Java 併發 Spring Netty 分散式相關 資料庫 快取 JVM 系統優化 手寫程式碼 Java基礎 1. List、Set、Map的區別 list和set是實現了colle

自己實戰整理試題--Mysql(帶答案不斷更新)

mysql目前用的版本? 5.1.21;目前最高5.7.* left join,right join,inner join? left join(左連線) 返回包括左表中的所有記錄和右表中連線欄位相等的記錄  right join(右連線) 返回包括右表中的所有記錄和左

自己實戰整理試題--鎖(帶答案不斷更新)

java有哪些鎖? Synchronized 和 ReentrantLock? 1、synchronized是重量級鎖? 從JDK 1.5 到 JDK 1.6 有一個高效併發方面的重要改進,HotSpot虛擬機器開發團隊在這個版本中花費了很大的精力去對Java中的鎖進行優化(synchr

自己實戰整理試題--JVM(帶答案不斷更新)

jvm記憶體模型,java記憶體模型,GC機制和原理; 物件是否可 GC? GC分哪兩種,Minor GC 和Full GC有什麼區別?什麼時候會觸發Full GC?分別採用什麼演算法? 垃圾回收演算法 垃圾回收器 G1 常見的JVM調優方法有哪些?可以具體到調整哪個引數,調成什麼值? JVM虛

自己實戰整理試題--集合(帶答案不斷更新)

Set 和 List 區別? ArrayList 和 LinkedList 區別? 如果存取相同的資料,ArrayList 和 LinkedList 誰佔用空間更大? List 和 Map 區別,Arraylist 與 LinkedList 區別,ArrayList 與 Vector 區別? S

自己實戰整理試題--Redis(帶答案不斷更新)

Redis應用場景? 分散式鎖:通過setnx/del命令來實現,不完美的是由於業務場景比較多,所以,有些瑕疵,比如:setnx/del命令是非原子性的,存在執行完setnx但是沒有執行del的情況,導致鎖無法釋放,針對這種情況Redis的團隊加入了一些引數特性,給鎖加上過期時間,我的理解時

自己實戰整理試題--Spring(帶答案不斷更新)

Spring 的原理? Spring的核心主要是IOC和AOP;從Spring簡單的來說,是通過對POJO開發的支援,來具體實現的;Spring通過為應用開發提供基於POJO的開發模式,把應用開發和複雜的Java EE服務,實現解耦,並通過提高單元測試的覆蓋率,從而有效的提

史上最全Java試題(帶全部答案你可能要收藏!)

原文地址:前幾天,有朋友去面試之前問我關於後端架構相關的問題,但奈於我去年更多的工作是在移動SDK開發上,對此有所遺忘,實屬無奈,後面準備總結下.今天要談的主題是關於求職.求職是在每個技術人員的生涯中都要經歷多次,對於我們大部分人而言,在進入自己心儀的公司之前少不了準備工作,

Java試題收集以及參考答案(100道)

str 單元 特殊的表 常見 文件的 邏輯判斷 浮點 類與對象 integer 不積跬步無以至千裏,這裏會不斷收集和更新Java基礎相關的面試題,目前已收集100題。 1.什麽是B/S架構?什麽是C/S架構 B/S(Browser/Server),瀏覽器/服務器程序

JAVA 試題(有部分答案

面試題(有答案):1. String類為什麼是final的。答:主要是為了“效率” 和 “安全性” 的緣故。若 String允許被繼承, 由於它的高度被使用率, 可能會降低程式的效能,所以String被定義成final。2. HashMap的原始碼,實現原理,底層結構。3. 說說你知道的幾個Java集合類:l

電子資訊、通訊、電類專業將會遇到的試題大全的部分答案

Setup/hold time是測試晶片對輸入訊號和時鐘訊號之間的時間要求。建立時間是指觸發器的時鐘訊號上升沿到來以前,資料穩定不變的時間。輸入訊號應提前時鐘上升沿(如上升沿有效)T時間到達晶片,這個T就是建立時間-Setup time.如不滿足setup time,這個資料就不能被這一時鐘打入觸發器,只有

自己實戰整理試題--Http網路相關(帶答案不斷更新)

*1.描述下網頁一個 Http 請求,到後端的整個請求過程: https://blog.csdn.net/w372426096/article/details/82012229 瀏覽器輸入https:www.koolearn.com這個URL,瀏覽器只知道名字是www.koolearn.

自己實戰整理試題--多執行緒(帶答案不斷更新)

一個執行緒兩次呼叫 start() 方法會出現什麼情況?執行緒的生命週期,狀態是如何轉移的? Java 的執行緒是不允許啟動兩次的,第二次呼叫必然會丟擲 IllegalThreadStateException,這是一種執行時異常,多次呼叫 start 被認為是程式設計錯誤。 關於執行緒生

自己實戰整理試題--java基礎(帶答案不斷更新)

Java基礎: 1、Object 類中的方法 registerNatives()   //私有方法 getClass()    //返回此 Object 的執行類。 hashCode()    //用於獲取物件的雜湊值。 equals(Object obj)     //

成為大數據頂尖程序員先過了這些Hadoop試題!(附答案解析)

大禮包 雲計 default blank mas 阻止 launcher inpu 建立 導讀:在大數據開發崗位的需求下,工資待遇水漲船高,不少編程人員在面對職業瓶頸期的時候,會選擇轉編程方向發展。你是否已經意識這是你人生中的一個重要轉機?能不能抓住這個時代的機遇,就在於你

去BAT面試完的Mysql試題總結(55道帶完整答案

1、一張表裡面有ID自增主鍵,當insert了17條記錄之後,刪除了第15,16,17條記錄,再把mysql重啟,再insert一條記錄,這條記錄的ID是18還是15 ? 2、mysql的技術特點是什麼? 3、Heap表是什麼? 4、mysql伺服器預設埠

騰訊公司試題中的部分值得探究值得學習

1、請定義一個巨集,比較兩個數a、b的大小,不能使用大於、小於、if語句  2、如何輸出原始檔的標題和目前執行行的行數  3、兩個數相乘,小數點後位數沒有限制,請寫一個高精度演算法  4、寫一個病毒  5、有A、B、C、D四個人,要在夜裡過一座橋。他們通過這座橋分別需要耗

試題:軟件測試如何測微信的朋友圈?

功能 此外 nal testing 測試 常用 tar pad 軟件 任何一個東西你都可以這麽測:記住sfdipot: s,structure,結構。考慮其組成部分,微信朋友圈的代碼組成,客戶端是怎麽樣的,服務端是怎麽樣的。 f,function,功能。考慮單個功

python的set和字典的詳細使用方法以及部分習題

作用域 整數 數組 set集合 b- 之前 and 每次 bytes 字符串 一個個字符組成的有序的序列,時字符的集合 使用單引,雙引,三引 引住的字符序列 字符時不可變的對象 bytes定義 bytes不可變字節序列 使用b前綴定義 只允許基本ASCII使用字符形式

hibernate相關試題(不看後悔一看必懂)

概述 hibernate框架應用在dao層,,hibernate的底層程式碼是jdbc,它是一個開源的輕量級的框架. hibernate通過orm思想對資料庫進行crud操作.orm中文翻譯過來就是物件關係對映,它讓實體類(就是通常所說的pojo)和資料庫表對應,讓實體類的欄位和表裡的欄