Java中高級面試題及答案【第三部分】

數據庫的三大範式

1 、第一範式(1NF)mysql

在任何一個關係數據庫中,第一範式(1NF)是對關係模式的基本要求,不知足第一範式(1NF)的數據庫就不是關係數據庫。web

所謂第一範式(1NF)是指數據庫表的每一列都是不可分割的基本數據項,同一列中不能有多個值,即實體中的某個屬性不能有多個值或者不能有重複的屬性。若是出現重複的屬性,就可能須要定義一個新的實體,新的實體由重複的屬性構成,新實體與原實體之間爲一對多關係。面試

在第一範式(1NF)中表的每一行只包含一個實例的信息。簡而言之,第一範式要求數據表中的每一列(每一個字段)必須是不可拆分的最小單元。redis

二、 第二範式(2NF)sql

第二範式(2NF)是在第一範式(1NF)的基礎上創建起來的,即知足第二範式(2NF)必須先知足第一範式(1NF)。第二範式(2NF)要求數據庫表中的每一個實例或行必須能夠被唯一地區分。爲實現區分一般須要爲表加上一個列,以存儲各個實例的唯一標識。數據庫

第二範式(2NF)要求實體的屬性徹底依賴於主關鍵字。所謂徹底依賴是指不能存在僅依賴主關鍵字一部分的屬性,若是存在,那麼這個屬性和主關鍵字的這一部分應該分離出來造成一個新的實體,新實體與原實體之間是一對多的關係。爲實現區分一般須要爲表加上一個列,以存儲各個實例的唯一標識。簡而言之,第二範式要求表中的全部列,都必須依賴於主鍵,而不能有任何一列與主鍵沒有關係。編程

3 、第三範式(3NF)設計模式

知足第三範式(3NF)必須先知足第二範式(2NF)。第三範式(3NF)要求一個數據庫表中不包含其它表中已包含的非主關鍵字信息。簡而言之,第三範式要求表中的每一列只與主鍵直接相關而不是間接相關,表中的每一列只能依賴於主鍵。數組

TCP和UDP的區別及其適用場景

首先說一下什麼是TCP和UDP:瀏覽器

TCP是傳輸控制協議,提供的是面向鏈接、可靠的字節流服務。

UDP是用戶數據報協議,是一個簡單的面向數據報的運輸層協議。

TCP和UDP的區別:

  • TCP面向鏈接的運輸層協議,UDP無鏈接

  • TCP是可靠交付,UDP是盡最大努力交付

  • TCP面向字節流,UDP面向報文

  • TCP是點對點鏈接的,UDP一對一,一對多,多對多均可以

  • TCP適合用於網頁,郵件等,UDP適合用於視頻,語音廣播等

TCP和UDP的適用場景:

整個數據要準確無誤的傳遞給對方,這每每用於一些要求可靠的應用,好比HTTP、HTTPS、FTP等傳輸文件的協議,POP、SMTP等郵件傳輸的協議。

當對網絡通信質量要求不高的時候,要求網絡通信速度能儘可能的快,好比視頻、廣播等,這時就可使用UDP。

說一下Spring的核心模塊

  • Spring Core【核心容器】:核心容器提供了Spring的基本功能。核心容器的核心功能是用IOC容器來管理類的依賴關係。

  • Spring AOP【面向切面】:Spring的AOP模塊提供了面向切面編程的支持。SpringAOP採用的是純Java實現,採用基於代理的AOP實現方案,AOP代理由IOC容器負責生成、管理,依賴關係也一併由IOC容器管理。

  • Spring ORM【對象實體映射】:提供了與多個第三方持久層框架的良好整合。

  • Spring DAO【持久層模塊】: Spring進一步簡化DAO開發步驟,能以一致的方式使用數據庫訪問技術,用統一的方式調用事務管理,避免具體的實現侵入業務邏輯層的代碼中。

  • Spring Context【應用上下文】:它是一個配置文件,爲Spring提供上下文信息,提供了框架式的對象訪問方法。

  • Spring Web【Web模塊】:提供了基礎的針對Web開發的集成特性。

  • Spring MVC【MVC模塊】:提供了Web應用的MVC實現。Spring的MVC框架並非僅僅提供一種傳統的實現,它提供了一種清晰的分離模型。

