Java基礎
基本類(lèi)型不是new出來(lái)的則是放在棧里面,對象的引用也是放在棧里面的,只要是用new()來(lái)新建對象的,都會(huì )在堆中創(chuàng )建
String類(lèi)被設計成為不可改變(immutable)的類(lèi)。如果你要改變其值,可以,但JVM在運行時(shí)根據新值悄悄創(chuàng )建了一個(gè)新對象,然后將這個(gè)對象的地址返回給原來(lái)類(lèi)的引用。這個(gè)創(chuàng )建過(guò)程雖說(shuō)是完全自動(dòng)進(jìn)行的,但它畢竟占用了更多的時(shí)間。在對時(shí)間要求比較敏感的環(huán)境中,會(huì )帶有一定的不良影響。
關(guān)于String str = "abc"的內部工作。Java內部將此語(yǔ)句轉化為以下幾個(gè)步驟:
(1)先定義一個(gè)名為str的對String類(lèi)的對象引用變量:String str;
(2)在棧中查找有沒(méi)有存放值為"abc"的地址,如果沒(méi)有,則開(kāi)辟一個(gè)存放字面值為"abc"的地址,接著(zhù)創(chuàng )建一個(gè)新的String類(lèi)的對象o,并將o的字符串值指向這個(gè)地址,而且在棧中這個(gè)地址旁邊記下這個(gè)引用的對象o。如果已經(jīng)有了值為"abc"的地址,則查找對象o,并返回o的地址。
(3)將str指向對象o的地址。
值得注意的是,一般String類(lèi)中字符串值都是直接存值的。但像String str = "abc";這種場(chǎng)合下,其字符串值卻是保存了一個(gè)指向存在棧中數據的引用!
java內存泄露的兩個(gè)條件:無(wú)用,無(wú)法回收。
BigInteger能表示任意精度的整數, BigDecimal能表示任意精度的浮點(diǎn)數, 但都是用精度換速度的做法
容器集合
List的子類(lèi)里面LinkedList和ArrayList是非同步的,而Vector和Stack是同步的, LinkedList底層采用的是雙向鏈表(前指針+數據+后指針), 因此查詢(xún)效率低, 增刪效率高; ArrayList底層采用的是Array,因此查詢(xún)效率高, 增刪效率低; Vecotr是重量級的List, 一般在并發(fā)保證線(xiàn)程安全時(shí)采用
如果想跟蹤添加給HashSet的元素的順序,LinkedHashSet實(shí)現會(huì )有幫助。LinkedHashSet的迭代器按照元素的插入順序來(lái)訪(fǎng)問(wèn)各個(gè)元素。
List中的元素有順序, 可重復, Set中的元素無(wú)序, 不可重復(SortedSet是有序的)
Iterator的next方法將跳到下一個(gè)元素, 并返回剛剛跳過(guò)的元素, 因此使用remove方法的時(shí)候,必須先調用next方法
在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。但如果您要按自然順序或自定義順序遍歷鍵,那么TreeMap會(huì )更好。
LinkedHashMap擴展HashMap,以插入順序將關(guān)鍵字/值對添加進(jìn)鏈接哈希映像中。
Hashtable 類(lèi)似于 HashMap,但是不允許 null 鍵和 null 值。它也比 HashMap 慢,因為它是同步的。
效率
在方法內部頻繁存取變量的時(shí)候, 使用局部變量比使用實(shí)例變量和靜態(tài)變量要有更高的效率
異常對性能不利。拋出異常首先要創(chuàng )建一個(gè)新的對象。Throwable接口的構造函數調用名為fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法檢查堆棧,收集調用跟蹤信息。只要有異常被拋出,VM就必須調整調用堆棧,因為在處理過(guò)程中創(chuàng )建了一個(gè)新的對象。
I/O
任何有能力產(chǎn)生數據流(源)的java io對象就可以看作是一個(gè)InputStream對象, 何有能力接收數據源(流)的java io對象我們就可以看作是一個(gè)OutputStream對象
io數據流有兩種類(lèi)別,一種就是InputStream/OutputStream,一種是Reader/Writer, 分別用來(lái)處理字節流和字符流, 字符也是一種字節, 為什么還要用Reader/Writer呢, 這是因為在處理字符的時(shí)候可能涉及到轉碼,轉碼主要是Reader與InputStream,以及Writer與OutputStream之間, 轉碼的過(guò)程都是通過(guò)InputStreamReader和OutputStreamWriter來(lái)做的, 有時(shí)候我們可能沒(méi)有顯式的調用這兩個(gè)類(lèi), 但是相關(guān)的類(lèi)在內部是做了這種轉換的, 比如FileReader其實(shí)就是通過(guò)InputStreamReader將數據從FileInputStream取過(guò)來(lái)的, 比如將操作系統的文字編碼轉化為Unicode(因為在Java內部都是采用Unicode來(lái)保存數據的), 記住這里有一個(gè)前提,那就是操作系統當前的編碼格式和文檔的編碼格式是一直的,如果不一致就必須指定當前的InputStream的編碼格式.如果不知道字符所采用何種編碼方式, 那么就不要使用Reader和Writer而應該改用InputStream和OutStream
對輸入/輸出流進(jìn)行緩沖可以提高代碼執行效率, 也就是使用BufferedXxxx和BufferedXxxx對原有的流進(jìn)行封裝, 緩沖是一個(gè)非常重要而基本的加速I(mǎi)/O訪(fǎng)問(wèn)的技術(shù)
final關(guān)鍵字修飾的變量,表明該變量是不變的, 但是這里的不變只是所引用本身不變, 至于引用的對象是否改變則沒(méi)有約束, 比如final StringBuffer a=new StringBuffer("immutable");a.append(" broken!"); //編譯通過(guò), 這里我們可以看出, final只對引用的"值"(也就是它所指向的那個(gè)對象的內存地址)有效, 它迫使引用只能指向初始指向的那個(gè)對象, 改變它的指向會(huì )導致編譯器錯誤, 至于它所指向的對象的變化, final是不負責的.
當你要clone的類(lèi)里面含有可修改的引用字段的時(shí)候,那么你一定要把整個(gè)類(lèi)的藍圖進(jìn)行復制,如果對你clone得到的對象進(jìn)行修改的時(shí)候還會(huì )影響到原來(lái)的實(shí)例,那么這是不可取的。所以應該這樣clone()
JVM
一般來(lái)說(shuō),我們使用虛擬機的類(lèi)裝載時(shí)需要繼承抽象類(lèi)java.lang.ClassLoader,其中必須實(shí)現的方法是loadClass(),對于這個(gè)方法需要實(shí)現如下操作:(1) 確認類(lèi)的名稱(chēng);(2) 檢查請求要裝載的類(lèi)是否已經(jīng)被裝載;(3) 檢查請求加載的類(lèi)是否是系統類(lèi);(4) 嘗試從類(lèi)裝載器的存儲區獲取所請求的類(lèi);(5) 在虛擬機中定義所請求的類(lèi);(6) 解析所請求的類(lèi);(7) 返回所請求的類(lèi)。
Java中的類(lèi)的裝載過(guò)程也就是代理裝載的過(guò)程。比如:Web瀏覽器中的JVM需要裝載一個(gè)小應用程序TestApplet。JVM調用小應用程序裝載器ACL(Applet ClassLoader)來(lái)完成裝載。ACL首先請求它的父裝載器, 即系統裝載器裝載TestApplet是否裝載了這個(gè)類(lèi), 由于TestApplet不在系統裝載器的裝載路徑中, 所以系統裝載器沒(méi)有找到這個(gè)類(lèi), 也就沒(méi)有裝載成功。接著(zhù)ACL自己裝載TestApplet。ACL通過(guò)網(wǎng)絡(luò )成功地找到了TestApplet.class 文件并將它導入到了JVM中。在裝載過(guò)程中, JVM發(fā)現TestAppet是從超類(lèi)java.applet.Applet繼承的。所以JVM再次調用ACL來(lái)裝載java.applet.Applet類(lèi)。ACL又再次按上面的順序裝載Applet類(lèi), 結果ACL發(fā)現他的父裝載器已經(jīng)裝載了這個(gè)類(lèi), 所以ACL就直接將這個(gè)已經(jīng)裝載的類(lèi)返回給了JVM , 完成了Applet類(lèi)的裝載。接下來(lái),Applet類(lèi)的超類(lèi)也一樣處理。最后, TestApplet及所有有關(guān)的類(lèi)都裝載到了JVM中。
當 JVM 需要使用類(lèi)時(shí),它根據名稱(chēng)向 ClassLoader 請求這個(gè)類(lèi),然后 ClassLoader 試圖返回一個(gè)表示這個(gè)類(lèi)的 Class 對象。通過(guò)覆蓋對應于這個(gè)過(guò)程不同階段的方法,可以創(chuàng )建定制的 ClassLoader。其中有個(gè)loadClass(String name, boolean resolve)方法,該方法為ClassLoader的入口點(diǎn),在jdk1.2以后,loadClass方法將缺省調用findClass方法,詳細內容可以參考API文檔,我們編寫(xiě)的ClassLoader主要就是為了覆蓋以上兩個(gè)方法。
Java EE
Tomcat Server處理一個(gè)http請求的過(guò)程
假設來(lái)自客戶(hù)的請求為:
http://localhost:8080/wsota/wsota_index.jsp
1) 請求被發(fā)送到本機端口8080,被在那里偵聽(tīng)的Coyote HTTP/1.1 Connector獲得
2) Connector把該請求交給它所在的Service的Engine來(lái)處理,并等待來(lái)自Engine的回應
3) Engine獲得請求localhost/wsota/wsota_index.jsp,匹配它所擁有的所有虛擬主機Host
4) Engine匹配到名為localhost的Host(即使匹配不到也把請求交給該Host處理,因為該Host被定義為該Engine的默認主機)
5) localhost Host獲得請求/wsota/wsota_index.jsp,匹配它所擁有的所有Context
6) Host匹配到路徑為/wsota的Context(如果匹配不到就把該請求交給路徑名為""的Context去處理)
7) path="/wsota"的Context獲得請求/wsota_index.jsp,在它的mapping table中尋找對應的servlet
8) Context匹配到URL PATTERN為*.jsp的servlet,對應于JspServlet類(lèi)
9) 構造HttpServletRequest對象和HttpServletResponse對象,作為參數調用JspServlet的doGet或doPost方法
10)Context把執行完了之后的HttpServletResponse對象返回給Host
11)Host把HttpServletResponse對象返回給Engine
12)Engine把HttpServletResponse對象返回給Connector
13)Connector把HttpServletResponse對象返回給客戶(hù)browser
JDK新特性
注釋(Annotation),得先提一提什么是元數據(metadata)。所謂元數據就是數據的數據。也就是說(shuō),元數據是描述數據的。就象數據表中的字段一樣,每個(gè)字段描述了這個(gè)字段下的數據的含義。而J2SE5.0中提供的注釋就是java源代碼的元數據,也就是說(shuō)注釋是描述java源代碼的。
多線(xiàn)程 線(xiàn)程
實(shí)現線(xiàn)程,有兩種方法,一種是繼承Thread類(lèi),一種是實(shí)現Runnable接口。優(yōu)先采用實(shí)現Runnable接口的方法。首先,把需要共享的數據放在一個(gè)實(shí)現Runnable接口的類(lèi)里面,然后,把這個(gè)類(lèi)的實(shí)例傳給多個(gè)Thread的構造方法。這樣,新創(chuàng )建的多個(gè)Thread,都共同擁有一個(gè)Runnable實(shí)例,共享同一份數據。如果采用繼承Thread類(lèi)的方法,就只好使用static靜態(tài)成員了。如果共享的數據比較多,就需要大量的static靜態(tài)成員,令程序數據結構混亂,難以擴展。這種情況應該盡量避免。使用Runnable還有一個(gè)好處就是多線(xiàn)程應用對象可以繼承別的對象而不是必須繼承Thread類(lèi), 從而提高多線(xiàn)程對象的靈活性.
多線(xiàn)程的執行過(guò)程是當一個(gè)程序(一個(gè)線(xiàn)程)執行的過(guò)程中, 通過(guò)調用Thread.start(), 讓當前的程序(線(xiàn)程)繼續執行的同時(shí), 那個(gè)start的Thread會(huì )同時(shí)執行位于run()方法中的代碼, 多段可執行代碼交替執行的過(guò)程.
由于多個(gè)線(xiàn)程擁有度大力的執行對戰和程序執行上下文, 因此定義在線(xiàn)程方法中的局部變量不會(huì )出現資源共享的問(wèn)題, 資源共享同步主要發(fā)生在成員變量上.
按照線(xiàn)程體在計算機系統內存中的狀態(tài)不同,可以將線(xiàn)程分為創(chuàng )建、就緒、運行、睡眠、掛起和死亡等類(lèi)型。這些線(xiàn)程狀態(tài)類(lèi)型下線(xiàn)程的特征為:
創(chuàng )建狀態(tài):當利用new關(guān)鍵字創(chuàng )建線(xiàn)程對象實(shí)例后,它僅僅作為一個(gè)對象實(shí)例存在,JVM沒(méi)有為其分配CPU時(shí)間片等線(xiàn)程運行資源;
就緒狀態(tài):在處于創(chuàng )建狀態(tài)的線(xiàn)程中調用start方法將線(xiàn)程的狀態(tài)轉換為就緒狀態(tài)。這時(shí),線(xiàn)程已經(jīng)得到除CPU時(shí)間之外的其它系統資源,只等JVM的線(xiàn)程調度器按照線(xiàn)程的優(yōu)先級對該線(xiàn)程進(jìn)行調度,從而使該線(xiàn)程擁有能夠獲得CPU時(shí)間片的機會(huì )。
睡眠狀態(tài):在線(xiàn)程運行過(guò)程中可以調用sleep方法并在方法參數中指定線(xiàn)程的睡眠時(shí)間將線(xiàn)程狀態(tài)轉換為睡眠狀態(tài)。這時(shí),該線(xiàn)程在不釋放占用資源的情況下停止運行指定的睡眠時(shí)間。時(shí)間到達后,線(xiàn)程重新由JVM線(xiàn)程調度器進(jìn)行調度和管理。
掛起狀態(tài):可以通過(guò)調用suspend方法將線(xiàn)程的狀態(tài)轉換為掛起狀態(tài)。這時(shí),線(xiàn)程將釋放占用的所有資源,由JVM調度轉入臨時(shí)存儲空間,直至應用程序調用resume方法恢復線(xiàn)程運行。
死亡狀態(tài):當線(xiàn)程體運行結束或者調用線(xiàn)程對象的stop方法后線(xiàn)程將終止運行,由JVM收回線(xiàn)程占用的資源。
在Java中比較特殊的線(xiàn)程是被稱(chēng)為守護(Daemon)線(xiàn)程的低級別線(xiàn)程。這個(gè)線(xiàn)程具有最低的優(yōu)先級,用于為系統中的其它對象和線(xiàn)程提供服務(wù)。將一個(gè)用戶(hù)線(xiàn)程設置為守護線(xiàn)程的方式是在線(xiàn)程對象創(chuàng )建之前調用線(xiàn)程對象的setDaemon方法。典型的守護線(xiàn)程例子是JVM中的系統資源自動(dòng)回收線(xiàn)程,它始終在低級別的狀態(tài)中運行,用于實(shí)時(shí)監控和管理系統中的可回收資源。
jvm固定了線(xiàn)程和內存之間的關(guān)系, jvm的內存又分為主內存和工作內存, 線(xiàn)程要操作的數據放在工作內存中, 工作內存的變臉是主內存的拷貝, 線(xiàn)程執行完后工作內存中的變量還需要更新主內存中對應的變量.對于使用了synchornized保護的代碼對主內存變量將被鎖定從而保證主內存和工作內存之間的變量報紙一致性.但是使用synchonrized的代碼在性能上會(huì )帶來(lái)不小的損失
ThreadLocal并非是一個(gè)線(xiàn)程的本地實(shí)現版本,它并不是一個(gè)Thread,而是thread local variable(線(xiàn)程局部變量)。也許把它命名為T(mén)hreadLocalVar更加合適。線(xiàn)程局部變量(ThreadLocal)其實(shí)的功用非常簡(jiǎn)單,就是為每一個(gè)使用該變量的線(xiàn)程都提供一個(gè)變量值的副本,是每一個(gè)線(xiàn)程都可以獨立地改變自己的副本,而不會(huì )和其它線(xiàn)程的副本沖突。它的內部其實(shí)可以理解為僅有一個(gè)Entry的Map, key是當前的thread, 而value則是要保存的變量.因此它只有兩個(gè)方法set()將變量跟thread關(guān)聯(lián), get()方法從map中取出變量, 還有一個(gè)就是initValue方法, 是用來(lái)生成自己的ThreadLocal的時(shí)候用的, 用來(lái)給出默認值, 在get和set方法中調用
發(fā)表評論
您還沒(méi)有登錄,請登錄后發(fā)表評論(快捷鍵 Alt+S / Ctrl+Enter)