facebook廣告





123

2013年8月3日 星期六

Java程式語言基本介紹(二)

語言特性

Java之所以被開發,是要達到以下五個目的:
  • 應當使用物件導向程式設計方法學
  • 應當允許同一程式在不同的電腦平台執行
  • 應當包括內建的對電腦網路的支援
  • 應當被設計成安全地執行遠端程式碼
  • 應當易於使用,並借鑒以前那些物件導向語言(如C++)的長處。
Java技術主要分成幾個部分:Java語言、Java執行環境、類別庫。一般情況下說Java時並不區分指的是哪個部分。
Java在1.5版本時,做了重大改變,昇陽公司並1.5版本重新命名為Java 5.0

物件導向

Java的特點之一就是物件導向, 是程式設計方法的一種。「物件導向程式語言」的核心之一就是開發者在設計軟體的時候可以使用自訂的型別和關聯操作。程式碼和資料的實際集合體叫做「物 件」。一個物件可以想像成繫結了很多「行為(程式碼)」和「狀態(資料)」的物體。對於資料結構的改變需要和程式碼進行通訊然後操作,反之亦然。物件導向 設計讓大型軟體工程的計劃和設計變得更容易管理,能增強工程的健康度,減少失敗工程的數量。
物件導向設計另外一個標的就是能產生很多的有關聯的類,可以讓軟體的再開發變得簡單。舉例來說,很多軟體工程都有同樣的功能,尤其是很多應用了同一 原理組織的軟體工程。軟體的二次開發者想自己為軟體開發外掛模組以增強功能的時候,絕對不想看到混亂的開發程式碼和管理計劃。物件導向的目的就是不生產難 懂且難以使用的程式碼,為軟體各個功能群之間建立有效的通訊通道。很多開源軟體社群正在計劃給軟體作者提供更多的類來讓軟體的二次開發變得簡便。

跨平台性

Java語言的第二個特性就是跨平臺性,也就是說使用Java語言編寫的程式可以在編譯後不用經過任何更改,就能在任何硬體裝置條件下執行。這個特性經常被稱為「一次編譯,到處執行」。
執行Java應用程式必須安裝Java Runtime Environment(JRE),JRE內部有一個Java虛擬機器(Java Virtual Machine,JVM)以及一些標準的類別庫(Class Library)。透過JVM才能在電腦系統執行Java應用程式(Java Application),這與.Net Framework的情況一樣,所以電腦上沒有安裝JVM,那麼這些程式將不能夠執行。
實作跨平台性的方法是大多數編譯器在進行Java語言程式的編碼時候會生成一個用位元組碼寫成的「半成品」,這個「半成品」會在Java虛擬機器(解釋層)的幫助下執行,虛擬機器會把它轉換成當前所處硬體平台的原始程式碼。之後,Java虛擬機器會開啟標準庫,進行資料(圖片、執行緒和網路)的存取工作。主要注意的是,儘管已經存在一個進行程式碼翻譯的解釋層,有些時候Java的位元組碼程式碼還是會被JIT編譯器進行二次編譯。
有些編譯器,比如GCJ,可以自動生成原始程式碼而不需要解釋層。但是這些編譯器所生成的程式碼只能應用於特定平台。並且GCJ目前只支援部分的Java API。
甲骨文公司對於Java的許可是「全相容的」,這也導致了微軟和昇陽關於微軟的程式不支援RMI和JNI介面、並且增加特性為己所用的法律爭端。昇陽最終贏得了官司,獲得了大約兩千萬美元的賠償,法院強制要求微軟執行昇陽公司關於Java的許可要求。作為回應,微軟不再在Windows系統中捆綁Java,最新的Windows版本,Windows Vista和Internet Explorer 7.0版本也不再提供對於Java應用程式和控制項的支援。但是昇陽公司和其他使用Java執行時系統的公司在Windows作業系統下對使用者提供無償的第三方外掛模組和程式支援。
Java語言使用解釋層最初是為了輕巧性。所以這些程式的執行效率比C語言和C++要低很多,使用者也對此頗有微詞。很多最近的調查顯示Java的程式執行速度比幾年前要高出許多,有些同樣功能的程式的效率甚至超過了C++和C語言編寫的程式[來源請求]
Java語言在最開始應用的時候是沒有解釋層的,所有需要編譯的程式碼都直接轉換成機器的原始程式碼。這樣做的後果就是獲得了最佳的效能,但是程式臃腫異常。從JIT技術開始,Java的程式都經過一次轉換之後才變成機器碼。很多老牌的第三方虛擬機器都使用一種叫做「動態編譯」的技術,也就是說虛擬機器即時監測和分析程式的執行行為,同時選擇性地對程式所需要的部分進行編譯和最佳化。所有這些技術都改善了程式碼的執行速度,但是又不會讓程式的體積變得失常。
程式的輕便性事實上是軟體編寫很難達到的一個標的,Java雖然成功地實作了「一次編譯,到處執行」,但是由於平台和平台之間的差異,所編寫的程式 在轉換程式碼的時候難免會出現微小的、不可察覺的錯誤和意外。有些程式設計師對此非常頭疼,他們嘲笑Java的程式不是「一次編譯,到處執行」,而是「一 次編譯,到處偵錯」。以Java AWT 為例,早期Java AWT 內提供的按鈕、文字區等均是以電腦系統所預設的樣式而顯示。這令Java程式在有些沒有提供圖案的電腦系統產生錯誤(在Microsoft Windows設有視窗管理員,在一些Linux distribution則沒有)。後來SUN公司針對Java AWT一些問題而推出Java Swing。
平台無關性讓Java在伺服器端軟體領域非常成功。很多伺服器端軟體都使用Java或相關技術建立。

