1. 程式人生 > >Java面試題集(86-115)

Java面試題集(86-115)

分享一個大神的人工智慧教程!http://blog.csdn.net/jiangjunshow

Java程式設計師面試題集(86-115)

摘要:下面的內容包括Struts 2和Hibernate的常見面試題,雖然Struts 2在2013年6月曝出高危漏洞後已經顯得江河日下,而Spring MVC的異軍突起更加加速了Struts 2的隕落,但面試中仍然有可能被問及和此框架相關的內容,畢竟Struts 2曾經被阿里巴巴、京東以及政府企業入口網站廣泛採用。另一方面,Hibernate目前仍然是ORM框架中的中堅力量,MyBatis在此領域也有不容忽視的一席之地,因此瞭解這兩個ORM框架對Java程式設計師是很有必要的。第一期釋出的Java面試題集中的150題並未包含Spring MVC和MyBatis的內容,後面會陸續為大家奉上。


86、Struts 2中,Action通過什麼方式獲得使用者從頁面輸入的資料,又是通過什麼方式把其自身的資料傳給檢視的?

答:Action從頁面獲取資料有三種方式:

①通過Action屬性接受引數

②通過域模型獲取引數

③通過模型驅動獲取引數 (ModelDriven<T>)

Action將資料存入值棧(Value Stack)中,檢視可以通過表示式語言(EL)從值棧中獲取資料。

 

87、簡述Struts 2是如何實現MVC架構模式的。

答:MVC架構模式要求應用程式的輸入、處理和輸出三者分離,將系統分成模型(Model)、檢視(View)、控制器(Controller)三個部分,通過控制器實現模型和檢視的解耦合,使得應用程式的開發和維護變得容易,如下圖所示。其中,模型代表了應用程式的資料和處理這些資料的規則,同時還可以為檢視提供的查詢儲存相關的狀態,通常由JavaBean來實現,模型的程式碼寫一次就可以被多個檢視重用;檢視用來組織模型的內容,它從模型中獲得資料,並將資料展現給使用者,在Struts 2中通常由JSP、Freemarker模板等來實現;控制器負責從客戶端接受請求並將其轉換為某種行為,行為完成後再選擇一個檢視來呈現給使用者,控制器本身不需要輸出任何內容,它只是接收請求並決定呼叫哪個模型元件去處理請求,StrutsPrepareAndExecuteFilter過濾器是Struts 2中的核心,它和一系列的Action構成了Struts 2中的控制器。


圖-1 MVC架構模式圖

88、闡述Struts 2如何實現使用者輸入驗證。在你做過的專案中使用的是那種驗證方式,為什麼選擇這種方式?

答:Struts 2可以使用手動驗證和自動驗證框架實現使用者輸入驗證。自動驗證框架是將對輸入的驗證規則放在XML檔案中,這種方式比較靈活,可以在不修改程式碼的情況下修改驗證的規則。

 

89、闡述Struts 2中的Action如何編寫?Action是否採用了單例?

答:Struts2的Action有三種寫法:

①POJO

②實現Action介面重寫execute()方法

③繼承ActionSupport類

Action沒有像Servlet一樣使用單例項多執行緒的工作方式,很明顯,每個Action要接收不同使用者的請求引數,這就意味著Action是有狀態的,因此在設計上使用了每個請求對應一個Action的處理方式。

 

90、Struts 2中的Action並沒有直接收到使用者的請求,那它為什麼可以處理使用者的請求,又憑什麼知道一個請求到底交給哪個Action來處理?

答:Struts2的核心過濾器接收到使用者請求後,會對使用者的請求進行簡單的預處理(例如解析、封裝引數),然後通過反射來建立Action例項,並呼叫Action中指定的方法來處理使用者請求。

要決定請求交給哪一個Action來處理有兩種方式:1利用配置檔案:可以在配置檔案中通過<action>標籤配置和請求對應的Action類以及要呼叫的方法;2利用約定:Struts2中可以使用約定(convention)外掛,例如約定xxx總是對應XxxAction,這是對約定優於配置理念的踐行。

 

91、你經常用到的Struts 2常量配置有哪些?

答:

①struts.i18n.encoding– 指定預設編碼

②struts.objectFactory/ struts.objectFactory.spring.autoWire – 物件工廠 / Spring的自動裝配方式(名字、型別)

③struts.devMode– 是否使用開發模式

④struts.locale– 指定預設區域,預設值是en_US

⑤struts.i18n.resources– 國際化使用的資原始檔

⑥struts.enable.DynamicMethodInvocation– 是否允許動態方法呼叫

 

92、簡述Struts2的異常處理機制。

答:Struts 2提供了宣告式的異常處理機制,可以在配置檔案中加入如下程式碼:

<global-exception-mappings>

<exception-mapping exception=”…” result=”…”/>

</global-exception-mappings>

 

93、說一下你對約定優於配置(CoC)的理解。

答:約定優於配置(convention over configuration),也稱作按約定程式設計,是一種軟體設計正規化,旨在減少軟體開發人員需做決定的數量,獲得簡單的好處而又不失靈活性。CoC本質是說,開發人員僅需規定應用中不符約定的部分。例如,如果模型中有個名為Sale的類,那麼資料庫中對應的表就會預設命名為sales。只有在偏離這一約定時,例如將該表命名為products_sold,才需寫有關這個名字的配置。如果您所用工具的約定與你的期待相符,便可省去配置;反之,你可以配置來達到你所期待的方式。遵循約定雖然損失了一定的靈活性,不能隨意安排目錄結構,不能隨意進行函式命名,但是卻能減少配置。更重要的是,遵循約定可以幫助開發人員遵守構建標準,包括各種命名的規範,這對團隊開發是非常有利的。

 

94、Struts2中如何實現I18N?

答:首先,為不同語言地區編寫不同的資原始檔;然後在Struts 2配置檔案中配置struts.i18n.custom.resources常量;在Action中可以通過呼叫getText()方法讀取資原始檔獲取國際化資源。

 

95、簡述攔截器的工作原理以及你在專案中使用過哪些自定義攔截器。

答:Struts 2中定義了攔截器的介面以及預設實現,實現了Interceptor介面或繼承了AbstractInterceptor的類可以作為攔截器。介面中的init()方法在攔截器被建立後立即被呼叫,它在攔截器的生命週期內只被呼叫一次,可以在該方法中對相關資源進行必要的初始化。每攔截一個請求,intercept()方法就會被呼叫一次。destory()方法將在攔截器被銷燬之前被呼叫, 它在攔截器的生命週期內也只被呼叫一次。

專案中使用過的有許可權攔截器、執行時間攔截器、令牌攔截器等。

 

96、如何在Struts2中使用Ajax功能?

答:以下是Struts 2中實現Ajax的可選方式:

①JSON plugin+ jQuery

②DOJO plugin

③DWR (DirectWeb Remoting)

 

97、談一下攔截器和過濾器的區別。

答:攔截器和過濾器都可以用來實現橫切關注功能,其區別主要在於:

①攔截器是基於Java反射機制的,而過濾器是基於介面回撥的。

②過濾器依賴於Servlet容器,而攔截器不依賴於Servlet容器。

③攔截器只能對Action請求起作用,而過濾器可以對所有請求起作用。

④攔截器可以訪問Action上下文、值棧裡的物件,而過濾器不能。

 

98、談一下Struts 1和Struts 2的區別。

答:



99、談一下你的專案選擇Struts 2的理由

答:①Action是POJO,沒有依賴Servlet API,具有良好的可測試性;②強大的攔截器簡化了開發的複雜度;③支援多種表現層技術:JSP、Freemarker等等;④靈活的驗證方式;⑤國際化(I18N)支援;⑥宣告式異常管理;⑦通過JSON外掛簡化Ajax;⑧通過Spring外掛跟Spring整合。

【補充】有人為選擇和評判Web框架提出了20條標準,包括:開發人員的工作效率(能用1-5天搭建一個CRUD頁面嗎)、開發人員的看法(用起來有意思嗎)、學習曲線(學了一個星期或一個月後能幹活嗎)、專案健康狀況(專案陷入絕境了嗎)、開發人員的充足性(能找到經驗豐富的開發人員嗎)、就業趨勢(將來能招到人嗎)、模板化(遵循DRY原則嗎)、元件(自帶日期選擇器之類的控制元件嗎)、Ajax(是否支援非同步呼叫和區域性重新整理)、外掛或附加項(能加入Facebook整合之類的功能嗎)、擴充套件性(預設的控制處理的併發使用者數能到500+嗎)、測試支援(能夠做測試驅動的開發嗎)、I18N和L10N(有多國語言、地域支援嗎)、校驗(能輕鬆校驗使用者輸入並迅速反饋嗎)、多程式語言支援(能夠同時使用多種語言開發嗎)、文件的質量(常見的用例和問題都在文件中有體現嗎)、出版的圖書(有沒有行業專家使用了它並分享了自己的使用經驗)、REST支援(能按HTTP協議的設計宗旨使用該協議嗎)、移動支援(是否很容易就能支援Android、iOS和其他移動智慧終端)、風險程度(能不能做大型專案)。很明顯,Java其實算不上最優秀的Web開發語言,但是它卻滿足了這20條中的很多,尤其是充足的開發人員、成熟的解決方案這兩點,而且Java的生態系統是非常良好的,這也是在Java已經顯得江河日下的今天大家仍然一如既往的將其作為開發語言首選的原因。

 

100、Struts 2中如何訪問HttpServletRequest、HttpSession和ServletContext三個域物件

答:有兩種方式:

①通過ServletActionContext的方法獲得;

②通過ServletRequestAware、SessionAware和ServletContextAware介面注入。

 

101、Struts 2中的預設包struts-default有什麼作用?

答:它定義了Struts 2內部的眾多攔截器和Result型別,而Struts 2很多核心的功能都是通過這些內建的攔截器實現,如:從請求中把請求引數封裝到action、檔案上傳和資料驗證等等都是通過攔截器實現的。在Struts 2的配置檔案中,自定義的包繼承了struts-default包就可以使用Struts 2為我們提供的這些功能。

 

102、簡述值棧(Value-Stack)的原理和生命週期

答:Value-Stack貫穿整個 Action 的生命週期,儲存在request作用域中,所以它和request的生命週期一樣。當Struts 2接受一個請求時,會建立ActionContext、Value-Stack和Action物件,然後把Action存放進Value-Stack,所以Action的例項變數可以通過OGNL訪問。由於Action是多例項的,和使用單例的Servlet不同,  每個Action都有一個對應的Value-Stack,Value-Stack存放的資料型別是該Action的例項,以及該Action中的例項變數,Action物件預設儲存在棧頂。

 

103、SessionFactory是執行緒安全的嗎?Session是執行緒安全的嗎,兩個執行緒能夠共享同一個Session嗎?

答:SessionFactory對應Hibernate的一個數據儲存的概念,它是執行緒安全的,可以被多個執行緒併發訪問。SessionFactory一般只會在啟動的時候構建。對於應用程式,最好將SessionFactory通過單例的模式進行封裝以便於訪問。Session是一個輕量級非執行緒安全的物件(執行緒間不能共享session),它表示與資料庫進行互動的一個工作單元。Session是由SessionFactory建立的,在任務完成之後它會被關閉。Session是持久層服務對外提供的主要介面。Session會延遲獲取資料庫連線(也就是在需要的時候才會獲取)。為了避免建立太多的session,可以使用ThreadLocal來取得當前的session,無論你呼叫多少次getCurrentSession()方法,返回的都是同一個session。

 

104、Session的load和get方法的區別是什麼?

答:主要有以下三項區別:

① 如果沒有找到符合條件的記錄, get方法返回null,load方法丟擲異常

②get方法直接返回實體類物件, load方法返回實體類物件的代理

③ 在Hibernate 3之前,get方法只在一級快取(內部快取)中進行資料查詢, 如果沒有找到對應的資料則越過二級快取, 直接發出SQL語句完成資料讀取; load方法則可以充分利用二級快取中的現有資料;當然從Hibernate 3開始,get方法不再是對二級快取只寫不讀,它也是可以訪問二級快取的

簡單的說,對於load()方法Hibernate認為該資料在資料庫中一定存在可以放心的使用代理來實現延遲載入,如果沒有資料就丟擲異常,而通過get()方法去取的資料可以不存在。

 

105、Session的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法有什麼區別?

答:Hibernate的物件有三種狀態:瞬態、持久態和遊離態。遊離狀態的例項可以通過呼叫save()、persist()或者saveOrUpdate()方法進行持久化;脫管狀態的例項可以通過呼叫 update()、0saveOrUpdate()、lock()或者replicate()進行持久化。save()和persist()將會引發SQL的INSERT語句,而update()或merge()會引發UPDATE語句。save()和update()的區別在於一個是將瞬態物件變成持久態,一個是將遊離態物件變為持久態。merge方法可以完成save()和update()方法的功能,它的意圖是將新的狀態合併到已有的持久化物件上或建立新的持久化物件。按照官方文件的說明:(1)persist()方法把一個瞬態的例項持久化,但是並"不保證"識別符號被立刻填入到持久化例項中,識別符號的填入可能被推遲到flush的時間;(2) persist"保證",當它在一個事務外部被呼叫的時候並不觸發一個Insert語句,當需要封裝一個長會話流程的時候,一個persist這樣的函式是需要的。(3)save"不保證"第2條,它要返回識別符號,所以它會立即執行Insert語句,不管是不是在事務內部還是外部。update()方法是把一個已經更改過的脫管狀態的物件變成持久狀態;lock()方法是把一個沒有更改過的脫管狀態的物件變成持久狀態。

 

