文章目錄:
1.生態(tài)圈概述。
2.說(shuō)說(shuō)Java,高級(jí)Java,Java生態(tài)圈的衍生
3. 說(shuō)說(shuō)servlet、servlet容器、比較tomcat、jetty、tomcat、jboss;
1. 哪個(gè)項(xiàng)目能離開網(wǎng)絡(luò),數(shù)據(jù)庫(kù),操作系統(tǒng)來(lái)運(yùn)行?所以生態(tài)圈的應(yīng)用技術(shù)主要在這些知識(shí)點(diǎn)處。
2. Java組件,組件其實(shí)就是一個(gè)應(yīng)用程序塊 但是它們不是完整的應(yīng)用程序,不能單獨(dú)運(yùn)行。就有如一輛汽車,車門是一個(gè)組件,車燈也是一個(gè)組件。但是光有車燈車門沒(méi)有用,它們不能跑上公路在java中這些組件就叫做javabean,有點(diǎn)像微軟以前的com組件,要特別說(shuō)明的是,由于任何一個(gè)java文件編譯以后都是以類的形式存在所以javabean肯定也是一個(gè)類,這是毫無(wú)疑問(wèn)的。那么容器里裝載的是什么呢?就是這些組件。而容器之外的程序需要和這些組件交互必須通過(guò)容器。舉個(gè)例子,IE發(fā)送了一個(gè)請(qǐng)求給容器,容器通過(guò)調(diào)用其中的一個(gè)組件進(jìn)行相關(guān)處理之后將結(jié)果反饋給IE,這種與客戶端軟件交互的組件就叫做servlet。但是組件有很多種,那么如何區(qū)分這些組件呢?有多種管理辦法,比如同是同樣是servlet,有些是通過(guò)jsp生成的而有些是開發(fā)人員自己寫的,那么通過(guò)jsp生成的servlet集中放在一個(gè)地方而開發(fā)人員自己寫的則需要在xml里面配置一些基本的參數(shù)同時(shí),不同組件有可能還需要繼承一些特定的父類或者接口,這也是容器管理的需要還有其他的一些組件。那么容器有很多種,按照他們裝載的組件類型劃分:比如有裝EJB的EJB容器,有裝servlet與jsp還有靜態(tài)頁(yè)面的web容器等等,這種只含有web容器的應(yīng)用服務(wù)器也被叫做web服務(wù)器。(當(dāng)表示層的應(yīng)用軟件通過(guò)網(wǎng)絡(luò)向appserver發(fā)送一個(gè)請(qǐng)求的時(shí)候appserver自動(dòng)找到相應(yīng)容器中的組件,執(zhí)行組件中的程序塊,把得到結(jié)果返還給客戶 而我們要做的事就是寫組件也就是javabean,然后放到appserver里面去就可以了 至于怎樣與IE通訊,怎樣截獲網(wǎng)絡(luò)上的請(qǐng)求,怎樣控制對(duì)象的數(shù)量等等 這些繁瑣而無(wú)味的工作我們都不管,都由appserver去做吧,把注意力集中在業(yè)務(wù)邏輯上)3.框架就是一組相互協(xié)作的類,對(duì)于特定的一類軟件,框架構(gòu)成了一種可重用的設(shè)計(jì)。
4. 對(duì)于Servlet,我們常常這樣稱呼:Servlet容器。Servlet工作原理:首先簡(jiǎn)單解釋一下Servlet接收和響應(yīng)客戶請(qǐng)求的過(guò)程,首先客戶發(fā)送一個(gè)請(qǐng)求,Servlet是調(diào)用service()方法對(duì)請(qǐng)求進(jìn)行響應(yīng)的,通過(guò)源代碼可見(jiàn),service()方法中對(duì)請(qǐng)求的方式進(jìn)行了匹配,選擇調(diào)用doGet,doPost等這些方法,然后再進(jìn)入對(duì)應(yīng)的方法中調(diào)用邏輯層的方法,實(shí)現(xiàn)對(duì)客戶的響應(yīng)。在Servlet接口和GenericServlet中是沒(méi)有doGet,doPost等等這些方法的,HttpServlet中定義了這些方法,但是都是返回error信息。所以,我們每次定義一個(gè)Servlet的時(shí)候,都必須實(shí)現(xiàn)doGet或doPost等這些方法。每一個(gè)自定義的Servlet都必須實(shí)現(xiàn)Servlet的接口,Servlet接口中定義了五個(gè)方法,其中比較重要的三個(gè)方法涉及到Servlet的生命周期,分別是上文提到的init(),service(),destroy()方法。GenericServlet是一個(gè)通用的,不特定于任何協(xié)議的Servlet,它實(shí)現(xiàn)了Servlet接口。而HttpServlet繼承于GenericServlet,因此HttpServlet也實(shí)現(xiàn)了Servlet接口。所以我們定義Servlet的時(shí)候只需要繼承HttpServlet即可。
Servlet生命周期分為三個(gè)階段: 1,初始化階段 調(diào)用init()方法 2,響應(yīng)客戶請(qǐng)求階段 調(diào)用service()方法 3,終止階段 調(diào)用destroy()方法
Servlet初始化階段: 在下列時(shí)刻Servlet容器裝載Servlet: 1,Servlet容器啟動(dòng)時(shí)自動(dòng)裝載某些Servlet,實(shí)現(xiàn)它只需要在web.XML文件中的<Servlet></Servlet>之間添加如下代碼: <loadon-startup> 1 </loadon-startup> 2,在Servlet容器啟動(dòng)后,客戶首次向Servlet發(fā)送請(qǐng)求 3,Servlet類文件被更新后,重新裝載Servlet。Servlet被裝載后,Servlet容器創(chuàng)建一個(gè)Servlet實(shí)例并且調(diào)用Servlet的init()方法進(jìn)行初始化。在Servlet的整個(gè)生命周期內(nèi),init()方法只被調(diào)用一次。
Servlet響應(yīng)請(qǐng)求階段: 對(duì)于用戶到達(dá)Servlet的請(qǐng)求,Servlet容器會(huì)創(chuàng)建特定于這個(gè)請(qǐng)求的ServletRequest對(duì)象和ServletResponse對(duì)象,然后調(diào)用Servlet的service方法。service方法從ServletRequest對(duì)象獲得客戶請(qǐng)求信息,處理該請(qǐng)求,并通過(guò)ServletResponse對(duì)象向客戶返回響應(yīng)信息。
對(duì)于Tomcat來(lái)說(shuō),它會(huì)將傳遞過(guò)來(lái)的參數(shù)放在一個(gè)Hashtable中,該Hashtable的定義是:PRivateHashtable<String String[]> paramHashStringArray =newHashtable<String String[]>();這是一個(gè)String-->String[]的鍵值映射。HashMap線程不安全的,Hashtable線程安全的。
Servlet終止階段: 當(dāng)WEB應(yīng)用被終止,或Servlet容器終止運(yùn)行,或Servlet容器重新裝載Servlet新實(shí)例時(shí),Servlet容器會(huì)先調(diào)用Servlet的destroy()方法,在destroy()方法中可以釋放掉Servlet所占用的資源。
Servlet何時(shí)被創(chuàng)建: 1,默認(rèn)情況下,當(dāng)WEB客戶第一次請(qǐng)求訪問(wèn)某個(gè)Servlet的時(shí)候,WEB容器將創(chuàng)建這個(gè)Servlet的實(shí)例。 2,當(dāng)web.xml文件中如果<servlet>元素中指定了<load-on-startup>子元素時(shí),Servlet容器在啟動(dòng)web服務(wù)器時(shí),將按照順序創(chuàng)建并初始化Servlet對(duì)象。 注意:在web.xml文件中,某些Servlet只有<serlvet>元素,沒(méi)有<servlet-mapping>元素,這樣我們無(wú)法通過(guò)url的方式訪問(wèn)這些Servlet,這種Servlet通常會(huì)在<servlet>元素中配置一個(gè)<load-on-startup>子元素,讓容器在啟動(dòng)的時(shí)候自動(dòng)加載這些Servlet并調(diào)用init()方法,完成一些全局性的初始化工作。
Web應(yīng)用何時(shí)被啟動(dòng): 1,當(dāng)Servlet容器啟動(dòng)的時(shí)候,所有的Web應(yīng)用都會(huì)被啟動(dòng) 2,控制器啟動(dòng)web應(yīng)用Servlet與JSP的比較: 有許多相似之處,都可以生成動(dòng)態(tài)網(wǎng)頁(yè)。 JSP的優(yōu)點(diǎn)是擅長(zhǎng)于網(wǎng)頁(yè)制作,生成動(dòng)態(tài)頁(yè)面比較直觀,缺點(diǎn)是不容易跟蹤與排錯(cuò)。 Servlet是純Java語(yǔ)言,擅長(zhǎng)于處理流程和業(yè)務(wù)邏輯,缺點(diǎn)是生成動(dòng)態(tài)網(wǎng)頁(yè)不直觀。
對(duì)于文中說(shuō)到的,Servlet生成動(dòng)態(tài)網(wǎng)頁(yè)不直觀。servlet可以用于生成動(dòng)態(tài)的Web頁(yè)內(nèi)容。但是,它的一個(gè)缺點(diǎn)是必須將HTML標(biāo)記和文本嵌入到Java源代碼中。這樣,如果要改變靜態(tài)的HTML文本,必須修改Java源代碼并重新進(jìn)行編譯。同時(shí),在servlet中有許多靜態(tài)HTML代碼和一些動(dòng)態(tài)HTML代碼混在一起,由于正常的HTML代碼是Java源代碼的一部分,所以很難閱讀和維護(hù)這些代碼。JSP就是為了克服上述缺點(diǎn)而引入的。
編輯文件:CurrentTime.jsp如下所示:
<html><head> <title>CurrentTime</title></head><body> CurrentTime is <%= new java.util.Date()%></body></html>
將程序放到tomcat容器下運(yùn)行。
5. 繼續(xù)探究Servlet,Servlet本身是單實(shí)例的,這樣當(dāng)多個(gè)用戶同時(shí)訪問(wèn)某個(gè)Servlet時(shí),會(huì)訪問(wèn)該唯一的Servlet實(shí)例中的成員變量,如果對(duì)成員變量進(jìn)行寫入工作,那就會(huì)導(dǎo)致Servlet的多線程問(wèn)題,即數(shù)據(jù)不一致。
解決Servlet多線程同步問(wèn)題的方案: 1,Servlet實(shí)現(xiàn)了javax.servlet.singleThreadModel(Servlet2.4中已經(jīng)廢棄該接口),此時(shí)的Servlet容器將保證Servlet實(shí)例是以單線程的方式運(yùn)行,也就是說(shuō)同一時(shí)刻,只會(huì)有一個(gè)線程運(yùn)行Servlet的service()方法。 不推薦使用,大大降低了效率。 2,去除實(shí)例變量,使用局部變量 推薦 3,使用同步代碼塊 synchronized{...} 不推薦使用6.Cookie的英文原意是“點(diǎn)心”,當(dāng)用戶訪問(wèn)WEB服務(wù)器時(shí),服務(wù)器在用戶硬盤上存放的信息,好像是服務(wù)器送給用戶的點(diǎn)心。服務(wù)器可以根據(jù)Cookie來(lái)跟蹤用戶,這對(duì)于需要區(qū)別用戶場(chǎng)合特別有用。先來(lái)看看我們?cè)贖TML5項(xiàng)目中使用過(guò)的cookie具體案例:
這個(gè)是在H5/js/common.js中的兩個(gè)函數(shù)。//保存到cookiefunction setCookie(name,value,days) { var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString(); } //讀取cookies function getCookie(name) { var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)"); if(arr=document.cookie.match(reg)) return unescape(arr[2]); else return null; }兩個(gè)函數(shù),分別用來(lái)設(shè)置cookie和取得cookie。
setTimeout(function() { var cookieChid=getCookie('chid'); if(cookieChid !='w5') { $("#underonlaodFlag").attr("style","display:display"); } },300);
在其他頁(yè)面中,通過(guò)這段JS,延遲300ms執(zhí)行,保證先加載了common.js, 再執(zhí)行這段JS。做出判斷。var uid=getCookie('uid')==null?'':getCookie('uid'); //這個(gè)同樣是其他頁(yè)面需要使用cookie的案例。繼續(xù)來(lái)讀代碼
public class CookieServlet extends HttpServlet{ private int count1; private int count2; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie("cookiename"+ count1++,"cookievalue"+ count2++); cookie.setMaxAge(10);//設(shè)置存活時(shí)間 單位秒 resp.addCookie(cookie); Cookie[] cookies = req.getCookies(); if(null == cookies) { return; } for(Cookie cookie2:cookies) { System.out.println(cookie2.getName()); System.out.println(cookie2.getValue()); } }}//代碼中展現(xiàn)了:Servlet 中創(chuàng)建、存儲(chǔ)和接收客戶端存儲(chǔ)Cookie的過(guò)程。7. 客戶端的cookie都有生命周期的,一般為兩周。又或者用戶主動(dòng)清空了自己的cookie,那么服務(wù)器怎么來(lái)跟蹤用戶記錄呢?session回話,Session用于跟蹤客戶的狀態(tài),Session是指在一段時(shí)間內(nèi),單個(gè)客戶與WEB服務(wù)器一連串相關(guān)的交互過(guò)程。在一個(gè)Session中,客戶可能多次請(qǐng)求訪問(wèn)同一個(gè)網(wǎng)頁(yè),也有可能請(qǐng)求訪問(wèn)各種不同的服務(wù)器資源。
Web服務(wù)器跟蹤客戶狀態(tài)通常有四種方法: 1,建立含有跟蹤數(shù)據(jù)的隱藏字段 type=hidden 2,重寫包含額外參數(shù)的URL 3,使用持續(xù)的Cookie 4,使用Servlet API中的Session(會(huì)話)機(jī)制
Session的生命周期: 當(dāng)客戶第一次訪問(wèn)WEB應(yīng)用中支持Session的某個(gè)頁(yè)面時(shí),就會(huì)開始一個(gè)新的session 接下來(lái)客戶訪問(wèn)這個(gè)WEB應(yīng)用中不同的網(wǎng)頁(yè)時(shí),都處于同一個(gè)session中 默認(rèn)情況下,JSP頁(yè)面是支持Session的,如果想不支持Session,可使用標(biāo)簽<%@ page session="false"%> 在下列情況下,Session將結(jié)束生命周期,Servlet容器將釋放HttpSession占用的資源: 1,客戶瀏覽器關(guān)閉? 2,Session過(guò)期 3,服務(wù)器端調(diào)用了HttpSession的Invalidate()方法 如何做到在瀏覽器關(guān)閉時(shí)刪除Session? 嚴(yán)格的講,做不到這一點(diǎn)。可以做的努力的辦法是在所有的客戶端頁(yè)面里使用javascript的方法window.onclose來(lái)監(jiān)視瀏覽器的關(guān)閉動(dòng)作,然后向服務(wù)器發(fā)送一個(gè)請(qǐng)求來(lái)刪除Session; 但是對(duì)于瀏覽器崩潰或強(qiáng)行殺死進(jìn)程這種非常規(guī)手段仍然無(wú)能為力。 實(shí)際上我們?cè)陧?xiàng)目中也不會(huì)這么做,而是讓服務(wù)器在Session過(guò)期時(shí)將其刪除。
Session運(yùn)行機(jī)制: 當(dāng)一個(gè)Session開始時(shí),Servlet容器將創(chuàng)建一個(gè)HttpSession對(duì)象,在HttpSession對(duì)象中可以存放客戶的狀態(tài)信息(例如購(gòu)物車)。 Servlet容器為HttpSession分配一個(gè)唯一標(biāo)志符,稱為SessionID。Servlet容器將SessionID作為Cookie保存在客戶的瀏覽器中。 每次客戶發(fā)送Http請(qǐng)求時(shí),Servlet容器可以從HttpServletRequest對(duì)象中讀取SessionID,然后根據(jù)SessionID找到對(duì)應(yīng)的HttpSession對(duì)象,從而獲取客戶的狀態(tài)信息。Session的接口HttpSession: getId() 返回session的ID Invalidate() 讓當(dāng)前session失效,Servlet容器會(huì)釋放HttpSession對(duì)象占用的資源 getAttribut() setAttribute() isNew() 判斷是否是新創(chuàng)建的session,如果是返回true 否則返回false setMaxInactiveInterval() 設(shè)置session的最大有效時(shí)間 單位為秒 如果設(shè)置為負(fù)數(shù),表示不限制session處于不活動(dòng)狀態(tài)的最大有效時(shí)間,默認(rèn)的設(shè)置時(shí)間為30分鐘
8. 說(shuō)完了Servlet,來(lái)到了我們常用的框架中是怎么攔截過(guò)濾Servlet的。那就說(shuō)到了Servlet的過(guò)濾器Filter。
Servlet過(guò)濾器是在Java Servlet規(guī)范2.3中定義的,它能夠?qū)ervlet容器的請(qǐng)求和響應(yīng)對(duì)象進(jìn)行檢查和修改。Servlet過(guò)濾器本身不生成請(qǐng)求和響應(yīng)對(duì)象,它只提供過(guò)濾作用。Servlet過(guò)濾器能夠在Servlet被調(diào)用之前檢查Request對(duì)象,修改Request Header和Request內(nèi)容。在Servlet被調(diào)用后檢查Response Header和Response內(nèi)容,Servlet過(guò)濾器負(fù)責(zé)過(guò)濾WEB的組件可以是Servlet,JSP和HTML。
Filter接口: 每一個(gè)Servlet過(guò)濾器都會(huì)實(shí)現(xiàn)javax.serlvet.Filter接口,這個(gè)接口中含有三個(gè)過(guò)濾方法必須實(shí)現(xiàn): init(FilterConfig):這是Servlet過(guò)濾器的初始化方法,Servlet容器創(chuàng)建Servlet過(guò)濾器后將調(diào)用這個(gè)方法,在這個(gè)方法中可以讀取web.xml中Servlet過(guò)濾器的初始化參數(shù) doFilter(ServletRequest,ServletResponse,FilterChain):這個(gè)方法完成實(shí)際的過(guò)濾操作。當(dāng)客戶請(qǐng)求訪問(wèn)與過(guò)濾器關(guān)聯(lián)的URL的時(shí)候,Servlet過(guò)濾器將先執(zhí)行doFilter方法。FilterChain參數(shù)用于訪問(wèn)后續(xù)過(guò)濾器。 destroy():Servlet容器在銷毀過(guò)濾器實(shí)例前調(diào)用這個(gè)方法,在這個(gè)方法中可以釋放Servlet過(guò)濾器占用的資源。
我們可以看一段使用struts框架中的過(guò)濾器的實(shí)例是怎么操作的。
//這個(gè)是在web.xml中使用struts2的過(guò)濾器的配置文件,<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class></filter><filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern></filter-mapping>
9. 數(shù)字九嘛,立即想到的就是JSP的九大內(nèi)置對(duì)象。
JSP有以下九種內(nèi)置對(duì)象: request,請(qǐng)求對(duì)象 response,響應(yīng)對(duì)象 pageContext,頁(yè)面上下文對(duì)象 session,會(huì)話對(duì)象 application,應(yīng)用程序?qū)ο? out,輸出對(duì)象 config,配置對(duì)象 page,頁(yè)面對(duì)象 exception,異常對(duì)象 重點(diǎn)學(xué)習(xí):request,session,application1. request的setAttribute()與getAttribute()方法一般都是成對(duì)出現(xiàn)的,首先通過(guò)setAttribute()設(shè)置屬性和屬性值,然后通過(guò)getAttribute()方法獲得與屬性對(duì)應(yīng)的對(duì)象值(獲取到之后,一般都要向下類型轉(zhuǎn)換,將屬性值轉(zhuǎn)換為真正的對(duì)象)。setAttribute()與ge tAttribute()都是在服務(wù)器端內(nèi)部執(zhí)行的,客戶端不知道服務(wù)器端是否執(zhí)行該方法。2. request.getParameter()是服務(wù)器端獲取到客戶端通過(guò)表單或url請(qǐng)求參數(shù)所發(fā)送過(guò)來(lái)的參數(shù)值,是客戶端與服務(wù)器端之間的交互。服務(wù)器端要想獲取客戶端發(fā)送過(guò)來(lái)的數(shù)據(jù),就需要通過(guò)getParameter()來(lái)獲取,沒(méi)有setParameter()方法。3. request內(nèi)數(shù)據(jù)的存活范圍就是在request對(duì)象的存活范圍內(nèi),當(dāng)客戶端向服務(wù)器端發(fā)送一個(gè)請(qǐng)求,服務(wù)器向客戶端返回一個(gè)響應(yīng)后,該請(qǐng)求對(duì)象就被銷毀了;之后再向服務(wù)器端發(fā)送新的請(qǐng)求時(shí),服務(wù)器會(huì)創(chuàng)建新的request對(duì)象,該request對(duì)象與之前的request對(duì)象沒(méi)有任 何關(guān)系,因此也無(wú)法獲得在之前的request對(duì)象中所存放的任何數(shù)據(jù)。4. session內(nèi)數(shù)據(jù)的存活范圍就是session對(duì)象的存活范圍。 application(應(yīng)用對(duì)象):存活范圍最大的對(duì)象,只要服務(wù)器沒(méi)有關(guān)閉,application對(duì)象就會(huì)一直存在。在整個(gè)服務(wù)器的運(yùn)行當(dāng)中,application對(duì)象只有一個(gè)。 application.getRealPath("")返回資源在服務(wù)器上的具體位置。
10. 做Java Web項(xiàng)目當(dāng)然離不開網(wǎng)絡(luò),你要學(xué)會(huì)HTTP協(xié)議以及衍生的一些知識(shí)。(另詳述)
11. 理論實(shí)在太枯燥,我們說(shuō)幾個(gè)實(shí)際開發(fā)中的場(chǎng)景吧:
//場(chǎng)景一:(異步消息機(jī)制)在J2EE系統(tǒng)中最常見(jiàn)的一個(gè)場(chǎng)景,前端瀏覽器 jsp/servlet向服務(wù)器端發(fā)出一個(gè)請(qǐng)求,jsp/servlet將請(qǐng)求傳遞給后端的應(yīng)用程序處理業(yè)務(wù)邏輯,業(yè)務(wù)模塊將響應(yīng)的結(jié)果直接返回給客戶端,而不是真正的計(jì)算結(jié)果,例如一個(gè)網(wǎng)站的用戶注冊(cè)功能,一個(gè)用戶點(diǎn)擊注冊(cè)以后,將會(huì)發(fā)送一份郵件給他當(dāng)時(shí)的注冊(cè)郵箱,如果需要等到郵件發(fā)送成功再返回給用戶結(jié)果的話,用戶體驗(yàn)將會(huì)很差,所以將結(jié)果直接返回給用戶,將用戶注冊(cè)的信息通過(guò)消息發(fā)送給后端程序慢慢處理。
上面的異步消息機(jī)制。JMS(Java消息服務(wù))大體上有 3個(gè)部分消息發(fā)送端、中間件服務(wù)器、消息接收端。三個(gè)組件缺一不可。 JMS消息分為兩種消息模式,點(diǎn)對(duì)點(diǎn)和發(fā)布者/訂閱者。許多提供商支持這種。程序員可以在他們的分布式軟件中實(shí)現(xiàn)面向消息的操作,這些操作將具有不同面向消息中間件產(chǎn)品的可移植性。Java消息服務(wù)器是指,將數(shù)據(jù)通過(guò)消息作為載體在網(wǎng)絡(luò)中從一個(gè)系統(tǒng)異步傳送給另一個(gè)系統(tǒng)。這樣的異步消息傳送意味著:發(fā)送者不需要等待接收者接收或處理該消息;它可以自由地發(fā)送消息并持續(xù)進(jìn)行處理。這樣一個(gè)異步式的架構(gòu)主要依賴于一臺(tái)消息服務(wù)器(message server)。消息服務(wù)器,也稱為消息路由器(message router)或代理(broker),它負(fù)責(zé)從一個(gè)消息傳送客戶端向其他消息傳送客戶端傳送消息。
JMS對(duì)與一個(gè)大型系統(tǒng)是必不可少的一個(gè)應(yīng)用組件,可以利用JMS來(lái)實(shí)現(xiàn)3個(gè)目的: 1.提高可伸縮性(Increase Scalability), 2.可以利用JMS來(lái)緩解系統(tǒng)瓶頸(Reduce Bottlenecks) 3.提高系統(tǒng)對(duì)用戶的響應(yīng)能力
Topic消息和Queen消息:
Topic 是一對(duì)多的消息傳送,你可以看做是QQ應(yīng)用程序 QQ群 聊天中的一對(duì)多發(fā)送消息,一個(gè)人發(fā)出消息,可以有多個(gè)訂閱的人閱讀的到,需要有一個(gè)消息主題作為支柱,Tocip消息發(fā)送完畢以后 無(wú)論有 沒(méi)有客戶端接收,JMS服務(wù)器中的Topic消息都不會(huì)存在JMS服務(wù)器中。
Queue是一對(duì)一的消息傳送,你可以看做是QQ應(yīng)用程序中的一對(duì)一發(fā)送消息,一個(gè)人發(fā)出消息,只能有另外一個(gè)人閱讀的到,中間需要通過(guò)一個(gè)隊(duì)列支持Queue消息發(fā)送完畢后會(huì)保存在 JMS服務(wù)器的隊(duì)列 中,如果接收端接收以后將從隊(duì)列中摘除。
Topic發(fā)送完畢以后不會(huì)保留在JMS服務(wù)器;Queue消息發(fā)送完畢后會(huì)保存在 JMS服務(wù)器的隊(duì)列中。
//場(chǎng)景一:(Java應(yīng)用多線程的場(chǎng)景)我們的項(xiàng)目部署在Tomcat上,上百個(gè)客戶端訪問(wèn)同一個(gè)web應(yīng)用,tomcat接入后都是把后續(xù)的處理扔給一個(gè)新的線程來(lái)處理,這個(gè)新的線程最后調(diào)用到我們的servlet程序,比如doGet或者doPost方法。你想,如果不采用多線程機(jī)制,上百個(gè)人同時(shí)訪問(wèn)一個(gè)web應(yīng)用的時(shí)候,tomcat就得排隊(duì)串行處理了,那樣客戶端根本是無(wú)法忍受那種訪問(wèn)速度的。所以說(shuō),Tomcat內(nèi)部采用的就是多線程。
比如任務(wù) a和任務(wù) b要并行處理,單個(gè)線程只能串行處理,先做完任務(wù) a然后再做任務(wù)b。如果想要多個(gè)任務(wù)同時(shí)執(zhí)行的話,就必須為每個(gè)任務(wù)分配一個(gè)線程,然后通過(guò)java虛擬機(jī)的線程調(diào)度,來(lái)同時(shí)執(zhí)行多個(gè)任務(wù)。比如你的CPU是多核心的話,就可以讓一個(gè)CPU執(zhí)行一個(gè)線程。如果只有一個(gè)CPU的話,底層是按照分時(shí)復(fù)用的原則,各個(gè)線程按照時(shí)間片來(lái)獲得CPU資源。那么這種場(chǎng)景,顯然也是需要使用多線程的。
在Javaweb 開發(fā)方面,幾乎用不到多線程!因?yàn)橛卸嗑€程的地方servlet容器或者其他開發(fā)框架都已經(jīng)實(shí)現(xiàn)掉了。
//場(chǎng)景一:(Java集合類的應(yīng)用場(chǎng)景)說(shuō)到集合類,無(wú)非就是三個(gè):List、Set和Map。集合類,就是容器。容器就是箱子,Java中的箱子就是用來(lái)盛放Java對(duì)象。他們這三個(gè)箱子還是不一樣的。第一個(gè)箱子,箱子List。在J2EE web項(xiàng)目中,我們的admin管理端,常常需要呈現(xiàn)一些數(shù)據(jù)。比如你做了一個(gè)淘寶,對(duì)于商家來(lái)說(shuō),商家登錄管理端。需要看到自己所有的訂單,以列表的形式展現(xiàn)出來(lái)。那么這是我們常常會(huì)使用ArrayList。
ArrayList<OrderList> list = new ArrayList<OrderList>(); list.add(new OrderList()); list.add(new OrderList()); list.save();
List這個(gè)箱子,按照對(duì)象進(jìn)入的數(shù)學(xué)保存元素,不做排序和編輯操作。元素可以重復(fù)。
強(qiáng)調(diào)一下,List是接口不是類。List接口繼承自 Collection接口。實(shí)現(xiàn)List接口的類主要有三個(gè):LinkedList, ArrayList, Vector(面試中常有三種的區(qū)別)List接口有三個(gè)實(shí)現(xiàn)類:LinkedList,ArrayList,VectorLinkedList:底層基于鏈表實(shí)現(xiàn),鏈表內(nèi)存是散亂的,每一個(gè)元素存儲(chǔ)本身內(nèi)存地址的同時(shí)還存儲(chǔ)下一個(gè)元素的地址。鏈表增刪快,查找慢ArrayList和Vector的區(qū)別:ArrayList是非線程安全的,效率高;Vector是基于線程安全的,效率低
List的功能方法
實(shí)際上有兩種List: 一種是基本的ArrayList,其優(yōu)點(diǎn)在于隨機(jī)訪問(wèn)元素,另一種是更強(qiáng)大的LinkedList,它并不是為快速隨機(jī)訪問(wèn)設(shè)計(jì)的,而是具有一套更通用的方法。
List : 次序是List最重要的特點(diǎn):它保證維護(hù)元素特定的順序。List為Collection添加了許多方法,使得能夠向List中間插入與移除元素(這只推薦LinkedList使用。)一個(gè)List可以生成ListIterator,使用它可以從兩個(gè)方向遍歷List,也可以從List中間插入和移除元素。
ArrayList : 由數(shù)組實(shí)現(xiàn)的List。允許對(duì)元素進(jìn)行快速隨機(jī)訪問(wèn),但是向List中間插入與移除元素的速度很慢。ListIterator只應(yīng)該用來(lái)由后向前遍歷ArrayList,而不是用來(lái)插入和移除元素。因?yàn)槟潜萀inkedList開銷要大很多。(ArrayList有默認(rèn)的初始化容量以及擴(kuò)容機(jī)制,面試中常被提及)
LinkedList : 對(duì)順序訪問(wèn)進(jìn)行了優(yōu)化,向List中間插入與刪除的開銷并不大。隨機(jī)訪問(wèn)則相對(duì)較慢。(使用ArrayList代替。)還具有下列方法:addFirst(), addLast(), getFirst(), getLast(), removeFirst() 和 removeLast(), 這些方法 (沒(méi)有在任何接口或基類中定義過(guò))使得LinkedList可以當(dāng)作堆棧、隊(duì)列和雙向隊(duì)列使用。
第二個(gè)箱子,箱子Set。假如我們現(xiàn)在想要在一大堆數(shù)據(jù)中查找 X 數(shù)據(jù)。LinkedList的數(shù)據(jù)結(jié)構(gòu)就不說(shuō)了,查找效率低的可怕。ArrayList哪,如果我們不知道X的位置序號(hào),還是一樣要全部遍歷一次直到查到結(jié)果,效率一樣可怕。HashSet天生就是為了提高查找效率的。當(dāng)使用HashSet時(shí),hashCode()方法就會(huì)得到調(diào)用,判斷已經(jīng)存儲(chǔ)在集合中的對(duì)象的hash code值是否與增加的對(duì)象的hash code值一致; 如果不一致,直接加進(jìn)去; 如果一致,再進(jìn)行equals方法的比較。equals方法如果返回true,表示對(duì)象已經(jīng)加進(jìn)去了,就不會(huì)再增加新的對(duì)象,否則添加進(jìn)去。
http://www.java3z.com/cwbwebhome/article/article8/83553.html?id=4632 關(guān)于HashSet與hasCode() 的關(guān)系,可以看這篇帖子。
Set無(wú)放入順序,元素不可重復(fù)。同樣的Set是接口,它繼承自Collection接口,它的實(shí)現(xiàn)類有:HashSet(底層由HashMap實(shí)現(xiàn)),TreeSet,LinkedHashSet
Set具有與Collection完全一樣的接口,因此沒(méi)有任何額外的功能,不像前面有兩個(gè)不同的List。實(shí)際上Set就是Collection,只是行為不同。(這是繼承與多態(tài)思想的典型應(yīng)用:表現(xiàn)不同的行為。)Set不保存重復(fù)的元素(至于如何判斷元素相同則較為負(fù)責(zé)) Set : 存入Set的每個(gè)元素都必須是唯一的,因?yàn)镾et不保存重復(fù)元素。加入Set的元素必須定義equals()方法以確保對(duì)象的唯一性。Set與Collection有完全一樣的接口。Set接口不保證維護(hù)元素的次序。 HashSet : 為快速查找設(shè)計(jì)的Set。存入HashSet的對(duì)象必須定義hashCode()。 TreeSet : 保存次序的Set, 底層為樹結(jié)構(gòu)。使用它可以從Set中提取有序的序列。 LinkedHashSet : 具有HashSet的查詢速度,且內(nèi)部使用鏈表維護(hù)元素的順序(插入的次序)。于是在使用迭代器遍歷Set時(shí),結(jié)果會(huì)按元素插入的次序顯示。
第三個(gè)箱子,箱子Map。List,Set,Map是否繼承自Collection接口?List,Set是,Map不是。Collection是最基本的集合接口。Map沒(méi)有繼承Collection接口,Map提供key到value的映射。一個(gè)Map中不能包含相同key,每個(gè)key只能映射一個(gè)value。Map接口提供3種集合的視圖,Map的內(nèi)容可以被當(dāng)做一組key集合和一組value集合,或者一組key-value映射。
Map的功能方法: 方法put(Object key, Object value)添加一個(gè)“值”(想要得東西)和與“值”相關(guān)聯(lián)的“鍵”(key)(使用它來(lái)查找)。方法get(Object key)返回與給定“鍵”相關(guān)聯(lián)的“值”。可以用containsKey()和containsValue()測(cè)試Map中是否包含某個(gè)“鍵”或“值”。標(biāo)準(zhǔn)的Java類庫(kù)中包含了幾種不同的Map:HashMap, TreeMap, LinkedHashMap, WeakHashMap, IdentityHashMap。它們都有同樣的基本接口Map,但是行為、效率、排序策略、保存對(duì)象的生命周期和判定“鍵”等價(jià)的策略等各不相同。 執(zhí)行效率是Map的一個(gè)大問(wèn)題。看看get()要做哪些事,就會(huì)明白為什么在ArrayList中搜索“鍵”是相當(dāng)慢的。而這正是HashMap提高速度的地方。HashMap使用了特殊的值,稱為“散列碼”(hash code),來(lái)取代對(duì)鍵的緩慢搜索。“散列碼”是“相對(duì)唯一”用以代表對(duì)象的int值,它是通過(guò)將該對(duì)象的某些信息進(jìn)行轉(zhuǎn)換而生成的。所有Java對(duì)象都能產(chǎn)生散列碼,因?yàn)閔ashCode()是定義在基類Object中的方法。 HashMap就是使用對(duì)象的hashCode()進(jìn)行快速查詢的。此方法能夠顯著提高性能。 Map : 維護(hù)“鍵值對(duì)”的關(guān)聯(lián)性,使你可以通過(guò)“鍵”查找“值” HashMap : Map基于散列表的實(shí)現(xiàn)。插入和查詢“鍵值對(duì)”的開銷是固定的。可以通過(guò)構(gòu)造器設(shè)置容量capacity和負(fù)載因子load factor,以調(diào)整容器的性能。 LinkedHashMap : 類似于HashMap,但是迭代遍歷它時(shí),取得“鍵值對(duì)”的順序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一點(diǎn)。而在迭代訪問(wèn)時(shí)發(fā)而更快,因?yàn)樗褂面湵砭S護(hù)內(nèi)部次序。 TreeMap : 基于紅黑樹數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)。查看“鍵”或“鍵值對(duì)”時(shí),它們會(huì)被排序(次序由Comparabel或Comparator決定)。TreeMap的特點(diǎn)在于,你得到的結(jié)果是經(jīng)過(guò)排序的。TreeMap是唯一的帶有subMap()方法的Map,它可以返回一個(gè)子樹。 WeakHashMao : 弱鍵(weak key)Map,Map中使用的對(duì)象也被允許釋放: 這是為解決特殊問(wèn)題設(shè)計(jì)的。如果沒(méi)有map之外的引用指向某個(gè)“鍵”,則此“鍵”可以被垃圾收集器回收。 IdentifyHashMap : 使用==代替equals()對(duì)“鍵”作比較的hash map。專為解決特殊問(wèn)題而設(shè)計(jì)。
12. 講到Java這門語(yǔ)言,就不得不說(shuō)其運(yùn)行時(shí)候很關(guān)鍵的一部分,Java虛擬機(jī)。它承上啟下,基于操作系統(tǒng),服務(wù)于我們的程序。我們的程序的具體執(zhí)行是在Java虛擬機(jī)中來(lái)執(zhí)行的。我們?cè)贘ava基礎(chǔ)知識(shí)中,知道有基本數(shù)據(jù)類型和引用數(shù)據(jù)類型。所以,很多人只知道有堆內(nèi)存和棧內(nèi)存。但是,很顯然程序真正運(yùn)行的時(shí)候,內(nèi)存的劃分遠(yuǎn)比這個(gè)發(fā)雜的多,接下來(lái)我們就詳細(xì)了解一下Java運(yùn)行時(shí),內(nèi)存是怎么劃分疆域的。
方法區(qū) 堆 本地方法棧 程序計(jì)數(shù)器 虛擬機(jī)棧
為了容易記憶,總共劃分為五個(gè)部分。方法區(qū)、本地方法棧、虛擬機(jī)棧、堆、程序計(jì)數(shù)器。
// 方法區(qū)方法區(qū)與java堆棧一樣,是各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等等。雖然java規(guī)范里面把方法區(qū)描述為堆的一個(gè)邏輯部分,但是其實(shí)它是一個(gè)非堆【Non-Heap】,其目的就是為了和java堆區(qū)分開。 在HotSpot虛擬機(jī)中,很多人都把方法區(qū)成為永久代,其實(shí)只在hotspot才存在方法區(qū),在其他的虛擬機(jī)沒(méi)有方法區(qū)這一個(gè)說(shuō)法的。本文是采用Hotspot,所以把方法區(qū)介紹了。在這里在啰嗦一下,如果方法區(qū)無(wú)法滿足內(nèi)存分配需求時(shí)候就會(huì)拋出OutOfMemoryError異常。
// 本地方法棧本地方法棧和虛擬機(jī)棧差不多,只不過(guò)java虛擬機(jī)執(zhí)行java的方法,本地方法區(qū)執(zhí)行的是native方法而已,有的虛擬機(jī)把本地方法和虛擬機(jī)棧合二為一。
// Java虛擬機(jī)棧java虛擬機(jī)棧和程序計(jì)數(shù)器一樣的,線程都是私有的,它的生命周期與線程一樣【虛擬機(jī)棧描述的是java方法執(zhí)行的內(nèi)存模型,每個(gè)方法執(zhí)行的時(shí)候都會(huì)創(chuàng)建一個(gè)棧幀,用于存儲(chǔ)局部變量表、操作棧、動(dòng)態(tài)鏈接、方法出口等信息。每一個(gè)方法被調(diào)用直至執(zhí)行完成的過(guò)程。就對(duì)應(yīng)著一個(gè)棧在虛擬機(jī)中從入棧道出棧的過(guò)程。
// 程序計(jì)數(shù)器程序計(jì)數(shù)器是一個(gè)很小的內(nèi)存空間,它的作用其實(shí)很簡(jiǎn)單,就是當(dāng)前線程所執(zhí)行的字節(jié)碼行號(hào)的指示器。在Hotspot虛擬機(jī)中字節(jié)碼解釋器工作的時(shí)候就是通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令、分支、循環(huán)、跳轉(zhuǎn)、異常處理等等。
// 堆在java程序員中堆,肯定不陌生,堆是使用的最多的,也是程序猿最關(guān)心的一快內(nèi)存區(qū)域。堆中所有的線程都共享一個(gè)內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)的時(shí)候就被創(chuàng)建,此內(nèi)存區(qū)域唯一的目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例都在這里分配,但是隨著JIT的成熟,有些時(shí)候可能不一樣。同時(shí)java堆中也是GC管理的主要區(qū)域,有時(shí)候也叫GC堆,java堆中可以分為新生代和老年代,Eden空間,from Survivor空間,To Survivor空間。新生代和Eden空間采用的GC算法【主要是分代收集算法】不一樣。
13. Java集合類框架。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注