2010年9月14日 星期二

【Java】程式的名詞與概念探討

內容是我開始學 programing 到現在對程式累積的概念,以 OOP 為主,using Java。
有任何錯誤,歡迎指證題點。

這是一張用於假想程式在記憶體中運作情況的 model。

public class Object{
    static int g=0;
    int i=1;
    Object b = new Object();  
}
當我們開始運作以上Java程式,它可能變成這樣。
由上述圖解我們可知圖中區塊分別存放的類型是
變數 ( Reference 或 Variable )
在程式中指定名稱給特定的記憶體區塊,並能使用此名稱重複的存取此特定記憶體
因其記憶體值可變動,故稱變數。 一個變數的組成包含型別與名稱,如
int variable=0;
int 即其型別,而 variable是名稱。

常數 ( Constant )
相對於記憶體值可變動的變數,你也可以在宣告時設定值在程式運行過程中不再改變
宣告為此狀態者稱常數。
final int constant=1;

型別 ( Type )
又分為存放在 stack 的基礎資料型別,包含
  • 型別---------預設值
  • boolean----false
  • char---------'\u0000'(等於null)
  • byte---------0
  • short--------0
  • int------------0
  • long---------0L
  • float---------0.0f
  • double-----0.0d
參考資料型別,它是由數值與字元組成的記憶體位址值,紀錄 heap 中實體的位址
基礎資料型別其實就是數值與字元資料,而參考資料型別則是可自定義的資料。

物件 ( Object )
也稱 實體 ( instance ),就是 heap 中的一塊內容自訂的記憶體,它將根據其類別指定的
規格產生內容。

宣告 ( Declare )
Object b;
像這樣我們在程式中新建一個變數的並取得 stack 或 global 空間的動作稱為宣告
指派 ( Assignment ) 
使用=符號指定值給變數的動作稱為指派。

初始化 ( Initialized )
b = new Object();
當我們對變數第一次指派值的動作稱為初始化
要注意的是初始化跟宣告是無關的,你可以宣告變數的同時替它初始化,也可以不做,
但是若程式中使用未初始化的變數可能會造成錯誤。 如果程式運行中使用到未初始化
的基礎資料型別變數,則程式會取用該型別之預設值,如果是參考資料型別,則會取用
到 null 值而造成錯誤。
類別 ( Class )
它是一個物件的屬性與方法的模型。 在OOP中,我們以模擬現實世界狀態的方式
來撰寫程式,一個程式的物件就相當於一個現實世界中的物體,它可以是動物、植物
或非生命,而類別就相當於製造此物體的模板
舉例來說,我桌上的杯子是一個物體,它有卡通人物的圖案(屬性),它能承裝我買的
冬瓜味很淡的冬瓜紅茶(方法),而工廠量產這種杯子的模板就相當於類別。

屬性 ( Property )
public class Object{
    int property;
}
即是一個屬於某物件的變數。 意義上它代表這個物件的特徵,也就是它跟同類別物件
的不同之處;比如同樣是杯子,但可能花紋不同

方法 ( Method )
public class Object{
    private void method(){
    } 
}
即一個屬於某物件的函式。 意義上它代表這個物件可以做的事情,比方杯子可以裝冬瓜紅

封裝 ( Packaging )
廣義的封裝是指我們將變數與方法寫進類別中,以物件為單位來存取,可以達到階層式
控管的優勢,而不是明明意義上彼此相關連的變數與函數散落在同一主程式的各處。
在程式面上還有透過存取修飾子來限定物件的屬性與方法的存取權限,主要為安全性考量
對軟體工程合作來說也比較簡便(特定的package、類別,只會取用到特定的屬性與方法)。

存取修飾子 ( Access Modifier )
包含...
  • private     : 僅同類別可存取。
  • default     : 僅同 package可存取。
  • protected: 不同 package可透過繼承關係存取。
  • public      : 自由存取。
※不打任何修飾子即為 default。

