網(wǎng)上有很多關(guān)于摩托車論壇pos機(jī),攢了一個(gè)月的安卓面試題的知識,也有很多人為大家解答關(guān)于摩托車論壇pos機(jī)的問題,今天pos機(jī)之家(m.afbey.com)為大家整理了關(guān)于這方面的知識,讓我們一起來看下吧!
本文目錄一覽:
摩托車論壇pos機(jī)
一個(gè)月前呢,為了鞏固下自己的基礎(chǔ)以及為以后的面試做準(zhǔn)備,每天去找一些大廠的面試真題,然后解答下,然后自己確實(shí)也在這個(gè)過程中能復(fù)習(xí)到不少以前沒有重視的問題,今天就總結(jié)下之前一個(gè)多月總結(jié)的面試題,難度不大,大佬可以直接路過,當(dāng)然發(fā)發(fā)善心點(diǎn)個(gè)贊也是可以的??。
進(jìn)入正題,下面為主要內(nèi)容,每三個(gè)問題為一個(gè)小節(jié),也就是一個(gè)專題文章,我就不具體區(qū)分了,由于字?jǐn)?shù)問題,也只節(jié)選了一些問題,大家見諒。另外答的不好的地方大家也可以留言敲敲我,感謝。
網(wǎng)頁中輸入url,到渲染整個(gè)界面的整個(gè)過程,以及中間用了什么協(xié)議?1)過程分析:主要分為三步
DNS解析。用戶輸入url后,需要通過DNS解析找到域名對應(yīng)的ip地址,有了ip地址才能找到服務(wù)器端。首先會(huì)查找瀏覽器緩存,是否有對應(yīng)的dns記錄。再繼續(xù)按照操作系統(tǒng)緩存—路由緩存—isp的dns服務(wù)器—根服務(wù)器的順序進(jìn)行DNS解析,直到找到對應(yīng)的ip地址。客戶端(瀏覽器)和服務(wù)器交互。瀏覽器根據(jù)解析到的ip地址和端口號發(fā)起HTTP請求,請求到達(dá)傳輸層,這里也就是TCP層,開始三次握手建立連接。服務(wù)器收到請求后,發(fā)送相應(yīng)報(bào)文給客戶端(瀏覽器),客戶端收到相應(yīng)報(bào)文并進(jìn)行解析,得到html頁面數(shù)據(jù),包括html,js,css等??蛻舳耍g覽器)解析html數(shù)據(jù),構(gòu)建DOM樹,再構(gòu)造呈現(xiàn)樹(render樹),最終繪制到瀏覽器頁面上。2)其中涉及到TCP/IP協(xié)議簇,包括DNS,TCP,IP,HTTP協(xié)議等等。
具體介紹下TCP/IPTCP/IP一般指的是TCP/IP協(xié)議簇,主要包括了多個(gè)不同網(wǎng)絡(luò)間實(shí)現(xiàn)信息傳輸涉及到的各種協(xié)議 主要包括以下幾層:
應(yīng)用層:主要提供數(shù)據(jù)和服務(wù)。比如HTTP,F(xiàn)TP,DNS等傳輸層:負(fù)責(zé)數(shù)據(jù)的組裝,分塊。比如TCP,UDP等網(wǎng)絡(luò)層:負(fù)責(zé)告訴通信的目的地,比如IP等數(shù)據(jù)鏈路層:負(fù)責(zé)連接網(wǎng)絡(luò)的硬件部分,比如以太網(wǎng),WIFI等TCP的三次握手和四次揮手,為什么不是兩次握手?為什么揮手多一次呢?客戶端簡稱A,服務(wù)器端簡稱B
1)TCP建立連接需要三次握手
A向B表示想跟B進(jìn)行連接(A發(fā)送syn包,A進(jìn)入SYN_SENT狀態(tài))B收到消息,表示我也準(zhǔn)備好和你連接了(B收到syn包,需要確認(rèn)syn包,并且自己也發(fā)送一個(gè)syn包,即發(fā)送了syn+ack包,B進(jìn)入SYN_RECV狀態(tài))A收到消息,并告訴B表示我收到你也準(zhǔn)備連接的信號了(A收到syn+ack包,向服務(wù)器發(fā)送確認(rèn)包ack,AB進(jìn)入established狀態(tài))開始連接。2)TCP斷開連接需要四次揮手
A向B表示想跟B斷開連接(A發(fā)送fin,進(jìn)入FIN_WAIT_1狀態(tài))B收到消息,但是B消息沒發(fā)送完,只能告訴A我收到你的斷開連接消息(B收到fin,發(fā)送ack,進(jìn)入CLOSE_WAIT狀態(tài))過一會(huì),B數(shù)據(jù)發(fā)送完畢,告訴A,我可以跟你斷開了(B發(fā)送fin,進(jìn)入LAST_ACK狀態(tài))A收到消息,告訴B,可以他斷開(A收到fin,發(fā)送ack,B進(jìn)入closed狀態(tài))3)為什么揮手多一次 其實(shí)正常的斷開和連接都是需要四次:
A發(fā)消息給BB反饋給A表示正確收到消息B發(fā)送消息給AA反饋給B表示正確收到消息。但是連接中,第二步和第三步是可以合并的,因?yàn)檫B接之前A和B是無聯(lián)系的,所以沒有其他情況需要處理。而斷開的話,因?yàn)橹皟啥耸钦_B接狀態(tài),所以第二步的時(shí)候不能保證B之前的消息已經(jīng)發(fā)送完畢,所以不能馬上告訴A要斷開的消息。這就是連接為什么可以少一步的原因。
4)為什么連接需要三次,而不是兩次。正常來說,我給你發(fā)消息,你告訴我能收到,不就代表我們之前通信是正常的嗎?
簡單回答就是,TCP是雙向通信協(xié)議,如果兩次握手,不能保證B發(fā)給A的消息正確到達(dá)。TCP 協(xié)議為了實(shí)現(xiàn)可靠傳輸, 通信雙方需要判斷自己已經(jīng)發(fā)送的數(shù)據(jù)包是否都被接收方收到, 如果沒收到, 就需要重發(fā)。
TCP是怎么保證可靠傳輸?shù)模?/p>序列號和確認(rèn)號。比如連接的一方發(fā)送一段80byte數(shù)據(jù),會(huì)帶上一個(gè)序列號,比如101。接收方收到數(shù)據(jù),回復(fù)確認(rèn)號181(180+1),這樣下一次發(fā)送消息就會(huì)從181開始發(fā)送了。
所以握手過程中,比如A發(fā)送syn信號給B,初始序列號為120,那么B收到消息,回復(fù)ack消息,序列號為120+1。同時(shí)B發(fā)送syn信號給A,初始序列號為256,如果收不到A的回復(fù)消息,就會(huì)重發(fā),否則丟失這個(gè)序列號,就無法正常完成后面的通信了。
這就是三次握手的原因。
TCP和UDP的區(qū)別?TCP提供的是面向連接,可靠的字節(jié)流服務(wù)。即客戶和服務(wù)器交換數(shù)據(jù)前,必須現(xiàn)在雙方之間建立一個(gè)TCP連接(三次握手),之后才能傳輸數(shù)據(jù)。并且提供超時(shí)重發(fā),丟棄重復(fù)數(shù)據(jù),檢驗(yàn)數(shù)據(jù),流量控制等功能,保證數(shù)據(jù)能從一端傳到另一端。
UDP 是一個(gè)簡單的面向數(shù)據(jù)報(bào)的運(yùn)輸層協(xié)議。它不提供可靠性,只是把應(yīng)用程序傳給IP層的數(shù)據(jù)報(bào)發(fā)送出去,但是不能保證它們能到達(dá)目的地。由于UDP在傳輸數(shù)據(jù)報(bào)前不用再客戶和服務(wù)器之間建立一個(gè)連接,且沒有超時(shí)重發(fā)等機(jī)制,所以傳輸速度很快。
所以總結(jié)下來就是:
TCP 是面向連接的,UDP 是面向無連接的TCP數(shù)據(jù)報(bào)頭包括序列號,確認(rèn)號,等等。相比之下UDP程序結(jié)構(gòu)較簡單。TCP 是面向字節(jié)流的,UDP 是基于數(shù)據(jù)報(bào)的TCP 保證數(shù)據(jù)正確性,UDP 可能丟包TCP 保證數(shù)據(jù)順序,UDP 不保證可以看到TCP適用于穩(wěn)定的應(yīng)用場景,他會(huì)保證數(shù)據(jù)的正確性和順序,所以一般的瀏覽網(wǎng)頁,接口訪問都使用的是TCP傳輸,所以才會(huì)有三次握手保證連接的穩(wěn)定性。而UDP是一種結(jié)構(gòu)簡單的協(xié)議,不會(huì)考慮丟包啊,建立連接等。優(yōu)點(diǎn)在于數(shù)據(jù)傳輸很快,所以適用于直播,游戲等場景。
HTTP的幾種請求方法具體介紹常見的有四種:
GET 獲取資源,沒有body,冪等性POST 增加或者修改資源,有bodyPUT 修改資源,有body,冪等性DELETE 刪除資源,冪等性HTTP請求和響應(yīng)報(bào)文的格式,以及常用狀態(tài)碼。1)請求報(bào)文:
//請求行(包括method、path、HTTP版本) GET /s HTTP/1.1 //Headers Host: www.baidu.com Content-Type: text/plain //Body 搜索****
2)響應(yīng)報(bào)文
//狀態(tài)行 (包括HTTP版本、狀態(tài)碼,狀態(tài)信息) HTTP/1.1 200 OK //Headers Content-Type: application/json; charset=utf-8 //Body [{"info":"xixi"}]
3)常用狀態(tài)碼
主要分為五種類型:
1開頭, 代表臨時(shí)性消息,比如100(繼續(xù)發(fā)送)2開頭, 代表請求成功,比如200(OK)3開頭, 代表重定向,比如304(內(nèi)容無改變)4開頭, 代表客戶端的一些錯(cuò)誤,比如403(禁止訪問)5開頭, 代表服務(wù)器的一些錯(cuò)誤,比如500介紹對稱加密和非對稱加密1)對稱加密,即加密和解密算法不同,但是密鑰相同。比如DES,AES算法。
數(shù)據(jù)A --> 算法D(密鑰S)--> 加密數(shù)據(jù)B加密數(shù)據(jù)B --> 算法E(密鑰S)--> 數(shù)據(jù)A
優(yōu)點(diǎn):缺點(diǎn):密鑰有可能被破解,容易被偽造。傳輸過程中一旦密鑰被其他人獲知?jiǎng)t可以進(jìn)行數(shù)據(jù)解密。
2)非對稱加密,即加密和解密算法相同,但是密鑰不同。私鑰自己保存,公鑰提供給對方。比如RSA,DSA算法。
數(shù)據(jù)A --> 算法D(公鑰)--> 加密數(shù)據(jù)B加密數(shù)據(jù)B --> 算法D(私鑰)--> 數(shù)據(jù)A
優(yōu)點(diǎn):安全,公鑰即使被其他人獲知,也無法解密數(shù)據(jù)。缺點(diǎn):需要通信雙方都有一套公鑰和私鑰
數(shù)字簽名的原理1)首先,為什么需要數(shù)字簽名?防止被攻擊,被偽造。由于公鑰是公開的,別人截獲到公鑰就能偽造數(shù)據(jù)進(jìn)行傳輸,所以我們需要驗(yàn)證數(shù)據(jù)的來源。
2)怎么簽名?由于公鑰能解密 私鑰加密的數(shù)據(jù),所以私鑰也能解密 公鑰加密的數(shù)據(jù)。(上圖非對稱加密A和B代號互換即可) 所以我們用公鑰進(jìn)行加密后,再用私鑰進(jìn)行一次加密,那么私鑰的這次加密就叫簽名,也就是只有我自己可以進(jìn)行加密的操作。所以傳輸數(shù)據(jù)流程就變成了加密數(shù)據(jù)和簽名數(shù)據(jù),如果解出來都是同樣的數(shù)據(jù),那么則數(shù)據(jù)安全可靠。
數(shù)據(jù)A --> 算法D(公鑰)--> 加密數(shù)據(jù)B數(shù)據(jù)A --> 算法D(私鑰)--> 簽名數(shù)據(jù)C加密數(shù)據(jù)B --> 算法D(私鑰)--> 數(shù)據(jù)A簽名數(shù)據(jù)C --> 算法D(公鑰)--> 數(shù)據(jù)ABase64算法是什么,是加密算法嗎?Base64是一種將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成64種字符組成的字符串的編碼算法,主要用于非文本數(shù)據(jù)的傳輸,比如圖片。可以將圖片這種二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成具體的字符串,進(jìn)行保存和傳輸。嚴(yán)格來說,不算。雖然它確實(shí)把一段二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成另外一段數(shù)據(jù),但是他的加密和解密是公開的,也就無秘密可言了。所以我更傾向于認(rèn)為它是一種編碼,每個(gè)人都可以用base64對二進(jìn)制數(shù)據(jù)進(jìn)行編碼和解碼。面試加分項(xiàng):為了減少混淆,方便復(fù)制,減少數(shù)據(jù)長度,就衍生出一種base58編碼。去掉了base64中一些容易混淆的數(shù)字和字母(數(shù)字0,字母O,字母I,數(shù)字1,符號+,符號/) 大名鼎鼎的比特幣就是用的改進(jìn)后的base58編碼,即Base58Check編碼方式,有了校驗(yàn)機(jī)制,加入了hash值。為什么多線程同時(shí)訪問(讀寫)同個(gè)變量,會(huì)有并發(fā)問題?java 內(nèi)存模型規(guī)定了所有的變量都存儲在主內(nèi)存中,每條線程有自己的工作內(nèi)存。線程的工作內(nèi)存中保存了該線程中用到的變量的主內(nèi)存副本拷貝,線程對變量的所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫主內(nèi)存。線程訪問一個(gè)變量,首先將變量從主內(nèi)存拷貝到工作內(nèi)存,對變量的寫操作,不會(huì)馬上同步到主內(nèi)存。不同的線程之間也無法直接訪問對方工作內(nèi)存中的變量,線程間變量的傳遞均需要自己的工作內(nèi)存和主存之間進(jìn)行數(shù)據(jù)同步。說說原子性,可見性,有序性分別是什么意思?原子性:在一個(gè)操作中,CPU 不可以在中途暫停然后再調(diào)度,即不被中斷操作,要么執(zhí)行完成,要么就不執(zhí)行。可見性:多個(gè)線程訪問同一個(gè)變量時(shí),一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值。有序性:程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。實(shí)際項(xiàng)目過程中,有用到多線程并發(fā)問題的例子嗎?
有,比如單例模式。
由于單例模式的特殊性,可能被程序中不同地方多個(gè)線程同時(shí)調(diào)用,所以為了避免多線程并發(fā)問題,一般要采用volatile+synchronized的方式進(jìn)行變量,方法保護(hù)。
private volatile static Singleton singleton;public static Singleton getSingleton4() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton;}介紹幾種啟動(dòng)模式。standard,默認(rèn)模式,每次啟動(dòng)都會(huì)新建一個(gè)Activity實(shí)例,并進(jìn)入當(dāng)前任務(wù)棧singleTop,如果要啟動(dòng)的Activity在棧頂存在實(shí)例,則不會(huì)重新創(chuàng)建Activity,而是直接使用棧頂?shù)腁ctivity實(shí)例,并回調(diào)onNewIntent方法。singleTask,如果要啟動(dòng)的Activity在棧中存在實(shí)例,則不會(huì)重新創(chuàng)建Activity,而是直接使用棧里的Activity實(shí)例,并回調(diào)onNewIntent方法。并且會(huì)把這個(gè)實(shí)例放到棧頂,之前在這個(gè)Activity之上的都會(huì)被出棧銷毀。singleInstance,有點(diǎn)單例的感覺,就是所啟動(dòng)的Activity會(huì)單獨(dú)放在一個(gè)任務(wù)棧里,并且后續(xù)所有啟動(dòng)該Activity都會(huì)直接用這個(gè)實(shí)例,同樣被重復(fù)調(diào)用的時(shí)候會(huì)調(diào)用并回調(diào)onNewIntent方法。
Activity依次A→B→C→B,其中B啟動(dòng)模式為singleTask,AC都為standard,生命周期分別怎么調(diào)用?如果B啟動(dòng)模式為singleInstance又會(huì)怎么調(diào)用?B啟動(dòng)模式為singleInstance不變,A→B→C的時(shí)候點(diǎn)擊兩次返回,生命周期如何調(diào)用。
1)A→B→C→B,B啟動(dòng)模式為singleTask
啟動(dòng)A的過程,生命周期調(diào)用是 (A)onCreate→(A)onStart→(A)onResume再啟動(dòng)B的過程,生命周期調(diào)用是 (A)onPause→(B)onCreate→(B)onStart→(B)onResume→(A)onStopB→C的過程同上C→B的過程,由于B啟動(dòng)模式為singleTask,所以B會(huì)調(diào)用onNewIntent,并且將B之上的實(shí)例移除,也就是C會(huì)被移出棧。所以生命周期調(diào)用是 (C)onPause→(B)onNewIntent→(B)onRestart→(B)onStart→(B)onResume→(C)onStop→(C)onDestory2)A→B→C→B,B啟動(dòng)模式為singleInstance
如果B為singleInstance,那么C→B的過程,C就不會(huì)被移除,因?yàn)锽和C不在一個(gè)任務(wù)棧里面。所以生命周期調(diào)用是 (C)onPause→(B)onNewIntent→(B)onRestart→(B)onStart→(B)onResume→(C)onStop3)A→B→C,B啟動(dòng)模式為singleInstance,點(diǎn)擊兩次返回鍵
如果B為singleInstance,A→B→C的過程,生命周期還是同前面一樣正常調(diào)用。但是點(diǎn)擊返回的時(shí)候,由于AC同任務(wù)棧,所以C點(diǎn)擊返回,會(huì)回到A,再點(diǎn)擊返回才回到B。所以生命周期是:(C)onPause→(A)onRestart→(A)onStart→(A)onResume→(C)onStop→(C)onDestory。再次點(diǎn)擊返回,就會(huì)回到B,所以生命周期是:(A)onPause→(B)onRestart→(B)onStart→(B)onResume→(A)onStop→(A)onDestory。屏幕旋轉(zhuǎn)時(shí)Activity的生命周期,如何防止Activity重建。
切換屏幕的生命周期是:onConfigurationChanged->onPause->onSaveInstanceState->onStop->onDestroy->onCreate->onStart->onRestoreInstanceState->onResume如果需要防止旋轉(zhuǎn)時(shí)候,Activity重新創(chuàng)建的話需要做如下配置:在targetSdkVersion的值小于或等于12時(shí),配置 android:configChanges="orientation", 在targetSdkVersion的值大于12時(shí),配置 android:configChanges="orientation|screenSize"。線程的幾種狀態(tài),相互之間是如何轉(zhuǎn)化的?1) 初始狀態(tài)(New)。新創(chuàng)建了一個(gè)線程對象就進(jìn)入了初始狀態(tài),也就是通過上述新建線程的幾個(gè)方法就能進(jìn)入該狀態(tài)。
2) 可運(yùn)行狀態(tài),就緒狀態(tài)(RUNNABLE)。線程對象創(chuàng)建后,其他線程(比如main線程)調(diào)用了該對象的start()方法。該狀態(tài)的線程位于可運(yùn)行線程池中,等待被線程調(diào)度選中,獲取cpu 的使用權(quán)。以下幾種方式會(huì)進(jìn)入可運(yùn)行狀態(tài):
調(diào)用start方法。拿到對象鎖調(diào)用yield方法3)運(yùn)行狀態(tài)(RUNNING)??蛇\(yùn)行狀態(tài)(runnable)的線程獲得了cpu 時(shí)間片 ,執(zhí)行程序代碼。線程調(diào)度程序從可運(yùn)行池中選擇一個(gè)線程作為當(dāng)前線程,就會(huì)進(jìn)入運(yùn)行狀態(tài)。
4)阻塞狀態(tài)(BLOCKED)。線程正在運(yùn)行的時(shí)候,被暫停,通常是為了等待某個(gè)時(shí)間的發(fā)生(比如說某項(xiàng)資源就緒)之后再繼續(xù)運(yùn)行。wait,sleep,suspend等方法都可以導(dǎo)致線程阻塞。
5)死亡狀態(tài)(DEAD)。線程run()、main() 方法執(zhí)行結(jié)束,或者因異常退出了run()方法,則該線程結(jié)束生命周期。死亡的線程不可再次復(fù)生。
String是java中的基本數(shù)據(jù)類型嗎?是可變的嗎?是線程安全的嗎?String不是基本數(shù)據(jù)類型,java中把大數(shù)據(jù)類型是:byte, short, int, long, char, float, double, booleanString是不可變的String是不可變類,一旦創(chuàng)建了String對象,我們就無法改變它的值。因此,它是線程安全的,可以安全地用于多線程環(huán)境中為什么要設(shè)計(jì)成不可變的呢?如果String是不可變的,那我們平時(shí)賦值是改的什么呢?1)為什么設(shè)計(jì)不可變
安全。由于String廣泛用于java類中的參數(shù),所以安全是非常重要的考慮點(diǎn)。包括線程安全,打開文件,存儲數(shù)據(jù)密碼等等。String的不變性保證哈希碼始終一,所以在用于HashMap等類的時(shí)候就不需要重新計(jì)算哈希碼,提高效率。因?yàn)閖ava字符串是不可變的,可以在java運(yùn)行時(shí)節(jié)省大量java堆空間。因?yàn)椴煌淖址兞靠梢砸贸刂械南嗤淖址?。如果字符串是可變得話,任何一個(gè)變量的值改變,就會(huì)反射到其他變量,那字符串池也就沒有任何意義了。2)平時(shí)使用雙引號方式賦值的時(shí)候其實(shí)是返回的字符串引用,并不是改變了這個(gè)字符串對象
淺談一下String, StringBuffer,StringBuilder的區(qū)別?String的兩種創(chuàng)建方式,在JVM的存儲方式相同嗎?String是不可變類,每當(dāng)我們對String進(jìn)行操作的時(shí)候,總是會(huì)創(chuàng)建新的字符串。操作String很耗資源,所以Java提供了兩個(gè)工具類來操作String - StringBuffer和StringBuilder。
StringBuffer和StringBuilder是可變類,StringBuffer是線程安全的,StringBuilder則不是線程安全的。所以在多線程對同一個(gè)字符串操作的時(shí)候,我們應(yīng)該選擇用StringBuffer。由于不需要處理多線程的情況,StringBuilder的效率比StringBuffer高。
1) String常見的創(chuàng)建方式有兩種
String s1 = “Java”String s2 = new String("Java")2)存儲方式不同
第一種,s1會(huì)先去字符串常量池中找字符串"Java”,如果有相同的字符則直接返回常量句柄,如果沒有此字符串則會(huì)先在常量池中創(chuàng)建此字符串,然后再返回常量句柄,或者說字符串引用。第二種,s2是直接在堆上創(chuàng)建一個(gè)變量對象,但不存儲到字符串池 ,調(diào)用intern方法才會(huì)把此字符串保存到常量池中線程池是干嘛的,優(yōu)點(diǎn)有哪些?線程池主要用作管理子線程,優(yōu)點(diǎn)有:
重用線程池中的線程,避免頻繁創(chuàng)建和銷毀線程所帶來的內(nèi)存開銷。有效控制線程的最大并發(fā)數(shù),避免因線程之間搶占資源而導(dǎo)致的阻塞現(xiàn)象。能夠?qū)€程進(jìn)行簡單的管理,提供定時(shí)執(zhí)行以及指定時(shí)間間隔循環(huán)執(zhí)行等功能。線程池的構(gòu)造方法每個(gè)參數(shù)是什么意思,執(zhí)行任務(wù)的流程public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {}corePoolSize:核心線程數(shù)。默認(rèn)情況下線程池是空的,只是任務(wù)提交時(shí)才會(huì)創(chuàng)建線程。如果當(dāng)前運(yùn)行的線程數(shù)少于corePoolSize,則會(huì)創(chuàng)建新線程來處理任務(wù);如果等于或者等于corePoolSize,則不再創(chuàng)建。如果調(diào)用線程池的prestartAllcoreThread方法,線程池會(huì)提前創(chuàng)建并啟動(dòng)所有的核心線程來等待任務(wù)。maximumPoolSize:線程池允許創(chuàng)建的最大線程數(shù)。如果任務(wù)隊(duì)列滿了并且線程數(shù)小于maximumPoolSize時(shí),則線程池仍然會(huì)創(chuàng)建新的線程來處理任務(wù)。keepAliveTime:非核心線程閑置的超時(shí)事件。超過這個(gè)事件則回收。如果任務(wù)很多,并且每個(gè)任務(wù)的執(zhí)行時(shí)間很短,則可以調(diào)大keepAliveTime來提高線程的利用率。另外,如果設(shè)置allowCoreThreadTimeOut屬性來true時(shí),keepAliveTime也會(huì)應(yīng)用到核心線程上。TimeUnit:keepAliveTime參數(shù)的時(shí)間單位??蛇x的單位有天Days、小時(shí)HOURS、分鐘MINUTES、秒SECONDS、毫秒MILLISECONDS等。workQueue:任務(wù)隊(duì)列。如果當(dāng)前線程數(shù)大于corePoolSzie,則將任務(wù)添加到此任務(wù)隊(duì)列中。該任務(wù)隊(duì)列是BlockingQueue類型的,即阻塞隊(duì)列。ThreadFactory:線程工廠??梢允褂镁€程工廠給每個(gè)創(chuàng)建出來的線程設(shè)置名字。一般情況下無須設(shè)置該參數(shù)。RejectedExecutionHandler:拒絕策略。這是當(dāng)前任務(wù)隊(duì)列和線程池都滿了時(shí)所采取的應(yīng)對策略,默認(rèn)是AbordPolicy,表示無法處理新任務(wù),并拋出RejectedExecutionException異常。
其中,拒絕策略有四種:
AbordPolicy:無法處理新任務(wù),并拋出RejectedExecutionException異常。CallerRunsPolicy:用調(diào)用者所在的線程來處理任務(wù)。此策略提供簡單的反饋控制機(jī)制,能夠減緩新任務(wù)的提交速度。DiscardPolicy:不能執(zhí)行的任務(wù),并將該任務(wù)刪除。DiscardOldestPolicy:丟棄隊(duì)列最近的任務(wù),并執(zhí)行當(dāng)前的任務(wù)。執(zhí)行任務(wù)流程:
如果線程池中的線程數(shù)量未達(dá)到核心線程的數(shù)量,會(huì)直接啟動(dòng)一個(gè)核心線程來執(zhí)行任務(wù)。如果線程池中的線程數(shù)量已經(jīng)達(dá)到或者超過核心線程的數(shù)量,那么任務(wù)會(huì)被插入到任務(wù)隊(duì)列中排隊(duì)等待執(zhí)行。如果任務(wù)隊(duì)列無法插入新任務(wù),說明任務(wù)隊(duì)列已滿,如果未達(dá)到規(guī)定的最大線程數(shù)量,則啟動(dòng)一個(gè)非核心線程來執(zhí)行任務(wù)。如果線程數(shù)量超過規(guī)定的最大值,則執(zhí)行拒絕策略-RejectedExecutionHandler。Android線程池主要分為哪幾類,分別代表了什么?主要有四類:FixedThreadPool、CachedThreadPool、SingleThreadExecutor、ScheduledTheadPool
1) FixedThreadPool——可重用固定線程數(shù)的線程池
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());}線程數(shù)量固定且都是核心線程:核心線程數(shù)量和最大線程數(shù)量都是nThreads;都是核心線程且不會(huì)被回收,快速相應(yīng)外界請求;沒有超時(shí)機(jī)制,任務(wù)隊(duì)列也沒有大小限制;新任務(wù)使用核心線程處理,如果沒有空閑的核心線程,則排隊(duì)等待執(zhí)行。
2)CachedThreadPool——按需創(chuàng)建的線程池
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());}線程數(shù)量不定,只有非核心線程,最大線程數(shù)任意大:傳入核心線程數(shù)量的參數(shù)為0,最大線程數(shù)為Integer.MAX_VALUE;有新任務(wù)時(shí)使用空閑線程執(zhí)行,沒有空閑線程則創(chuàng)建新的線程來處理。該線程池的每個(gè)空閑線程都有超時(shí)機(jī)制,時(shí)常為60s(參數(shù):60L, TimeUnit.SECONDS),空閑超過60s則回收空閑線程。適合執(zhí)行大量的耗時(shí)較少的任務(wù),當(dāng)所有線程閑置超過60s都會(huì)被停止,所以這時(shí)幾乎不占用系統(tǒng)資源。
3)SingleThreadExecutor——單線程的線程池
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));}只有一個(gè)核心線程,所有任務(wù)在同一個(gè)線程按順序執(zhí)行。所有的外界任務(wù)統(tǒng)一到一個(gè)線程中,所以不需要處理線程同步的問題。
4)ScheduledThreadPool——定時(shí)和周期性的線程池
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue());}核心線程數(shù)量固定,非核心線程數(shù)量無限制;非核心線程閑置超過10s會(huì)被回收;主要用于執(zhí)行定時(shí)任務(wù)和具有固定周期的重復(fù)任務(wù);索引是什么,優(yōu)缺點(diǎn)
數(shù)據(jù)庫索引,是數(shù)據(jù)庫管理系統(tǒng)中一個(gè)排序的數(shù)據(jù)結(jié)構(gòu),以協(xié)助快速查詢,更新數(shù)據(jù)庫中表的數(shù)據(jù).索引的實(shí)現(xiàn)通常使用B樹和變種的B+樹(mysql常用的索引就是B+樹)
優(yōu)點(diǎn)
通過創(chuàng)建索引,可以在查詢的過程中,提高系統(tǒng)的性能通過創(chuàng)建唯一性索引,可以保證數(shù)據(jù)庫表中每一行數(shù)據(jù)的唯一性在使用分組和排序子句進(jìn)行數(shù)據(jù)檢索時(shí),可以減少查詢中分組和排序的時(shí)間缺點(diǎn)
創(chuàng)建索引和維護(hù)索引要耗費(fèi)時(shí)間,而且時(shí)間隨著數(shù)據(jù)量的增加而增大索引需要占用物理空間,如果要建立聚簇索引,所需要的空間會(huì)更大在對表中的數(shù)據(jù)進(jìn)行增加刪除和修改時(shí)需要耗費(fèi)較多的時(shí)間,因?yàn)樗饕惨獎(jiǎng)討B(tài)地維護(hù)事務(wù)四大特性數(shù)據(jù)庫事務(wù)必須具備ACID特性,ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔離性)和Durability(持久性)的英文縮寫。
原子性一個(gè)事務(wù)中的所有操作,要么全部完成,要么全部不完成,不會(huì)結(jié)束在中間某個(gè)環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯(cuò)誤,會(huì)被回滾到事務(wù)開始前的狀態(tài),就像這個(gè)事務(wù)從來沒有執(zhí)行過一樣。
一致性事務(wù)的一致性指的是在一個(gè)事務(wù)執(zhí)行之前和執(zhí)行之后數(shù)據(jù)庫都必須處于一致性狀態(tài)。如果事務(wù)成功地完成,那么系統(tǒng)中所有變化將正確地應(yīng)用,系統(tǒng)處于有效狀態(tài)。如果在事務(wù)中出現(xiàn)錯(cuò)誤,那么系統(tǒng)中的所有變化將自動(dòng)地回滾,系統(tǒng)返回到原始狀態(tài)。
隔離性指的是在并發(fā)環(huán)境中,當(dāng)不同的事務(wù)同時(shí)操縱相同的數(shù)據(jù)時(shí),每個(gè)事務(wù)都有各自的完整數(shù)據(jù)空間。由并發(fā)事務(wù)所做的修改必須與任何其他并發(fā)事務(wù)所做的修改隔離。事務(wù)查看數(shù)據(jù)更新時(shí),數(shù)據(jù)所處的狀態(tài)要么是另一事務(wù)修改它之前的狀態(tài),要么是另一事務(wù)修改它之后的狀態(tài),事務(wù)不會(huì)查看到中間狀態(tài)的數(shù)據(jù)。
持久性指的是只要事務(wù)成功結(jié)束,它對數(shù)據(jù)庫所做的更新就必須永久保存下來。即使發(fā)生系統(tǒng)崩潰,重新啟動(dòng)數(shù)據(jù)庫系統(tǒng)后,數(shù)據(jù)庫還能恢復(fù)到事務(wù)成功結(jié)束時(shí)的狀態(tài)。
講講幾個(gè)范式范式的英文名稱是Normal Form,它是英國人E.F.Codd(關(guān)系數(shù)據(jù)庫的老祖宗)在上個(gè)世紀(jì)70年代提出關(guān)系數(shù)據(jù)庫模型后總結(jié)出來的。范式是關(guān)系數(shù)據(jù)庫理論的基礎(chǔ),也是我們在設(shè)計(jì)數(shù)據(jù)庫結(jié)構(gòu)過程中所要遵循的規(guī)則和指導(dǎo)方法。通常所用到的只是前三個(gè)范式,即:第一范式(1NF),第二范式(2NF),第三范式(3NF)。
第一范式就是屬性不可分割,每個(gè)字段都應(yīng)該是不可再拆分的。比如一個(gè)字段是姓名(NAME),在國內(nèi)的話通常理解都是姓名是一個(gè)不可再拆分的單位,這時(shí)候就符合第一范式;但是在國外的話還要分為FIRST NAME和LAST NAME,這時(shí)候姓名這個(gè)字段就是還可以拆分為更小的單位的字段,就不符合第一范式了。第二范式就是要求表中要有主鍵,表中其他其他字段都依賴于主鍵,因此第二范式只要記住主鍵約束就好了。比如說有一個(gè)表是學(xué)生表,學(xué)生表中有一個(gè)值唯一的字段學(xué)號,那么學(xué)生表中的其他所有字段都可以根據(jù)這個(gè)學(xué)號字段去獲取,依賴主鍵的意思也就是相關(guān)的意思,因?yàn)閷W(xué)號的值是唯一的,因此就不會(huì)造成存儲的信息對不上的問題,即學(xué)生001的姓名不會(huì)存到學(xué)生002那里去。第三范式就是要求表中不能有其他表中存在的、存儲相同信息的字段,通常實(shí)現(xiàn)是在通過外鍵去建立關(guān)聯(lián),因此第三范式只要記住外鍵約束就好了。比如說有一個(gè)表是學(xué)生表,學(xué)生表中有學(xué)號,姓名等字段,那如果要把他的系編號,系主任,系主任也存到這個(gè)學(xué)生表中,那就會(huì)造成數(shù)據(jù)大量的冗余,一是這些信息在系信息表中已存在,二是系中有1000個(gè)學(xué)生的話這些信息就要存1000遍。因此第三范式的做法是在學(xué)生表中增加一個(gè)系編號的字段(外鍵),與系信息表做關(guān)聯(lián)。Recycleview和listview區(qū)別Recycleview布局效果更多,增加了縱向,表格,瀑布流等效果Recycleview去掉了一些api,比如setEmptyview,onItemClickListener等等,給到用戶更多的自定義可能Recycleview去掉了設(shè)置頭部底部item的功能,專向通過viewholder的不同type實(shí)現(xiàn)Recycleview實(shí)現(xiàn)了一些局部刷新,比如notifyitemchangedRecycleview自帶了一些布局變化的動(dòng)畫效果,也可以通過自定義ItemAnimator類實(shí)現(xiàn)自定義動(dòng)畫效果Recycleview緩存機(jī)制更全面,增加兩級緩存,還支持自定義緩存邏輯Recycleview有幾級緩存,緩存過程?Recycleview有四級緩存,分別是mAttachedScrap(屏幕內(nèi)),mCacheViews(屏幕外),mViewCacheExtension(自定義緩存),mRecyclerPool(緩存池)
mAttachedScrap(屏幕內(nèi)),用于屏幕內(nèi)itemview快速重用,不需要重新createView和bindViewmCacheViews(屏幕外),保存最近移出屏幕的ViewHolder,包含數(shù)據(jù)和position信息,復(fù)用時(shí)必須是相同位置的ViewHolder才能復(fù)用,應(yīng)用場景在那些需要來回滑動(dòng)的列表中,當(dāng)往回滑動(dòng)時(shí),能直接復(fù)用ViewHolder數(shù)據(jù),不需要重新bindView。mViewCacheExtension(自定義緩存),不直接使用,需要用戶自定義實(shí)現(xiàn),默認(rèn)不實(shí)現(xiàn)。mRecyclerPool(緩存池),當(dāng)cacheView滿了后或者adapter被更換,將cacheView中移出的ViewHolder放到Pool中,放之前會(huì)把ViewHolder數(shù)據(jù)清除掉,所以復(fù)用時(shí)需要重新bindView。四級緩存按照順序需要依次讀取。所以完整緩存流程是:
保存緩存流程:插入或是刪除itemView時(shí),先把屏幕內(nèi)的ViewHolder保存至AttachedScrap中滑動(dòng)屏幕的時(shí)候,先消失的itemview會(huì)保存到CacheView,CacheView大小默認(rèn)是2,超過數(shù)量的話按照先入先出原則,移出頭部的itemview保存到RecyclerPool緩存池(如果有自定義緩存就會(huì)保存到自定義緩存里),RecyclerPool緩存池會(huì)按照itemview的itemtype進(jìn)行保存,每個(gè)itemTyep緩存?zhèn)€數(shù)為5個(gè),超過就會(huì)被回收。獲取緩存流程:AttachedScrap中獲取,通過pos匹配holder——>獲取失敗,從CacheView中獲取,也是通過pos獲取holder緩存 ——>獲取失敗,從自定義緩存中獲取緩存——>獲取失敗,從mRecyclerPool中獲取 ——>獲取失敗,重新創(chuàng)建viewholder——createViewHolder并bindview。需要注意的是,如果從緩存池找到緩存,還需要重新bindview。
說說RecyclerView性能優(yōu)化。bindViewHolder方法是在UI線程進(jìn)行的,此方法不能耗時(shí)操作,不然將會(huì)影響滑動(dòng)流暢性。比如進(jìn)行日期的格式化。對于新增或刪除的時(shí)候,可以使用diffutil進(jìn)行局部刷新,少用全局刷新對于itemVIew進(jìn)行布局優(yōu)化,比如少嵌套等。25.1.0 (>=21)及以上使用Prefetch 功能,也就是預(yù)取功能,嵌套時(shí)且使用的是LinearLayoutManager,子RecyclerView可通過setInitialPrefatchItemCount設(shè)置預(yù)取個(gè)數(shù)加大RecyclerView緩存,比如cacheview大小默認(rèn)為2,可以設(shè)置大點(diǎn),用空間來換取時(shí)間,提高流暢度如果高度固定,可以設(shè)置setHasFixedSize(true)來避免requestLayout浪費(fèi)資源,否則每次更新數(shù)據(jù)都會(huì)重新測量高度。void onItemsInsertedOrRemoved() { if (hasFixedSize) layoutChildren(); else requestLayout();}如果多個(gè)RecycledView 的 Adapter 是一樣的,比如嵌套的 RecyclerView 中存在一樣的 Adapter,可以通過設(shè)置 RecyclerView.setRecycledViewPool(pool);來共用一個(gè) RecycledViewPool。這樣就減少了創(chuàng)建VIewholder的開銷。在RecyclerView的元素比較高,一屏只能顯示一個(gè)元素的時(shí)候,第一次滑動(dòng)到第二個(gè)元素會(huì)卡頓。這種情況就可以通過設(shè)置額外的緩存空間,重寫getExtraLayoutSpace方法即可。
new LinearLayoutManager(this) { @Override protected int getExtraLayoutSpace(RecyclerView.State state) { return size; }};設(shè)置RecyclerView.addOnScrollListener();來在滑動(dòng)過程中停止加載的操作。減少對象的創(chuàng)建,比如設(shè)置監(jiān)聽事件,可以全局創(chuàng)建一個(gè),所有view公用一個(gè)listener,并且放到CreateView里面去創(chuàng)建監(jiān)聽,因?yàn)镃reateView調(diào)用要少于bindview。這樣就減少了對象創(chuàng)建所造成的消耗用notifyDataSetChange時(shí),適配器不知道整個(gè)數(shù)據(jù)集中的那些內(nèi)容以及存在,再重新匹配ViewHolder時(shí)會(huì)花生閃爍。設(shè)置adapter.setHasStableIds(true),并重寫getItemId()來給每個(gè)Item一個(gè)唯一的ID,也就是唯一標(biāo)識,就使itemview的焦點(diǎn)固定,解決了閃爍問題。說說雙重校驗(yàn)鎖,以及volatile的作用
先回顧下雙重校驗(yàn)鎖的原型,也就是單例模式的實(shí)現(xiàn):
public class Singleton { private volatile static Singleton mSingleton; private Singleton() { } public Singleton getInstance() { if (null == mSingleton) { synchronized (Singleton.class) { if (null == mSingleton) { mSingleton = new Singleton(); } } } return mSingleton; }}
有幾個(gè)疑問需要解決:
為什么要加鎖?為什么不直接給getInstance方法加鎖?為什么需要雙重判斷是否為空?為什么還要加volatile修飾變量?接下來一一解答:
如果不加鎖的話,是線程不安全的,也就是有可能多個(gè)線程同時(shí)訪問getInstance方法會(huì)得到兩個(gè)實(shí)例化的對象。如果給getInstance方法加鎖,就每次訪問mSingleton都需要加鎖,增加了性能開銷第一次判空是為了判斷是否已經(jīng)實(shí)例化,如果已經(jīng)實(shí)例化就直接返回變量,不需要加鎖了。第二次判空是因?yàn)樽叩郊渔i這一步,如果線程A已經(jīng)實(shí)例化,等B獲得鎖,進(jìn)入的時(shí)候其實(shí)對象已經(jīng)實(shí)例化完成了,如果不二次判空就會(huì)再次實(shí)例化。加volatile是為了禁止指令重排。指令重排指的是在程序運(yùn)行過程中,并不是完全按照代碼順序執(zhí)行的,會(huì)考慮到性能等原因,將不影響結(jié)果的指令順序有可能進(jìn)行調(diào)換。所以初始化的順序本來是這三步:1)分配內(nèi)存空間 2)初始化對象 3)將對象指向分配的空間如果進(jìn)行了指令重排,由于不影響結(jié)果,所以2和3有可能被調(diào)換。所以就變成了:
1)分配內(nèi)存空間2)將對象指向分配的空間3)初始化對象
就有可能會(huì)導(dǎo)致,假如線程A中已經(jīng)進(jìn)行到第二步,線程B進(jìn)入第二次判空的時(shí)候,判斷mSingleton不為空,就直接返回了,但是實(shí)際此時(shí)mSingleton還沒有初始化。
synchronized和volatile的區(qū)別volatile本質(zhì)是在告訴jvm當(dāng)前變量在寄存器中的值是不確定的,需要從主存中讀取,synchronized則是鎖定當(dāng)前變量,只有當(dāng)前線程可以訪問該變量,其他線程被阻塞住.volatile僅能使用在變量級別,synchronized則可以使用在變量,方法.volatile僅能實(shí)現(xiàn)變量的修改可見性,而synchronized則可以保證變量的修改可見性和原子性.volatile不會(huì)造成線程的阻塞,而synchronized可能會(huì)造成線程的阻塞.當(dāng)一個(gè)域的值依賴于它之前的值時(shí),volatile就無法工作了,如n=n+1,n++等,也就是不保證原子性。使用volatile而不是synchronized的唯一安全的情況是類中只有一個(gè)可變的域。synchronized修飾static方法和修飾普通方法有什么區(qū)別Synchronized修飾非靜態(tài)方法,實(shí)際上是對調(diào)用該方法的對象加鎖,俗稱“對象鎖”。也就是鎖住的是這個(gè)對象,即this。如果同一個(gè)對象在兩個(gè)線程分別訪問對象的兩個(gè)同步方法,就會(huì)產(chǎn)生互斥,這就是對象鎖,一個(gè)對象一次只能進(jìn)入一個(gè)操作。Synchronized修飾靜態(tài)方法,實(shí)際上是對該類對象加鎖,俗稱“類鎖”。也就是鎖住的是這個(gè)類,即xx.class。如果一個(gè)對象在兩個(gè)線程中分別調(diào)用一個(gè)靜態(tài)同步方法和一個(gè)非靜態(tài)同步方法,由于靜態(tài)方法會(huì)收到類鎖限制,但是非靜態(tài)方法會(huì)收到對象限制,所以兩個(gè)方法并不是同一個(gè)對象鎖,因此不會(huì)排斥。內(nèi)存泄漏是什么,為什么會(huì)發(fā)生?內(nèi)存泄漏(Memory Leak)是指程序中己動(dòng)態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無法釋放,造成系統(tǒng)內(nèi)存的浪費(fèi),導(dǎo)致程序運(yùn)行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果。簡單點(diǎn)說,手機(jī)給我們的應(yīng)用提供了一定大小的堆內(nèi)存,在不斷創(chuàng)建對象的過程中,也在不斷的GC(java的垃圾回收機(jī)制),所以內(nèi)存正常情況下會(huì)保持一個(gè)平穩(wěn)的值。但是出現(xiàn)內(nèi)存泄漏就會(huì)導(dǎo)致某個(gè)實(shí)例,比如Activity的實(shí)例,應(yīng)用被某個(gè)地方引用到了,不能正常釋放,從而導(dǎo)致內(nèi)存占用越來越大,這就是內(nèi)存泄漏。
內(nèi)存泄漏發(fā)生的情況有哪些?主要有四類情況:
集合類泄漏單例/靜態(tài)變量造成的內(nèi)存泄漏匿名內(nèi)部類/非靜態(tài)內(nèi)部類資源未關(guān)閉造成的內(nèi)存泄漏1)集合類泄漏
集合類添加元素后,仍引用著集合元素對象,導(dǎo)致該集合中的元素對象無法被回收,從而導(dǎo)致內(nèi)存泄露。
static List<Object> mList = new ArrayList<>(); for (int i = 0; i < 100; i++) { Object obj = new Object(); mList.add(obj); obj = null; }
解決辦法就是把集合也釋放掉。
mList.clear(); mList = null;
2)單例/靜態(tài)變量造成的內(nèi)存泄漏
單例模式具有其靜態(tài)特性,它的生命周期等于應(yīng)用程序的生命周期,正是因?yàn)檫@一點(diǎn),往往很容易造成內(nèi)存泄漏。
public class SingleInstance { private static SingleInstance mInstance; private Context mContext; private SingleInstance(Context context){ this.mContext = context; } public static SingleInstance newInstance(Context context){ if(mInstance == null){ mInstance = new SingleInstance(context); } return sInstance; }}
比如這個(gè)單例模式,如果我們調(diào)用newInstance方法時(shí)候把Activity的context傳進(jìn)去,那么就是生命周期長的持有了生命周期短的引用,造成了內(nèi)存泄漏。要修改的話把context改成context.getApplicationContext()即可。
3)匿名內(nèi)部類/非靜態(tài)內(nèi)部類
非靜態(tài)內(nèi)部類他會(huì)持有他外部類的強(qiáng)引用,所以就有可能導(dǎo)致非靜態(tài)內(nèi)部類的生命周期可能比外部類更長,容易造成內(nèi)存泄漏,最常見的就是Handler。
public class TestActivity extends Activity {private TextView mText; private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(100000); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); mHandler. sendEmptyMessageDelayed(0, 100000); }
怎么修改呢?改成靜態(tài)內(nèi)部類,然后弱引用方式修飾外部類
public class TestActivity extends Activity { private TextView mText; private MyHandler myHandler = new MyHandler(TestActivity.this); private MyThread myThread = new MyThread(); private static class MyHandler extends Handler { WeakReference<TestActivity> weakReference; MyHandler(TestActivity testActivity) { this.weakReference = new WeakReference<TestActivity>(testActivity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); weakReference.get().mText.setText("do someThing"); } } @Override protected void onDestroy() { super.onDestroy(); myHandler.removeCallbacksAndMessages(null); }
4)資源未關(guān)閉造成的內(nèi)存泄漏
比如:
網(wǎng)絡(luò)、文件等流忘記關(guān)閉手動(dòng)注冊廣播時(shí),退出時(shí)忘記unregisterReceiver()Service 執(zhí)行完后忘記 stopSelf()EventBus 等觀察者模式的框架忘記手動(dòng)解除注冊該怎么發(fā)現(xiàn)和解決內(nèi)存泄漏?1、使用工具,比如Memory Profiler,可以查看app的內(nèi)存實(shí)時(shí)情況,捕獲堆轉(zhuǎn)儲,就生成了一個(gè)內(nèi)存快照,hprof文件。通過查看文件,可以看到哪些類發(fā)生了內(nèi)存泄漏。
2、使用庫,比較出名的就是LeakCanary,導(dǎo)入庫,然后運(yùn)行后,就可以發(fā)現(xiàn)app內(nèi)的內(nèi)存泄漏情況。
這里說下LeakCanary的原理:
監(jiān)聽 首先通過ActivityLifecycleCallbacks和FragmentLifeCycleCallbacks監(jiān)聽Activity和Fragment的生命周期。判斷 然后在銷毀的生命周期中判斷對象是否被回收。弱引用在定義的時(shí)候可以指定引用對象和一個(gè) ReferenceQueue,通過該弱引用是否被加入ReferenceQueue就可以判斷該對象是否被回收。分析 最后通過haha庫來分析hprof文件,從而找出類之前的引用關(guān)系。鴻洋注:新版 LeakCanary 使用的是 shark 庫分析內(nèi)存,效果更好一些。
什么是類加載機(jī)制?我們編寫的java文件會(huì)在編譯后變成.class文件,類加載器就是負(fù)責(zé)加載class字節(jié)碼文件,class文件在文件開頭有特定的文件標(biāo)識,將class文件字節(jié)碼內(nèi)容加載到內(nèi)存中,并將這些內(nèi)容轉(zhuǎn)換成方法區(qū)中的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)并且ClassLoader只負(fù)責(zé)class文件的加載,至于它是否可以運(yùn)行,則由執(zhí)行引擎Execution Engine決定。
簡單來說類加載機(jī)制就是從文件系統(tǒng)將一系列的 class 文件讀入 JVM 內(nèi)存中為后續(xù)程序運(yùn)行提供資源的動(dòng)作。
類加載器種類。類加載器種類主要有四種:
BootstrapClassLoader:啟動(dòng)類加載器,使用C++實(shí)現(xiàn)ExtClassLoader:擴(kuò)展類加載器,使用Java實(shí)現(xiàn)AppClassLoader:應(yīng)用程序類加載器,加載當(dāng)前應(yīng)用的classpath的所有類UserDefinedClassLoader:用戶自定義類加載器屬于依次繼承關(guān)系,也就是上一級是下一級的父加載器。
什么是雙親委派機(jī)制,為什么這么設(shè)計(jì)?當(dāng)一個(gè)類加載器收到了類加載的請求,它不會(huì)直接去加載這類,而是先把這個(gè)請求委派給父加載器去完成,依次會(huì)傳遞到最上級也就是啟動(dòng)類加載器,然后父加載器會(huì)檢查是否已經(jīng)加載過該類,如果沒加載過,就會(huì)去加載,加載失敗才會(huì)交給子加載器去加載,一直到最底層,如果都沒辦法能正確加載,則會(huì)跑出ClassNotFoundException異常。
舉例:
當(dāng)Application ClassLoader 收到一個(gè)類加載請求時(shí),他首先不會(huì)自己去嘗試加載這個(gè)類,而是將這個(gè)請求委派給父類加載器Extension ClassLoader去完成。當(dāng)Extension ClassLoader收到一個(gè)類加載請求時(shí),他首先也不會(huì)自己去嘗試加載這個(gè)類,而是將請求委派給父類加載器Bootstrap ClassLoader去完成。如果Bootstrap ClassLoader加載失敗(在<JAVA_HOME>\\lib中未找到所需類),就會(huì)讓Extension ClassLoader嘗試加載。如果Extension ClassLoader也加載失敗,就會(huì)使用Application ClassLoader加載。如果Application ClassLoader也加載失敗,就會(huì)使用自定義加載器去嘗試加載。如果均加載失敗,就會(huì)拋出ClassNotFoundException異常。這么設(shè)計(jì)的原因是為了防止危險(xiǎn)代碼的植入,比如String類,如果在AppClassLoader就直接被加載,就相當(dāng)于會(huì)被篡改了,所以都要經(jīng)過老大,也就是BootstrapClassLoader進(jìn)行檢查,已經(jīng)加載過的類就不需要再去加載了。
更多面試復(fù)習(xí)資源去好公司面試,能答出來只是第一步,延伸問答、靈活運(yùn)用才是面試官的目的,你越能答,他們越能問。我希望讀者們能知道深入了解的含義,這真的是一個(gè)過程。
自己的知識準(zhǔn)備得怎么樣,這直接決定了你能否順利通過一面和二面,所以在面試前來一個(gè)知識梳理,看需不需要提升自己的知識儲備是很有必要的。
關(guān)于知識梳理,這里再分享一下我面試這段時(shí)間的復(fù)習(xí)路線:(以下體系的復(fù)習(xí)資料是我從各路大佬收集整理好的)
知識梳理完之后,就需要進(jìn)行查漏補(bǔ)缺,所以針對這些知識點(diǎn),我手頭上也準(zhǔn)備了不少的電子書和筆記,這些筆記將各個(gè)知識點(diǎn)進(jìn)行了完美的總結(jié)。
《379頁Android開發(fā)面試寶典》
歷時(shí)半年,我們整理了這份市面上最全面的安卓面試題解析大全包含了騰訊、百度、小米、阿里、樂視、美團(tuán)、58、獵豹、360、新浪、搜狐等一線互聯(lián)網(wǎng)公司面試被問到的題目。熟悉本文中列出的知識點(diǎn)會(huì)大大增加通過前兩輪技術(shù)面試的幾率。
如何使用它?
1.可以通過目錄索引直接翻看需要的知識點(diǎn),查漏補(bǔ)缺。2.五角星數(shù)表示面試問到的頻率,代表重要推薦指數(shù)
《507頁Android開發(fā)相關(guān)源碼解析》
只要是程序員,不管是Java還是Android,如果不去閱讀源碼,只看API文檔,那就只是停留于皮毛,這對我們知識體系的建立和完備以及實(shí)戰(zhàn)技術(shù)的提升都是不利的。
真正最能鍛煉能力的便是直接去閱讀源碼,不僅限于閱讀各大系統(tǒng)源碼,還包括各種優(yōu)秀的開源庫。
以上文章中的資料,均可以免費(fèi)分享給大家來學(xué)習(xí),
資料太多,全部展示會(huì)影響篇幅,暫時(shí)就先列舉這些部分截圖;
需要的朋友,直接轉(zhuǎn)發(fā)+點(diǎn)贊+私信回復(fù)【資料】一鍵領(lǐng)?。。?!
以上就是關(guān)于摩托車論壇pos機(jī),攢了一個(gè)月的安卓面試題的知識,后面我們會(huì)繼續(xù)為大家整理關(guān)于摩托車論壇pos機(jī)的知識,希望能夠幫助到大家!
![](/style/images/zhouzong.jpg)