1. 程式人生 > 程式設計 >2019年末尾總結面試常問的基礎22道Java面試題,值得收藏學習!

2019年末尾總結面試常問的基礎22道Java面試題,值得收藏學習!

1)集合類:List和Set比較,各自的子類比較(ArrayList,Vector,LinkedList;HashSet,TreeSet)

List:元素是有順序的,元素可以重複因為每個元素有自己的角標(索引)   |-- ArrayList:底層是陣列結構,特點是:查詢很快,增刪稍微慢點,執行緒不同步:A執行緒將元素放在索引0位置,CPU排程執行緒A停止,B執行,也將元素放在索引0位置,當A和B同時執行的時候Size就程式設計了2.   |-- LinkedList:底層使用的是連結串列資料結構,特點是:增刪很快,查詢慢。執行緒不安全,執行緒安全問題是由多個執行緒同時寫或同時讀寫同一個資源造成的。   |--Vector:底層是陣列資料結構,執行緒同步,Vector的方法前面加了synchronized關鍵字,被ArrayList代替了,現在用的只有他的列舉。

Set:元素是無序的,且不可以重複(存入和取出的順序不一定一致),執行緒不同步。set底層是使用Map實現的,故可以通過ConcurrentHashMap的方式變通實現執行緒安全的Set。 |--HashSet:底層是雜湊表資料結構。根據hashCode和equals方法來確定元素的唯一性。 hashCode和equals:作用一樣,都是用來比較兩個物件是否相等一致。 equals比較的比較全面,而利用hashCode()進行對比,則只要生成一個hash值進行比較久可以了,效率高。 equal()相等的兩個物件他們的hashCode()肯定相等,也就是equal()是絕對可靠的。 hashCode()相等的兩個物件他們的equal()不一定相等,hashCode()不是絕對可靠的。

Map:這個集合是儲存鍵值對的,一對一對往裡存,而且要確保鍵的唯一性(01,張三)這樣的形式打印出來就是  01=張三 |--HashTable:底層是雜湊表資料結構,不可以存入null鍵和null值,該集合執行緒是同步的,效率比較低。出現於JDK1.0。執行緒安全,使用synchronized鎖住整張Hash表實現執行緒安全,即每次鎖住整張表讓執行緒獨佔。 |--HashMap:底層是雜湊表資料結構,可以存入null鍵和null值,執行緒不同步,效率較高,代替了HashTable,出現於JDK 1.2 |--TreeMap:底層是二叉樹資料結構,執行緒不同步,可以用於對map集合中的鍵進行排序 ConcurrentHashMap:執行緒安全,允許多個修改操作併發進行,其關鍵在於使用了鎖分離技術,它使用了多個鎖來控制對hash表的不同部分進行的修改。ConcurrentHashMap內部使用段(Segment)來表示這些不同的部分,每個段其實就是一個小的Hashtable,它們有自己的鎖。只要多個修改操作發生在不同的段上,它們就可以併發進行。 當兩個物件需要對比的時候,首先用hashCode()去對比,如果不一樣,則表示這兩個物件肯定不相等(也就不用再比較equal(0)了),如果hashCode()相同,再比較equal(),如果equal()相同,那兩個物件就是相同的。 |--TreeSet:可以對Set集合中的元素進行排序(自然循序),底層的資料結構是二叉樹,

2)HashMap的底層實現,之後會問ConcurrentHashMap的底層實現

HashMap實際上是一個“連結串列雜湊”的資料結構,即陣列和連結串列的結合體。允許使用null值和null鍵。

HashMap底層就是一個陣列結構,陣列中的每一項又是一個連結串列。當新建一個HashMap的時候,就會初始化一個陣列。

HashMap是基於hash演演算法實現的,通過put(key,value)儲存物件到HashMap中,也可以通過get(key)從HashMap中獲取物件。

當我們使用put的時候,首先HashMap會對key的hashCode()的值進行hash計算,根據hash值得到這個元素在陣列中的位置,將元素儲存在該位置的連結串列上。 當我們使用get的時候,首先HashMap會對key的hashCode()的值進行hash計算,根據hash值得到這個元素在陣列中的位置,將元素從該位置上的連結串列中取出

當多執行緒的情況下,可能產生條件競爭。當重新調整HashMap大小的時候,確實存在條件競爭,如果兩個執行緒都發現HashMap需要重新調整大小了, 它們會同時試著調整大小。在調整大小的過程中,儲存在連結串列中的元素的次序會反過來,因為移動到新的陣列位置的時候, HashMap並不會將元素放在LinkedList的尾部,而是放在頭部,這是為了避免尾部遍歷(tail traversing)。如果條件競爭發生了,那麼就死迴圈了

  • ConcurrentHashMap基於雙陣列和連結串列的Map介面的同步實現
  • ConcurrentHashMap中元素的key是唯一的、value值可重複
  • ConcurrentHashMap不允許使用null值和null鍵
  • ConcurrentHashMap是無序的