(轉發)forward與(重定向)redirect的區別

  • forward是服務器請求資源,服務器直接訪問目標地址的URL,把那個URL的響應內容讀取過來,而後把這些內容再發給瀏覽器。瀏覽器根本不知道服務器發送的內容從哪裏來的,因此它的地址欄仍是原來的地址。

  • redirect是服務端根據邏輯,發送一個狀態碼,告訴瀏覽器從新去請求那個地址,因此地址欄顯示的是新的URL。

  • forward轉發頁面和轉發到的頁面能夠共享request裏面的數據。

  • redirect不能共享數據。

  • redirect不只能夠重定向到當前應用程序的其餘資源,還能夠重定向到同一個站點上的其餘應用程序中的資源,甚至是使用絕對URL重定向到其餘站點的資源。

  • forward只能在同一個Web應用程序內的資源之間轉發請求。

  • forward是服務器內部的一種操做。

  • redirect是服務器通知客戶端,讓客戶端從新發起請求。

  • forward通常用於用戶登錄的時候根據角色轉發到相應的模塊。

  • redirect通常用於用戶註銷登錄時返回主頁面和跳轉到其它的網站等。

  • forward效率高。

  • redirect效率低。

redis經常使用的五種數據類型

1.String(字符串)

String是簡單的 key-value 鍵值對,value 不只能夠是 String,也能夠是數字。它是Redis最基本的數據類型,一個redis中字符串value最多能夠是512M。

2.Hash(哈希)

Redis hash 是一個鍵值對集合,對應Value內部實際就是一個HashMap,Hash特別適合用於存儲對象。

3.List(列表)

Redis 列表是簡單的字符串列表,按照插入順序排序。你能夠添加一個元素導列表的頭部(左邊)或者尾部(右邊)。

底層實現爲一個雙向鏈表,便可以支持反向查找和遍歷,更方便操做,不過帶來了部分額外的內存開銷,Redis內部的不少實現,包括髮送緩衝隊列等也都是用的這個數據結構。

4.Set(集合)

Redis的Set是String類型的無序集合,它的內部實現是一個 value永遠爲null的HashMap,實際就是經過計算hash的方式來快速排重的,這也是set能提供判斷一個成員是否在集合內的緣由。

5.zset(有序集合)

Redis zset 和 set 同樣也是String類型元素的集合,且不容許重複的成員,不一樣的是每一個元素都會關聯一個double類型的分數,用來排序。

多線程中sleep()、 wait()、 yield()和 join()的用法與區別

1.sleep()方法

在指定時間內讓當前正在執行的線程暫停執行,但不會釋放「鎖標誌」。不推薦使用。

sleep()使當前線程進入阻塞狀態,在指定時間內不會執行。

2.wait()方法

在其餘線程調用對象的notify或notifyAll方法前,當前線程處於等待狀態。線程會釋放掉它所佔有的「鎖標誌」,從而使別的線程有機會搶佔該鎖。

當前線程必須擁有當前對象鎖。若是當前線程不是此鎖的擁有者,會拋出IllegalMonitorStateException異常。

喚醒當前對象鎖的等待線程使用notify或notifyAll方法,也必須擁有相同的對象鎖,不然也會拋出IllegalMonitorStateException異常。

waite()和notify()必須在synchronized函數或synchronizedblock中進行調用。若是在non-synchronized函數和non-synchronizedblock中進行調用,雖然能編譯經過,但在運行時會發生IllegalMonitorStateException的異常。

3.yield方法

暫停當前正在執行的線程對象。

yield()只是使當前線程從新回到可執行狀態,因此執行yield()的線程有可能在進入到可執行狀態後立刻又被執行。

