天天看點

java面試題

為什麼java隻有值傳遞?

  • 按值調用,表示方法接收的是調用者的值
  • 按引用調用,表示方法接收的是調用者提供的變量位址

    一個方法可以修改傳遞引用所對應的變量值,而不能修改傳遞值調用所對應的變量值

    java采用按值傳遞,也就是說,方法得到的是所有參數值的一個拷貝,也就是說,方法不能修改傳遞給它任何參數變量的内容。

ArrayList

ArrayList,他的底層結構是數組,在初始化的時候,資料量為零,當你add的時候,它會預設變為10,擴容機制是每次擴容為之前的1.5倍,他的特性是查詢比較快,删除效率比較低。

首先它是有序的,可重複的資料,作為List接口的主要實作者。

線程不安全,效率高,底層資料結構是數組,在jdk1.7的情況下,在new的時候,底層會建立一個長度為10的數組,但在1.8的時候,new的時候并不會建立數組,而是在第一次調用add的時候,底層才建立了一個長度為10的數組,也就是說在jdk1.7線程不安全,效率高,底層資料結構是數組,在jdk1.7的情況下。在new的時候,底層建立的是一個長度為10的Object數組,但在jdk1.8的時候,new的時候并不建立數組,而是在第一次調用add的時候,底層才建立了一個長度為10的數組。也就是說在jdk1.7的ArrayList的對象的建立類似于單例的餓漢式,在jdk1.8,ArrayList對象的建立類似于單例的懶漢式,延遲了數組的建立,節省記憶體

在擴容方面,是原來的1.5倍,同時将原來的數組中的資料複制到新的數組中。

LinkedList

LinkedList底層結構是一個帶有頭結點和尾結點的雙向連結清單,他提供了兩種插入方式,一個是頭插LikedFirst,還有一個是尾插LikedLast,他的特性非常适合增加 删除的場景,他的查詢在量大的時候比較慢

LinkedList為什麼查詢慢呢

他在查詢的時候,因為是連結清單查詢,從第一個開始一個一個的去比較

ArrayList 查詢的時候就會很快嗎

它是根據角标查詢的,是以會很快。

對于頻繁的插入和删除操作,效率比ArrayList高,底層使用雙向連結清單存儲。

Vector

他跟ArrayList一樣底層都是一個數組,它又有一點差別,它其中大部分的方法都被Synchronized關鍵字所修飾,是以說它是一個線程安全的,他擴容的時候與ArrayList還是有點差別的,他的擴容的大小是以兩倍擴容。

HashMap

HashMap首先從他的底層結構來說,在1.7和1.8的時候是不一樣的,在1.7的時候,底層資料結構是一個長度是16的數組和一個單連結清單,到了1.8的時候,改成了數組加上一個單連結清單或者紅黑樹的一個方式,在單連結清單和紅黑樹之間轉換,它的單連結清單的長度大于等于8,并且它的Hash桶大于等于64的時候,它會将單連結清單轉換為紅黑樹的形式存儲,要是紅黑樹的結點的數量小于或等于6的時候,它會重新再轉成一個單連結清單,這是它底層結構的一個變化,另外一個關于它的hash桶的數量,它的預設是16個,有一個預設的門檻值,門檻值預設是0.75,這關系到它的擴容

擴容它是首先檢查數組裡元素的個數,因為有一個loadFactor(負載因子)預設值是0.75,哈希桶預設是16,他的門檻值是16*0.75=12,當它哈希桶占用的容量大于12的時候,就會觸發一個擴容,他擴容成之前哈希桶容量的兩倍,并将原來的資料複制過來

LinkedHashMap

底層使用的結構與HashMap相同,因為LinedHashMap繼承與HashMap,他們的差別在于LinedHashMap内部提供了Entry,替換HashMap中的Node,能夠記錄添加元素的先後順序。

TreeMap

向TreeMap中添加key-value,要求key必須是由一個類建立的對象

因為要安裝key進行排序:自然排序,定制排序

HashSet