package ( 套件 )
即裝載了java的類別檔的資料夾,在使用package定義套件名稱時必須注意對應的資料夾
名稱必須是正確的。 比方在類別檔宣告package pet.water; 那此一類別檔就一定必須
存在...\pet\water目錄下。當我們要使用特定套件中的類別時我們可以打上完整的套件路徑
pet.water.Fish myFish = new pet.water.Fish();
或者在程式開頭使用 import 關鍵字引用套件進類別檔。

import ( 匯入 )
import pet.water.*;
即可直接在該類別檔使用
Fish myFish = new Fish();

抽象化 ( Abstraction )
指從某種現實生活中的物體或概念,抽出其擁有的具有代表性的特質,做為其形象
判別的方法。從程式的角度來看,抽象化就是類別化;將現實生活中的物體或概念(物件)
,抽出其擁有的具有代表性的特質(屬性與方法),做為其形象判別(類別)。

參數與引數 ( Parameter & Argument )
class Student {
    void method(int i ){}
    public static void main(){
        new Student().method(0);
    }
}
上述的 i 為參數,而 0 為引數。 故我們可知引數即為參數的傳入值
參數在使用上和一般同宣告的變數相同,唯一不同之處是參數的初始化是由調用該方法的
時數入的引數為指派。

方法多載 ( Overloading )
即具有不同方法簽名之方法會被程式視為不同的方法

方法簽名 ( Method Identification )
包括 方法的名稱、參數的型別、順序與數量
public class Object{
    private void method(){}
    private void method(int i){}
    private void method(int i, String s){}
    private void method(String s, int i){}
    private void method2(){}
}
如上述5個方法將被程式程式解讀為不同方法。

16/5/26 補充
回傳值並不是方法簽名的一環,因為方法的識別取決於使用端怎麼區隔,比方
int GetVal()
{
    return 0;
}

string GetVal()
{
    return "x";
}
對 GetVal() 的使用端來說是無法分別這兩個的(不知道要取 0 或 "x"),所以編譯器也不會讓這樣的寫法通過。

建構子 ( Constructor )
當類別使用 new 關鍵字建構成實體時會執行的一段敘述,一般用於對該實體初始設定。
public class Object{
    int i;
    public Object(int i){
        this.i = i;
    }
}

new 關鍵字
new Object();
這個關鍵字的作用是在 heap 產生一塊指定類別的實體。 當你使用 new 產生實體時
不一定要搭配變數宣告,但是未參考的實體會被系統預期為只使用一次,而很快被
記憶體回收機制回收掉。

this 關鍵字
是程式正在運作的類別產生之實體的參考。
public class Desktop{
    Desktop mine = new Desktop();
    private Dialog dialog = new Dialog(){
        public void onClick(Dialog dialog){
            Desktop.this.remove(dialog);
        }
    }.show();
    private boolean remove(Dialog dialog){
        dialog.finish();
    }
}
上述程式碼是一個舉例,我設置了一個類別它代表桌面(Desktop),當第二行我宣告 一個名為 mine 的 Desktop變數並初始化它,記憶體模型會變成這樣。
當執行到第三行時,程式設置了一個訊息視窗,它的功能是跳出來解釋一些廢話,
然後再按下確定後它會從桌面被移除。  由於 new Dialog 是一個內置類別,所以當
執行到第四行的時候正在運作的類別由 Desktop 切換為 Dialog,this 的指向也將會
由 new Desktop()實體切換為 new Dialog()實體。
但如果我要在 Dialog 這個類別中使用 Desktop 的實體該怎麼做呢? 當然,由於我們
有宣告一個叫做 mine 的參考,所以可以直接使用 mine;但是如果是沒有宣告參考的
情況我們也可以透過像第五行的指定某類別之 this 的方式來指定特定類別運行中的實體。
繼承 ( Inherit )
當我們要定義某一新類別,而此一類別之內容大多為過去定義之某類別之內容時,
可以直接定義新類別是繼承自舊類別的方式,如此可以沿用舊類別的屬性方法而
不需要重打一遍。 繼承中的父類別為過去定義之類別,子類別為欲新建之類別。