自動垃圾回收(Garbage Collection)

C++語言被用戶詬病的原因之一是大多數C++編譯器不支援垃圾收集機制。通常使用C++編程的時候,程式設計師於程式中初始化對象時,會在主機記憶體堆疊上分配一塊記憶體與位址,當不需要此對象時,進行解構或者刪除的時候再釋放分配的記憶體位址。如果對象是在堆疊上分配的,而程式員又忘記進行刪除,那麼就會造成記憶體洩漏(Memory Leak)。長此以往,程式執行的時候可能會生成很多不清除的垃圾,浪費了不必要的記憶體空間。而且如果同一記憶體位址被刪除兩次的話,程式會變得不穩 定,甚至崩潰。因此有經驗的C++程式員都會在刪除之後將指標重置為0,然後在刪除之前先判斷指標是否為0。
C++中也可以使用「智慧指標」(Smart Pointer)或者使用C++託管擴展編譯器的方法來實現自動化記憶體釋放,智慧指標可以在標準類庫中 找到,而C++託管擴展被微軟的Visual C++ 7.0及以上版本所支援。智慧指標的優點是不需引入緩慢的垃圾收集機制,而且可以不考慮線程安全的問題,但是缺點是如果不善使用智慧指標的話,效能有可能 不如垃圾收集機制,而且不斷地分配和釋放記憶體可能造成記憶體碎片,需要手動對堆進行壓縮。除此之外,由於智慧指標是一個基於模板的功能,所以沒有經驗的 程式員在需要使用多態特性進行自動清理時也可能束手無策。
Java語言則不同,上述的情況被自動垃圾收集功能自動處理。對象的建立和放置都是在記憶體堆疊上面進行的。當一個物件沒有任何參照的時候,Java的自動垃圾收集機制就發揮作用,自動刪除這個物件所佔用的空間,釋放記憶體以避免記憶體洩漏。
注意程式設計師不需要修改finalize方法,自動垃圾收集也會發生作用。但是記憶體洩漏並不是就此避免了,當程式員疏忽大意地忘記解除一個物件不應該有的參考的時候,記憶體洩漏仍然不可避免,例如以下的程式:
// ...
String str = "這是一段字串";
System.out.println(s);
 
for(int i){
    // 無關緊要的迴圈
    System.out.println("Hello World"+i);
}
// ...
在迴圈開始之前,字串str已經不會再用到了,但未將這個參考指向null,因此字串str無法被gc所回收。這種記憶體洩漏必須等到一個函式結束之後才會被系統取回,只不過發生的機率要比不啟用垃圾收集機制的C++程式少很多。但是總體來講,自動垃圾收集機制要安全和簡單許多。
不同廠商、不同版本的JVM中的記憶體垃圾回收機制並不完全一樣,通常越新版本的記憶體回收機制越快,IBM、BEA、SUN等等開發JVM的公司都曾宣稱過自己製造出了世界上最快的JVM[來源請求],JVM效能的世界紀錄也在不斷的被打破並提高。
IBM有一篇有關Java記憶體回收機制比不啟用垃圾收集機制的C++記憶體處理快數倍的技術文章[8],而著名的Java技術書籍《Java編程思想》(Thinking in Java)也有一段論述Java記憶體及效能達到甚至超過C++的章節[9]