添加過程:

  1. 調用HashCode,計算哈希值,根據某種算法求出索引的位置
  2. 如果該位置沒有其他元素,就添加成功
  3. 有元素,但hash值不相同,添加成功
  4. 有元素,Hash值相同,但equal不相同,添加成功
  5. 有元素,Hash值相同且equal元素也相同,添加失敗

對于添加成功的情況2和情況3而言:元素a 與已經存在指定索引位置上資料以連結清單的方式存儲。

jdk 7:元素a放到數組中,指向原來的元素。

jdk 8:原來的元素在數組中,指向元素a

LinkedHashSet是HashSet的子類,在添加資料的同時,每個資料還維護了兩個引用,記錄此資料前一個資料和後一個資料

優點:對于頻繁的周遊操作,LinedHashSet效率高于HashSet

Array和ArrayList的不同點

  • Array可以包含基本類型和對象類型,ArrayList隻能包含對象類型
  • Array大小是固定的,ArrayList的大小是動态變化的

TreeSet

向TreeSet中添加的資料,要求是相同類的對象

  1. 兩種排列順序,自然排序(實作Comparable接口)和定制排序(Comparatot)
    • 自然排序中,比較兩個對象是否相同的标準為:compareTo()傳回0.不再是equals().
    • 定制排序中,比較兩個對象是否相同的标準為:compare()傳回0.不再是equals().

ConcurrentHashMap

ConcurrentHashMap具有鎖分段的技術,使用起來比HashMap和HashTable要優良。

Hashmap是線程不安全的,是以一般在并發環境下不建議使用;而HashTable雖然是線程安全的,但是由于使用了同步鎖,在并發環境下效率低下,因為所有通路HashTable的線程都必須競争同一把鎖。假如容器中有多把鎖,每把鎖用于用于鎖容器其中一部分的資料,那麼當線程通路容器裡不同資料段的資料時,線程間就不會存在鎖競争,進而可以有效的提高并發效率,這就是ConcurrentHashMap鎖所使用的鎖分段技術。

Map和ConcurrentHashMap的差別

  • Map是線程不安全的,put在多線程情況下,會形成環進而導緻死循環
  • ConcurrentHashMap是線程安全的,采用分段鎖機制,減少鎖的粒度

Synchronized

  • Synchronized修飾的靜态方法以及同步代碼塊鎖的是類,線程想要執行對應同步代碼,需要獲得類鎖
  • Synchronized修飾成員方法,線程擷取的是目前調用該方法的對象執行個體的對象鎖

volatile

  • 可見性:可以使得一個線程修改後的變量立即對其他線程可見
  • 不保證原子性
  • 禁止指令重排

sychronized和lock

sychronized是java的關鍵字,當它用來修飾一個方法或一個代碼塊的時候,能夠保證在同一時刻最多隻有一個線程執行該段。jdk1.5之後引入了自旋鎖,鎖粗化,輕量級鎖,偏向鎖來有優化關鍵字的性能。

Lock是一個接口,而synchronized是java中的關鍵字。sychronized在發生異常時,會自動釋放線程占有的鎖。是以不會導緻死鎖現象的發生,而Lock在發生異常時,如果沒有主動unLock()釋放鎖,則很有可能導緻死鎖的現象。是以使用Lock時需要在finally塊中釋放鎖。Lock可以讓等待鎖的線程響應中斷,而synchroniezd卻不行,使用synchrnized時,等待的線程會一直等待下去,不能夠響應中斷。通過Lock可以知道沒有成功擷取鎖,而synchronized卻無法辦到。

final

  • final修飾一個類時,表示該類無法被繼承
  • final修飾一個方法時,表示該方法不可被覆寫
  • final修飾一個屬性時,表示該屬性一旦被初始化便不可更改

static

  • static表示一個成員變量或者成員方法可以在沒有所屬類的執行個體變量的情況下能被通路
  • java中的static方法不能被覆寫。因為方法覆寫是基于運作時動态綁定的,而static方法是編譯時靜态綁定的,static方法跟類的任何執行個體都不相關