為什麼使用ConcurrentHashMap: 我們都知道HashMap是非執行緒安全的,當我們只有一個執行緒在使用HashMap的時候,自然不會有問題,但如果涉及到多個執行緒,並且有讀有寫的過程中,HashMap就會fail-fast。要解決HashMap同步的問題,我們的解決方案有:Hashtable 、Collections.synchronizedMap(hashMap)  這兩種方式基本都是對整個hash表結構加上同步鎖,這樣在鎖表的期間,別的執行緒就需要等待了,無疑效能不高,所以我們引入ConcurrentHashMap,既能同步又能多執行緒訪問

ConcurrentHashMap的資料結構: ConcurrentHashMap的資料結構為一個Segment陣列,Segment的資料結構為HashEntry的陣列,而HashEntry存的是我們的鍵值對,可以構成連結串列。可以簡單的理解為陣列裡裝的是HashMap

3)如何實現HashMap順序儲存:可以參考LinkedHashMap的底層實現

LinkedHashMap底層使用雜湊表與雙向連結串列來儲存所有元素,它維護著一個執行於所有條目的雙向連結串列(如果學過雙向連結串列的同學會更好的理解它的原始碼),此連結串列定義了迭代順序,該迭代順序可以是插入順序或者是訪問順序

  1. 按插入順序的連結串列:在LinkedHashMap呼叫get方法後,輸出的順序和輸入時的相同,這就是按插入順序的連結串列,預設是按插入順序排序
  2. 按訪問順序的連結串列:在LinkedHashMap呼叫get方法後,會將這次訪問的元素移至連結串列尾部,不斷訪問可以形成按訪問順序排序的連結串列。簡單的說,按最近最少訪問的元素進行排序(類似LRU演演算法)

4)String,StringBuffer和StringBuilder的區別

執行速度快慢為:StringBuilder > StringBuffer > String

String最慢的原因: String為字串常量,而StringBuilder和StringBuffer均為字串變數,即String物件一旦建立之後該物件是不可更改的,但後兩者的物件是變數,是可以更改的。

  • String:適用於少量的字串操作的情況,
  • StringBuilder:適用於單執行緒下在字元緩衝區進行大量操作的情況(執行緒不安全)
  • StringBuffer:適用多執行緒下在字元緩衝區進行大量操作的情況(執行緒安全)

5)Object的方法有哪些:比如有wait方法,為什麼會有wait、notify、notifuAll

  1. 使用wait()、notify()和notifyAll()時需要首先對呼叫物件加鎖
  2. 呼叫wait()方法後,執行緒狀態會從RUNNING變為WAITING,並將當執行緒加入到lock物件的等待佇列中
  3. 呼叫notify()或者notifyAll()方法後,等待在lock物件的等待佇列的執行緒不會馬上從wait()方法返回,必須要等到呼叫notify()或者notifyAll()方法的執行緒將lock鎖釋放,等待執行緒才有機會從等待佇列返回。這裡只是有機會,因為鎖釋放後,等待執行緒會出現競爭,只有競爭到該鎖的執行緒才會從wait()方法返回,其他的執行緒只能繼續等待
  4. notify()方法將等待佇列中的一個執行緒移到lock物件的同步佇列,notifyAll()方法則是將等待佇列中所有執行緒移到lock物件的同步佇列,被移動的執行緒的狀態由WAITING變為BLOCKED
  5. wait()方法上等待鎖,可以通過wait(long timeout)設定等待的超時時間

6)wait和sleep的區別,必須理解

sleep方法屬於執行緒,wait方法屬於物件 sleep休眠當前執行緒,不會釋放物件鎖,wait使當前執行緒進入等待狀態,釋放物件鎖,只有針對此物件呼叫notify()方法(且共享物件資源釋放)後本執行緒才會繼續執行

7)JVM的記憶體結構,JVM的演演算法

JVM記憶體結構主要有三大塊:堆記憶體、方法區和棧,幾乎所有的物件例項都存放在堆裡,如果在堆中沒有記憶體完成例項分配,並且堆也無法再擴充套件時,將會丟擲OutOfMemoryError異常。 方法區用於儲存已被虛擬機器器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料,當方法區無法滿足記憶體分配需求時,將丟擲OutOfMemoryError異常。 每個方法被執行的時候都會同時建立一個棧幀(Stack Frame)用於儲存區域性變量表、操作棧、動態連結、方法出口等資訊。 每一個方法被呼叫直至執行完成的過程,就對應著一個棧幀在虛擬機器器棧中從入棧到出棧的過程。  如果執行緒請求的棧深度大於虛擬機器器所允許的深度,將丟擲StackOverflowError異常。

