天天看點

【面試】JAVA程式員面霸之初級知識

該系列文章也是來自于一篇CSDN的gitchat,将其中的答案補全,本篇是最簡單的初級知識。

1,面向對象和面向過程的差別和聯系。

    網上有個蓋澆飯和蛋炒飯的例子比較好。

    面向過程是蛋炒飯,混在一起,一個一個炒。

    面向對象是蓋澆飯,飯和菜分開,想要換掉飯或者菜都可以,需要啥加啥,需要啥方法,或者啥其他對象就加上,靈活性高,也更抽象。

    面向對象是高度抽象化,面向過程是一種自頂向下的過程。

2,對象和類的關系。

    對象是類的一個具體。它是一個實實在在存在的東西。

    對象是有真實的存儲空間的,類沒有,是抽象的。

3,Java的記憶體布局是怎樣的。另外一篇文章有

    JVM運作的時候會自動配置設定方法區和堆,然後每遇到一個線程,為之配置設定程式計數器、虛拟機棧、本地方法棧,終止時,後面三個也會釋放

    (1)方法區:常量、靜态變量、類的方法代碼(變量名,方法名,通路權限,傳回值等等)

    (2)堆:唯一一個程式員可以管理的記憶體區域。幾乎所有的對象執行個體都在這裡建立,是以該區域經常發生垃圾回收操作

    (3)程式計數器: 目前線程執行的位元組碼的位置訓示器。

    (4)虛拟機棧(棧記憶體):儲存局部變量、基本資料類型變量以及堆記憶體中某個對象的引用變量

    (5)本地方法棧 :為JVM提供使用native 方法的服務

4,Java中的工作記憶體之間是怎樣進行通信的。

    JAVA每一個線程都有自己的工作記憶體,線程對變量的所有操作(讀取、指派)都必須在工作記憶體中進行,而不能直接讀寫主記憶體中的變量。

    線程間變量值的傳遞均需要在主記憶體來完成。

    主記憶體與工作記憶體之間的具體互動協定,有八種操作:鎖定、解鎖、讀取、載入、使用、指派、存儲、寫入

    JMM對上述的互動指令有限制。總之,通過主記憶體進行線程通信與互動。

5,堆和棧是什麼關系,主要放什麼東西。

    棧:一些基本類型的變量和對象的引用變量;超過變量的作用域後,被JAVA釋放。

    堆:new建立的對象和數組;由Java虛拟機的自動垃圾回收器來管理

6,Java中安全機制是什麼。

    (1)類裝載器,對Java的沙箱起作用:防止惡意代碼區幹涉善意的代碼,守護了被信任的代碼的邊界,将代碼歸于某類(稱為保護域),該類确定了代碼可以進行哪種操作

    (2)Class檔案檢查器:保證程式的健壯性。包括Class檔案的結構檢查、類型資料的語義檢查、位元組碼驗證、符号引用的驗證。

    (3)内置的安全特性:類型安全的引用轉換;結構化的記憶體通路(無指針算法);自動垃圾手機(不必顯式地釋放被配置設定的記憶體);數組邊界檢查;空引用檢查。

7,String類的修飾符是什麼,為什麼是它。

    final。為了“效率” 和 “安全性” 的緣故。若 String允許被繼承, 由于它的高度被使用率, 可能會降低程式的性能

    這種類是非常底層的,和作業系統交流頻繁的,調用的作業系統本地的API,這就是著名的“本地方法調用”。

    那麼如果這種類可以被繼承的話,

    如果我們再把它的方法重寫了,往作業系統内部寫入一段具有惡意攻擊性質的代碼什麼的,這不就成了核心病毒了麼? 

8,重寫和重載的差別和意義。

    (1)都是JAVA多态性的不同表現。重寫展現的是子類與父類之間的多态性表現。重載展現的是一個類中多态性一種表現。

    (2)差別在于前者實作的是編譯時的多态性,而後者實作的是運作時的多态性。

        重載發生在一個類中,同名的方法如果有不同的參數清單(參數類型不同、參數個數不同或者二者都不同)則視為重載;

        重寫發生在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的參數清單,有相容的傳回類型,比父類被重寫方法更好通路,不能比父類被重寫方法聲明更多的異常(裡氏代換原則)。

        重載對傳回類型沒有特殊的要求,不能根據傳回類型進行區分。

    (3)重寫多态性起作用,對調用被重載過的方法可以大大減少代碼的輸入量,同一個方法名隻要往裡面傳遞不同的參數就可以擁有不同的功能或傳回值。

        重寫,使用多态是為了避免在父類裡大量重載引起代碼臃腫且難于維護