yield()只能使同優先級或更高優先級的線程有執行的機會。

4.join方法

等待該線程終止。

等待調用join方法的線程結束,再繼續執行。如:t.join();它主要用於等待t線程運行結束,若無此句,main則會執行完畢,致使結果不可預測。

線程和進程的區別?

進程和線程都是一個時間段的描述,是CPU工做時間段的描述,不過是顆粒大小不一樣。

進程是資源的分配和調度的一個獨立單元,而線程是CPU調度的基本單元。

同一個進程中能夠包括多個線程,而且線程共享整個進程的資源(寄存器、堆棧、上下文),一個進行至少包括一個線程。

進程的建立調用fork或者vfork,而線程的建立調用pthread_create,進程結束後它擁有的全部線程都將銷燬,而線程的結束不會影響同個進程中的其餘線程的結束。

線程是輕兩級的進程,它的建立和銷燬所須要的時間比進程小不少,全部操做系統中的執行功能都是建立線程去完成的。

線程中執行時通常都要進行同步和互斥,由於他們共享同一進程的全部資源。

線程有本身的私有屬性TCB,線程id,寄存器、硬件上下文,而進程也有本身的私有屬性進程控制塊PCB,這些私有屬性是不被共享的,用來標示一個進程或一個線程的標誌。

Array和ArrayList有何區別?何時更適合用Array?

存儲內容比較:

Array數組能夠包含基本類型和對象類型,ArrayList只能包含對象類型。

Array數組存放的必定是同種類型的元素,ArrayList能夠存聽任意對象(object的子類)。

空間大小比較:

Array的空間大小是固定的,空間不夠時也不能再次申請,因此須要事前肯定合適的空間大小。

ArrayList的空間是動態增加的,若是空間不夠,它會建立一個空間比原空間大的新數組,而後將全部元素複製到新數組中,接着拋棄舊數組。並且,每次添加新的元素的時候都會檢查內部數組的空間是否足夠。(比較麻煩的地方)。

方法上的比較:

ArrayList做爲Array的加強版,在方法上比Array更多樣化,好比添加所有addAll()、刪除所有removeAll()、返回迭代器iterator()等。

適用場景:

若是想要保存一些在整個程序運行期間都會存在並且不變的數據,咱們能夠將它們放進一個全局數組裏;可是若是咱們單純只是想要以數組的形式保存數據,而不對數據進行增長等操做,只是方便咱們進行查找的話,那麼,咱們就選擇ArrayList。

並且還有一個地方是必須知道的,就是若是咱們須要對元素進行頻繁的移動或刪除,或者是處理的是超大量的數據,那麼,使用ArrayList就真的不是一個好的選擇,由於它的效率很低,咱們能夠考慮選擇LinkedList。

Tomcat服務器優化(內存,併發鏈接數,緩存)

  • 內存優化:主要是對Tomcat啓動參數進行優化,咱們能夠在Tomcat啓動腳本中修改它的最大內存數等等。

  • 線程數優化:Tomcat的併發鏈接參數,主要在Tomcat配置文件中server.xml中配置,好比修改最小空閒鏈接線程數,用於提升系統處理性能等等。

  • 優化緩存:打開壓縮功能,修改參數,好比壓縮的輸出內容大小默認爲2KB,能夠適當的修改。

手寫一段單例中的懶漢模式和餓漢模式,而且簡單說一下他們的區別

懶漢模式:

public class LazySingleton {
   //懶漢式單例模式
   //在類加載時,不建立實例,所以類加載速度快,但運行時獲取對象的速度慢
   
   
   private static LazySingleton intance = null;
    //靜態私用成員,沒有初始化
   
   private LazySingleton()
   {
       //私有構造函數
   }
   
   public static synchronized LazySingleton getInstance()    
   //靜態,同步,公開訪問點
   {
       if(intance == null)
       {
           intance = new LazySingleton();
       }
       return intance;
   }
}
複製代碼