8)強引用,軟引用和弱引用的區別

強引用: 以前我們使用的大部分引用實際上都是強引用,這是使用最普遍的引用。如果一個物件具有強引用,那就類似於必不可少的生活用品,垃圾回收器絕不會回收它。 當記憶體空間不足,Java虛擬機器器寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會靠隨意回收具有強引用的物件來解決記憶體不足問題。

軟引用: 如果一個物件只具有軟引用,那就類似於可有可物的生活用品。如果記憶體空間足夠,垃圾回收器就不會回收它,如果記憶體空間不足了,就會回收這些物件的記憶體

弱引用: 弱引用與軟引用的區別在於:只具有弱引用的物件擁有更短暫的生命週期。 在垃圾回收器執行緒掃描它所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體

總結:

  • 強引用:String str = “abc”; list.add(str);
  • 軟引用:如果弱引用物件回收完之後,記憶體還是報警,繼續回收軟引用物件
  • 弱引用:如果虛引用物件回收完之後,記憶體還是報警,繼續回收弱引用物件
  • 虛引用:虛擬機器器的記憶體不夠使用,開始報警,這時候垃圾回收機制開始執行System.gc(); String s = “abc”;如果沒有物件回收了, 就回收沒虛引用的物件

9)陣列在記憶體中如何分配

當一個物件使用關鍵字“new”建立時,會在堆上分配記憶體空間,然後返回物件的引用,這對陣列來說也是一樣的,因為陣列也是一個物件 簡單的值型別的陣列,每個陣列成員是一個引用(指標),引用到棧上的空間

10)用過哪些設計模式,手寫一個(除單例)

1.懶漢模式

    public class SingletonDemo {
        private static SingletonDemo instance;
        private SingletonDemo(){}
        public static SingletonDemo getInstance(){
            if(instance==null){
                instance=new SingletonDemo();
            }
            return instance;
        }
    }
複製程式碼

2.餓漢模式

    public class SingletonDemo {
        private static SingletonDemo instance=new SingletonDemo();
        private SingletonDemo(){}
        public static SingletonDemo getInstance(){
            return instance;
        }
    }
複製程式碼

3.簡單工廠模式

麵條工廠:

    public abstract class INoodles {
        /**
         * 描述每種麵條啥樣的
         */
        public abstract void desc();
    }
複製程式碼

先來一份蘭州拉麵(具體的產品類):

    public class LzNoodles extends INoodles {
        @Override
        public void desc() {
            System.out.println("蘭州拉麵 上海的好貴 家裡才5 6塊錢一碗");
        }
    }
複製程式碼

程式設計師加班必備也要吃泡麵(具體的產品類):

    public class PaoNoodles extends INoodles {
        @Override
        public void desc() {
            System.out.println("泡麵好吃 可不要貪杯");
        }
    }
複製程式碼

準備工作做完了,我們來到一家“簡單面館”(簡單工廠類),選單如下:

    public class SimpleNoodlesFactory {
        public static final int TYPE_LZ = 1;//蘭州拉麵
        public static final int TYPE_PM = 2;//泡麵
        public static INoodles createNoodles(int type) {
            switch (type) {
                case TYPE_LZ:
                    return new LzNoodles();
                case TYPE_PM:
                    return new PaoNoodles();
                default:
                    return new PaoNoodles();
            }
        }
        /**
         * 簡單工廠模式
         */
        void creat(){
            INoodles noodles = SimpleNoodlesFactory.createNoodles(SimpleNoodlesFactory.TYPE_PM);
                noodles.desc();
        }
    } 
複製程式碼

11)springmvc的核心是什麼,請求的流程是怎麼處理的,控制反轉怎麼實現的aop和ioc

流程:使用者傳送請求給伺服器。url:user.do--->Dispatchservlet處理-->DispatchServlet通過HandleMapping呼叫這個url對應的Controller Controller執行完畢後,如果返回字串,則ViewResolver將字串轉化成相應的檢視物件;如果返回ModelAndView物件,該物件本身就包含了檢視物件資訊。 DispatchServlet將執檢視物件中的資料,輸出給伺服器並呈現給客戶

IOC控制反轉:典型的工廠模式,就是具有依賴注入功能的容器,是可以建立物件的容器,IOC容器負責例項化、定位、配置應用程式中的物件及建立這些物件間的依賴。 通常new一個例項,控制權由程式設計師控制,而"控制反轉"是指new例項工作不由程式設計師來做而是交給Spring容器來做。。在Spring中BeanFactory是IOC容器的實際代表者

AOP依賴注入:典型的代理模式,面向切面程式設計將程式中的交叉業務邏輯(比如安全,日誌,事務),封裝成一個切面,然後注入到目標業務邏輯中去。

