# 2.6 構建Java程序
正式構建自己的第一個Java程序前,還有幾個問題需要注意。
## 2.6.1 名字的可見性
在所有程序設計語言里,一個不可避免的問題是對名字或名稱的控制。假設您在程序的某個模塊里使用了一個名字,而另一名程序員在另一個模塊里使用了相同的名字。此時,如何區分兩個名字,并防止兩個名字互相沖突呢?這個問題在C語言里特別突出。因為程序未提供很好的名字管理方法。C++的類(即Java類的基礎)嵌套使用類里的函數,使其不至于同其他類里的嵌套函數名沖突。然而,C++仍然允許使用全局數據以及全局函數,所以仍然難以避免沖突。為解決這個問題,C++用額外的關鍵字引入了“命名空間”的概念。
由于采用全新的機制,所以Java能完全避免這些問題。為了給一個庫生成明確的名字,采用了與Internet域名類似的名字。事實上,Java的設計者鼓勵程序員反轉使用自己的Internet域名,因為它們肯定是獨一無二的。由于我的域名是`BruceEckel.com`,所以我的實用工具庫就可命名為`com.bruceeckel.utility.foibles`。反轉了域名后,可將點號想象成子目錄。
在Java 1.0和Java 1.1中,域擴展名`com`,`edu`,`org`,`net`等都約定為大寫形式。所以庫的樣子就變成:`COM.bruceeckel.utility.foibles`。然而,在Java 1.2的開發過程中,設計者發現這樣做會造成一些問題。所以目前的整個軟件包都以小寫字母為標準。
Java的這種特殊機制意味著所有文件都自動存在于自己的命名空間里。而且一個文件里的每個類都自動獲得一個獨一無二的標識符(當然,一個文件里的類名必須是唯一的)。所以不必學習特殊的語言知識來解決這個問題——語言本身已幫我們照顧到這一點。
## 2.6.2 使用其他組件
一旦要在自己的程序里使用一個預先定義好的類,編譯器就必須知道如何找到它。當然,這個類可能就在發出調用的那個相同的源碼文件里。如果是那種情況,只需簡單地使用這個類即可——即使它直到文件的后面仍未得到定義。Java消除了“向前引用”的問題,所以不要關心這些事情。
但假若那個類位于其他文件里呢?您或許認為編譯器應該足夠“聯盟”,可以自行發現它。但實情并非如此。假設我們想使用一個具有特定名稱的類,但那個類的定義位于多個文件里。或者更糟,假設我們準備寫一個程序,但在創建它的時候,卻向自己的庫加入了一個新類,它與現有某個類的名字發生了沖突。
為解決這個問題,必須消除所有潛在的、糾纏不清的情況。為達到這個目的,要用`import`關鍵字準確告訴Java編譯器我們希望的類是什么。`import`的作用是指示編譯器導入一個“包”——或者說一個“類庫”(在其他語言里,可將“庫”想象成一系列函數、數據以及類的集合。但請記住,Java的所有代碼都必須寫入一個類中)。
大多數時候,我們直接采用來自標準Java庫的組件(部件)即可,它們是與編譯器配套提供的。使用這些組件時,沒有必要關心冗長的保留域名;舉個例子來說,只需象下面這樣寫一行代碼即可:
```
import java.util.Vector;
```
它的作用是告訴編譯器我們想使用Java的`Vector`類。然而,`util`包含了數量眾多的類,我們有時希望使用其中的幾個,同時不想全部明確地聲明它們。為達到這個目的,可使用`*`通配符。如下所示:
```
import java.util.*;
```
需導入一系列類時,采用的通常是這個辦法。應盡量避免一個一個地導入類。
## 2.6.3 `static`關鍵字
通常,我們創建類時會指出那個類的對象的外觀與行為。除非用`new`創建那個類的一個對象,否則實際上并未得到任何東西。只有執行了`new`后,才會正式生成數據存儲空間,并可使用相應的方法。
但在兩種特殊的情形下,上述方法并不堪用。一種情形是只想用一個存儲區域來保存一個特定的數據——無論要創建多少個對象,甚至根本不創建對象。另一種情形是我們需要一個特殊的方法,它沒有與這個類的任何對象關聯。也就是說,即使沒有創建對象,也需要一個能調用的方法。為滿足這兩方面的要求,可使用`static`(靜態)關鍵字。一旦將什么東西設為`static`,數據或方法就不會同那個類的任何對象實例聯系到一起。所以盡管從未創建那個類的一個對象,仍能調用一個`static`方法,或訪問一些`static`數據。而在這之前,對于非`static`數據和方法,我們必須創建一個對象,并用那個對象訪問數據或方法。這是由于非`static`數據和方法必須知道它們操作的具體對象。當然,在正式使用前,由于`static`方法不需要創建任何對象,所以它們不可簡單地調用其他那些成員,同時不引用一個已命名的對象,從而直接訪問非`static`成員或方法(因為非`static`成員和方法必須同一個特定的對象關聯到一起)。
有些面向對象的語言使用了“類數據”和“類方法”這兩個術語。它們意味著數據和方法只是為作為一個整體的類而存在的,并不是為那個類的任何特定對象。有時,您會在其他一些Java書刊里發現這樣的稱呼。
為了將數據成員或方法設為`static`,只需在定義前置和這個關鍵字即可。例如,下述代碼能生成一個`static`數據成員,并對其初始化:
```
class StaticTest {
Static int i = 47;
}
```
現在,盡管我們制作了兩個`StaticTest`對象,但它們仍然只占據`StaticTest.i`的一個存儲空間。這兩個對象都共享同樣的`i`。請考察下述代碼:
```
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();
```
此時,無論`st1.i`還是`st2.i`都有同樣的值47,因為它們引用的是同樣的內存區域。
有兩個辦法可引用一個`static`變量。正如上面展示的那樣,可通過一個對象命名它,如`st2.i`。亦可直接用它的類名引用,而這在非靜態成員里是行不通的(最好用這個辦法引用`static`變量,因為它強調了那個變量的“靜態”本質)。
```
StaticTest.i++;
```
其中,`++`運算符會使變量自增。此時,無論`st1.i`還是`st2.i`的值都是48。
類似的邏輯也適用于靜態方法。既可象對其他任何方法那樣通過一個對象引用靜態方法,亦可用特殊的語法格式`類名.方法()`加以引用。靜態方法的定義是類似的:
```
class StaticFun {
static void incr() { StaticTest.i++; }
}
```
從中可看出,`StaticFun`的方法`incr()`使靜態數據`i`自增。通過對象,可用典型的方法調用`incr()`:
```
StaticFun sf = new StaticFun();
sf.incr();
```
或者,由于`incr()`是一種靜態方法,所以可通過它的類直接調用:
```
StaticFun.incr();
```
盡管是“靜態”的,但只要應用于一個數據成員,就會明確改變數據的創建方式(一個類一個成員,以及每個對象一個非靜態成員)。若應用于一個方法,就沒有那么戲劇化了。對方法來說,`static`一項重要的用途就是幫助我們在不必創建對象的前提下調用那個方法。正如以后會看到的那樣,這一點是至關重要的——特別是在定義程序運行入口方法`main()`的時候。
和其他任何方法一樣,`static`方法也能創建自己類型的命名對象。所以經常把`static`方法作為一個“領頭羊”使用,用它生成一系列自己類型的“實例”。
- Java 編程思想
- 寫在前面的話
- 引言
- 第1章 對象入門
- 1.1 抽象的進步
- 1.2 對象的接口
- 1.3 實現方案的隱藏
- 1.4 方案的重復使用
- 1.5 繼承:重新使用接口
- 1.6 多態對象的互換使用
- 1.7 對象的創建和存在時間
- 1.8 異常控制:解決錯誤
- 1.9 多線程
- 1.10 永久性
- 1.11 Java和因特網
- 1.12 分析和設計
- 1.13 Java還是C++
- 第2章 一切都是對象
- 2.1 用引用操縱對象
- 2.2 所有對象都必須創建
- 2.3 絕對不要清除對象
- 2.4 新建數據類型:類
- 2.5 方法、參數和返回值
- 2.6 構建Java程序
- 2.7 我們的第一個Java程序
- 2.8 注釋和嵌入文檔
- 2.9 編碼樣式
- 2.10 總結
- 2.11 練習
- 第3章 控制程序流程
- 3.1 使用Java運算符
- 3.2 執行控制
- 3.3 總結
- 3.4 練習
- 第4章 初始化和清除
- 4.1 用構造器自動初始化
- 4.2 方法重載
- 4.3 清除:收尾和垃圾收集
- 4.4 成員初始化
- 4.5 數組初始化
- 4.6 總結
- 4.7 練習
- 第5章 隱藏實現過程
- 5.1 包:庫單元
- 5.2 Java訪問指示符
- 5.3 接口與實現
- 5.4 類訪問
- 5.5 總結
- 5.6 練習
- 第6章 類復用
- 6.1 組合的語法
- 6.2 繼承的語法
- 6.3 組合與繼承的結合
- 6.4 到底選擇組合還是繼承
- 6.5 protected
- 6.6 累積開發
- 6.7 向上轉換
- 6.8 final關鍵字
- 6.9 初始化和類裝載
- 6.10 總結
- 6.11 練習
- 第7章 多態性
- 7.1 向上轉換
- 7.2 深入理解
- 7.3 覆蓋與重載
- 7.4 抽象類和方法
- 7.5 接口
- 7.6 內部類
- 7.7 構造器和多態性
- 7.8 通過繼承進行設計
- 7.9 總結
- 7.10 練習
- 第8章 對象的容納
- 8.1 數組
- 8.2 集合
- 8.3 枚舉器(迭代器)
- 8.4 集合的類型
- 8.5 排序
- 8.6 通用集合庫
- 8.7 新集合
- 8.8 總結
- 8.9 練習
- 第9章 異常差錯控制
- 9.1 基本異常
- 9.2 異常的捕獲
- 9.3 標準Java異常
- 9.4 創建自己的異常
- 9.5 異常的限制
- 9.6 用finally清除
- 9.7 構造器
- 9.8 異常匹配
- 9.9 總結
- 9.10 練習
- 第10章 Java IO系統
- 10.1 輸入和輸出
- 10.2 增添屬性和有用的接口
- 10.3 本身的缺陷:RandomAccessFile
- 10.4 File類
- 10.5 IO流的典型應用
- 10.6 StreamTokenizer
- 10.7 Java 1.1的IO流
- 10.8 壓縮
- 10.9 對象序列化
- 10.10 總結
- 10.11 練習
- 第11章 運行期類型識別
- 11.1 對RTTI的需要
- 11.2 RTTI語法
- 11.3 反射:運行期類信息
- 11.4 總結
- 11.5 練習
- 第12章 傳遞和返回對象
- 12.1 傳遞引用
- 12.2 制作本地副本
- 12.3 克隆的控制
- 12.4 只讀類
- 12.5 總結
- 12.6 練習
- 第13章 創建窗口和程序片
- 13.1 為何要用AWT?
- 13.2 基本程序片
- 13.3 制作按鈕
- 13.4 捕獲事件
- 13.5 文本字段
- 13.6 文本區域
- 13.7 標簽
- 13.8 復選框
- 13.9 單選鈕
- 13.10 下拉列表
- 13.11 列表框
- 13.12 布局的控制
- 13.13 action的替代品
- 13.14 程序片的局限
- 13.15 視窗化應用
- 13.16 新型AWT
- 13.17 Java 1.1用戶接口API
- 13.18 可視編程和Beans
- 13.19 Swing入門
- 13.20 總結
- 13.21 練習
- 第14章 多線程
- 14.1 反應靈敏的用戶界面
- 14.2 共享有限的資源
- 14.3 堵塞
- 14.4 優先級
- 14.5 回顧runnable
- 14.6 總結
- 14.7 練習
- 第15章 網絡編程
- 15.1 機器的標識
- 15.2 套接字
- 15.3 服務多個客戶
- 15.4 數據報
- 15.5 一個Web應用
- 15.6 Java與CGI的溝通
- 15.7 用JDBC連接數據庫
- 15.8 遠程方法
- 15.9 總結
- 15.10 練習
- 第16章 設計模式
- 16.1 模式的概念
- 16.2 觀察器模式
- 16.3 模擬垃圾回收站
- 16.4 改進設計
- 16.5 抽象的應用
- 16.6 多重分發
- 16.7 訪問器模式
- 16.8 RTTI真的有害嗎
- 16.9 總結
- 16.10 練習
- 第17章 項目
- 17.1 文字處理
- 17.2 方法查找工具
- 17.3 復雜性理論
- 17.4 總結
- 17.5 練習
- 附錄A 使用非JAVA代碼
- 附錄B 對比C++和Java
- 附錄C Java編程規則
- 附錄D 性能
- 附錄E 關于垃圾收集的一些話
- 附錄F 推薦讀物