介面和類別

Java內建了建立介面的類別,可以這樣使用:
 public interface Deleteable {
    void delete();
 }
這段程式碼的意思是任何實作(implement)Deleteable介面的類別都必須實作delete()方法。每個類別對這個方法的實作可以自行客製。由此概念可以引出很多種使用方法,下面是一個類別的例子:
 public class Fred implements Deleteable {
     // 必須實作 Deleteable 介面中的 delete 方法
     @Override public void delete() { 
         // 實作的程式碼
     }
 
     // 這個類別也可以包含其他方法
     public void doOtherStuff() {
     }
 }
在另外一個類別中,可以使用這樣的程式碼:
 public void deleteAll (Deleteable [] list) {
     for (int i = 0; i < list.length; i++) {
          list[i].delete();
     }
 }
因為佇列中所有的物件都可以使用delete()方法。Deleteable佇列中包含Fred物件的參照,而這個類別和其他Deleteable類別在使用deleteAll()方法時候不需要進行任何改變。
之所以這樣做就是為了在介面的執行和其程式碼之間進行區別。舉例來說,一個名叫Collection的介面可以包含任何物件所需要的引入、轉換和儲存資料的方法,其他的類都可以使用這個介面。但是這個介面可以是一個可重定義大小的佇列、一個連結串列或者是其他功能的集合。
這種特性其實是一種折中的辦法。Java的設計者們不想讓Java有多重繼承的特性,因為C++的多重繼承顯示了這種特性的困難。Java的介面功能可以提供同樣的功能,但是又不會很複雜。

應用程式開發介面

在Java語言中,應用程式介面(API)化身成類別,並且分組成為套件。每個包中包含有相關的介面和類。對於不同的平台,Java提供了不同版本的包。API的設定由sun公司和其他公司透過JCP(Java社群程式)決定。任何公司和個人都可以參與這個工程,對API進行設計。2004年IBMBEA公司準備聯合對官方的Java開源軟體工程進行支援,但是2005年初,sun公司拒絕了這個支援。

Hello World

下面這個程式顯示「Hello, world!」然後結束執行,注意java.lang套件是自動載入的,所以不需要在程式之前加入import java.lang.*;
public class HelloWorld 
{
    public static void main(String args[]) 
    {
        System.out.println("Hello, World!");
    }
}

用途

關於Java的批評


JavaOne研討會發言紀念戒指,側面刻有昇陽字樣
Java試圖通過新的方式解決軟體編寫的複雜性。很多人認為Java語言做到了它承諾的一切。但是Java並不是一門完美的語言。

整體性問題

並不是所有的工程和環境需要企業等級的複雜性,比如一個簡單的個人網站或者獨自編程的程式師所寫的程式。這些程式師會發現Java的複雜管理對於自己要做的程式來說過於強大了。一些人覺得Java在物件導向上面做的沒有RubySmalltalk純粹。但是最新出現的用Java實現的語言Groovy解決了這些問題。
作為一種已經建立的新技術,Java顯然綜合了很多語言的特性,比如C++、C語言、Python等等。一些對於Java的評論認為Java的不變性在動搖。

語言問題

有些程式師不喜歡原始類型(primitive type)和型別(class)的分離,尤其是那些曾經使用過SmalltalkRuby的程式師。Java的代碼相對於其他的代碼來說過於冗長,這與它的輕便化聲明相違背。
Java是一種單層繼承的語言。這也導致了程式師在試圖使用多重繼承時候的不便,而很多語言都可以使用這個特性。但是Java可以使用介面類,把多重繼承可能導致的風險減少到最小。Java不支援運算符重載,這是為了防止運算符重載使得代碼的功能變得不清晰。但是用Java實現的語言Groovy可以進行運算符重載。過去Java對於文字的操作和其他語言,比如PerlPHP相比差的較多,但Java在1.4版本時候引入了正規表式
至Java 1.7為止,Java語言不支援閉包(closure)和混入(mixin)特性。預計這兩項特性將在Java 1.8中加入。