從模型來看子類別產生的實體,其實是一個包含父類別實體的複合實體
class Father{}
class Child extends Father{}
Child c = new Child();

super 關鍵字
而相對於 this 指向正在運行的類別實體,super則是指向正在運行類別實體中的
父類別實體,此一父類別實體是在子類別實體 new 時由系統自動產生。
多型 ( Polymorphism )
指在繼承架構下,同一實體可被宣告為多種型別。 程式實作上來說即你可以指派
子類別實體給父類別的變數(但不可逆)。 舉例來說...
class Animal {}
class Cat extends Animal {}
class Tiger extends Cat {
    Animal a = new Tiger(); //OK!
    Tiger t = new Animal(); //Error!
}
老虎繼承貓,而貓繼承動物,一隻老虎必然是一種動物,但你不能用老虎來詮釋
可能是任何種類的一隻動物。 從程式的角度來看,子類別變數的容量比較大而父類別
變數的容量比較小,所以你無法拿父類別變數這個小盒子來裝子類別實體這隻大肥貓。

陣列 ( Array )
是一個物件,其內容為根據宣告型別所指定的多個基礎或物件型別資料值,你可以透過
索引機制來對陣列中的特定元素作存取。
int[] ary = new int[3];
索引 ( Index )
是為了識別陣列中的許多元素,依據陣列宣告的維度從0開始到陣列維度-1的數值
0代表第一個元素、1代表第二個...依此類推。
int[] i = {1, 2, 3};
以上述程式碼來看,索引 0 即 i[0] 值為 1、i[1] 為 2等...。

元素 ( Element )
陣列或集合實體中的切塊。

集合 ( Collection )
類似於陣列,集合的內容是依據宣告型別所指定的多個實體(不可為基本型別值)。
最初發展集合的原因是在,陣列宣告同時就必須給定維度,造成動態存取的困難;
所以開發了可以隨時變動維度的集合。 而填入集合的任何資料都會被轉型為Object型別,
所以即使你填入的是int,在讀出時也需要經過型別迫換才能指定值給int型別的變數。

從程式的角度來看,集合其實是利用 Object[] 實作的API。
在分類上大致可分成三種...
  1. set:元素內容不可重複的集合(使用equals比較)。
  2. list:就像陣列,不過任何元素取出後,其後的元素會向前遞補。
  3. map:使用鍵值存取元素,你可以自訂鍵值的類型。

泛型 ( Generics )
是指當我們實作集合物件時,所有的輸入元素值都會被轉Object,但如果我們輸入的值
的類型其實是統一為某統一型別,比方String,那讀出的時候還要轉型就顯得很啟智。
Vector v = new Vector();
v.add("A");
v.add("B");
for(Object obj : v)
    System.out.println( (String)obj );
所以 Java 提供了所謂泛型方式,讓我們可以在宣告集合的同時就限定可填入的元素類型。
Vector<string> v = new Vector<string>();
這樣一來Vector v 這個集合就只能填入String類型的元素值了,而使用時也不需轉型。

泛化 ( Generalization )
在宣告集合時指定泛型這個動作即稱泛化。
Vector v = new Vector(); //無泛化
Vector<string> v = new Vector<string>(); //有泛化
但是要注意到,你不能泛化集合為基本資料型別,只能泛化為資本資料型別類別。
Vector<int> v = new Vector<int>(); //Error
Vector<integer> v = new Vector<integer>(); //OK

向量 ( Vector )
在演算法中的向量與集合是同一種概念,使用「向量」這個名詞通常是為了強調該集合是具有方向性(索引值表達)、大小關係,或是做為維度的表達(通常是多維度)。

在 Java 中,Vector本身是一個集合類別,與 ArrayList 同為一維表達,但是 Vector是與主執行緒同步的,而 ArrayList 不是, 且消耗較少的記憶體空間。

機率向量 ( Probability Vector )
是表示一個非負值浮點點數向量,個別元素值表達特定機率,且所有元素值和為1。