9,Final、Finally和Finalize的差別。

    (1)final修飾類,不能被繼承。

        修飾方法,若父類中final方法的通路權限為private,将導緻子類中不能直接繼承該方法

        是以,此時可以在子類中定義相同方法名的函數,此時不會與重寫final的沖突,而是在子類中重新地定義了新方法。

        final成員變量表示常量,隻能被指派一次,指派後其值不再改變。

    (2)finally,隻有try執行完畢才會執行,如果還沒執行就異常了不會執行try,更不會執行finally了

        第二種情況,try裡面有System.exit (0),終止JAVA虛拟機的執行。或者Kill,interrrupted

        易錯點,如果try,catch,finally都有return語句,finally會撤銷之前的return,以他為準。

    (3)Finalize,每個對象都有,被回收的時候調用,一般不用自己調用,而且容易出問題,不推薦。

10,Static代碼塊、普通代碼塊和構造代碼塊之間的調用順序,以及一些常用場景。

(1)靜态代碼塊是随着類的加載而加載,而構造代碼塊和構造方法都是随着對象的建立而加載

(2)構造代碼塊:直接在類中定義且沒有加static關鍵字的代碼塊稱為{}構造代碼塊。

    構造代碼塊在建立對象時被調用,每次建立對象都會被調用,并且構造代碼塊的執行次序優先于類構造函數。

(3)普通代碼塊就是方法,比如main函數裡面的直接用{}包着的代碼。

(4)場景:

    構造代碼塊的調用時機:每生成一個對象就會被調用一次,而且優先于構造函數被調用.

    靜态代碼塊的調用時機:在類加載到記憶體的時候調用一次!

    現在基本不用構造代碼塊了,也不推薦。

11,StringBudiler和StringBuffer的差別和聯系。

    (1)線程安全與不安全:string的對象是不可變的,安全。StringBuffer也安全,主要方法都加了synchronized。

    安全不安全導緻了速度不同,由慢到快:String<StringBuffer<StringBuilder

    舉例:String s8 = s6+s7;

    string最慢,因為底層使用StringBuilder首先初始化s6,在用append方法合并s7的字元串cd,在tostring新對象引用給s8.

    經常要操作字元串的情況下:如果字元串不改就用final string。如果可以改變,多線程使用StringBuffer,其他用StringBuilder

    (2)String s1= "abc";"abc"放在常量池。

    String s3 = new String("abc");"abc"常量池,string對象放在堆中(new String("abc")),對象引用s3放在棧中

12,繪制容器繼承關系圖。

(1)LIST:可以按照序号通路。ArrayList、linkedlist

(2)SET:HashSet、LinkedHashSet、TreeSet

(3)QUEUE:PriorityQueue

    說白了,HashSet就是限制了功能的HashMap,LinkedHashSet也是如此,treeset也是treemap

    資料不重複,底層是依賴hashmap的key值直接替代了新的value值,隻不過key值沒變罷了。

13,Collections和Collection的差別和聯系。

(1)Collection 是一個集合接口(集合類的一個頂級接口)。它提供了對集合對象進行基本操作的通用接口方法。

     Collection接口的意義是為各種具體的集合提供了最大化的統一操作方式,其直接繼承接口有List與Set。

     Collections則是集合類的一個工具類/幫助類,其中提供了一系列靜态方法,用于對集合中元素進行排序、搜尋以及線程安全等各種操作。

     比如,排序,混排,反轉,拷貝等等

(2)Collection使用,比如a.add(),remove(),isempty(),size(),contains()

     Collections使用,比如Collections.sort(list);

14,ArrayList和Vector的差別和聯系。

(1)Vector的方法都是同步的(Synchronized),是線程安全的,而ArrayList的方法不是,由于線程的同步必然要影響性能,是以,ArrayList的性能比Vector好。 

(2)當Vector或ArrayList中的元素超過它的初始大小時,Vector會将它的容量翻倍,而ArrayList隻增加50%的大小,這樣,ArrayList就有利于節約記憶體空間。

15,Set和Map有聯系嗎。

    看起來,set的實作是依賴map的key值的不重複,來達到set不重複的目的。

(1)set分為hashSet和treeSet,前者無序,無重複,後者實作了sortset接口,二叉樹進行排序

    list有arraylist和linkedList,arraylist有個兄弟vector,見上一條。linkedlist鍊路的,增删改查友善。查詢用arraylist,其他用linkedlist

(2)map是鍵值對.

    兩者關系,就是map種的Keys或者values可以生成set和Collection.

    Map中元素,可以将key序列、value序列單獨抽取出來。

    使用keySet()抽取key序列,将map中的所有keys生成一個Set。

    使用values()抽取value序列,将map中的所有values生成一個Collection。

