阿里《JAVA實習生入職測試題—2019最新》之答案詳解(連載二)


力爭清晰完整准確(逐步完善,持續更新)


3、反射中,Class.forName和ClassLoader.loadClass的區別

更准確的說,是Class.forName("SomeClass")和ClassLoader.getSystemClassLoader().loadClass("SomeClass")的區別

Class.forName(“SomeClass”)= Class.forName(className,true,classloader)
ClassLoader.loadClass(className) = ClassLoader.loadClass(className,false)
  • 第一個會用調用這段代碼的class loader 來load class, 並且完成初始化(true——表示要初始化,即靜態初始化會被執行,包括靜態代碼塊,靜態變量)
  • 第二個會調用“system" class loader (這個會被覆蓋掉),不會被初始化(如果用它來load  JDBC driver,那么它不會被注冊,因此你無法使用JDBC)

類和接口的裝載及初始化,請參看 https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.2 之12.2和12.4。

詳情參看 https://stackoverflow.com/questions/8100376/class-forname-vs-classloader-loadclass-which-to-use-for-dynamic-loading

 

4、session和cookie的區別與聯系,session的生命周期,多個服務部署時session管理

這個得從HTTP協議是無狀態說起。http協議就是客服端向服務器發起一次請求,鏈接,然后傳數據,斷開鏈接。

  • 無狀態(stateless)

就是第一次客戶端A來了,在服務器要了些資源,干了些事,然后斷開鏈接,走了。沒有誰記錄這次鏈接的一些狀態,下一次客戶端A再來一次,服務器不認識它,不知道它來過,也不知道它上次干過啥,完全當作新的一次鏈接訪問來處理。就如同生活中有個陌生人來你家做客,吃飯打牌玩游戲,然后他回家了。下次這家伙又來,結果你有遺忘症,完全認不得這哥們。

  • 有狀態 (stateful)

就是第一次客戶端A來了,在服務器要了些資源,干了些事,然后斷開鏈接,走了。有東西記錄這次訪問的一些信息,比如操作記錄、用戶名密碼等。這次就是有個陌生人來你家做客,吃飯打牌玩游戲,然后他回家了。下次這家伙又來,主人是有正常記憶的人,對這哥們有印象。

 

session和cookie的作用就是記錄訪問用戶和服務器的交互狀態。它們讓http請求變成了能記錄狀態的會話。cookie存在客戶端,B/S里客戶端就是瀏覽器;session存在服務器。

cookie截圖如下:

 

  •  cookie

存放key/value鍵值對數據。當用戶通過HTTP訪問服務器,服務器會在reponse里將這些“鍵值對”信息給到客戶端瀏覽器,並對這些數據做一定的限制。在條件符合的時候,用戶再次訪問這個服務器,數據又被完整帶回服務器(在HTTP header里)。 Cookie設計的初衷是記錄一段時間內用戶訪問Web應用的行為路徑。

缺點:Cookie可能被盜和偽造等

  • session(會話)

session基於cookie工作的。同一客戶端和服務器交互時,不需要每次都回傳所有Cookie信息,只需要傳個唯一ID(客戶端第一次訪問服務器時服務器生成的唯一標識),服務端每次回傳這個唯一標識(sessionId),服務器就知道這個客戶端是誰了,來服務器端干過啥。減少了客服端和服務端每次交互傳輸的數據量,節省帶寬。在PV過億的情況,節省的帶寬就相當可觀了

缺點:Session不易在多台服務器間共享等

  • session生命周期
  • 多服務器部署的session管理

5、Java中的隊列都有哪些,有什么區別


6、詳談一下Java的內存模型以及GC算法

Java內存模型(Java Memory Model, JMM)

  • 為什么Java要抽象和實現這么一套東西?

簡單地先從硬件角度說,CPU和存儲設備速度相差幾個數量級,而CPU高速運算的數據源及運算的結構,不可避免要和存儲設備有讀寫交互,單靠寄存器搞不定。所以中間搞個高速緩存,解決CPU與內存的速度矛盾。這樣一來,解決了問題,又增加了新的問題,復雜性增加了(自不待言),緩存一致性的問題又來了(Cache Coherence)。

