Java開發成長之路第一年
-
介紹
本文總結了要走Java開發這條路的成長軌跡。歡迎各位看客留下腳印。
正文開始之前,先申明幾點:本文說的“Java開發”指的是以Java後端為主、少量的前端開發,如果要了解更多前端開發的內容請移步;另外,本文假設您在學習Java之前沒有其他語言的基礎。
這是一個係列的文章,而且會長期更新,歡迎各位看客關注!
-
前言
這時候,作為剛剛加入“碼農”行列的您,還是有幾點是必須要搞定的:Java語法、行業代碼規範(類、方法、變量等的命名規範)、計算機基本的運行原理、Java程序運行的基本原理。我這裏說的“搞定”的更加準確的含義指的是:要很掌握的很熟練。因為這就像是樓梯的第一個台階、高樓大廈的地基一樣,它直接影響你後續階段的發展。
您別小瞧這幾點,以為在學校裏就早早地對Java語法很了解了;不止您在成長,Java本身也在發展中,所以Java的不同版本之間可能也會有或大或小、或多或少的差異,忽略這一點的童鞋可能會被“絆腳石”磕碰的很慘。
程序是運行在計算機上的,為了以後的發展,可以說對計算機的原理研究的不管多麼深入都是很有必要的。但現在,你至少要對CPU、內存、硬盤、網卡等大部件的性能參數有所了解。
而Java呢,是需要編譯為class文件(而不是機器碼),然後在虛擬機(JVM)上運行的。這一點,決定了Java是跨平台(Linux、Windows等)的,也決定了它可能會比C語言要運行的慢一點。它的內存管理機製,又決定了Java是比較耗內存的——雖然所占用的內存並不是時時刻刻都需要的。
以上三點相對來說都是比較範範的東西,下麵我來說一下對於初出茅廬的Java程序員們有哪些技能是必須掌握的,換句話說說,掌握了下麵的內容你就可以輕鬆應對一些麵試了:
-
封裝、繼承、多態
學Java或者其他麵向對象的童鞋們,這三個特性應該都是記得很清楚。它很重要,會一直貫穿你的Java開發生涯;但這六個字過於濃縮、抽象,是很難理解的。其實,我建議作為Java開發人員的第一年,反而可以忘記這些特性,先是多多動手,積累一些項目經驗,積累足夠的業務場景,積累足夠的常見問題後,再回過頭來細細研究這三個特性。
-
方法的重寫的重載
方法的重寫(override),指的是子類重寫(覆蓋)父類的方法。要求方法簽名要保持一致,不能拋出更多的異常(封裝),不能有更加嚴格的訪問權限(多態)。
方法的重載(overload),指的是在同一個類或者子類中來重載一個方法,兩個方法之間要求參數列表不能相同,而其他的要相同。這是為了能夠有多個過程類似的方法實現的。
-
Java接口、類(抽象)的概念
接口可以理解為是一種規範、標準、協議,而並不關心具體實現過程。接口中的方法隻能使用public、abstract來修飾,而屬性隻能是用public、static、final來修飾。換句話說,在接口中即使不添加任何修飾符也可以,因為它隻能添加那些修飾符——方法必然是public abstract的,而屬性一定是public static final的。根據接口中屬性的特點,我們可以推斷,在申明屬性變量時必須給賦初始值,而且不能被修改。
如果有人要問,在接口的方法上添加abstract或者不添加有沒有區別的話,從上文就可以推斷出——是一樣的。對應的屬性也是這個道理。
普通類中,所有的方法必須要有方法體——也就是說必須要有實現,同樣的方法不可以使用abstract來修飾。類隻允許繼承一個類,但是可以實現多個接口。類A實現類B,可以理解為類A也是一種B;而類A實現接口B、C,則可以理解為類A有B和C的特性(特點)。
而抽象類,就像是接口和普通類的結合體,方法既可以有抽象的(abstract)的,也可以沒有。對屬性則沒有任何限製。另外,由於抽象類中有可能有抽象方法,所以是無法直接進行實例化的。
-
Java的集合(Collection)體係
這是必須要弄明白的,在日常工作中跟它打交道的次數太多了。首先是java.util.Collection這個接口,它是集合的頂層接口,另外要留心它所在的包(package)是java.util。它的子接口包括List、Set,List的實現類包括:ArrayList、LinkedList、Vector。Set的實現類包括:HashSet、LinkedHashSet、TreeSet。
ArrayList的內部是用動態數組(普通數組的容量必須在初始化時給定)實現的,根據數組下標來對元素進行操作,具有訪問快速、修改較慢的特點,這些是由於ArrayList的存儲結構導致的。如果調用無參構造函數的話,會把內部數組elementData賦值一個空數組;如果調用的是帶有一個int類型參數(容量capacity)的構造函數的話,會創建一個指定大小的數組。在調用add方法時,會先檢查當前數組是否可以在容納一個元素(這時候,保證至少會有10的容量),如果不夠的話就準備擴容(最大容量為Integer.MAX_VALUE)——先是創建一個新容量的數組,然後把之前的元素拷貝過去,最後把要添加的元素加入新的數組中。
根據以上ArrayList類的特點,我們可以總結出一些ArrayList的使用技巧:如果您估計ArrayList中要添加的元素個數超過10個的話,最好調用有int參數的構造函數給定一個初始容量,這樣能有效地避免頻繁擴容導致的頻繁申請內存(堆),甚至能避免一些不必要的gc事件;ArrayList適合讀取和修改操作較多的場合,而不適合於添加或者刪除較多的場合;ArrayList並沒有實現了多線程之間的同步,所以是線程不安全的。
可以通過工具類java.util.Collections的sort方法給List排序。
通過Collections的binarySearch來快速地查找List中的元素。
LinkedList<E>實現的是雙向鏈表(Doubly-linked),另外還實現了接口java.util.Deque<E>。它有add、addFirst、addLast、addAll等方法,add等同於addLast,set方法可以添加到指定位置。調用get方法時,它會從所有的元素中遍曆查找。所以,LinkedList和ArrayList相反,插入、新增速度快,查找速度慢。另外,它也是線程不安全的。
Vector<E>除了實現了List接口外還實現了java.util.RandomAccess接口,默認的容量為10,內部也是用數組的形式實現的。它的add以及其他操作元素的方法使用了關鍵字synchronized,因此是線程安全的,也可以認為Vector是線程安全版本的ArrayList。
TreeSet<E>內部是利用TreeMap實現的,調用add方法時其實就是向map中添加一條key,value的值統一都是Object類。
HashSet<E>內部是利用HashMap實現的。add方法實現過程與TreeSet類似。
Java集合體係中的java.util.Map比較特殊,和Collection不是一類,它本身就是頂層接口。它的實現類包括:HashMap、Hashtable。
HashMap<K,V>默認的構造函數會初始化一個容量(應該是2的倍數,最大容量為Integer.MAX_VALUE / 2 + 1,或者為1 << 30)為10、因子(load factor)為0.75的集合,之後調用init方法(默認為空實現,是讓子類來擴展的)。
HashMap第一次調用put方法時,內部會調用方法inflateTable來初始化,所以說如果隻是申明了一個Map對象後並不會占用任何空間。從這裏可以看到HashMap內部也是通過數組實現的,數組的元素類型為HashMap.Entry;而Entry類包含了key、value、下一個Entry的引用、hash值。而當key為null時,則內部會調用方法putForNullKey,把key為null的Entry會放到數組的第一個。這時候會通過hash方法來計算key的hash值,hash方法會先調用key對象的hashCode,然後再進行一些hash運算。之後利用hash值和數組的長度計算出來一個數組下標值,如果數組中的該下標(index)中已經有了Entry對象,則從Entry鏈中通過hash查找是否已經存在了相同的key,有的話更新;沒有的話,則新增一個Entry放到數組的該下標中,然後該Entry指向之前的Entry,形成一個Entry鏈。
從上麵對HashMap的分析,可以得出一些結論。它內部是由多個元素類型為HashMap.Entry的數組實現,因此占用的內存空間還是比較大的。key可以為null。它從Java1.6開始出現,是非線程安全的。
Hashtable實現了抽象類Dictionary<K,V>。它從Java1.0開始出現。它的初始化過程和HashMap類似,但初始容量(capacity)為11,因子(load factor)為0.75f。
Hashtable的put方法使用關鍵字(synchronized)保證了線程間的同步,當key或者value為空(null)時會拋出空指針異常。
TreeMap<K,V>實現了接口NavigableMap<K,V>。該集合和HashMap實現的過程類似,隻是在調用put方法時就會先對key進行排序。這個排序就要求key實現接口Comparator<? super K>,或者是在實例化TreeMap時給定一個Comparator。
NavigableMap<K,V>該接口繼承了接口SortedMap<K,V>,從Java1.6開始出現。
-
Java流(Stream)體係
Java中的“流”相關內容都在包java.io中,想要係統的了解Java流就要知道有哪些分類。
按照大的類型分為:輸入流(InputStream)和輸出流(OutputStream)。這兩個頂層的流都是用抽象類來表示的,它們規定了輸入、輸出流的基本方法,它們是基於字節(byte)流。
java.lang.Readable和java.lang.Appendable是另一對Java“流”接口。注意這兩接口在java.io中對應的頂層抽象類分別為:Reader、Writer,它們是針對字符類型的流。
DataInput和DataOutput是Java“流”中的麵向數據類型的輸入、輸出流。
所有類型的輸入、輸出流都實現了接口Closeable,它隻有一個close方法,那這個close方法應該由誰來調用呢,我這裏有個建議——誰打開的“流”誰來關閉。
-
Java異常(Exception)體係
Java的異常體係的根類是java.lang.Throwable,它有兩個子類java.lang.Error和java.lang.Exception。
Error是比較嚴重的錯誤,基本會在程序無法繼續執行的情況下拋出,我們無法捕獲更無法處理。
Exception是“編譯時”異常的根類,它的子類java.lang.RuntimeException是“運行時”異常。所謂編譯時指的是在編譯Java源文件時拋出的異常,必須在處理(捕獲或者拋出)後源文件才可以正常編譯;所謂運行時異常,指的是在Java程序運行過程中有“可能”拋出的異常。
除了Error之外,編譯時異常也是比較嚴重的異常情況,所以必須顯示地處理。而運行時異常通常不嚴重,即遇到了這種異常情況程序也還是可以繼續的。
-
Java多線程(Thread)
實現一個多線程類,可以實現接口java.lang.Runnable或者類java.lang.Thread。這裏要注意的是類Thread也實現了接口Runnable。啟動一個線程要調用start方法,如下例子所示:
package test; /** * @author suren * @date 2016年6月29日 下午1:58:37 */ public class Test { public static void main(String[] args) throws Exception { new ThreadTestA().start(); new Thread(new ThreadTestB()).start(); } static class ThreadTestA extends Thread { @Override public void run() { System.out.println("from thread test a."); } } static class ThreadTestB implements Runnable { @Override public void run() { System.out.println("from thread test b."); } } }
線程間同步,可以在類或者方法上使用關鍵字synchronized。
另外,還有個線程相關的知識點。使用Thread.sleep和Object.wait相比較,前者會一直占用CPU,而後者會放棄CPU的控製權。
有關Java多線程的更多內容請搜索“Java多線程”。
-
設計模式“單例”
好多人都在談論“設計模式”,還有一些人可以說出很多種設計模式,例如:單例模式、工廠模式、觀察者模式等等。但對於剛剛進入這個行業的人來說,我不認為記住這麼多的名稱有任何用處,而且在實際項目中也不會有實踐的機會。學習設計模式這是一件長期的事情,而且在有一定項目編程經驗以後再來學習會更有效果。
我在這裏先介紹一個很容易理解的設計模式——單例模式。所謂單例,指的是在虛擬機(JVM)中某個類隻有一份實例。通常情況下,單例的實現有幾個需要注意的地方:構造函數私有化、提供一個靜態方法用於獲取實例對象、考慮多線程情況。
-
反射
在Java中所有的都是類,而所有java.lang.Object類則是所有類的父類——即使沒有“顯示地”繼承。而JDK中有些類是用來描述“類本身”信息的,它們大多數在包java.lang.reflect中,例如:Method、Field、Constructor、Member、Type、Modifier等。
而我這裏提到的反射就是JDK提供的用於獲取“類本身”信息的API,換句話說:通過反射技術可以動態第獲取甚至修改“類本身”的信息。如果你對C語言有點了解的話,這一點可以和C語言的指針來做對比理解。
那麼反射技術通常都用在什麼場合呢?利用反射可以做到很多常規程序無法做到的事情,例如代理,而在框架設計中經常會用到反射技術。
-
Java用戶界麵編程Swing
JDK中提供的用於編程GUI(用戶界麵)程序的是Swing框架。這裏的API都是在java.awt和javax.swing中,我們可以認為javax.swing包中的API是對java.awt中API的升級或者改進。例如,按鈕的實現類Button就有兩份,分別是java.awt.Button和javax.swing.JButton。從包的分布和類名上,我們可以看到區別,在javax.swing包中的組件類基本都是大寫字母J開頭的。這裏,我推薦使用javax.swing包中的API。
Java是跨平台的,由此提供的Swing框架也是在所有的平台上運行時可以保持一致的風格。但由此引發的一個問題是:利用Swing編寫的GUI程序(C/S架構)相對比較醜陋一點,導致在企業開發中用的相對比較少。但是,利用Swing來開發一些小的工具還是完全可以的。
這裏我介紹一些GUI程序原理性的內容,這樣你就算沒寫過也可以多少有點了解。所有的GUI程序的底層都是在調動API在屏幕上“繪製圖形”,因此呢,可以理解GUI程序中會有一個單獨的線程來做“繪製”工作,而且這個線程就是主線程。然後,我們繼續思考,如果一個界麵中的組件(元素)過多的話,就會導致“繪製”線程占用的時間比較多,使用起來顯得比較“卡”。
既然上麵我已經提到了線程,那麼在程序中讀取、處理數據時占用的時間比較長的話,就會導致界麵更加“卡”。為了解決這個問題,我們通常都會選擇新開一個線程來處理占用時間比較長的任務。到此為止呢,我們至少已經有了兩個線程,那麼在讀取、處理數據的線程中要修改界麵元素的話,就必然涉及到了線程間同步的問題——我們不可以在新的線程中修改界麵元素,否則會發生數據不同步的問題。
-
Java網絡編程
Java網絡編程是很大的一塊內容,現在呢,您隻需要了解Java網絡編程中都有哪些接口(功能),以及簡單了解網絡協議即可。
TCP/IP協議棧分為應用層、傳輸層、鏈路層等。Java提供了傳輸層協議的網絡編程接口,也就是可以對TCP、UDP協議數據包進行操作。而應用層的網絡協議,例如:FTP、HTTP、SFTP、SNAP等都需要利用第三方的庫了。HttpClient即是Apache的一個子項目,用於操作HTTP協議。
TCP協議是可靠的協議,它能保證數據包完整地在網絡中進行傳輸,而UDP協議則不能保證這一點,但UDP傳輸的速度相對比較快。據此特點,UDP經常用在需要傳輸大量數據,但對數據完整性要求不是很高的場合,例如視頻傳輸等。另外,TCP在發送數據包時,必須要求對方正在簡體你指定的端口,否則無法發送;而UDP協議則不需要對方一定是正在監聽中,它隻管發送,不關心對方是否已經接收了。
-
JSP原理
JSP(Java Server Pages)作為常用的一種動態頁麵技術,有幾個很關鍵的地方是必須理解清楚、正確的,這也是作為一個合格、稱職、專業的程序員所應該了解的。
首先,從JSP的字麵上來說,它一定是服務器端(後端)的技術,而不是前端技術,不可以和HTML混為一談。和其他的動態頁麵技術不同,它是需要編譯的——當它被訪問時,將會由JSP引擎編譯(class字節碼文件);如果是在Tomcat中運行的話,編譯後的字節碼文件會保存在目錄work/Catalina/localhost/${webroot}/org/apache/jsp中。
那JSP編譯完的class文件是什麼呢?從它所繼承(實現)的類上看,就能一目了然了。它繼承了類org.apache.jasper.runtime.HttpJspBase,實現了接口org.apache.jasper.runtime.JspSourceDependent。
而HttpJspBase是個抽象類,它繼承了抽象類javax.servlet.http.HttpServlet,實現了接口javax.servlet.jsp.HttpJspPage。
接口JspSourceDependent是為了能夠跟蹤JSP源碼文件的依賴關係,從而能夠編譯過期的JSP文件。
-
jQuery常用方法
這裏至少應該知道如何使用jQuery定位元素、設置(獲取)值、ajax請求。
jQuery有類(class)選擇器、名稱(tagname)選擇器、ID選擇器、屬性(attribute)選擇器、派生選擇器等。
-
JavaScript基本概念
-
各種瀏覽器的調試技巧
本站有一篇《JavaScript調試介紹》您可以搜索一下,介紹了各種主流瀏覽器的JavaScript調試方法和技巧。
-
集成開發工具Eclipse的熟練使用
本站有一篇《Eclipse使用技巧》您可以搜索一下,介紹了Eclipse的一些使用技巧。
-
應用服務器配置
本章有一篇《玩轉Tomcat》您可以搜索一下,介紹了Tomcat所有常用的配置方法。
-
數據庫
除了要會使用數據庫的帶有界麵的客戶端(例如:Navicat等)連接,還需要知道如何通過命令行來連接,例如:mysql -uroot -proot。
創建數據庫(database)、表(table)的SQL應該會寫,簡單的查詢語句更是必須的。
-
Struts2框架
-
Hibernate框架
本站有幾篇有關Hibernate的專題介紹,大家可以搜索一下《Hibernate注解類介紹》、《Hibernate常見異常》。
-
Spring框架
-
本係列文章
最後更新:2017-08-15 10:32:30