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