16,HashMap和HashTable的差別和聯系。

    HashMap和Hashtable,線程安全ConcurrentHashMap結合了兩者并且鎖是細粒度的,16個桶,隻鎖一個

    一般來說,Hashtable效率低,而且基本不太維護了,已經被棄用

    ConcurrentHashMap結合兩者,多線程可以使用,并引入了分段鎖,每一把鎖用于鎖容器其中一部分資料,

    線程安全,防止髒資料和死鎖。

17,類型擦除是什麼意思。

(1)泛型資訊隻存在于代碼編譯階段,在進入 JVM 之前,與泛型相關的資訊會被擦除掉,專業術語叫做類型擦除。

    Java的泛型機制是在編譯級别實作的。編譯器生成的位元組碼在運作期間并不包含泛型的類型資訊。

    在編譯之後,List<Object>和List<String>将變成List,Object和String類型資訊對于JVM來說是不可見的。

(2)類型擦除可以讓泛型相容JDK1.5版本之前的版本。

18,HashSet和TreeSet的原理。

(1)HashSet的工作原理;計算對象的hashcode後,集合中查找是否包含哈希值相同的元素.有的話,挨個equals對比,都是false的話,插入資料

(2)TreeSet需要排序。二叉樹排序,int,string這種基礎類型可以直接排序,但是自定義的需要implement Comparable,重寫compareTo()方法

19,數組和字元串誰有Length方法,誰有Length屬性。

    數組是length屬性,字元串有Length的方法。

    數組a.length

    str.length()

20,能比較清楚地簡述各個集合類的特點及适用場合。摘自https://blog.csdn.net/qq_22118507/article/details/51576319

(1)線程安全集合類與非線程安全集合類 

    LinkedList、ArrayList、HashSet是非線程安全的,Vector是線程安全的;

    HashMap是非線程安全的,HashTable是線程安全的;

    StringBuilder是非線程安全的,StringBuffer是線程安全的。

(2)ArrayList與LinkedList的差別和适用場景

    Arraylist位址連續,記憶體裡連着放的,是以查詢效率較高,插入和删除效率低

    LinkedList基于連結清單的資料結構,位址是任意的,不需要連續的位址,新增和删除占優勢。專門用于操作表頭和表尾元素,可以當作堆棧、隊列和雙向隊列使用。

(3)ArrayList與Vector的差別和适用場景

    Vector是多線程安全的,有synchronized進行修飾,是以導緻效率比ArrayList低很多。

    如果集合中的元素的數目大于目前集合數組的長度時,在集合中使用資料量比較大的資料,用Vector有一定的優勢。

    因為Vector可以設定增長因子,是以在集合中使用資料量比較大的資料,用Vector有一定的優勢。

(4)HashSet與Treeset的适用場景

    HashSet是基于Hash算法實作的,其性能通常都優于TreeSet。

    為快速查找而設計的Set,我們通常都應該使用HashSe。

    在我們需要排序的功能時,我們才使用TreeSet。

(5)HashMap與TreeMap、HashTable的差別及适用場景

    HashMap是非同步的,效率上比HashTable要高。HashMap允許空鍵值,而HashTable不允許。

    HashMap:适用于Map中插入、删除和定位元素。 一般不怎麼用HashTable。

    Treemap:适用于按自然順序或自定義順序周遊鍵(key)。 

21,線程的幾個狀态。

(1)建立狀态,暫未調用start()之前

(2)就緒狀态,調用start()之後。建立線程運作的系統資源,并排程線程運作run()方法。

    當start()方法傳回後,線程就處于就緒狀态。不一定立即調用run()方法,線程還必須同其他線程競争CPU時間,隻有獲得CPU時間才可以運作線程。

    一個時刻隻有一個線程處于運作狀态。

(3)運作狀态。

    線程排程程式從可運作池中選擇一個線程作為目前線程時線程所處的狀态。這也是線程進入運作狀态的唯一一種方式。

    當線程獲得CPU時間後,它才進入運作狀态,真正開始執行run()方法。

(4)阻塞狀态。

    線程仍舊是活的,但是目前沒有條件運作。有機會還可以繼續運作,或者直接變成就緒狀态。

    怎麼進入阻塞呢?sleep、調用一個在I/O上被阻塞的操作、試圖得到一個鎖,而該鎖正被其他線程持有、等待某個觸發條件

    等于是暫時讓出CPU,這時其他處于就緒狀态的線程就可以獲得CPU時間,進入運作狀态。

(5)死亡狀态。

    ①run方法正常退出而自然死亡;

    ②一個未捕獲的異常終止了run方法而使線程猝死;

    isAlive判斷是否或者,true是運作或阻塞,false是就緒或者死亡。