快手Android一面复盘
人生第一次面試,回答得特別特別爛,我覺得面試官肯定對我很無語。但是真的很感謝這個面試官,讓我有了一次對我面試幫助很大很大的面經(jīng)。這次面試之后,記錄了面試的要點,進行了針對性查缺補漏,對我后面的面試幫助很大。
不要畏懼面試,多面試漲漲經(jīng)驗總是好的。不要有自己在浪費面試官時間的想法,面試官在面試你的時候也是在完成他的任務(wù)(公司不可能讓他白干活),你要學(xué)會合理利用資源,利用面試長經(jīng)驗。當你面試得足夠多了,準備得足夠充分了,offer自然也就到手了。
加油!
1. Java四種訪問修飾符
- public: Java語言中訪問限制最寬的修飾符,一般稱之為“公共的”。被其修飾的類、屬性以及方法不
僅可以跨類訪問,而且允許跨包(package)訪問。 - private: Java語言中對訪問權(quán)限限制的最窄的修飾符,一般稱之為“私有的”。被其修飾的類、屬性以
及方法只能被該類的對象訪問,其子類不能訪問,更不能允許跨包訪問。 - protected: 介于public 和 private 之間的一種訪問修飾符,一般稱之為“保護形”。被其修飾的類、
屬性以及方法只能被類本身的方法及子類訪問,即使子類在不同的包中也可以訪問。 - default:即不加任何訪問修飾符,通常稱為“默認訪問模式“。該模式下,只允許在同一個包中進行訪
問。
2.Java常見的基本類型
8種 = 非數(shù)值型2種 + 數(shù)值型6種
- 數(shù)值型
- byte
- short
- int
- long
- float
- double
- 非數(shù)值型
- boolean
- char
3. Java值傳遞和引用傳遞
- 基本數(shù)據(jù)類型傳值,對形參的修改不會影響實參;
- 引用類型傳引用
- 形參和實參指向同一個內(nèi)存地址,所以對參數(shù)的修改會影響到實際的對象;
- 形參和實參指向不同內(nèi)存地址的,則對參數(shù)的修改不會影響到實際的對象;
String 是引用類型
Integer m1 = 127;Integer m2 = 127;//當在-128~127范圍內(nèi)返回true,否則返回falseint x1 = 500;int x2 = 500;//全都返回true 存在棧中,棧中的數(shù)據(jù)可以共享Integer n1 = new Integer(22);//全都返回false,不同的對象對應(yīng)不同的地址Integer n2 = new Integer(22);System.out.println(m1 == m2);System.out.println(x1 == x2);System.out.println(n1 == n2);// 輸出:true,true,false運行時常量池一直在方法區(qū)(Method Area),里面包含了每一個.class文件中的常量池中的內(nèi)容。而字符串池在Java 7之前保存在方法區(qū),在Java 7之后保存在堆上。
-
以上提到的幾種基本類型包裝類均實現(xiàn)了常量池技術(shù),但它們維護的常量僅僅是【-128~127】這個范圍內(nèi)的常量,如果常量值超過這個范圍,就會從堆中創(chuàng)建對象,不再從常量池中取。比如,把上邊例子改成Integer i1 = 400; Integer i2 = 400;,很明顯超過了127,無法從常量池獲取常量,就要從堆中new新的Integer對象,這時i1和i2就不相等了。
-
String類型也實現(xiàn)了常量池技術(shù),但是稍微有點不同。String型是先檢測常量池中有沒有對應(yīng)字符串,如果有,則取出來;如果沒有,則把當前的添加進去。
4. Java單例
(1)懶漢式–線程不安全【不可用】
(2)懶漢式–線程安全 【不可用】
缺點:效率太低了,每個線程在想獲得類的實例時候,執(zhí)行g(shù)etInstance()方法都要進行同步。而其實這個方法只執(zhí)行一次實例化代碼就夠了,后面的想獲得該類實例,直接return就行了。方法進行同步效率太低要改進。
public class Singleton{private static Singleton instance ;private Singleton(){}public static synchronized Singleton getInstance(){if(instance == null) { // 被動創(chuàng)建,在真正需要使用時才去創(chuàng)建instance = new Singleton();}return instance;} }(3)餓漢式(靜態(tài)代碼塊)【可用】
將類實例化的過程放在了靜態(tài)代碼塊中,也是在類裝載的時候,就執(zhí)行靜態(tài)代碼塊中的代碼,初始化類的實例。
優(yōu)點:這種寫法比較簡單,就是在類裝載的時候就完成實例化。避免了線程同步問題。
缺點:在類裝載的時候就完成實例化,沒有達到Lazy Loading的效果。如果從始至終從未使用過這個實例,則會造成內(nèi)存的浪費。
(4)雙重檢查【推薦使用】
進行了兩次if (singleton == null)檢查,這樣就可以保證線程安全了。
這樣,實例化代碼只用執(zhí)行一次,后面再次訪問時,判斷if (singleton == null),直接return實例化對象。
優(yōu)點:線程安全;延遲加載;效率較高。
public class Singleton {//對保存實例的變量添加volatile的修飾private static volatile Singleton instance;private Singleton(){}public static Singleton getInstance() {if(instance == null) {//假如一個線程進入了if (singleton == null)判斷語句塊,還未來得及往下執(zhí)行,另一個線程也通過了這個判斷語句,//這時要保證不會產(chǎn)生多個實例。(所以下面要進行第二次驗證)synchronized (Singleton.class) {if(instance == null) {instance = new Singleton();}}}return instance;} }5.static的用法
一定要提到類加載!!!
方便在沒有創(chuàng)建對象的情況下來進行調(diào)用(方法/變量)。
static:靜態(tài)的,用于修飾成員(成員變量,成員方法);
被static所修飾的變量或者方法會儲存在數(shù)據(jù)共享區(qū)(靜態(tài)存儲區(qū));
被static修飾后的類成員變量只有一份!
當成員被static修飾之后,就多了一種訪問方式,除了可以被對象調(diào)用之外,還可以直接被類名調(diào)用,(類名.靜態(tài)成員);
使用:(一定要提到類加載)
- 靜態(tài)變量屬于類,被所有的對象所共享,在內(nèi)存中只有一個副本(靜態(tài)變量之所以又稱為類變量,是因為它和類關(guān)聯(lián)在在一起,隨著類加載而存在于方法區(qū)中(而不是堆中))
- 靜態(tài)變量當且僅當在類初次加載時會被初始化
- 靜態(tài)成員變量可以通過類直接訪問
- 非靜態(tài)變量是對象所擁有的,在創(chuàng)建對象的時候被初始化,存在多個副本,各個對象擁有的副本互不影響
- 非靜態(tài)成員變量必須要創(chuàng)建實例后才能訪問
- 在靜態(tài)方法中只能訪問靜態(tài)成員(靜態(tài)變量或者靜態(tài)方法),不能訪問非靜態(tài)成員
- 靜態(tài)方法隨著類加載而加載
- 調(diào)用方式:類名.靜態(tài)方法名
- static塊可以置于類中的任何地方,類中可以有多個static塊。在類初次被加載的時候,會按照static塊的順序來執(zhí)行每個static塊,并且只會執(zhí)行一次。
注:
獨立于類
在類加載的時候執(zhí)行,只執(zhí)行一次
修飾的方法只在調(diào)用時執(zhí)行
方法注意事項:
靜態(tài)的方法只能訪問靜態(tài)的成員;
非靜態(tài)得方法即能訪問靜態(tài)得成員(成員變量,成員方法)又能訪問非靜態(tài)得成員;
局部變量不能被static修飾;
靜態(tài)得方法中是不可以定義this、super關(guān)鍵字的,因為靜態(tài)優(yōu)先于對象存在,所以靜態(tài)方法不可以出this;
6. synchronized的用法
三種用法:
synchronized的作用:
- 原子性:synchronized保證語句塊內(nèi)操作是原子的
- 可見性:synchronized保證可見性(通過“在執(zhí)行unlock之前,必須先把此變量同步回主內(nèi)存”實現(xiàn))
- 有序性:synchronized保證有序性(通過“一個變量在同一時刻只允許一條線程對其進行l(wèi)ock操作”)
一、四種用法(3種)
synchronized是Java中的關(guān)鍵字,是一種同步鎖。
synchronized關(guān)鍵字不能繼承。
修飾一個類和修飾一個靜態(tài)方法,作用的對象都是這個類的所有對象。
修飾一個普通方法和一個代碼塊,作用的對象是調(diào)用這個方法或者代碼塊的對象。
(1)修飾一個類
在synchronized后面的()里面加入要鎖住的類名
class ClassA{public void funcA() {synchorized(ClassName.class) {//TODO}} }(2)修飾一個普通方法
直接在方法的返回類型之前加synchronized關(guān)鍵字修飾
//1 作用的對象是調(diào)用這個方法的對象; public synchronized void method(){//TODO } //2 public void method(){synchronized(this) {// todo }寫法一修飾的是一個方法,寫法二修飾的是一個代碼塊,但寫法一與寫法二是等價的,都是鎖定了整個方法時的內(nèi)容。
接口方法不能使用synchronized關(guān)鍵字
構(gòu)造方法不能使用關(guān)鍵字,但可以使用synchronized代碼塊進行同步
如果在父類中的某個方法使用了synchronized關(guān)鍵字,而在子類中覆蓋了這個方法,在子類中的這個方法默認情況下并不是同步的,而必須顯式地在子類的這個方法中加上synchronized關(guān)鍵字才可以。當然,還可以在子類方法中調(diào)用父類中相應(yīng)的方法,這樣雖然子類中的方法不是同步的,但子類調(diào)用了父類的同步方法,因此,子類的方法也就相當于同步了。
//在子類方法中加上synchronized關(guān)鍵字 class Parent {public synchronized void method() { }}class Child extends Parent {public synchronized void method() { }} ----//在子類方法中調(diào)用父類的同步方法class Parent {public synchronized void method() { }}class Child extends Parent {public void method() { super.method(); }}(3)修飾一個靜態(tài)方法
靜態(tài)方法時屬于類而不是屬于對象的,所以synchronized修飾的靜態(tài)方法鎖住的是這個類的所有對象。
//其作用的范圍是整個方法,作用的對象是這個類的所有對象; public synchronized static void func2(){//TODO }(4)修飾一個代碼塊
一個線程訪問一個對象中的synchronized(this)同步代碼塊時,其他試圖訪問該對象的線程將被阻塞
public class ClassB {//類的實例對象: 同步代碼塊,鎖住的是該類的實例對象//其作用范圍是大括號{}括起來的代碼塊,作用的對象是調(diào)用這個代碼塊的對象synchronized(this) {//TODO}//類對象: 同步代碼塊,鎖住的是該類的類對象(該類的所有實例),所有實例對象共用一把類鎖synchronized(ClassB.class) {//TODO}//任意實例對象Object: 同步代碼塊,鎖住的是配置的實例對象//String對象作為鎖String lock = "";synchronized(lock) {//TODO} }二、synchronized底層實現(xiàn)
在Java里面,最基本的互斥同步手段是synchronized
(1)對象
在HotSpot中,對象在堆內(nèi)存中的存儲布局劃分為:對象頭(Header)、實例數(shù)據(jù)(Instance Data)和對齊填充(Padding)
- 對象頭:(實現(xiàn)synchronized的鎖對象的基礎(chǔ))
- 存儲對象自身的運行時數(shù)據(jù)(如hashCode,GC分代年齡,鎖狀態(tài)標志,線程持有的鎖等)-----MarkWord
- 類型指針:對象指向它的類型元數(shù)據(jù)的指針,Java虛擬機通過這個指針來確定該對象是哪個類的實例(如果對象是數(shù)據(jù),還必須在對象頭中存儲數(shù)組大小)
- Java虛擬機可以通過普通Java對象的元數(shù)據(jù)信息確定Java對象大小,但是如果數(shù)組的長度不確定的話,就無法通過元數(shù)據(jù)中的信息推斷出數(shù)組的大小
- 實例數(shù)據(jù):對象真正存儲的有效信息
- **對齊填充:**不是必然存在的,僅僅起著占位符的作用。HotSpot虛擬機中要求所有對象的大小必須是8字節(jié)的整數(shù)倍。對象頭部分已經(jīng)是8字節(jié)的整數(shù)倍了,如果對象實例數(shù)據(jù)部分沒有對齊的話,就需要通過對齊方式填充來補全。
(2)原理
- synchronized關(guān)鍵字經(jīng)過反編譯之后,會在同步塊的前后分別形成 monitorenter和monitorexit這兩個字節(jié)碼指令。~~這兩個字節(jié)碼指令都需要一個reference類型的參數(shù)來指明要鎖定和解鎖的對象。(這句話就不必說了)~~其中monitorenter指令指向同步代碼塊的開始位置,monitorexit指令則指明同步代碼塊的結(jié)束位置。
- 當代碼執(zhí)行到monitorenter 指令時,將會嘗試獲取該對象對應(yīng)的Monitor的所有權(quán),即嘗試獲得該對象的鎖。當該對象的 monitor 的進入計數(shù)器為 0,那線程可以成功取得 monitor,并將計數(shù)器值設(shè)置為 1,取鎖成功。如果當前線程已經(jīng)擁有該對象monitor的持有權(quán),那它可以重入這個 monitor ,計數(shù)器的值也會加 1。
- 與之對應(yīng)的執(zhí)行monitorexit指令時,鎖的計數(shù)器會減1。倘若其他線程已經(jīng)擁有monitor 的所有權(quán),那么當前線程獲取鎖失敗將被阻塞并進入到_WaitSet 中,直到等待的鎖被釋放為止。也就是說,當所有相應(yīng)的monitorexit指令都被執(zhí)行,計數(shù)器的值減為0,執(zhí)行線程將釋放 monitor(鎖),其他線程才有機會持有 monitor 。(當鎖計數(shù)器為0時,該鎖就會被釋放)
被synchronized修飾的同步塊對同一條線程來說是可重入的。這意味著同一線程反復(fù)進入同步塊也不會出現(xiàn)自己把自己鎖死的情況。
被synchronized修飾的同步塊在持有鎖的線程執(zhí)行完畢并釋放鎖之前,會無條件地阻塞后面其他線程的進入。這意味著無法像處理某些數(shù)據(jù)庫中的鎖那樣,強制已獲取鎖的線程釋放鎖;也無法強制正在等待鎖的線程中斷等待或超時退出。
**ACC_SYNCHRONIZED標識,**該標識指明了該方法是一個同步方法,JVM通過該ACC_SYNCHRONIZED訪問標志來辨別一個方法是否聲明為同步方法,從而執(zhí)行相應(yīng)的同步調(diào)用。
javac----編譯.java文件成為字節(jié)碼文件(class文件)
java-----執(zhí)行字節(jié)碼文件(.class文件)
javap—JDK自帶的反匯編器,可以查看Java編譯器為我們生成的字節(jié)碼
(4)synchronized的缺點
從執(zhí)行成本的角度看,持有鎖是一個重量級(Heavy-Weight)的操作。在第10章中我們知道了在主流Java虛擬機實現(xiàn)中,Java的線程是映射到操作系統(tǒng)的原生內(nèi)核線程之上的,如果要阻塞或喚醒一條線程,則需要操作系統(tǒng)來幫忙完成,這就不可避免地陷入用戶態(tài)到核心態(tài)的轉(zhuǎn)換中,進行這種狀態(tài)轉(zhuǎn)換需要耗費很多的處理器時間。尤其是對于代碼特別簡單的同步塊(譬如被synchronized修飾的getter()或setter()方法),狀態(tài)轉(zhuǎn)換消耗的時間甚至?xí)扔脩舸a本身執(zhí)行的時間還要長。
7. MVP架構(gòu)
8. 數(shù)組實現(xiàn)一個ArrayList
9. 數(shù)組和鏈表有什么區(qū)別
數(shù)組下標越界不同于內(nèi)存溢出(嗚嗚嗚,我居然答混了)
LinkedList是采用鏈表的方式來實現(xiàn)List接口的,它本身有自己特定的方法,如: addFirst(),addLast(),getFirst(),removeFirst()等. 由于是采用鏈表實現(xiàn)的,因此在進行insert和remove動作時在效率上要比ArrayList要好得多!適合用來實現(xiàn)Stack(堆棧)與Queue(隊列),前者先進后出,后者是先進先出.
10. 棧和隊列有什么區(qū)別
棧和隊列的區(qū)別
- 棧是限定只能在表的一端進行插入和刪除操作的線性表
- 隊列是限定只能在表的一端進行插入和在另一端進行刪除操作的線性表。
- 線性表允許在表內(nèi)任一位置進行插入和刪除
- 棧只能從頭部取數(shù)據(jù),也就最先放入的需要遍歷整個棧最后才能取出來,而且在遍歷數(shù)據(jù)的時候還得為數(shù)據(jù)開辟臨時空間,保持數(shù)據(jù)在遍歷前的一致性
- 隊列則不同,它基于地址指針進行遍歷,而且可以從頭或尾部開始遍歷,但不能同時遍歷,無需開辟臨時空間,因為在遍歷的過程中不影響數(shù)據(jù)結(jié)構(gòu),速度要快的多
要比ArrayList要好得多!適合用來實現(xiàn)Stack(堆棧)與Queue(隊列),前者先進后出,后者是先進先出.
10. 棧和隊列有什么區(qū)別
棧和隊列的區(qū)別
- 棧是限定只能在表的一端進行插入和刪除操作的線性表
- 隊列是限定只能在表的一端進行插入和在另一端進行刪除操作的線性表。
- 線性表允許在表內(nèi)任一位置進行插入和刪除
- 棧只能從頭部取數(shù)據(jù),也就最先放入的需要遍歷整個棧最后才能取出來,而且在遍歷數(shù)據(jù)的時候還得為數(shù)據(jù)開辟臨時空間,保持數(shù)據(jù)在遍歷前的一致性
- 隊列則不同,它基于地址指針進行遍歷,而且可以從頭或尾部開始遍歷,但不能同時遍歷,無需開辟臨時空間,因為在遍歷的過程中不影響數(shù)據(jù)結(jié)構(gòu),速度要快的多
總結(jié)
以上是生活随笔為你收集整理的快手Android一面复盘的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: uniapp ios端云打包失败,求助
- 下一篇: CSS实现最简洁的加载动画