Android面試題(一)Java基礎篇

前言

“Android工程師面試爲什麼要講Java基礎?
答:Android基於Java語言開發,所以Java知識也是Android開發人員必不可少的知識。Java基礎不過關的話,Android開發也走不遠。Java的基礎以及一些常用的底層原理,肯定是要懂的。所以,面試時很多公司都會問到Java基礎及原理,一般來說基礎的準備是必要並且通用的,所以這部分真的很重要!”

1、Java基本數據類型

  1. 整形:byte(1個字節), short(2個字節) , int (4個字節), long(8個字節) 。

  2. 浮點型:float(4個字節),double(8個字節)。

  3. 浮點型:char(2個字節) 。

  4. 布爾型:boolean (1個字節)

2、String ,Stringbuffer ,Stringbulider的區別?

  1. String底層是一個final類型的字符數組,所以String的值是不可變的,每次對String的操作都會生成新的String對象,造成內存浪費。

  2. StringBuffer和StringBuilder都繼承AbstractStringBuilder抽象類,底層是可變的字符數組。

  3. StringBuffer是在jdk1.0出現的,是線程安全的,查看源碼有synchronized關鍵字修飾,但是執行速度慢 ,StringBuilder是jdk1.5出現的,是非線程安全的,執行速度快。

執行效率:在串行的的情況下,String 的效率最低(因爲底層是不可變數組,每次操作都是新建一個Sting對象),Stringbuffer因爲是同步,有syncchronzed稀釋,效率相對低,StringBulider效率最高。

3、final ,finally ,fianlize的區別?

  1. final是一個關鍵字,用來修飾類,變量,方法.修飾的類不能被繼承,但是可以繼承其他的類,修飾的方法不能被子類衝重寫,修飾的變量是常量,只能被賦值一次。

  2. finally是try-catch-finally語句的一個模塊,正常情況下里面的代碼永遠會被執行,一般用來釋放資源。

  3. finalize是Object類中的方法,當對象變成垃圾的時候,由GC來調用finalize()方法回收。

4、靜態變量和成員變量的不同?

  1. 所屬範圍不同。靜態變量是屬於類範圍的;成員變量是屬於對象範圍的。

  2. 存活時間不同。類的一生有着靜態變量的伴隨;而成員變量只能陪類走一程,對象產生的時候它就產生,而且它會隨着對象的消亡而消亡。

  3. 存儲位置不同。靜態變量時存儲在方法區裏的靜態區;成員變量存儲在堆棧內存區。

  4. 調用方式不同。靜態變量可以通過類名調用,也可以通過對象來調用;成員變量只能通過對象名調用。

5、集合框架,list,map,set都有哪些具體的實現類,區別都是什麼?

  1. List:有序、可重複;索引查詢速度快;插入、刪除伴隨數據移動,速度慢;

  2. Set:無序,不可重複;

  3. Map:鍵值對,鍵唯一,值多個;

  4. List、Set都是繼承自Collection接口,Map則不是;

6、ArrayList和LinkedList有什麼區別?

  1. ArrayList和LinkedList都實現了List接口

  2. ArrayList是基於索引的數據接口,它的底層是數組。它可以以O(1)時間複雜度對元素進行隨機訪問。與此對應,LinkedList是以元素鏈表的形式存儲它的數據,每一個元素都和它的前一個和後一個元素鏈接在一起,在這種情況下,查找某個元素的時間複雜度是O(n)

  3. 相對於ArrayList,LinkedList的插入,添加,刪除操作速度更快

  4. LinkedList比ArrayList更佔內存,因爲LinkedList爲每一個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素

7、HashMap和Hashtable有什麼區別?

  1. HashMap和Hashtable都實現了Map接口,因此很多特性非常相似

  2. HashMap允許鍵和值是null,而Hashtable不允許鍵或者值是null

  3. Hashtable是同步的,而HashMap不是。因此,HashMap更適合於單線程環境,而Hashtable適合於多線程環境

  4. HashMap提供了可供應用迭代的鍵的集合,因此,HashMap是快速失敗的。另一方面,Hashtable提供了對鍵的列舉(Enumeration)

8、Java四種應用類型

  1. 強引用:如果一個對象具有強引用,它就不會被垃圾回收器回收;

  2. 軟引用:只有在內存不足時,軟引用纔會被垃圾回收器回收;

  3. 弱引用:具有弱引用的對象擁有的生命週期更短暫;

  4. 虛引用:顧名思義,就是形同虛設。

9、線程安全集合類與非線程安全集合類

  1. LinkedList、ArrayList、HashSet是非線程安全的,Vector是線程安全的;

  2. HashMap是非線程安全的,HashTable是線程安全的;

  3. StringBuilder是非線程安全的,StringBuffer是線程安的。

10、HashMap在jdk1.7和1.8有什麼變化?

  1. jdk1.7,底層採用了數組+鏈表,頭插法;

    ● 負載因子:默認容量爲16,負載因子爲0.75,當超過16*0.75=12時,就要僅從hashmap的擴容,擴容涉及到rehash和複製數據等,會非常消耗性能。

    ● 真正存儲數據的是entry<key,value>[] table,entry是hashmap的一個靜態內部類,有key,value,next,hash(key的hashcode)成員變量。

  2. jdk1.8,採用是的底層+鏈表+紅黑樹;

    ● 增加一個閾值進行判斷是否將鏈表轉紅黑樹,HashEntry 修改爲 Node。解決hash衝突,造成鏈表越來越長,查詢慢的問題。

11、Java反射獲取字節碼有幾種方式?

  1. Class.forName(className)

  2. 類名.class

  3. this.getClass()

12、Java內存區域

運行時內存區域如圖所示:

  1. 程序計數器

    ① 當線程切換時,用來標記當前線程所執行的字節碼的行號指示器;

    ② 每一個線程都有一個獨立的程序計數器,用來線程切換時能恢復到正確的位置;

  2. JAVA虛擬機棧

    ① 線程私有,生命週期和線程一樣,用來存儲:局部變量表,操作數棧,動態鏈接,方法出口等;

    ② 局部變量表所佔用的內存在編譯器就完成分配,當進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小;

  3. 本地方法棧

    ① 爲虛擬機執行native方法;

  4. JAVA堆

    ① 存放對象實例;

    ② java堆是垃圾收集器管理的主要區域;

    ③被所有線程共享的一塊內存區域;

  5. 方法區

    ① 各個線程所共享的內存區域;

    ② 用於存儲class二進制文件,包含了虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據;

    ③ 線程安全的;

    ④ 運行時常量池存在方法區中;

常量池

① 常量是指被final修飾的變量,值一旦確定就無法改變;

② 常量池在編譯期間就將一部分數據存放於該區域,包含基本數據類型如int、long等以final聲明的常量值,和String字符串、特別注意的是對於方法運行期位於棧中的局部變量String常量的值可以通過 String.intern()方法將該值置入到常量池中。

③ 分類:

● Class文件常量池;
● 運行時常量池;
● 全局字符串常量池;
● 基本類型包裝類對象常量池;

13、Java-GC的原理和回收策略?

Java-GC機制內容較多,建議深入理解。

1.GC核心思想:對虛擬機可用內存空間,即堆空間中的對象進行識別,如果對象正在被引用,那麼稱其爲存活對象
,反之,如果對象不再被引用,則爲垃圾對象,可以回收其佔據的空間,用於再分配。垃圾收集算法的選擇和垃圾收集系統參數的合理調節直接影響着系統性能。

  1. GC roots 來源:

    ① 虛擬機棧中引用的對象。

    ② 方法區中類靜態屬性引用的對象。

    ③ 方法區中常量引用的對象。

    ④ 本地方法棧中JNI引用的對象

  2. 四種引用對象

    ① 強引用:不進行垃圾回收

    ② 軟引用:在內存溢出前進行垃圾回收

    ③ 弱引用:只要有垃圾回收就會回收掉

    ④ 虛引用:無用對象,垃圾收集時會接收到通知

  3. 方法區的垃圾回收

    ① 廢棄常量

    ② 無用的類

    ● 該類的所有實例都已經被回收,即Java堆中不存在該類的任何實例。

    ● 加載該類的ClassLoader已經被回收。

    ● 該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。

  4. 回收算法

① 複製算法:
複製算法將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另外一塊上面,然後再把已使用過的內存空間一次清理掉。這種算法適用於對象存活率低的場景,比如新生代。這樣使得每次都是對整個半區進行內存回收,內存分配時也就不用考慮內存碎片等複雜情況。

② 標記-清除:
標記-清除算法採用從根集合進行掃描,對存活的對象進行標記,標記完畢後,再掃描整個空間中未被標記的對象,進行回收。標記-清除算法不需要進行對象的移動,並且僅對不存活的對象進行處理,在存活對象比較多的情況下極爲高效,但由於標記-清除算法直接回收不存活的對象,因此會造成內存碎片。

③ 標記-整理:

標記-整理算法採用標記-清除算法一樣的方式進行對象的標記,但在清除時不同,在回收不存活的對象佔用的空間後,會將所有的存活對象往左端空閒空間移動,並更新對應的指針。標記-整理算法是在標記-清除算法的基礎上,又進行了對象的移動,因此成本更高,但是卻解決了內存碎片的問題。該垃圾回收算法適用於對象存活率高的場景(老年代)。

④ 分代收集算法:

● 不同的對象的生命週期(存活情況)是不一樣的,而不同生命週期的對象位於堆中不同的區域,因此對堆內存不同區域採用不同的策略進行回收可以提高 JVM 的執行效率。當代商用虛擬機使用的都是分代收集算法:新生代對象存活率低,就採用複製算法;老年代存活率高,就用標記清除算法或者標記整理算法。

● 新生代:

● 老年代:

  1. 垃圾收集器

    ① Serial收集器(複製算法):
    

    新生代單線程收集器,標記和清理都是單線程,優點是簡單高效;

    ② **ParNew收集器 (複製算法): **
    

    新生代收並行集器,實際上是Serial收集器的多線程版本,在多核CPU環境下有着比Serial更好的表現;

    ③ Parallel Scavenge收集器 (複製算法):
    

    新生代並行收集器,追求高吞吐量,高效利用 CPU。吞吐量 = 用戶線程時間/(用戶線程時間+GC線程時間),高吞吐量可以高效率的利用CPU時間,儘快完成程序的運算任務,適合後臺應用等對交互相應要求不高的場景

    ④ **Serial Old收集器 (標記-整理算法): **
    

    老年代單線程收集器,Serial收集器的老年代版本

    ⑤ CMS(Concurrent Mark Sweep)收集器(標記-清除算法):

    老年代並行收集器,以獲取最短回收停頓時間爲目標的收集器,具有高併發、低停頓的特點,追求最短GC回收停頓時間。

    ⑥ Parallel Old收集器 (標記-整理算法):
    

    老年代並行收集器,吞吐量優先,Parallel Scavenge收集器的老年代版本;

    ⑦ G1(Garbage First)收集器 (標記-整理算法):
    

    Java堆並行收集器,G1收集器是JDK1.7提供的一個新收集器,G1收集器基於“標記-整理”算法實現,也就是說不會產生內存碎片。此外,G1收集器不同於之前的收集器的一個重要特點是:G1回收的範圍是整個Java堆(包括新生代,老年代),而前六種收集器回收的範圍僅限於新生代或老年代。

14、Java虛擬機和Dalvik虛擬機區別?

  1. Java虛擬機:

① java虛擬機基於棧。基於棧的機器必須使用指令來載入和操作棧上數據,所需指令更多更多。

② java虛擬機運行的是java字節碼。(java類會被編譯成一個或多個字節碼.class文件)

  1. Dalvik虛擬機:

① dalvik虛擬機是基於寄存器的