Java虛擬機的“內存模型”可以說是對特定的內存和高速緩存讀寫過程的抽象,屏蔽掉各種硬件和操作系統的內存訪問差異,確保Java程序在各種平台上運行效果一樣,不需要再改代碼(跨平台, Write once, run everywhere)。而諸如C++等語言,直接使用物理硬件和操作系統的內存模型,換個平台可能就會出錯,所以在某些場景必須針對不同的平台編寫不同的分支處理程序。

不說了,上圖

  • 運行時數據區域

這個和前面的划分不是同一層次角度,面試時有面試官,隨手寫段代碼,讓我區分里面的變量等分別是放在那個區域的。

1)程序計數器(Program Counter Register)

  • 據說可以理解為JAVA底層執行的字節碼的行號指示器,通過改變此的數值,來選取執行不同的字節碼指令,完成分支、循環、跳轉、異常處理、線程恢復等編程語言基本都具備的基礎功能
  • 不會拋出OutOfMemoryError

線程私有(有游戲主程問我哪些是線程私有的,這個問題在思考什么呢)

2)  Java虛擬機棧(Java Virtual Machine Stacks)

  • 生命周期 = 線程的生命周期,數據結構:stack
  • 棧幀(Stack Frame) ,存儲局部變量表、操作數棧、動態鏈接、方法出口等。和方法息息相關,方法的調用一直到執行完畢,對應着一個棧楨的入棧到出棧之全過程。
  • 局部變量表,存放8種基本數據類型(boolean、byte、char、short、integer、long、float、double)、對象引用、returnAddress(指向一條字節碼指令的地址)
  • 會拋出StackOverflowError及OutOfMemoryError

線程私有

3)  本地方法棧 (Native Method Stack)

  • 為Native方法服務(類似虛擬機棧)
  • 實現方式、數據結構、語言可靈活處理,如Sun HotSpot 虛擬機把本地方法棧和虛擬機棧合二為一
  • 會拋出StackOverflowError及OutOfMemoryError

4)堆(heap)

  • 存放幾乎所有對象
  • 細分為新生代和老年代(內存空間占比為1:2),新生代根據死亡概率,會有不同的回收算法(有游戲主程專門問到,老年代的回收算法有哪些?)

  • 會拋出OutOfMemoryError

線程共享

5)   方法區(Method Area)

  • 存儲被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等
  • 會拋出OutOfMemoryError

線程共享

6)運行時常量池(Runtime Constant Pool)

  •  存放編譯期生成的各種字面量和符號引用及直接引用(?)
  • 動態可變,即在運行期也可將新的常量放入池中,如通過String類的intern()方法
  • 會拋出OutOfMemoryError

 

  • GC算法

 

 推薦 https://blog.csdn.net/javazejian/article/details/72772461

7、JAVA10、JAVA11的新特性

JAVA10 新特性官方發布頁: https://www.oracle.com/technetwork/java/javase/10-relnote-issues-4108729.html

  • 新增 Optional.orElseThrow() 方法
  • 新增幾個Unmodifiable Collections API,如Collectors 類新增 toUnmodifiableListtoUnmodifiableSet, 和toUnmodifiableMap 方法
  • ...

JAVA11 新特性官方發布頁:  https://www.oracle.com/technetwork/java/javase/11-relnote-issues-5012449.html

卧槽,最近jdk更新有點快,都13了

8、Java內存泄漏的問題調查定位:jmap, jstack的使用
9、Spring的體系結構和jar用途
10、Spring MVC的運行原理 
11、Spring Boot的執行過程

spring boot 類加載過程和tomcat類加載過程的區別,也是面試常問

12、Spring IOC和AOP的底層實現
14、Spring boot 的優勢和劣勢,以及適用場景等
15、講一下Sping Cloud和Dubbo的優缺點
16、什么是Hystrix?它如何實現容錯?

hystrx 工作原理,詳見 https://segmentfault.com/a/1190000012439580


17、什么Netflix Feign?它的優點是什么?


18、談一談分布式一致性到CAP理論,BASE理論

  • CAP — C:Consistency(一致性),A:Avaliaility(可用性)和P:Partition tolerance(分區容錯性)