aop框架具有的兩個特徵:

  • 各個步驟之間的良好隔離性
  • 原始碼無關性

12)mybatis如何處理結果集:反射,建議看看原始碼

通過在mapper配置檔案裡配置的屬性對照反射進物件裡

13)java的多型表現在哪裡

多型是同一個行為具有多個不同表現形式或形態的能力。 多型就是同一個介面,使用不同的例項而執行不同操作 比如同一個印表機,可以列印黑白的紙張也可以列印彩色的,同樣是人,卻有黑人白人之分

14)介面有什麼用

介面是一種規範,在這裡舉兩個例子

  • 介面就比如KFC,你一聽KFC就知道是賣炸雞薯條的,他可以有不同的分店,也可以有自己的創新食品(多型),但是招牌炸雞、雞肉卷、全家桶什麼的肯定會有,你不用進店看選單就知道他有,但如果不叫KFC換成炸雞店你也可以吃到炸雞,但是你不進店看選單你不知道他具體都賣的有哪些食品,這就是介面的好處
  • 比如電插孔,多是兩孔和三孔的那種,如果沒有這種規範那每家電器公司都來做一種插孔的話,試想一下插頭換了怎麼辦?是不是隻能買原裝的來替換了

15)說說http,https協議

http是一種超文字協議,預設埠80,以明文傳輸。 https是http協議的安全版,安全基礎是SSL,以密文傳輸

16)osi五層網路協議

  • 應用層
  • 傳輸層
  • 網路層
  • 資料鏈路層
  • 物理層

17)用過哪些加密演演算法

  • 對稱加密
  • 非對稱加密演演算法
  • Base64加密演演算法
  • MD5加密演演算法
  • SHA1加密演演算法

18)說說tcp三次握手,四次揮手

  1. 客戶端向伺服器傳送一個syn包,進入傳送狀態
  2. 伺服器收到syn包,確認客戶的syn,並向客戶端傳送syn+ack包,進入接受狀態
  3. 客戶端接受的來自服務的的syn包資訊,向服務的發出ack包,次數兩者進入tcp連線成功狀態

19)cookie和session的區別,分散式環境怎麼儲存使用者狀態

cookie存在客戶端,session存在服務端

分散式Session的幾種實現方式

  1. 基於資料庫的Session共享
  2. 基於NFS共享檔案系統
  3. 基於memcached 的session,如何保證 memcached 本身的高可用性?
  4. 基於resin/tomcat web容器本身的session複製機制
  5. 基於TT/Redis 或 jbosscache 進行 session 共享。
  6. 基於cookie 進行session共享(唯一值token)

20)git,svn區別

Git是分散式的,而Svn不是分佈的 Git下載下來後,在OffLine狀態下可以看到所有的Log,SVN不可以 SVN的特點是簡單,只是需要一個放程式碼的地方時用是OK的,Git的特點版本控制可以不依賴網路做任何事情,對分支和合並有更好的支援

21)請寫一段棧溢位、堆溢位的程式碼

堆溢位,死迴圈存值,JVM就會丟擲OutOfMemoryError:java heap space異常

    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        int i=0;
        while(true){
            list.add(new byte[5*1024*1024]);
            System.out.println("分配次數:"+(++i));
        }
    }
棧溢位,棧空間不足——StackOverflowError例項

    public class StackSOFTest {
        int depth = 0;
        public void sofMethod(){
            depth ++ ;
            sofMethod();
        }
        public static void main(String[] args) {
            StackSOFTest test = null;
            try {
                test = new StackSOFTest();
                test.sofMethod();
            } finally {
                System.out.println("遞迴次數:"+test.depth);
            }
        }
    }
複製程式碼

22)ThreadLocal可以用來共享資料嗎

可以 ThreadLocal使用場合主要解決多執行緒中資料資料因併發產生不一致問題。ThreadLocal為每個執行緒的中併發訪問的資料提供一個副本,通過訪問副本來執行業務,這樣的結果是耗費了記憶體,單大大減少了執行緒同步所帶來效能消耗,也減少了執行緒併發控制的複雜度。 ThreadLocal和Synchonized都用於解決多執行緒併發訪問。但是ThreadLocal與synchronized有本質的區別。synchronized是利用鎖的機制,使變數或程式碼塊在某一時該只能被一個執行緒訪問。而ThreadLocal為每一個執行緒都提供了變數的副本,使得每個執行緒在某一時間訪問到的並不是同一個物件,這樣就隔離了多個執行緒對資料的資料共享。而Synchronized卻正好相反,它用於在多個執行緒間通訊時能夠獲得資料共享。 Synchronized用於執行緒間的資料共享,而ThreadLocal則用於執行緒間的資料隔離。