API ( Application Programming Interface )
應用程式介面為:「『電腦作業系統』或『程式函式庫』提供給應用程式呼叫使用的程式碼」。其主要目的是讓應用程式開發人員得以呼叫一組常式功能,而無須考慮其底層的原始碼為何、或理解其內部工作機制的細節。API本身是抽象的,它僅定義了一個介面,而不涉入應用程式如何實現的細節。 (以上定義擷自維基百科)

所以,根據分析自上面那一堆又臭又長定義:API 是一組程式碼,目的是讓開發者透過實作它
來取用與之相關的類別組,而不需要知道這些類別組的原始碼內容。 不過一般講的 API 到不是真的單是指其中的 interface 的部分,而是指與該介面相關的所有類別,甚至 Lib 或整個Framework、SDK。

介面 ( Interface )
名詞上的定義是讓兩個不同的東西得以連接或溝通的方式。 從程式面來說,指在某類別尚未撰寫之前,預先訂義類別所需常數與方法的一種程式碼。 這種設計是為了方便撰寫軟體時
的分工合作;讓軟體可以達到像硬體一樣分割為許多零件讓個別的程式設計師去生產副程式
,最後在拼裝為成品。

例如,A 跟 B 共寫一個學生課程管理的系統,A 寫學生,B 寫課程;B 要求 A 要在類別中
加入某個方法;可以傳入課程就知道該生該科目有沒有通過。 但是如果每要求一個類似的
方法或屬性就要講或列清單,那很麻煩。
public interface IStudent{
    public boolean isPass(Course c);
}
A 定義這個介面要求 B 的 Student必須實作它,這樣一來當 B 在撰寫 Student 類別時就必定
要實作介面中宣告的方法,否則無法編譯通過。
public class Student implements IStudent{
    public boolean isPass(Course c){
        //實作過程
    }
}
在介面中的屬性是被定義為 static final 的,也就是屬於類別的常數,而方法為全
public abstract,public 是由於 Java設計者假定 interface 是一種合作的手段,其內含的
method 在實作後必然是要讓其他合作程式者撰寫的 class 作存取的。

abstract 關鍵字
抽象,一般此關鍵字是標記在方法與類別,標記為 abstract 的方法不得實作內容,而將由
繼承它的類別來實作,內涵一個以上 abstract 方法的 class 同樣必須標記上 abstract。

介面 vs 抽象類別
interface與abstract class在概念與實務上個有意義上的不同與應用上的差別
  1. 概念上,實作介面並不給予類別上的意義(is-a)。
  2. 另外,介面算是一種對於類別部分功能的施工約定,它具有上下雙向、居中整合的概念,而類別只有由上而下。
  3. 實務上,實作介面於不同的類別,可以提供你在不同類別(或甚至不知道類別)下操作具相同功能的功能的實例的可能性,提供了「彈性」,尤其是在開發過程中。
  4. 技術上來說,抽象類別可以完整取代介面,但是不實作任何非抽象方法的抽象類別是比較沒道理的,因為這樣只有復用到屬性而已。
  5. 介面於Java最主要的實務意義還有可以模擬多重繼承(介面繼承多個介面或類別實作多的介面)。
  6. 另外,介面某種程度上可以輔助開放封閉原則,當補充功能為連動父子類別的修改時,可以以介面時作為補充。
※第4點,類別只復用到屬性在概念上有什麼不妥嗎? 另外為什麼介面要規定屬性為常數?

15/?/?
時過境遷,真的很佩服學生時代的自己有毅力打這麼長一串的鬼東西(笑)
4-1 沒有
4-2 介面的假設是"通用的",是任何實作者都須遵守規範、或者提供所有實作者一致需求的值,所以介面的屬性不該有需要改變的情況,具變動性的屬性(需求)應在實作端"類別"定義。
※第6點純粹是我自己想的,不知道會不會有不妥?
不會

16/4/28
第3點才是程式實務上使用介面的核心原因,而軟工藍圖概念不是實務上的重點。
第4點是錯的因為抽象類別不能模擬多重繼承。