三者至多同時滿足其中的兩個,具體參見CAP猜想: https://people.eecs.berkeley.edu/~brewer/cs262b-2004/PODC-keynote.pdf  —— 由加州大學伯克利分校的Eric Brewer教授於2000年在ACM PODC(Principles of Distributed Computing)會議上提出

  • BASE — BA: Basic Available(基本可用),S: Soft state(軟狀態)和 E: Eventually consistent(最終一致性)

基於大規模互聯網分布式實踐經驗和CAP理論,權衡考慮CAP中的一致性和可用性,大家發現即使難以滿足強一致性(Strong consistency),但應用應該結合自身業務特點,實現在有限時間內達到最終一致性(Eventual consistency)


19、常用的線程池模式以及不同線程池的使用場景—面試出現頻率非常高

  • FixedThreadPool

  定長線程池

源碼如下:

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

 

  • CachedThreadPool

  可緩存的線程池

 

源碼如下:

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

 

  • SingleThreadExecutor

  單一線程池

  1)只會創建一個工作線程,即corePoolSize和maxiumPoolSize都被設置1,其它的同FixedThreadPool

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

  2)采用的阻塞隊列為LinkedBlockingQueue

  • ScheduledThreadPool

  可調度線程池  (處理延時或定時任務)

源碼如下:

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
  •  線程池如果滿了,會拋什么異常,拒絕策略包含哪些?(游戲主程的問題)

拋出 "RejectedExecutionException"異常,拒絕策略包括4種(implements RejectedExecutionHandler), 

  1. CallerRunsPolicy —— 讓新來的task在調用它的線程里自己跑
  2. AbortPolicy —— 不執行新來的task,直接拋出異常——默認
  3. DiscardPolicy —— 靜悄悄地丟棄掉被拒絕地task,不拋出異常通知一下哈
  4. DiscardOldestPolicy —— 丟棄掉最早地未處理地請求;如果線程池關閉,則丟棄掉新來的task

推薦: https://blog.csdn.net/z_s_z2016/article/details/81674893

20、ReentrantLock 和 synchronized的區別
21、AtomicInteger和volatile等線程安全操作的關鍵字的理解和使用

關於線程安全,我的理解就是一段代碼在單線程下跑完的結果在多線程下跑完一樣。

volatie作用於fields,語義表示內存可見性。也就是說,一旦最新對此值作了修改,所有的線程都知道;又可防止指令重排序,保證多線程下的線程安全。

AtomicInteger的存儲值的value被volatile修飾符限定,即保證了其線程安全。主要用來計數

源碼片段如下:

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

 AtomicInteger源碼比較簡單,主要的幾個接口,都是調用Unsafe類的 compareAndSwapInt()、getAndAddInt()、getAndSetInt()實現。

valueOffset,也就是內存偏移量。AtomicInteger的原子操作就是依靠內存偏移量來實現的。

  • 關於CAS(compareAndSwap)后面單獨寫篇博客吧,樂觀鎖的機制就是CAS
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

 

簡單說,就是更新之前,先讀取,看讀取的actual vulue 和原來的值(expect)是否是一致的。如果一致,就直接更新;否則更新expect,再次讀取。保證了在不加鎖的情況下,不會出問題,效率還高

  • Unsafe類讀寫速度很快,類似C的指針,可以操作系統內存資源、自主管理內存。沒有JVM的內存管理,可能會有內存泄漏等安全問題,所以JAVA官方不推薦使用

關於volatile詳情,請參見:

關於Unsafe類,請移步 https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html

關於
22、分布式鎖三種實現方式
23、socket框架netty的使用,以及NIO的實現原理,為什么是異步非阻塞
24、簡述NIO的最佳實踐
25、Zookeeper的用途,選舉的原理是什么
26、手寫一個哈夫曼樹

哈夫曼樹(Huffman Tree)又稱最優二叉樹

*****************************************************************************************************

精力有限,想法太多,專注做好一件事就行

  • 我只是一個程序猿。5年內把代碼寫好,技術博客字字推敲,堅持零拷貝和原創
  • 寫博客的意義在於鍛煉邏輯條理性,加深對知識的系統性理解,鍛煉文筆,如果恰好又對別人有點幫助,那真是一件令人開心的事

*****************************************************************************************************


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM