知乎上一篇文章
1)JAVA基礎
1.java基本數據類型有哪些,int, long佔幾個字節
四類八種基本數據類型:
數值型 | 整型(byte-1,short-2,int-4,long-8) 浮點型(float-4, double-8) |
字符型 | char |
布爾型 | boolean |
2.== 和 equals有什麼區別
==判斷基本數據類型是判斷值是否相等,判斷引用類型是判斷地址是否指向同一個對象
equals在Object類裏面和==一樣,重寫後可以判斷內容是否相同
3.hashcode 和 equals作用
equal()相等的兩個對象他們的hashCode()肯定相等,也就是用equal()對比是絕對可靠的。
hashCode()相等的兩個對象他們的equal()不一定相等,也就是hashCode()不是絕對可靠的。
所有對於需要大量並且快速的對比的話如果都用equal()去做顯然效率太低,所以解決方式是,
每當需要對比的時候,首先用hashCode()去對比,如果hashCode()不一樣,
則表示這兩個對象肯定不相等(也就是不必再用equal()去再對比了),
如果hashCode()相同,此時再對比他們的equal(),如果equal()也相同,
則表示這兩個對象是真的相同了,這樣既能大大提高了效率也保證了對比的絕對正確性!
4.new String創建了幾個對象
一個是字符串字面量"xyz"所對應的、駐留(intern)在一個全局共享的字符串常量池中的實例,
另一個是通過new String(String)創建並初始化的、內容與"xyz"相同的實例
String s = new String( "xyz ");
首先在string字符串常量池內找,找到?不創建string對象,否則創建, 這樣就一個string對象
遇到new運算符號了,在堆內存上創建string對象,並將其返回給其引用s,又一個對象
6.java的拆裝箱
爲了滿足Java面向對象的思想,針對基本數據類型設計了相對於的包裝類。基本數據類型轉換成包裝類是裝箱,反之是拆箱。
Java5.0之後引入了自動裝箱拆箱功能。
7.compareable 和 compartor的區別
* (01) "Comparable"
* 它是一個排序接口,只包含一個函數compareTo()。
* 一個類實現了Comparable接口,就意味着“該類本身支持排序”,它可以直接通過Arrays.sort() 或 Collections.sort()進行排序。
* (02) "Comparator"
* 它是一個比較器接口,包括兩個函數:compare() 和 equals()。
* 一個類實現了Comparator接口,那麼它就是一個“比較器”。其它的類,可以根據該比較器去排序。
*
* 綜上所述:Comparable是內部比較器,而Comparator是外部比較器。
* 一個類本身實現了Comparable比較器,就意味着它本身支持排序;若它本身沒實現Comparable,也可以通過外部比較器Comparator進行排序。
2)數據結構和算法
常見的數據結構就是:數組,棧,隊列,集合,映射,鏈表,堆,二分搜索樹,紅黑樹,AVL平衡樹等一些數據結構。
我們要做的就是了解它們的實現原理和各自的優缺點。
數據結構部分面試遇到最多的就是:
1.ArrayList和LinkedList的區別,優缺點
List集合:
ArrayList 基於動態數組,值可爲null 線程不安全 每次擴容0.5倍空間
LinkedList 雙向鏈表 增刪速度快,查詢速度慢 線程不安全
vector 線程安全,效率比ArrayList低 每次擴容2倍空間
ArrayList是實現了基於動態數組的數據結構,而LinkedList是基於鏈表的數據結構
Queue 隊列是一種特殊的線性表,它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作。
2.hashmap實現,擴容是怎麼做的,怎麼處理hash衝突,hashcode算法等
3.鏈表需要知道。LinkedHashMap一般再問LRU的時候會問到
4.二分搜索樹的特性和原理。前中後序遍歷寫出其中一種,當問到二分搜索樹的缺點的時候,你需要提出基於二分搜索樹的紅黑樹,說出他的特性。
5.堆的實現,最大堆,最小堆,優先隊列原理。
HashMap的本質 = 1個存儲Entry類對象的數組 + 多個單鏈表 https://blog.csdn.net/vking_wang/article/details/14166593
HashMap 線程不安全 可以使用Collection類靜態方法Map map = Collections.synchronizedMap(new HashMap());
獲取線程安全hashMap
key可爲空(只能有一個null key)值可爲null
初始容量16,擴容<<1 2n
計算hash值方法不同(擾動函數)
HashTable 線程安全
key value不能爲空(nullPointerException) key.hashCode();
初始容量11,擴容 2n+1
LinkedHashMap HashMap子類,記錄了插入順序,具有HashMap全部特性,遍歷速度比HashMap慢
TreeHashMap 保存的順序按照鍵排序,默認是升序排序,可以指定排序比較器。鍵值都不能爲空
ConcurrentHashMap 數據結構 多個segment -> HashEntry
算法
算法其實就是我們平時常見的一些排序:選擇排序,插入排序,冒泡排序,希爾排序,歸併排序,快速排序。以及和數據結構相關聯的解決部分問題的一些計算方法。
算法面試遇到的一些題:
1.手寫快速排序,插入排序,冒泡排序
2.翻轉一個數字
3.翻轉一個鏈表
4.O(n)複雜度找出數組中和是9的兩個數的索引
5.寫出二分搜索樹前中後序遍歷中的其中一個
6.實現一個隊列,並能記錄隊列中最大的數。
二叉樹最大深度:
//計算二叉樹的最大深度 BFS實現
public static int maxDepth(TreeNode root){
if(root == null){
return 0;
}
List<TreeNode> list = new ArrayList<>();
int count = 0;
list.add(root);
while (list.size()>0){
int size = list.size();
for(int i=size; i>0; i--){
TreeNode node = list.remove(0);
if(node.left != null){
list.add(node.left);
}
if(node.right != null){
list.add(node.right);
}
}
count ++;
}
return count;
}
//二叉樹最大深度,遞歸實現
public static int maxDepth1(TreeNode root){
if(root == null){
return 0;
}
if(root.left == null && root.right == null){
return 1;
}else {
int left = maxDepth1(root.left);
int right = maxDepth1(root.right);
return 1+Math.max(left,right);
}
}
public static void quickSort(int[] arr, int L, int R) {
int i = L;
int j = R;
int pivot = arr[(L + R) / 2];
while (i <= j) {
while (pivot > arr[i])
i++;
while (pivot < arr[j])
j--;
if (i <= j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
if (L < j)
quickSort(arr, L, j);
if (i < R)
quickSort(arr, i, R);
}
3)JVM虛擬機
JVM虛擬機我們需要知道他們內部組成:堆,虛擬機棧,本地方法棧,方法區,計數器。每一塊都存放什麼東西,以及垃圾回收的時候主要回收哪些塊的東西。GC-ROOT鏈是從哪些地方開始的,垃圾回收集算法(很少遇到問的)。
類加載ClassLoader已經雙親委派機制,類加載的過程,類加載的信息對應在JVM的哪些塊中。
(1)JVM的內存結構大概分爲:
堆(heap):線程共享,所有的對象實例以及數組都要在堆上分配。回收器主要管理的對象。
方法區(MEATHOD AREA):線程共享,存儲類信息、常量、靜態變量、即時編譯器編譯後的代碼。
虛擬機棧(JVM Stack):線程私有、存儲局部變量表、操作棧、動態鏈接、方法出口,對象指針。
本地方法棧(NATIVE METHOD STACK):線程私有。爲虛擬機使用到的Native 方法服務。如Java使用c或者c++編寫的接口服務時,代碼在此區運行。
程序計數器(PC Register):線程私有。指向下一條要執行的指令
(2)JVM垃圾回收算法
標記-清除算法 (效率低,產生內存碎片)
標記-整理算法 (效率低)
複製算法 (效率高,浪費內存空間)
現代虛擬機採用 分代回收算法
java堆分爲新生代和老年代。
將內存分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor 。當回收時,
將Eden和Survivor中還存活着的對象一次性地複製到另外一塊Survivor空間上,
最後清理掉Eden和剛纔用過的Survivor空間。當Survivor空間不夠用時,需要依賴其他內存(這裏指老年代)進行分配擔保
4)線程安全
當多個線程訪問一個對象的時候,如果不用考慮這些線程在運行時環境下的調度和交替執行,也不需要進行額外的同步,或者在調用方進行任何其他的協調操作,調用這個對象的行爲都可以獲取正確的結果,我們就認爲這個對象時線程安全的。
線程安全就是一些多線程下載,同步,鎖,死鎖,線程池。volatile關鍵字的特性,變量的原子性。以及java.util.concurrent包下的類,也需要了解一下。
一般會問的是手寫單例,以及雙重鎖式單例的優點。還有就是讓你自己實現一個多線程下載,看你怎麼設計。
java線程安全問題
(1).*非線程安全存在於'實例變量'中,如果是方法內部的'局部變量'則不存在非線程安全
(2)."鎖重入" 的概念是:自己可以再次獲取自己的內部鎖 (在一個synchronized方法內可以調用本地其他的synchronized方法); 當存在父子類繼承關係時,子類完全可以通過‘可重入鎖’調用父類的同步方法的。
(3).線程共享變量和私有變量 靜態成員變量在同一個對象或者不同對象都是共享變量,非靜態成員變量在同一個對象間共享,在不同對象間不共享
(4). synchronized關鍵字加到static方法上是給class類加鎖,而synchronized關鍵字加到非static方法上是給對象加鎖。 對象鎖只對同一個對象有用,而class鎖可以對類的所有對象實例起作用。
三大性質:單線程下操作可以認爲都是原子操作,多線程則不一定。
(1) 原子性:java中原子操作包括除long和double之外的基本類型的賦值操作
(2) 可見性:當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值,java提供volatile,synchronized和lock關鍵字來保證可見性。
(3) 有序性:程序執行的順序按照代碼的先後順序執行。(爲了效率java允許編譯器和處理器對指令進行重排序,對單線程不會有影響,但是多線程下會有影響)java提供volatile,synchronized和lock關鍵字來保證有序性。
Thread線程相關:
https://www.jianshu.com/p/fa6667dfb4ca
(1) sleep 和 wait區別
由於CPU分配的每個線程的時間片極爲短暫(一般爲幾十毫秒),所以,CPU通過不停地切換線程執行,這樣就給程序員一種錯覺,以爲多個線程是在同時執行。sleep就是正在執行的線程主動讓出CPU,CPU去執行其他線程,在sleep指定的時間過後,CPU纔會回到這個線程上繼續往下執行,如果當前線程進入了同步鎖,sleep方法並不會釋放鎖,即使當前線程使用sleep方法讓出了CPU,但其他被同步鎖擋住了的線程也無法得到執行。
(2) sleep方法只讓出了CPU,並不會釋放同步資源鎖
wait方法則讓當前線程退讓出同步資源鎖,以便其他等待該資源線程可以得到執行。只有調用了notify()方法,之前調用wait()的線程纔會解除wait狀態,可以去參與競爭同步資源鎖,進而得到執行。notify()會喚醒wait狀態的線程。
(3)sleep可以在任何地方執行,wait只能在同步代碼塊中執行
(4)join讓指定的線程先執行完再執行其他的線程,而且會阻塞當前線程。thread.join();
(5) yield讓指定線程讓出當前cpu資源,從運行狀態轉爲就緒狀態,等待CPU調度。
(6) volatile 保證可見性,禁止指令重排。
ReentrantReadWriteLock(讀寫鎖)。讀寫所允許同一時刻被多個讀線程訪問,但是在寫線程訪問時,所有的讀線程和其他的寫線程都會被阻塞
ReentrantLock 可重入、可中斷、可限時、公平鎖
停止線程
(1)非阻塞線程,設置標誌位,while循環時判斷是否可以繼續進行
(2)阻塞線程(線程中sleep),interrupt(),方法打斷線程執行。
異常情況:線程休眠時,使用interrupt()方法來拋出InterruptedException異常,在catch中break出循環
在正常狀態下調用interrupt方法使用isInterrupted()作爲標誌位來退出,在阻塞狀態下通過捕獲異常來退出。因此使用interrupt()來退出線程的最好的方式應該是兩種情況都要考慮:
(3)使用stop強行退出,會出現異常情況,不建議使用。(有可能數據不一致)
線程池
優點 (1)重用線程池中的線程,避免頻繁創建和銷燬所帶來的性能損耗
(2)有效控制線程的最大併發數量,避免線程過大搶佔資源帶來的系統阻塞
(3)可以對線程進行一定的管理
線程池運行規則:
(1)如果線程池中的線程數未達到核心線程數,則會立馬啓用一個核心線程去執行。
(2)如果線程池中的線程數已經達到核心線程數,且workQueue未滿,則將新線程放入workQueue中等待執行
(3)如果線程池中的線程數已經達到核心線程數但未超過非核心線程數,且workQueue已滿,則開啓一個非核心線程來執行任務。
(4)excute一個線程之後,如果線程池中的線程數已經超過非核心線程數,則拒絕執行該任務
分類:(1) FixedThreadPool (任務列隊長度Integer.MAX_VALUE)
FixedThreadPool是一個核心線程數量固定的線程池,當所有的核心線程都在執行任務的時候,新的任務只能進入線程隊列中進行等待,直到有線程被空閒出來
(2) SingleThreadExecutor
SingleThreadExecutor的核心線程數只有1,任務列隊長度 Integer.MAX_VALUE
(3) CacheThreadPool
沒有核心線程,每當我們添加一個新任務進來的時候,如果線程池中有空閒的線程,則由該空閒的線程執行新任務,如果沒有空閒線程,則創建新線程來執行任務
超時時間60秒 (適合執行大量耗時較少的操作,SynchronousQueue任務隊列其實相當於一個空集合,這將導致任何任務都會被立即執行)
(4)ScheduleThreadPool
定時定期執行任務功能的線程池,核心線程固定(構造參數傳入),非核心線程無限制,非核心線程閒置會被馬上回收
如果調用了shutDown方法,則線程池處於shutDown狀態,此時不能接受新的任務,它會等待所有任務執行完畢。
如果調用shutDownNow方法,線程池處於stop狀態,此時不能接受新的任務,並且嘗試去終止正在執行的任務。
當線程池處於shutDown或者stop狀態,並且所有工作線程已經銷燬,任務列隊已被清空。此時線程池處於Terminaled狀態。
AsyncTask:內部封裝了線程池和handler的框架
onPreExecute() 主線程 初始化準備工作
doInBackgroud() 子線程,耗時操作
onProgressUpdate() 主線程,用於更新進度
onPostExecute() 主線程,任務執行完畢
5)編程思想
封裝,繼承,多態,抽象,反射,註解,設計模式,設計模式的原則。
面試中一般會問下:
1.抽象和接口有什麼不一樣
2.工作中常用的設計模式,一些源碼中的設計模式
3.具體給你一個設計模式讓你說說你對他的瞭解,比如觀察者,工廠。
淺談常用設計模式
以上這些東西主要考察你的代碼設計能力。
6)網絡協議
1.互聯網的實現主要分爲幾層,http、ftp、tcp、ip分別位於哪一層。
2.http和https的區別
3.爲什麼tcp要經過三次握手,四次揮手
4.socket瞭解過嗎
一般http和https問的比較多,對稱加密和非對稱加密也會問。tcp和socket偶爾遇見問的。
三 Android相關
Android部分我就不分幾大塊了。直接列舉,但是列舉到的每一項都是面試經常會問到並且會延伸問的,所以需要深入的去研究。
1.四大組件有哪些,說出你對他們在Android系統中的作用和理解。
2.Activity生命週期,A啓動B兩個頁面生命週期怎麼運行的,爲什麼會 這樣,生命週期爲什麼這麼設計,你有了解過嗎。
3.四種啓動模式,內部堆棧是怎麼回事,你工作中怎麼使用的。
4.Activity的啓動過程,這個我強烈建議每個Android開發人員都要清楚的知道,並且跟一下源碼,幾個核心類的作用。你會對Android有一個更好的認識。
5.事件分發流程,怎麼處理滑動衝突。舉例:長按ListView的一個Item它變灰了。這個時候在滑動。item恢復原來的樣子,這個時候他們內部的事件傳遞是什麼樣子。有很多種問法,所以你一定要搞清楚。
6.自定義View,View的繪製流程。onMeasure,onLayout,onDraw都是什麼作用。
ViewGroup是怎麼分發繪製的。onDraw裏面怎麼去做繪製,Canvas,Path,Paint你都需要了解。
並且配合ValueAnimtor或者Scroller去實現動畫。有時候面試的會突發奇想問你ViewGroup是樹形結構,我想知道樹的深度,你怎麼計算,突然就變成了一個數據結構和算法的題。
自定義view生命週期
->構造方法
->onFinishInflate(從xml加載完成後調用)
->onAttachedToWindow (將view綁定到actvity所在的window,隨後開始進行所有view的繪製) 在宿主activity的onResume之後調用。
->onMeasure
->onLayout
->onDraw
->onWindowFocusChanged (判斷view是否獲得焦點,可以判斷view進出前後臺)
->onDetachedFromWindow (activity銷燬後,view會從activity抽離,此時view銷燬)
7.Bitmap和Drawable
8.Animation和Animator
9.LinearLayout、RelativeLayout、FrameLayout三種常用佈局的特性,他在佈局的時候是怎麼計算的。效率如何。CoordinatorLayout配合AppbarLayout的使用,以及自定義Behavior。ConstraintLayout的使用。用來減少層級。
10.Handler消息機制,推薦看一下Looper的源碼
11.進程間通信,Binder機制
12.AsyncTask源碼看一下。
13.圖片的壓縮處理,三級緩存,Lru算法
14.分辨率和屏幕密度,以及計算一個圖片大小。mdpi,hdpi的關係和比例。
15.優化,內存優化,佈局優化,啓動優化,性能優化。內存泄露,內存溢出。怎麼優化,用了什麼工具,具體怎麼做的。
LeakCanary內存泄漏檢查工具
16.listView和RecycleView對比,以及緩存策略。
17.JNI(很少問)
18.MVC,MVP,MVVM
19.開源框架Okhttp,Glide,EventBus,Rxjava等,以及JetPack下的開源庫,要會用,還說說出一些東西,推薦 Retrofit,Okhttp,Glide,EventBus這些看一下源碼。
20.RecyclerView四大塊,能實現什麼效果,大致怎麼實現的,心裏要有數
21.DecorView,Window,WindowManager,PhoneWindow關係,以及個子的職責。
加分項:Kotlin,Gradle,Flutter,組件化,插件化,熱修復。
熱修復原理
我們知道Java虛擬機 —— JVM 是加載類的class文件的,而Android虛擬機——Dalvik/ART VM 是加載類的dex文件,
而他們加載類的時候都需要ClassLoader,ClassLoader有一個子類BaseDexClassLoader,而BaseDexClassLoader下有一個
數組——DexPathList,是用來存放dex文件,當BaseDexClassLoader通過調用findClass方法時,實際上就是遍歷數組,
找到相應的dex文件,找到,則直接將它return。而熱修復的解決方法就是將新的dex添加到該集合中,並且是在舊的dex的前面,
所以就會優先被取出來並且return返回。
webView相關
(1) webView生命週期:
onResume -> webView活躍狀態時回調,可以正常執行網頁的響應
onPause -> webView被切換到後臺使用,頁面失去焦點,變成不可見狀態。通知內核暫停所有動作,DOM解析,js執行...
pauseTimers() ->應用程序切換到後臺時調用,針對全應用。暫停所有webView的layout,javaScriptTimer,降低CPU功耗。
resumeTimers() -> 回覆pauseTimers時的動作
destroy() ->關閉activity時回調,webView destroy時仍綁定在activity上,需要先移除,再銷燬webView.
(2) webViewClient幫助webView處理各種通知請求時間
shouldOverrideUrlLoading作用: 打開網頁時不調用系統瀏覽器,而是在本webView中顯示,在網頁上所有加載都經過這個方法
除了你主動用webview加載的url,其他的加載都會回調shouldOverrideLoading。後面還有一句註釋:post請求不會回調shouldOverrideLoading
返回true和false的區別:返回ture 網頁會被加載到webView的歷史記錄中,加載後按返回鍵會回到這個網頁。返回false不會加載到webView歷史記錄中,返回也不會回到加載的網頁
webChromeClient 輔助webView處理javaScript的對話框,網站標題,進度等。
onProgressChanged() 獲取進度
onReceivedTitle() 獲取web頁中的標題
UTF-8中文三個字節 英文一個字節