106、闡述Session載入實體物件的過程。

答:Session載入實體物件的步驟是:

① Session在呼叫資料庫查詢功能之前, 首先會在快取中進行查詢, 在一級快取中, 通過實體型別和主鍵進行查詢, 如果一級快取查詢命中且資料狀態合法, 則直接返回

③ 如果一級快取沒有命中, 接下來Session會在當前NonExists記錄(相當於一個查詢黑名單, 如果出現重複的無效查詢可以迅速判斷, 從而提升效能)中進行查詢, 如果NonExists中存在同樣的查詢條件,則返回null

③ 對於load方法, 如果一級快取查詢失敗則查詢二級快取, 如果二級快取命中則直接返回

④ 如果之前的查詢都未命中, 則發出SQL語句, 如果查詢未發現對應記錄則將此次查詢新增到Session的NonExists中加以記錄, 並返回null

⑤ 根據對映配置和SQL語句得到ResultSet,並建立對應的實體物件

⑥ 將物件納入Session(一級快取)管理

⑦ 執行攔截器的onLoad方法(如果有對應的攔截器)

⑧將資料物件納入二級快取

⑨返回資料物件

 

107、Query介面的list方法和iterate方法有什麼區別?

答:

1) list方法無法利用快取,它對快取只寫不讀; iterate方法可以充分利用快取, 如果目標資料只讀或者讀取頻繁, iterate可以減少效能開銷

2) list方法不會引起N+1查詢問題, 而iterate方法會引起N+1查詢問題

 

108、Hibernate如何實現分頁查詢?

答:通過Hibernate實現分頁查詢,開發人員只需要提供HQL語句、查詢起始行數(setFirstresult()方法)和最大查詢行數(setMaxResult()方法),並呼叫Query介面的list()方法,Hibernate會自動生成分頁查詢的SQL語句。


109、鎖機制有什麼用?簡述Hibernate的悲觀鎖和樂觀鎖機制。

答:有些業務邏輯在執行過程中往往需要保證資料訪問的排他性,於是需要通過一些機制保證在此過程中資料被鎖住不會被外界修改,這就是所謂的鎖機制。

Hibernate支援悲觀鎖和樂觀鎖兩種鎖機制。悲觀鎖,顧名思義,它悲觀的認為在資料處理過程中一定存在修改資料的併發事務(包括本系統的其他事務或來自外部系統的事務),於是將處理的資料設定為鎖定狀態。悲觀鎖必須依賴資料庫本身的鎖機制才能真正保證資料訪問的排他性。樂觀鎖,顧名思義,對併發事務持樂觀態度(認為對資料的併發操作很少發生),通過更加寬鬆的鎖機制解決悲觀鎖排他的資料訪問對系統性能造成的嚴重影響。最常見的樂觀鎖是通過資料版本標識來實現的,讀取資料時獲得資料的版本號,更新資料時將此版本號加1,然後和資料庫表對應記錄的當前版本號進行比較,如果提交的資料版本號大於資料庫中此記錄的當前版本號則更新資料,否則認為是過期資料。

 

110、闡述實體物件的三種狀態以及轉換關係。

答:Hibernate中物件有三種狀態:臨時態(transient)、持久態(persistent)和遊狀態(detached),如下圖所示。


圖 Hibernate實體狀態轉換圖

  • 臨時狀態:當new一個實體物件後,這個物件處於臨時狀態,即這個物件只是一個儲存臨時資料的記憶體區域,如果沒有變數引用這個物件,則會被JVM的垃圾回收機制回收。這個物件所儲存的資料與資料庫沒有任何關係,除非通過Session的save或者saveOrUpdate把臨時物件與資料庫關聯,並把資料插入或者更新到資料庫,這個物件才轉換為持久物件。
  • 持久狀態:持久化物件的例項在資料庫中有對應的記錄,並擁有一個持久化標識。對持久化物件進行delete操作後,資料庫中對應的記錄將被刪除,那麼持久化物件與資料庫記錄不再存在對應關係,持久化物件變成臨時狀態。持久化物件被修改變更後,不會馬上同步到資料庫,直到資料庫事務提交。
  • 遊離狀態:當Session進行了close、clear或者evict後,持久化物件雖然擁有持久化識別符號和與資料庫對應記錄一致的值,但是因為會話已經消失,物件不在持久化管理之內,所以處於遊離狀態(也叫脫管狀態)。遊離狀態的物件與臨時狀態物件是十分相似的,只是它還含有持久化標識。

 

