摘要:這一部分主要包括了UML(統一建模語言)、面向對象的設計原則(六原則一法則)、GoF設計模式、企業級設計模式、JDBC(Java數據庫連接)、XML(可擴展標記語言)等知識。
71、UML是什麼?UML中有哪些圖?
答:UML是統一建模語言(Unified Modeling Language)的縮寫,它發表於1997年,綜合了當時已經存在的面向對象的建模語言、方法和過程,是一個支持模型化和軟件系統開發的圖形化語言,爲軟件開發的所有階段提供模型化和可視化支持。使用UML可以幫助溝通與交流,輔助應用設計和文檔的生成,還能夠闡釋系統的結構和行爲。UML定義了多種圖形化的符號來描述軟件系統部分或全部的靜態結構和動態結構,包括:用例圖(use case diagram)、類圖(class diagram)、時序圖(sequence diagram)、協作圖(collaboration diagram)、狀態圖(statechart diagram)、活動圖(activity diagram)、構件圖(component diagram)、部署圖(deployment diagram)等。在這些圖形化符號中,有三種圖最爲重要,分別是:用例圖(用來捕獲需求,描述系統的功能,通過該圖可以迅速的瞭解系統的功能模塊及其關係)、類圖(描述類以及類與類之間的關係,通過該圖可以快速瞭解系統)、時序圖(描述執行特定任務時對象之間的交互關係以及執行順序,通過該圖可以瞭解對象能接收的消息也就是說對象能夠向外界提供的服務)。
用例圖:
類圖:
時序圖:
72、寫一個單例類。
答:單例模式主要作用是保證在Java應用程序中,一個類只有一個實例存在。下面給出兩種不同形式的單例:
第一種形式:餓漢式單例
- public class Singleton {
- private Singleton(){}
- private static Singleton instance = new Singleton();
- public static Singleton getInstance(){
- return instance;
- }
- }
第二種形式:懶漢式單例
- public class Singleton {
- private static Singleton instance = null;
- private Singleton() {}
- public static synchronized Singleton getInstance(){
- if (instance==null) instance=newSingleton();
- return instance;
- }
- }
單例的特點:外界無法通過構造器來創建對象,該類必須提供一個靜態方法向外界提供該類的唯一實例。
【補充】用Java進行服務器端編程時,使用單例模式的機會還是很多的,服務器上的資源都是很寶貴的,對於那些無狀態的對象其實都可以單例化或者靜態化(在內存中僅有唯一拷貝),如果使用了spring這樣的框架來進行對象託管,Spring的IoC容器在默認情況下對所有託管對象都是進行了單例化處理的。
73、說說你所熟悉或聽說過的設計模式以及你對設計模式的看法。
答:在GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》中給出了三類(創建型[對類的實例化過程的抽象化]、結構型[描述如何將類或對象結合在一起形成更大的結構]、行爲型[對在不同的對象之間劃分責任和算法的抽象化])共23種設計模式,包括:Abstract Factory(抽象工廠模式),Builder(建造者模式),Factory Method(工廠方法模式),Prototype(原始模型模式),Singleton(單例模式);Facade(門面模式),Adapter(適配器模式),Bridge(橋樑模式),Composite(合成模式),Decorator(裝飾模式),Flyweight(享元模式),Proxy(代理模式);Command(命令模式),Interpreter(解釋器模式),Visitor(訪問者模式),Iterator(迭代子模式),Mediator(調停者模式),Memento(備忘錄模式),Observer(觀察者模式),State(狀態模式),Strategy(策略模式),Template Method(模板方法模式), Chain Of Responsibility(責任鏈模式)。
所謂設計模式,就是一套被反覆使用的代碼設計經驗的總結(情境中一個問題經過證實的一個解決方案)。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。設計模式使人們可以更加簡單方便的複用成功的設計和體系結構。將已證實的技術表述成設計模式也會使新系統開發者更加容易理解其設計思路。
【補充】設計模式並不是像某些地方吹噓的那樣是遙不可及的編程理念,說白了設計模式就是對面向對象的編程原則的實踐,面向對象的編程原則包括:
- 單一職責原則:一個類只做它該做的事情。(單一職責原則想表達的就是“高內聚”,寫代碼最終極的原則只有六個字“高內聚、低耦合”,就如同葵花寶典或辟邪劍譜的中心思想就八個字“欲練此功必先自宮”,所謂的高內聚就是一個代碼模塊只完成一項功能,在面向對象中,如果只讓一個類完成它該做的事,而不涉及與它無關的領域就是踐行了高內聚的原則,這個類就只有單一職責。我們都知道一句話叫“因爲專注,所以專業”,一個對象如果承擔太多的職責,那麼註定它什麼都做不好。這個世界上任何好的東西都有兩個特徵,一個是功能單一,好的相機絕對不是電視購物裏面賣的那種一個機器有一百多種功能的,它基本上只能照相;另一個是模塊化,好的自行車是組裝車,從減震叉、剎車到變速器,所有的部件都是可以拆卸和重新組裝的,好的乒乓球拍也不是成品拍,一定是底板和膠皮可以拆分和自行組裝的,一個好的軟件系統,它裏面的每個功能模塊也應該是可以輕易的拿到其他系統中使用的,這樣才能實現軟件複用的目標。)
- 開閉原則:軟件實體應當對擴展開放,對修改關閉。(在理想的狀態下,當我們需要爲一個軟件系統增加新功能時,只需要從原來的系統派生出一些新類就可以,不需要修改原來的任何一行代碼。要做到開閉有兩個要點:①抽象是關鍵,一個系統中如果沒有抽象類或接口系統就沒有擴展點;②封裝可變性,將系統中的各種可變因素封裝到一個繼承結構中,如果多個可變因素混雜在一起,系統將變得複雜而換亂,如果不清楚如何封裝可變性,可以參考《設計模式精解》一書中對橋樑模式的講解的章節。)
- 依賴倒轉原則:面向接口編程。(該原則說得直白和具體一些就是聲明方法的參數類型、方法的返回類型、變量的引用類型時,儘可能使用抽象類型而不用具體類型,因爲抽象類型可以被它的任何一個子類型所替代,請參考下面的里氏替換原則。)
- 里氏替換原則:任何時候都可以用子類型替換掉父類型。(關於里氏替換原則的描述,Barbara Liskov女士的描述比這個要複雜得多,但簡單的說就是能用父類型的地方就一定能使用子類型。里氏替換原則可以檢查繼承關係是否合理,如果一個繼承關係違背了里氏替換原則,那麼這個繼承關係一定是錯誤的,需要對代碼進行重構。例如讓貓繼承狗,或者狗繼承貓,又或者讓正方形繼承長方形都是錯誤的繼承關係,因爲你很容易找到違反里氏替換原則的場景。需要注意的是:子類一定是增加父類的能力而不是減少父類的能力,因爲子類比父類的能力更多,把能力多的對象當成能力少的對象來用當然沒有任何問題。)
- 接口隔離原則:接口要小而專,絕不能大而全。(臃腫的接口是對接口的污染,既然接口表示能力,那麼一個接口只應該描述一種能力,接口也應該是高度內聚的。例如,琴棋書畫就應該分別設計爲四個接口,而不應設計成一個接口中的四個方法,因爲如果設計成一個接口中的四個方法,那麼這個接口很難用,畢竟琴棋書畫四樣都精通的人還是少數,而如果設計成四個接口,會幾項就實現幾個接口,這樣的話每個接口被複用的可能性是很高的。Java中的接口代表能力、代表約定、代表角色,能否正確的使用接口一定是編程水平高低的重要標識。)
- 合成聚合複用原則:優先使用聚合或合成關係複用代碼。(通過繼承來複用代碼是面向對象程序設計中被濫用得最多的東西,因爲所有的教科書都無一例外的對繼承進行了鼓吹從而誤導了初學者,類與類之間簡單的說有三種關係,IS-A關係、HAS-A關係、USE-A關係,分別代表繼承、關聯和依賴。其中,關聯關係根據其關聯的強度又可以進一步劃分爲關聯、聚合和合成,但說白了都是HAS-A關係,合成聚合複用原則想表達的是優先考慮HAS-A關係而不是IS-A關係複用代碼,原因嘛可以自己從百度上找到一萬個理由,需要說明的是,即使在Java的API中也有不少濫用繼承的例子,例如Properties類繼承了Hashtable類,Stack類繼承了Vector類,這些繼承明顯就是錯誤的,更好的做法是在Properties類中放置一個Hashtable類型的成員並且將其鍵和值都設置爲字符串來存儲數據,而Stack類的設計也應該是在Stack類中放一個Vector對象來存儲數據。記住:任何時候都不要繼承工具類,工具是可以擁有並可以使用的(HAS/USE),而不是拿來繼承的。)
- 迪米特法則:迪米特法則又叫最少知識原則,一個對象應當對其他對象有儘可能少的瞭解。(迪米特法則簡單的說就是如何做到“低耦合”,門面模式和調停者模式就是對迪米特法則的踐行。對於門面模式可以舉一個簡單的例子,你去一家公司洽談業務,你不需要了解這個公司內部是如何運作的,你甚至可以對這個公司一無所知,去的時候只需要找到公司入口處的前臺美女,告訴她們你要做什麼,她們會找到合適的人跟你接洽,前臺的美女就是公司這個系統的門面。再複雜的系統都可以爲用戶提供一個簡單的門面,Java Web開發中作爲前端控制器的Servlet或Filter不就是一個門面嗎,瀏覽器對服務器的運作方式一無所知,但是通過前端控制器就能夠根據你的請求得到相應的服務。調停者模式也可以舉一個簡單的例子來說明,例如一臺計算機,CPU、內存、硬盤、顯卡、聲卡各種設備需要相互配合才能很好的工作,但是如果這些東西都直接連接到一起,計算機的佈線將異常複雜,在這種情況下,主板作爲一個調停者的身份出現,它將各個設備連接在一起而不需要每個設備之間直接交換數據,這樣就減小了系統的耦合度和複雜度。迪米特法則用通俗的話來將就是不要和陌生人打交道,如果真的需要,找一個自己的朋友,讓他替你和陌生人打交道。)
74、Java企業級開發中常用的設計模式有哪些?
答: 按照分層開發的觀點,可以將應用劃分爲:表示層、業務邏輯層和持久層,每一層都有屬於自己類別的設計模式。
表示層設計模式:
1)Interceptor Filter:攔截過濾器,提供請求預處理和後處理的方案,可以對請求和響應進行過濾。
2)Front Controller:通過中央控制器提供請求管理和處理,管理內容讀取、安全性、視圖管理和導航等功能。Struts 2中的StrutsPrepareAndExecuteFilter、Spring MVC中的DispatcherServlet都是前端控制器,後者如下圖所示:
3)View Helper:視圖幫助器,負責將顯示邏輯和業務邏輯分開。顯示的部分放在視圖組件中,業務邏輯代碼放在幫助器中,典型的功能是內容讀取、驗證與適配。
4)Composite View:複合視圖。
業務邏輯層設計模式:
1)Business Delegate:業務委託,減少表示層和業務邏輯層之間的耦合。
2)Value Object:值對象,解決層之間交換數據的開銷問題。
3)Session Façade:會話門面,隱藏業務邏輯組件的細節,集中工作流程。
4)Value Object Assembler:靈活的組裝不同的值對象
5)Value List Handler:提供執行查詢和處理結果的解決方案,還可以緩存查詢結果,從而達到提升性能的目的。
6)Service Locator:服務定位器,可以查找、創建和定位服務工廠,封裝其實現細節,減少複雜性,提供單個控制點,通過緩存提高性能。
持久層設計模式:
1)Data Access Object:數據訪問對象,以面向對象的方式完成對數據的增刪改查。
【補充】如果想深入的瞭解Java企業級應用的設計模式和架構模式,可以參考這些書籍: 《Pro Java EE Spring Patterns》、《POJO in Action》、《Patterns of Enterprise Application Architecture》。
75、你在開發中都用到了那些設計模式?用在什麼場合?
答:面試被問到關於設計模式的知識時,可以揀最常用的作答,例如:
1)工廠模式:工廠類可以根據條件生成不同的子類實例,這些子類有一個公共的抽象父類並且實現了相同的方法,但是這些方法針對不同的數據進行了不同的操作(多態方法)。當得到子類的實例後,開發人員可以調用基類中的方法而不必考慮到底返回的是哪一個子類的實例。
2)代理模式:給一個對象提供一個代理對象,並由代理對象控制原對象的引用。實際開發中,按照使用目的的不同,代理可以分爲:遠程代理、虛擬代理、保護代理、Cache代理、防火牆代理、同步化代理、智能引用代理。
3)適配器模式:把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起使用的類能夠一起工作。
4)模板方法模式:提供一個抽象類,將部分邏輯以具體方法或構造器的形式實現,然後聲明一些抽象方法來迫使子類實現剩餘的邏輯。不同的子類可以以不同的方式實現這些抽象方法(多態實現),從而實現不同的業務邏輯。
除此之外,還可以講講上面提到的門面模式、橋樑模式、單例模式、裝潢模式(Collections工具類裏面的synchronizedXXX方法把一個線程不安全的容器變成線程安全容器就是對裝潢模式的應用,而Java IO裏面的過濾流(有的翻譯成處理流)也是應用裝潢模式的經典例子)等,反正原則就是揀自己最熟悉的用得最多的作答,以免言多必失。
76、XML 文檔定義有幾種形式?它們之間有何本質區別?解析XML 文檔有哪幾種方式?
答:XML文檔定義分爲DTD和Schema兩種形式;其本質區別在於Schema本身也是一個XML文件,可以被XML解析器解析。對XML的解析主要有DOM(文檔對象模型)、SAX、StAX(JDK 1.6中引入的新的解析XML的方式,Streaming API for XML) 等,其中DOM處理大型文件時其性能下降的非常厲害,這個問題是由DOM 的樹結構所造成的,這種結構佔用的內存較多,而且DOM 必須在解析文件之前把整個文檔裝入內存,適合對XML 的隨機訪問(典型的用空間換取時間的策略);SAX是事件驅動型的XML解析方式,它順序讀取XML文件,不需要一次全部裝載整個文件。當遇到像文件開頭,文檔結束,或者標籤開頭與標籤結束時,它會觸發一個事件,用戶通過在其回調事件中寫入處理代碼來處理XML文件,適合對XML 的順序訪問;如其名稱所暗示的那樣,StAX把重點放在流上。實際上,StAX與其他方法的區別就在於應用程序能夠把XML作爲一個事件流來處理。將XML作爲一組事件來處理的想法並不新穎(事實上 SAX 已經提出來了),但不同之處在於StAX允許應用程序代碼把這些事件逐個拉出來,而不用提供在解析器方便時從解析器中接收事件的處理程序。
77、你在項目中哪些地方用到了XML?
答:XML的主要作用有兩個方面:數據交換(曾經被稱爲業界數據交換的事實標準,現在此項功能在很多時候都被JSON取代)和信息配置。在做數據交換時,XML將數據用標籤組裝成起來,然後壓縮打包加密後通過網絡傳送給接收者,接收解密與解壓縮後再從XML文件中還原相關信息進行處理。目前很多軟件都使用XML來存儲配置信息,很多項目中我們通常也會將作爲配置的硬代碼(hard code)寫在XML文件中,Java的很多框架也是這麼做的。
78、在進行數據庫編程時,連接池有什麼作用?
答:由於創建連接和釋放連接都有很大的開銷(尤其是數據庫服務器不在本地時,每次建立連接都需要進行TCP的三次握手,再加上網絡延遲,造成的開銷是不可忽視的),爲了提升系統訪問數據庫的性能,可以事先創建若干連接置於連接池中,需要時直接從連接池獲取,使用結束時歸還連接池而不必關閉連接,從而避免頻繁創建和釋放連接所造成的開銷,這是典型的用空間換取時間的策略(浪費了空間存儲連接,但節省了創建和釋放連接的時間)。池化技術在Java開發中是很常見的,在使用線程時創建線程池的道理與此相同。基於Java的開源數據庫連接池主要有: C3P0、Proxool、DBCP、BoneCP、Druid等。
【補充】在計算機系統中時間和空間是不可調和的矛盾,理解這一點對設計滿足性能要求的算法是至關重要的。大型網站性能優化的一個關鍵就是使用緩存,而緩存跟上面講的連接池道理非常類似,也是使用空間換時間的策略。可以將熱點數據置於緩存中,當用戶查詢這些數據時可以直接從緩存中得到,這無論如何也快過去數據庫中查詢。當然,緩存的置換策略等也會對系統性能產生重要影響,對於這個問題的討論已經超出了這裏要闡述的範圍。
79、什麼是DAO模式?
答:DAO(DataAccess Object)顧名思義是一個爲數據庫或其他持久化機制提供了抽象接口的對象,在不暴露數據庫實現細節的前提下提供了各種數據操作。爲了建立一個健壯的java EE應用,應該將所有對數據源的訪問操作進行抽象化後封裝在一個公共API中。用程序設計語言來說,就是建立一個接口,接口中定義了此應用程序中將會用到的所有事務方法。在這個應用程序中,當需要和數據源進行交互的時候則使用這個接口,並且編寫一個單獨的類來實現這個接口,在邏輯上該類對應一個特定的數據存儲。DAO模式實際上包含了兩個模式,一是Data Accessor(數據訪問器),二是Data Object(數據對象),前者要解決如何訪問數據的問題,而後者要解決的是如何用對象封裝數據。
80、什麼是ORM?
答:對象關係映射(Object-Relational Mapping,簡稱ORM)是一種爲了解決程序的面向對象模型與數據庫的關係模型互不匹配問題的技術;簡單的說,ORM 是通過使用描述對象和數據庫之間映射的元數據(可以用XML或者是註解),將Java程序中的對象自動持久化到關係數據庫中或者將關係數據庫表中的行轉換成Java對象,其本質上就是將數據從一種形式轉換到另外一種形式。
81、JDBC中如何進行事務處理?
答:Connection提供了事務處理的方法,通過調用setAutoCommit(false)可以設置手動提交事務;當事務完成後用commit()顯式提交事務;如果在事務處理過程中發生異常則通過rollback()進行事務回滾。除此之外,較新的JDBC標準還引入了Savepoint(保存點)的概念,允許事務回滾到指定的保存點。
82、 事務的ACID是指什麼?
答:
1)原子性(Atomic):事務中各項操作,要麼全做要麼全不做,任何一項操作的失敗都會導致整個事務的失敗;
2)一致性(Consistent):事務結束後系統狀態是一致的;
3)隔離性(Isolated):併發執行的事務彼此無法看到對方的中間狀態;
4)持久性(Durable):事務完成後所做的改動都會被持久化,即使發生災難性的失敗。通過日誌和同步備份可以在故障發生後重建數據。
【補充】關於事務,在面試中被問到的概率是很高的,可以問的問題也是很多的。首先需要知道的是,只有存在併發數據訪問時才需要事務。當多個事務訪問同一數據時,可能會存在5類問題,包括3類數據讀取問題(髒讀、不可重複讀和幻讀)和2類數據更新問題(第1類丟失更新和第2類丟失更新)。
- 髒讀(Dirty Read):A事務讀取B事務尚未提交的數據並在此基礎上操作,而B事務執行回滾,那麼A讀取到的數據就是髒數據。
時間 |
轉賬事務A |
取款事務B |
T1 |
|
開始事務 |
T2 |
開始事務 |
|
T3 |
|
查詢賬戶餘額爲1000元 |
T4 |
|
取出500元餘額修改爲500元 |
T5 |
查詢賬戶餘額爲500元(髒讀) |
|
T6 |
|
撤銷事務餘額恢復爲1000元 |
T7 |
匯入100元把餘額修改爲600元 |
|
T8 |
提交事務 |
|
- 不可重複讀(Unrepeatable Read):事務A重新讀取前面讀取過的數據,發現該數據已經被另一個已提交的事務B修改過了。
時間 |
轉賬事務A |
取款事務B |
T1 |
|
開始事務 |
T2 |
開始事務 |
|
T3 |
|
查詢賬戶餘額爲1000元 |
T4 |
查詢賬戶餘額爲1000元 |
|
T5 |
|
取出100元修改餘額爲900元 |
T6 |
|
提交事務 |
T7 |
查詢賬戶餘額爲900元(不可重複讀) |
|
- 幻讀(Phantom Read):事務A重新執行一個查詢,返回一系列符合查詢條件的行,發現其中插入了被事務B提交的行。
時間 |
統計金額事務A |
轉賬事務B |
T1 |
|
開始事務 |
T2 |
開始事務 |
|
T3 |
統計總存款爲10000元 |
|
T4 |
|
新增一個存款賬戶存入100元 |
T5 |
|
提交事務 |
T6 |
再次統計總存款爲10100元(幻讀) |
|
- 第1類丟失更新:事務A撤銷時,把已經提交的事務B的更新數據覆蓋了。
時間 |
取款事務A |
轉賬事務B |
T1 |
開始事務 |
|
T2 |
|
開始事務 |
T3 |
查詢賬戶餘額爲1000元 |
|
T4 |
|
查詢賬戶餘額爲1000元 |
T5 |
|
匯入100元修改餘額爲1100元 |
T6 |
|
提交事務 |
T7 |
取出100元將餘額修改爲900元 |
|
T8 |
撤銷事務 |
|
T9 |
餘額恢復爲1000元(丟失更新) |
|
- 第2類丟失更新:事務A覆蓋事務B已經提交的數據,造成事務B所做的操作丟失。
時間 |
轉賬事務A |
取款事務B |
T1 |
|
開始事務 |
T2 |
開始事務 |
|
T3 |
|
查詢賬戶餘額爲1000元 |
T4 |
查詢賬戶餘額爲1000元 |
|
T5 |
|
取出100元將餘額修改爲900元 |
T6 |
|
提交事務 |
T7 |
匯入100元將餘額修改爲1100元 |
|
T8 |
提交事務 |
|
T9 |
查詢賬戶餘額爲1100元(丟失更新) |
|
數據併發訪問所產生的問題,在有些場景下可能是允許的,但是有些場景下可能就是致命的,數據庫通常會通過鎖機制來解決數據併發訪問問題,按鎖定對象不同可以分爲表級鎖和行級鎖;按併發事務鎖定關係可以分爲共享鎖和獨佔鎖,具體的內容大家可以自行查閱資料進行了解。
直接使用鎖是非常麻煩的,爲此數據庫爲用戶提供了自動鎖機制,只要用戶指定會話的事務隔離級別,數據庫就會通過分析SQL語句然後爲事務訪問的資源加上合適的鎖,此外,數據庫還會維護這些鎖通過各種手段提高系統的性能,這些對用戶來說都是透明的(就是說你不用理解,事實上我確實也不知道)。ANSI/ISO SQL 92標準定義了4個等級的事務隔離級別,如下表所示:
隔離級別 |
髒讀 |
不可重複讀 |
幻讀 |
第一類丟失更新 |
第二類丟失更新 |
READ UNCOMMITED |
允許 |
允許 |
允許 |
不允許 |
允許 |
READ COMMITTED |
不允許 |
允許 |
允許 |
不允許 |
允許 |
REPEATABLE READ |
不允許 |
不允許 |
允許 |
不允許 |
不允許 |
SERIALIZABLE |
不允許 |
不允許 |
不允許 |
不允許 |
不允許 |
需要說明的是,事務隔離級別和數據訪問的併發性是對立的,事務隔離級別越高併發性就越差。所以要根據具體的應用來確定合適的事務隔離級別,這個地方沒有萬能的原則。
83、Statement和PreparedStatement有什麼區別?哪個性能更好?
答: 與Statement相比,①PreparedStatement接口代表預編譯的語句,它主要的優勢在於可以減少SQL的編譯錯誤並增加SQL的安全性(減少SQL注射攻擊的可能性);②PreparedStatement中的SQL語句是可以帶參數的;③當批量處理SQL時或頻繁執行相同的查詢時,PreparedStatement有明顯的性能上的優勢,由於數據庫可以將編譯優化後的SQL語句緩存起來,下次執行相同語句時就會很快。
【補充】爲了提供對存儲過程的調用,JDBC API中還提供了CallableStatement接口。存儲過程(Stored Procedure)是數據庫系統中,一組爲了完成特定功能的SQL語句的集合,經編譯後存儲在數據庫中,用戶通過指定存儲過程的名字並給出參數(如果該存儲過程帶有參數)來執行它。雖然調用存儲過程會帶來網絡開銷、安全性、性能上的好處,但是存在如果底層數據庫發生遷移時就會有很多麻煩,因爲每種數據庫的存儲過程在書寫上存在不少的差別。
84、使用JDBC操作數據庫時,如何提升讀取數據的性能?如何提升更新數據的性能?
答:要提升讀取數據的性能,可以指定通過結果集(ResultSet)對象指定每次抓取數據的大小(fetch size);要提升更新數據的性能可以使用PreparedStatement語句構建批處理(batch)。
85、JDBC能否處理Blob和Clob?
答: Blob是指二進制大對象(Binary Large Object),而Clob是指大字符對象(Character Large Objec),因此其中Blob是爲存儲大的二進制數據而設計的,而Clob是爲存儲大的文本數據而設計的。JDBC的PreparedStatement和ResultSet都提供了相應的方法來支持Blob和Clob操作。下面的代碼展示瞭如何使用JDBC操作LOB:
- create table tb_user
- (
- id int primary key auto_increment,
- name varchar(20) unique not null,
- photo longblob
- );
- package com.lovo.demo;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.sql.PreparedStatement;
- import java.sql.SQLException;
- class JdbcLobTest {
- public static void main(String[] args) {
- Connection con = null;
- try {
- // 1. 加載驅動(Java6以上版本可以省略)
- Class.forName(”com.mysql.jdbc.Driver”);
- // 2. 建立連接
- con = DriverManager.getConnection(”jdbc:mysql://localhost:3306/test”, “root”, “123456”);
- // 3. 創建語句對象
- PreparedStatement ps = con.prepareStatement(”insert into tb_user values (default, ?, ?)”);
- ps.setString(1, “SOMNUS”); // 將SQL語句中第一個佔位符換成字符串
- try (InputStream in = new FileInputStream(“test.jpg”)) { // Java 7的TWR
- ps.setBinaryStream(2, in); // 將SQL語句中第二個佔位符換成二進制流
- // 4. 發出SQL語句獲得受影響行數
- System.out.println(ps.executeUpdate() == 1 ? “插入成功” : “插入失敗”);
- } catch(IOException e) {
- System.out.println(”讀取照片失敗!”);
- }
- } catch (ClassNotFoundException | SQLException e) { // Java 7的多異常捕獲
- e.printStackTrace();
- } finally { // 釋放外部資源的代碼都應當放在finally中保證其能夠得到執行
- try {
- if(con != null && !con.isClosed()) {
- con.close(); // 5. 釋放數據庫連接
- con = null; // 指示垃圾回收器可以回收該對象
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- }