餓漢模式:

public class EagerSingleton {
   //餓漢單例模式
   //類加載較慢,但獲取對象的速度快
   
   private static EagerSingleton instance = new EagerSingleton();
 //靜態私有成員,已初始化
   
   private EagerSingleton() 
   {
       //私有構造函數
   }
   
   public static EagerSingleton getInstance()    
   //靜態,不用同步
   {
       return instance;
   }
}
複製代碼

懶漢模式和餓漢模式的比較:

餓漢模式的特色是加載類時比較慢,但運行時獲取對象的速度比較快,線程安全。餓漢式是線程安全的,在類建立的同時就已經建立好一個靜態的對象供系統使用,之後不在改變。

懶漢模式的特色是加載類時比較快,可是在運行時獲取對象的速度比較慢,線程不安全, 懶漢式若是在建立實例對象時不加上synchronized則會致使對象的訪問不是線程安全的。

抽象類和接口的區別,類能夠繼承多個類嗎,接口能夠繼承多個接口嗎,類能夠實現多個接口嗎?

  • 抽象類和接口都不能直接實例化,若是要實例化,抽象類變量必須指向實現全部抽象方法的子類對象,接口變量必須指向實現全部接口方法的類對象。

  • 抽象類要被子類繼承,接口要被類實現。  

  • 接口只能作方法聲明,抽象類中能夠作方法聲明,也能夠作方法實現

  • 接口裏定義的變量只能是公共的靜態的常量,抽象類中的變量是普通變量。

  • 抽象類裏的抽象方法必須所有被子類所實現,若是子類不能所有實現父類抽象方法,那麼該子類只能是抽象類。一樣,一個實現接口的時候,如不能所有實現接口方法,那麼該 類也只能爲抽象類。

  • 抽象方法只能申明,不能實現。abstract void abc();不能寫成abstract void abc(){}。

  • 抽象類裏能夠沒有抽象方法 。

  • 若是一個類裏有抽象方法,那麼這個類只能是抽象類 。

  • 抽象方法要被實現,因此不能是靜態的,也不能是私有的。

  • 接口可繼承接口,並可多繼承接口,但類只能單根繼承。

Tomcat,Apache,Jboss的區別?

Tomcat是servlet容器,用於解析jsp,servlet。是一個輕量級的高效的容器;缺點是不支持EJB,只能用於Java應用。

Apache是http服務器(web服務器),相似於IIS能夠用來創建虛擬站點,編譯處理靜態頁面。支持SSL技術,支持多個虛擬主機等功能。

Jboss是應用服務器,運行EJB的Javaee應用服務器,遵循Javaee規範,可以提供更多平臺的支持和更多集成功能,如數據庫鏈接,JCA等。其對servlet的支持是經過集成其餘servlet容器來實現的。

說出Servlet的生命週期,並說出Servlet和CGI的區別。

Servlet被服務器實例化後,容器運行其init方法,請求到達時運行其service方法,service方法自動派遣運行與請求對應的doXXX方法(doGet,doPost)等,當服務器決定將實例銷燬的時候調用其destroy()方法。

與CGI的區別在於Servlet處於服務器進程中,它經過多線程方式運行其service方法,一個實例能夠服務於多個請求,而且其實例通常不會銷燬,而CGI對每一個請求都產生新的進程,服務完成後就銷燬,因此效率上低於Servlet。

談談你對MVC的理解

MVC是Model—View—Controler的簡稱。即模型—視圖—控制器。MVC是一種設計模式,它強制性的把應用程序的輸入、處理和輸出分開。

MVC中的模型、視圖、控制器它們分別擔負着不一樣的任務。

視圖: 視圖是用戶看到並與之交互的界面。視圖向用戶顯示相關的數據,並接受用戶的輸入。視圖不進行任何業務邏輯處理。

模型: 模型表示業務數據和業務處理,至關於JavaBean。一個模型能爲多個視圖提供數據。這提升了應用程序的重用性。