② Dalvik運行的是自定義的.dex字節碼格式。(java類被編譯成.class文件後,會通過一個dx工具將所有的.class文件轉換成一個.dex文件,然後dalvik虛擬機會從其中讀取指令和數據

③ 常量池已被修改爲只使用32位的索引,以 簡化解釋器。

④ 一個應用,一個虛擬機實例,一個進程(所有android應用的線程都是對應一個linux線程,都運行在自己的沙盒中,不同的應用在不同的進程中運行。每個android dalvik應用程序都被賦予了一個獨立的linux PID(app_*))

15、Java類加載過程

  1. 通過一個類的全限定名來獲取定義此類的二進制字節流;

  2. 將這個字節流所代表的靜態存儲結構轉化爲方法區的運行時數據結構;

  3. 將類的class文件讀入內存,併爲之創建一個java.lang.Class對象;

  4. 當程序中使用任何類時,系統都會爲之建立一個java.lang.Class對象;

16、Synchronized三種應用方式?

  1. 修飾普通方法

一個對象中的加鎖方法只允許一個線程訪問。但要注意這種情況下鎖的是訪問該方法的實例對象, 如果多個線程不同對象訪問該方法,則無法保證同步。

  1. 修飾靜態方法

由於靜態方法是類方法, 所以這種情況下鎖的是包含這個方法的類,也就是類對象;這樣如果多個線程不同對象訪問該靜態方法,也是可以保證同步的。

  1. 修飾代碼塊

其中普通代碼塊 如Synchronized(obj) 這裏的obj 可以爲類中的一個屬性、也可以是當前的對象,它的同步效果和修飾普通方法一樣;Synchronized方法 (obj.class)靜態代碼塊它的同步效果和修飾靜態方法類似。

17、Java線程的創建方式有幾種?

  1. 繼承Thread類

創建方法

    ① 定義threade的子類,重寫run方法

    ② 創建Thread子類的實例,即創建了線程對象

    ③ 調用線程對象的start()方法來啓動該線程

優缺點

    優點:編寫簡單,如果需要訪問當前線程,無需使用Thread.currentThread()方法,直接使用this,即可獲得當前線程。

    缺點:因爲線程類已經繼承了Thread類,所以不能再繼承其他的父類。
  1. 實現Runnable接口

創建方法

    ① 定義runnable接口的實現類,並重寫該接口的run()方法

    ② 創建 Runnable實現類的實例,並依此實例作爲Thread的target來創建Thread對象,該Thread對象纔是真正的線程對象。

    ③ 調用線程對象的start()方法來啓動該線程

優缺點

    優點:線程類只是實現了Runable接口,還可以繼承其他的類。

    缺點:編程稍微複雜,如果需要訪問當前線程,必須使用Thread.currentThread()方法。
  1. 實現Callable接口

創建方式

    ① 創建Callable接口的實現類,並實現call()方法,該call()方法將作爲線程執行體,並且有返回值

    ② 創建Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。

    ③ 使用FutureTask對象作爲Thread對象的target創建並啓動新線程

    ④ 調用FutureTask對象的get()方法來獲得子線程執行結束後的返回值

Runnable和Callable的區別:

    ① Runnable實現是run方法,callable 重寫的是call(),並有返回值。

    ② call方法可以拋異常,run方法不行。

    ③ 運行Callable任務可以拿到一個Future對象,表示異步計算的結果.它提供了檢查計算是否完成的方法,以等待計算的完成,並檢索計算的結果。通過Future對象可以瞭解任務執行情況,可取消任務的執行,還可獲取執行結果。

18、Java線程生命週期

新建:

就是剛使用new方法,new出來的線程;

就緒:

就是調用的線程的start()方法後,這時候線程處於等待CPU分配資源階段,誰先搶的CPU資源,誰開始執行

運行:

當就緒的線程被調度並獲得CPU資源時,便進入運行狀態,run方法定義了線程的操作和功能;

阻塞:

在運行狀態的時候,可能因爲某些原因導致運行狀態的線程變成了阻塞狀態,比如sleep()、wait()之後線程就處於了阻塞狀態,這個時候需要其他機制將處於阻塞狀態的線程喚醒,比如調用notify或者notifyAll()方法。喚醒的線程不會立刻執行run方法,它們要再次等待CPU分配資源進入運行狀態;

死亡:

如果線程正常執行完畢後或線程被提前強制性的終止或出現異常導致結束,那麼線程就要被銷燬,釋放資源;

19、sleep()、wait()的區別?

  1. sleep()

線程類(Thread)的靜態方法,讓調用線程進入睡眠狀態,讓出執行機會給其他線程,等到休眠時間結束後,線程進入就緒狀態和其他線程一起競爭cpu的執行時間。因爲sleep() 是static靜態的方法,他不能改變對象的機鎖,當一個synchronized塊中調用了sleep()方法,線程雖然進入休眠,但是對象的機鎖沒有被釋放,其他線程依然無法訪問這個對象。

  1. wait()

Object類的方法,當一個線程執行到wait方法時,它就進入到一個和該對象相關的等待池,同時釋放對象的機鎖,使得其他線程能夠訪問,可以通過notify,notifyAll方法來喚醒等待的線程。

sleep() 和 wait() 的區別:

調用sleep方法的線程不會釋放對象鎖,而調用wait() 方法會釋放對象鎖 線程池

20、線程池

  1. 常見的線程池

① CacheThreadPool(),可緩存的線程池。

    ● 線程數無限制。

    ● 有空閒線程則複用空閒線程,若無空閒線程則新建線程。

    ● 一定程序減少頻繁創建/銷燬線程,減少系統開銷。

② FixedThreadPool(n)定長線程池

    ● 可控制線程最大併發數(同時執行的線程數)

    ● 超出的線程會在隊列中等待

③ SingleThreadExecutor()單線程化的線程池

    ● 有且僅有一個工作線程執行任務。

    ● 所有任務按照指定順序執行,即遵循隊列的入隊出隊規則。串行

④ 4.ScheduledThreadPool(int n)

    ● 創建一個定長線程池,支持定時及週期性任務執行
  1. 使用線程池的好處

    ① 重用,減少創建和銷燬所產生的性能開銷
    ② 避免過多的線程搶佔CPU而產生阻塞現象
    ③ 統一管理

附:Java思維導圖

在這裏插入圖片描述

更多精彩文章,請關注:

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章