概述
在過去的20年里,如果要選一個最成功的編程語言那一定非Java莫屬。根據TIOBE Index發布的編程語言排行榜,自2001年以來Java語言在這個排行榜上最差的名次是第二名。在20年的發展歷程中,Java已經不僅僅是一門編程語言,它更是一個平臺,是一系列的計算機軟件和規范形成的技術體系,從嵌入式系統、移動終端到個人計算機、服務器等領域,Java都占據著舉足輕重的位置。按照官方網站在2016年提供的數據,全球有900萬Java開發人員并有約60億臺設備都在運行Java程序,這些數字都是非常震撼的。
術語
想了解Java,有三個術語必須要知道:
JDK:Java開發者工具 - Java Developer's Kit
JRE:Java運行時環境 - Java Runtime Environment
JVM:Java虛擬機 - Java Virtual Machine
歷史
關于Java這20年的故事,不是一兩句話能說清楚的,下面用編年史的方式做一個簡要的介紹。
- 1991年4月,James Gosling(計算機界的全能奇才,被稱為“Java之父”)領導的Green Project啟動,雖然項目項目并不成功,但是在項目中誕生的Oak語言伴隨著互聯網潮流的興起,迅速找到了適合自己發展的市場定位并最終蛻變為Java語言。
- 1995年5月23日,Java誕生。
- 1996年1月23日,JDK 1.0正式發布。
- 1996年5月,第一屆JavaOne大會在舊金山舉行。
- 1997年2月19日, SUN公司發布JDK 1.1,代表性的技術包括:JAR文件格式、JDBC、JavaBeans、RMI、內部類和反射等。
- 1998年12月4日,JDK迎來了里程碑式的版本JDK 1.2,SUN公司將Java技術拆分為3個方向,分別是面向桌面應用開發的J2SE、面向企業級開發的J2EE和面向移動終端開發的J2ME。該版本首次在Java虛擬機中內置了JIT編譯器。
- 2000年5月8日,JDK 1.3發布,代號Kestrel,改進了數學運算、時間以及Java 2D等API,新增了和聲音處理相關的類庫。此后,SUN公司基本每個兩年發布一個JDK的主版本,產品代號都以動物命名。
- 2002年2月13日,JDK 1.4發布,代號Merlin,Java開始走向成熟并逐漸達到巔峰,今天很多主流框架仍然可以在JDK 1.4上直接運行。該版本中引入了正則表達式、異常鏈、NIO、日志類、XML解析器等。
- 2004年9月30日,JDK 1.5發布,代號Tiger,該版本在Java語法的易用性上做出了很大的改進,引入了自動裝箱、泛型、注解、枚舉、可變參數列表、for-each循環等特性,同時在這個版本中還對Java內存模型進行了改進,同時引入了更優質的用于多線程編程的java.util.concurrent包。
- 2006年12月11日,JDK 1.6發布,代號Mustang,在這個版本中,J2SE、J2EE和J2ME被更名為Java SE、Java EE和Java ME,提供了對腳本語言的支持以及編譯器API和微型HTTP服務器API等內容。此外,該版本還對Java虛擬機內部進行了大量的優化,包括垃圾回收、類加載、鎖機制等。同年,SUN公司開始逐漸陷入困境。
- 2009年2月19日,JDK 1.7發布,代號Dolphin,JDK 1.7最初計劃引入很多開發者翹首以盼的功能,包括Lambda表達式,Jigsaw項目、動態語言支持、G1收集器和Coin項目,但是如前所述,由于SUN公司已經陷入困境,無力推動研發工作按正常計劃進行,在4月20日Oracle公司宣布以74億美元收購SUN公司,Java商標從此歸Oracle所有,此后Oracle公司執行了所謂的“B計劃”,大幅裁剪了JDK 1.7的預定目標,把諸多內容推遲到了JDK 1.8中,隨后又宣布Jigsaw在JDK 1.8中依然無法完成。
- 2014年3月18日,在漫長的等待后,“跳票王”Oracle公司終于推出了JDK 1.8,沒有產品代號,這一次真可以說得上是“千呼萬喚始出來”。
特點
自Java誕生以來,你可能聽過很多關于Java有多么牛逼的描述,但是其中很多內容都是過去時,當下的Java最吸引人的地方應該在于以下幾個方面。
- 對函數式編程的支持:面向對象的編程思想讓應用程序的開發變得簡單輕松,通過給對象發送消息很多問題就可以得到解決;而函數式編程的思想則源于λ 演算,強調使用表達式而不是語句,通過不改變狀態的操作讓代碼沒有副作用,這些都迎合了很多領域的開發者的需求,當下很多高級編程語言都提供了對函數式編程的支持,Java終于在JDK 1.8中引入了Lambda表達式、函數式接口、方法引用等高級語法來支持函數式編程,且看一小段代碼。
import java.util.List;
import java.util.Arrays;
class Hello {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "grape", "pitaya");
list.forEach(System.out::println);
}
}
- 始終如一的平臺可移植性:平臺可移植性從來都是Java最大的賣點,通過在不同的平臺下安裝對應的JVM來加載類文件執行,雖然在犧牲了執行效率,但獲得的是完美的平臺可移植性。
- 支持多種編程語言混編:目前的JVM已經能夠支持諸如Groovy、Scala、Clojure這樣的語言進行開發。使用非Java語言主要是出于這些考慮:首先使用領域特定語言可以方便構建、持續集成、業務規則建模等操作;其次也可以利用一些動態語言的特性來彌補Java本身的不足,滿足快速Web開發、腳本編程和測試等方面的需求。今天有很多軟件和系統的開發都是通過多語言混編實現的。
- 對多核并發編程的支持:Java語言內置的對多線程編程的支持是Java早期的一大賣點,但在今天看來已經不是什么了不起的事情。隨著CPU進入多核時代,Java在并發編程上也做出了與時俱進的升級,從JDK 1.5的java.util.concurrent包,到JDK 1.7引入Fork/Join模式,Java程序已經能夠輕松的利用多個CPU核心提供的計算能力來協作完成更復雜的任務。
探秘
想要深入學習和了解Java,可以對JVM進行一次探秘。
JVM中的內存
- 程序計數器(Program Counter Register)程序計數器可以視為當前線程所執行的字節碼的行號指示器,字節碼解釋器工作時就是通過改變程序計數器的值來選取下一條需要執行的字節碼指令。對于多線程的應用程序,每個線程都需要有一個獨立的程序計數器,線程之間互不影響,因此程序計數器是線程私有的內存。
- 虛擬機棧(VM Stack)
Java虛擬機棧也是線程私有的,它描述了Java方法執行的內存模型,即每個方法在執行時都會創建一個棧幀用于存儲局部變量表(存放了編譯期可知的基本數據類型,包括:byte、short、int、long、float、double、char、boolean,對象的引用以及返回地址)、操作數棧、動態鏈接、方法出口等信息。在JVM規范中,對這個區域規定了兩種異常狀況:如果線程請求的棧深度大于虛擬機所允許的深度,將引發StackOverflowError;如果虛擬機棧需要擴展空間但無法申請到足夠的內存,將引發OutOfMemoryError。 - 本地方法棧(Native Method Stack)
本地方法棧顧名思義是調用本地方法(用C/C++編寫的方法)時使用的棧。 - 堆(Heap)
堆是JVM所管理的內存中最大的一塊,它是被所有線程共享的內存區域,在JVM啟動時創建,用于存放對象實例。在JVM規范中明確指出:The heap is the runtime data area from which memory for all class instances and arrays is allocated. 但是隨著JIT編譯器的發展與逃逸分析技術的逐漸成熟,對象實例的棧上分配、標量替換優化將使得“對象一定在堆上”這個說法變得不那么絕對了。堆是Java垃圾收集器管理的主要區域,現在的垃圾收集器都基于分代收集算法,堆通常分為新生代和老生代,新生代具體又可細分為Eden、From Survivor和To Survivor。在啟動JVM時可以通過-Xmx和-Xms來控制堆的大小,還可以通過-Xmn和-XX:NewRatio來控制新生代的大小以及新生代和老生代的比例,如果想了解垃圾收集器的工作細節,可以使用-XX:+PrintGCDetails來做到。
- 方法區(Method Area)
方法區也是線程共享的內存區域,用于存儲已被JVM加載的類的信息、常量、靜態變量、JIT編譯后的代碼等數據。方法區中比較重要的一塊是運行時常量池(Constant Pool),它允許運行期間將新的常量放入池中,String類的intern()方法就是一個例子。當常量池無法再申請到內存時也會引發OutOfMemoryError。 - 直接內存
JDK1.4中引入了NIO(非阻塞式I/O),它可以使用本地方法分配堆以外的內存,然后通過ByteBuffer/CharBuffer對象來操作這些內存,從而實現提升性能的目標。
說明:Java虛擬機是非常復雜的,如果想要真正理解它的工作原理,可以嘗試自己寫一個Java虛擬機。不要覺得這件事情遙不可及,有一本名為《自己動手寫Java虛擬機》的書就用Go語言實現了一個自己的JVM。
JVM中的對象
- 對象的創建
虛擬機遇到一條new指令時,首先將檢查這個指令對應的參數是不是能在常量池中定位到的一個類的符號引用,然后還要檢查這個對應的類是否已經被加載、解析和初始化過,如果沒有就要執行類加載(具體的細節將在后續的文章中探討)。接下來需要為對象分配內存空間,而且內存空間除對象頭外都要被初始化為0值,這就保證了對象的屬性在不賦初始值的情況下就能直接使用。再下來,JVM要對對象進行必要的設置,主要是對象頭的設置以及對象的初始化工作(按照程序員的意愿為屬性設定對應的值),到此一個對象就真正的完成了創建過程。 - 對象的內存布局
對于HotSpot虛擬機而言,對象在內存中的布局可以為分3塊區域:對象頭、實力數據和對齊填充。對象頭包含兩部分信息,第一部分用于存儲對象自身的運行時數據,如哈希碼、GC分代年齡、鎖狀態標識等;另一部分是類型指針,也就是對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。 - 對象的訪問定位
開發
如果只想運行Java程序,那么有JRE就足夠了,因為它包含了運行Java程序所需的虛擬機還有就是支撐程序運行的上下文環境;如果想要用Java開發應用程序那么就得有JDK,因為JDK中包含了Java開發的必要工具。當然,在開發過程中使用IDE(集成開發環境)可以大大提升開發效率。對于Java開發而言,可以選擇的IDE主要有三個:
-
Eclipse:國內有很多程序員都在使用Eclipse,因為它簡單、免費、還有很多提升開發效率的特性。
IntelliJ:集成的插件和功能較多,代碼提示和修復能力強大,可以根據需求對IDE進行定制,有免費和付費兩種版本,后者支持的功能更多更強大。
- NetBeans:我自己使用這個IDE的次數非常少,因此也只能說這是官方提供的開發工具。
書籍
- 入門讀物:Core Java(中文名:《Java核心技術》,目前最新的版本是第10版,非常棒的入門書?。?/li>
- 進階讀物:Thinking in Java(中文名:《Java編程思想》)、Effective Java(提升Java水平必讀書籍)、Refactor(中文名《重構:改善既有代碼設計》,編程大師Martin Fowler神作)、《Java與模式》(美籍華人閻宏博士力作)