StringBuffer和StringBuilder

  • StringBuffer是線程安全的,StringBuildr是線程不安全的,底層實作上的話,StringBuffer其實就是比StringBuilder多了一個Synchronized修飾符

接口和抽象類的差別

不同點:

  1. 接口中所有的方法隐含都是抽象的,而抽象類中則可以同時包含抽象和非抽象的方法
  2. 類可以實作多個接口,但是隻能繼承一個抽象類
  3. java接口中聲明的變量都是final的,而抽象類可以包含非final的變量
  4. 接口的成員函數預設都是public的,抽象類的成員函數可以是private,protected或者是public
  5. 接口是絕對抽象的,不可以被執行個體化,抽象類也不可以被執行個體化,但是如果它包含main方法的話是可以被調用的

Comparable和Comparator

  1. 自然排序:使用Comparable接口重寫compareTo(obj)方法
  2. 定制排序:使用Comparator接口重寫compare(Object o1,Object o2)方法

反射

反射機制是指,在運作狀态中,對于任意一個類,可以知道它的任意屬性和方法,對于任意一個對象,都能夠調用它的任意方法和屬性,對于這種動态調用對象方法的功能成為java語言的反射機制

反射建立對象

  1. 通過類對象調用newInstance()方法
  2. getConstructor()或者getDeclaredConstructor()方法獲得構造器并調用其newInstance()方法建立對象。

擷取和設定對象的私有字段的值

通過類對象getDeclaredField()方法,再通過setAccessible(true)将其設定為可以通路,接下來通過get/set方法類擷取/設定字段的值了

程序和線程的差別

  • 程序是資源配置設定的最小機關,線程是cpu排程的最小機關

start和run的差別

  • start方法會建立一個新的子線程并啟動
  • run()方法隻是Thread的一個普通方法的調用(還是在主線程裡執行)

Thread和Runnable的去呗

  • Thread是一個類,Runnable是一個接口,Thread類實作了Runnable接口
  • Thread是實作了Runnable接口的類,通過start給Runnable的run方法賦上多線程的特性,因為java是單一繼承原則,為了提升系統的可擴充性,推薦通過使業務類實作Runnable接口将業務邏輯封裝在run方法裡

線程的狀态

  1. 建立
  2. 運作
  3. 限期等待
  4. 無限期等待
  5. 阻塞
  6. 結束

sleep和wait的差別

sleep()方法是Thread類方法,wait方法是Object類中定義的方法

sleep可以在任何地方使用

wait方法隻能在sychronized方法或者synchronized塊中使用

最本質的差别

sleep方法隻會讓出cpu,不會導緻鎖的變化,如果目前線程是擁有鎖的,那麼Thread.sleep方法不會讓線程釋放鎖,而隻會主動讓出cpu,讓出cpu,cpu就可以去執行其他任務了

wait方法不僅會讓出cpu,而且還會釋放以及占有的同步資源鎖,以便它在等待該資源的線程得到該資源進而去執行

notify和notifyAll的差別

  • notifyAll會讓所有處于等待池中的線程全部進入鎖池去競争擷取鎖的機會,沒有擷取到鎖的隻能等待其他機會去擷取鎖,不能主動回到線程池中。
  • notify 隻能随機選取一個處于等待池中的線程去競争擷取鎖的機會

yield函數

  • 當調用Thread.yield()方法時,會給線程排程器一個目前線程願意讓出CPU使用的暗示,但是線程排程器會忽略這個暗示。
  • yield方法對鎖的行為不會有影響的,不會讓目前線程讓出鎖

interrupt

調用interrupt方法,通知線程應該中斷了,該線程到底是中斷還是繼續執行,應該由這個線程自己去處理

如果線程處于被阻塞狀态,例如:sleep,wait,join狀态,那麼線程将立即退出被阻塞狀态,并抛出一個InterruptedException異常

如果線程處于正常活動狀态,那麼會将該線程的中斷辨別設定為true,被設定中斷标示的線程将繼續正常運作,不受影響