類庫問題

使用Swing平臺編寫的帶有GUI(圖形用戶介面)的程式和其他原始程式非常不同。選用AWT工 具包編寫程式的程式師看到的都是原始介面,而且也無法獲得先進的GUI編程支援,如果使用的話,就要提供每個平臺上面所需的API,這將是一項龐大的工 程。Swing則是完全用Java語言所寫的程式,避免了介面元素重複的問題,只使用所有平臺都支援的最基本的繪圖機制。但是很多用戶不知道如何在 Java風格和Windows風格之間進行轉換,結果造成了Java程式的介面在很多程式中非常特殊。蘋果電腦已經提供了優化過的Java執行時程式,包含了Mac OS X的經典Aqua介面風格。
在IBM捐贈給Eclipse基金會的SWT介面框架中,使用者會看到熟悉的本地風格介面。但這又引起了不同喜好的開發人員之間的爭論。

效能問題

由於Java編譯器和虛擬機的不同對Java代碼的效能影響比語言本身的影響大的多,所以統一討論Java的程式的效能經常是有誤導性的。據IBM的資料,在同樣的硬體上2001年時的IBM JDK版本的效能是1996年的JDK版本的十倍左右。見IBM東京研究院的資料: http://www.is.titech.ac.jp/ppl2004/proceedings/ishizaki_slides.pdf 而即使是在同一時期,不同公司的JDK和JRE的效能也不一樣,比如SUN、IBM、BEA等公司都有自己開發的JDK和JRE
Java語言的一些特性不可避免的有額外的效能代價,例如陣列範圍檢查、執行時類型檢查等等。Java程式的效能還會因為不同的動態複雜性和垃圾處 理機制使用的多少而各有不同。如果JVM的實現比較優化的話,那麼這些功能甚至可以增加記憶體分配的效能。這和總是使用STL或者託管C++的程式的情況 類似。
儘管如此,仍然有許多人認為Java的效能低。這部分歸因於Sun公司最初的JVM實現使用未優化的解釋機制來執行位元組碼。一些新版本的JVM使用Just-In-Time(JIT) 編譯器,在載入位元組碼的時候將其編譯成針對執行環境的本地代碼來實現一些本地編譯器的優化特性。Just-In-Time機制和本地編譯的效能比較仍舊 是一個有爭議的話題。JIT編譯需要很多時間,對於執行時間不長或者代碼很多的大型程式並不適宜。但是不算JIT編譯階段的話,程式的執行效能在很多 JVM下可以和本地編譯的程式一爭短長,甚至在一些計算比較密集的數值計算領域也是這樣。目前,Java已經使用更先進的HotSpot技 術來代替JIT技術,Java的效能有了更進一步的提升。另外,在使用-server選項執行Java程式時,也可以對Java進行更深入的優化,比如在 執行時將調用較多的方法內聯(inline)到程式中來提高執行速度,這就是所謂的「動態優化」,而本地編譯器是無法做到這一點的;這也是一些Java代 碼比對應用C/C++等語言編寫的本地代碼執行的更快的原因之一。微軟的.NET平臺也使用JIT編譯器,所以也有類似問題。
Java的設計目的主要是安全性和可攜性,所以對於一些特性,比如對硬體架構和記憶體位址訪問的直接訪問都被去除了。如果需要間接調用這些底層功能的話,就需要使用JNI(Java 本地介面)來調用本地代碼,而間接訪問意味著頻繁調用這些特性時效能損失會很大,微軟的.NET平臺也有這樣的問題。所以到目前為止,效能敏感的代碼,例 如驅動程式和3D視頻遊戲,還是大多使用本地編譯,甚至直接以不直接支援物件導向的C語言或機器碼編寫。但最近已經有了許多用純Java編寫的3D遊戲, 其效果與用C語言編寫的不相上下,例如「合金戰士」(英文名:Chrome)。這主要是因為新版的Java 3D技術已經能像C++一樣調用硬體加速,也就是使用顯示卡來加速,無論是C++還是Java語言寫的3D遊戲都是使用顯示卡及GPU來處理,從而使得CPU可以專注於其他方面的工作。

文章出處:https://zh.wikipedia.org/wiki/Java

沒有留言:

張貼留言