控制器: 當用戶單擊Web頁面中的提交按鈕時,控制器接受請求並調用相應的模型去處理請求,而後根據處理的結果調用相應的視圖來顯示處理的結果。

MVC的處理過程:首先控制器接受用戶的請求,調用相應的模型來進行業務處理,並返回數據給控制器。控制器調用相應的視圖來顯示處理的結果。並經過視圖呈現給用戶。

如何避免瀏覽器緩存?

  • HTTP信息頭中包含Cache-Control:no-cache,pragma:no-cache,或Cache-Control:max-age=0等設置瀏覽器不用緩存請求。

  • 須要根據Cookie,認證信息等決定輸入內容的動態請求是不能被緩存的。

  • 通過HTTPS安全加密的請求不能被緩存。

  • HTTP響應頭中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的請求沒法被緩存 。

如何防止緩存雪崩?

緣由:

緩存雪崩多是由於數據未加載到緩存中,或者緩存同一時間大面積的失效,從而致使全部請求都去查數據庫,致使數據庫CPU和內存負載太高,甚至宕機。

對應解決:

  • 採用加鎖計數,或者使用合理的隊列數量來避免緩存失效時對數據庫形成太大的壓力。這種辦法雖然能緩解數據庫的壓力,可是同時又下降了系統的吞吐量。

  • 分析用戶行爲,儘可能讓失效時間點均勻分佈。避免緩存雪崩的出現。

  • 若是是由於某臺緩存服務器宕機,能夠考慮作主備,好比:redis主備,可是雙緩存涉及到更新事務的問題,update可能讀到髒數據,須要好好解決。

數據庫會死鎖嗎,舉一個死鎖的例子,mysql 怎麼解決死鎖。

產生死鎖的緣由主要是:

  • 系統資源不足。

  • 進程運行推動的順序不合適。

  • 資源分配不當等。

若是系統資源充足,進程的資源請求都可以獲得知足,死鎖出現的可能性就很低,不然就會因爭奪有限的資源而陷入死鎖。其次,進程運行推動順序與速度不一樣,也可能產生死鎖。

產生死鎖的四個必要條件:

  • 互斥條件:一個資源每次只能被一個進程使用。

  • 請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。

  • 不剝奪條件:進程已得到的資源,在末使用完以前,不能強行剝奪。

  • 循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。

這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不知足,就不會發生死鎖。

解決數據庫死鎖的方法:

  • 重啓數據庫。

  • 殺掉搶資源的進程。

Dubbo源碼使用了哪些設計模式?

工廠模式:

ExtenstionLoader.getExtenstionLoader(Protocol.class).getAdaptiveExtenstion()

裝飾器模式+責任鏈:

以provider的調用鏈爲例,具體調用鏈代碼是在protocolFilterWrapper的buildInvokeChain完成的,將註解中含有group=provider的Filter實現。

調用順序:

  1. EchoFilter

  2. ClassLoaderFilter

  3. GenericFilter

  4. ContextFilter

  5. ExceptionFilter

  6. TimeoutFilter

  7. MonitorFilter

  8. TraceFilter

裝飾器模式和責任鏈混合使用,Echo是回聲測試請求,ClassLoaderFilter則只是在其主功能上添加了功能。

觀察者模式:

provider啓動時須要與註冊中心交互,先註冊本身的服務,再訂閱本身的服務,訂閱時採用了觀察者模式,註冊中心每5s定時檢查是否有服務更新,有更新則向服務提供者發送1個notify消息後便可運行NotifyListener的notity方法,執行監聽器方法。

動態代理模式:

擴展JDK的ExtensionLoaderdeAdaptive實現,根據調用階段動態參數決定調用哪一個類,生成代理類的代碼是ExtensionLoader的createAdaptiveExtenstionClassLoader方法。


最後,歡迎關注微信公衆號「Java知音」,回覆面試題,送你一個大驚喜!
相關文章
相關標籤/搜索