SDK ( Software Development Kit ) 
泛指用於特定軟體之開發的各種工具資源集合,內容可能包括數組 API 與 Lib、範例、說明文件、模擬器與圖檔等任何與該特定軟體開發相關之工具。 一般 SDK 是由應用程式運作的作業系統之廠商提供。

SDK vs API
一般語意上SDK 與 API 的簡單分別是,SDK 強調你下載下來的這一包是用於開發特定領域之應用,而 API 則是你可以用下載下來的這一包來輔助你正在開發的東西。

例如,想開發 Android APP 你要先下載它的 SDK,而要在上面做圖表你可以用 Jfreechart API,但 Jfreechart 也可用在 JSP、Swing,不一定是 Android 應用,而是 Android SDK 就必定是要開發Android 的相關應用,所以一般我們會說 Jfreechart 是 API,Android SDK 就是 SDK。

Framework
是開發軟體時,過去的開發者對許多共通的基本功能所做的基礎建設,降低一般開發上對共通性活動的負擔。 例如在互動式網站中,幾乎都有以表格的模式檢視資料庫資料的需求,所以在 .NET framework 中就提供了 Gridview + DataSource 等規格,讓開發者不必每次都要從 ADO 到 html 表格輸出都要花很多時間自己重作。

Lib ( Libary ) 函式庫
即一般所謂的函式庫,是多組類別與介面組成的程式碼集,讓我們在軟體開發時,取用其內容之特定功能。 一般 Java 程式可以在編輯器裡,專案的 Lib 資料夾中匯入你需要的功能的 jar 檔或類別集來組成你程式需要的 lib。

以大小來區分,一包 SDK 中可能有一到多組 Framework,一組 Framework 中可能有多個 Lib,一個 Lib 裡可能有多種 API。 所以 SDK >= Framework > Lib > API。

.jar檔 ( Java ARchive )
是一種將 Java 原始碼檔(介面與類別集)壓縮後的壓縮檔,此檔案壓縮後匯入到編輯器的 lib 資料夾中仍能讓其他類別讀取使用。

IDE ( Integrated Development Environment ) 整合式開發環境
是指用來開發程式的軟體,一般說的是 key in 程式碼並提供執行預覽與偵錯的編輯器,比方VB、C# 的 Visual Studio,Java 的 Eclipse、NetBean,如果你用記事本來輸入程式碼然後用 dos 執行那記事本就是你的 IDE。

Compile、Run、Build
一般來說 IDE 都會有這三個功能,其中

編譯 (Compile)
是只編譯上次Build後有異動的檔案,但不會執行App。

執行 (Run)
同樣是編譯所有異動檔案,且完成編譯後會執行App。

建置 (Build)
無論異動於否都重新編過專案中的所有檔案,不會執行App。

JSP ( Java Server Page )
是一種以 Java 作為指令語言,用於實踐互動式網頁功能的伺服端網頁技術。 JSP 將 Java 程式嵌入在靜態頁面之中, JSP 的編譯器則會將 JSP 編譯成 Java 編寫的 servlet。

Servlet
是以 Java 撰寫的伺服端程式模組,用於實踐互動式動態網頁功能。 狹義的 servlet 是以 Java 語言的一個介面,廣義的 servlet 則是指任何實作該介面的類別,一般我們說的 servlet 是指廣義的 servlet。

HttpServerlet
javax.servlet.http.HttpServlet;
用於實作回應 http request ( post & get ) 的 servlet,servlet 可以回應多種請求,但大多數時候是 http。

由於 HttpServlet是用來回應 http request,並返回 html 頁面,所以在撰寫 servlet 時會包含大量 html 內容,這對程式設計師與網頁設計師都不方便,所以設計了 JSP 將 Java code 與 html 分立開來方便設計師的程式撰寫;JSP中可包含 html 與 Java code,由網頁設計師設計網頁,後由程式設計師撰寫 Java code。

沒有留言:

張貼留言