111、如何理解Hibernate的延遲載入機制。在實際應用中,延遲載入與session關閉的矛盾是如何處理的?

答:延遲載入就是並不是在讀取的時候就把資料載入進來,而是等到使用時再載入。Hibernate使用了虛擬代理機制實現延遲載入。返回給使用者的並不是實體本身,而是實體物件的代理。代理物件在使用者呼叫getter方法時就會去資料庫載入資料。但載入資料就需要資料庫連線。而當我們把會話關閉時,資料庫連線就同時關閉了。

延遲載入與session關閉的矛盾一般可以這樣處理:

① 關閉延遲載入特性。這種方式操作起來比較簡單,因為hibernate的延遲載入特性是可以通過對映檔案或者註解進行配置的,但這種解決方案存在明顯的缺陷。首先,出現no session or session was closed就證明了系統中已經存在主外來鍵關聯,如果去掉延遲載入的話,則每次查詢的開銷都會變得很大。

②在session關閉之前先獲取需要查詢的資料(Hibernate.initialize()方法)。

③ 使用攔截器(Interceptor)或過濾器(Filter)控制Session。

 

112、舉一個多對多關聯的例子,並說明如何實現多對多關聯對映。

答:例如:商品和訂單、學生和課程都是典型的多對多關係。可以在實體類上通過@ManyToMany註解配置多對多關聯或者通過對映檔案中的<set>和<many-to-many>標籤配置多對多關聯,但是通常情況下,可以將多對多關聯轉換成兩個多對一關聯來實現多對多關聯對映。

 

113、談一下你對繼承對映的理解。

答:繼承關係的對映策略有三種:

①每個繼承結構一張表(table per class hierarchy)

②每個子類一張表(table per subclass)

③ 每個具體類一張表(table per concrete class)

第一種方式屬於單表策略,其優點在於查詢子類物件的時候無需表連線,查詢速度快,適合多型查詢;缺點是可能導致表很大。後兩種方式屬於多表策略,其優點在於資料儲存緊湊,其缺點是需要進行連線查詢,不適合多型查詢。

 

114、簡述Hibernate常見優化策略。

答:

①制定合理的快取策略

② 採用合理的Session管理機制

③ 儘量使用延遲載入特性

④設定合理的批處理引數

⑤ 如果可以, 選用UUID作為主鍵生成器

⑥如果可以, 選用基於version的樂觀鎖替代悲觀鎖

⑦ 在開發過程中, 開啟hibernate.show_sql選項檢視生成的SQL, 從而瞭解底層的狀況;開發完成後關閉此選項

⑧ 資料庫本身的優化(合理的索引, 快取, 資料分割槽策略等)也會對持久層的效能帶來可觀的提升, 這些需要專業的DBA提供支援

 

115、談一談Hibernate的一級快取、二級快取和查詢快取。

答:Hibernate的Session提供了一級快取的功能,預設總是有效的,當應用程式儲存持久化實體、修改持久化實體時,Session並不會立即把這種改變提交到資料庫,而是快取在當前的Session中,除非顯示呼叫了Session的flush()方法或通過close()方法關閉Session。通過一級快取,可以減少程式與資料庫的互動,從而提高資料庫訪問效能。

SessionFactory級別的二級快取是全域性性的,所有的Session可以共享這個二級快取。不過二級快取預設是關閉的,需要顯示開啟並指定需要使用哪種二級快取實現類(可以使用第三方提供的實現)。一旦開啟了二級快取並設定了需要使用二級快取的實體類,SessionFactory就會快取訪問過的該實體類的每個物件,除非快取的資料超出了指定的快取空間。

一級快取和二級快取都是對整個實體進行快取,不會快取普通屬性,如果希望對普通屬性進行快取,可以使用查詢快取。查詢快取是將HQL或SQL語句以及它們的查詢結果作為鍵值對進行快取,對於同樣的查詢可以直接從快取中獲取資料。查詢快取預設也是關閉的,需要顯示開啟。

分享一個大神的人工智慧教程!http://blog.csdn.net/jiangjunshow