A. java項目案例分析的前言
學習語言的目的是為了開發項目,很多初學者在學習技術時很順利,但要真正開發起項目,往往會感到迷茫和手足無措。而對於即將畢業的學生來說,即將提交的畢業設計又會讓他們焦頭爛額。
為了幫助讀者解決這些問題,引導讀者學有所用,我們編寫了本書。本書精選了6個實用項目與兩個畢業設計,內容涉及Java開發的主流開發技術,並以最簡單通俗的方式向讀者介紹如何開發軟體項目。相信通過本書的學習,讀者會在Java開發的道路上奠定更加堅實的基礎,同時也為正在忙於畢業設計的同學提供指導。
如果讀者在學習過程中遇到任何問題,創作團隊會通過QQ、論壇等方式為讀者提供解答,真正排除讀者在Java學習中遇到的各種障礙。
本書內容
本書共8章,包括6個實用項目和兩個畢業設計。實用項目包括超市管理系統、物資管理系統、網上商城系統、英漢雙語詞典、學生成績管理系統、彩票預測系統;畢業設計包括進銷存管理系統和圖書館管理系統。筆者注重每個項目界面的美觀,下面給出幾個項目的運行效果圖。
本書特色
* 技術豐富。本書涉及技術廣泛,而且都是讀者較為關心的內容,如Hibernate框架開發、網路開發、CVS團隊開發等。
* 橫向連接。屬於Java系列叢書的一部分,讀者經過基礎、實例等內容學習後,再學習本書可以在鞏固基礎的同時,真正達到學以致用的效果。
* 講解到位。剖析每種技術最有價值的部分,絕不拖泥帶水。
* 贈送所有項目源代碼。書中所有項目均提供源代碼,用戶在開發中可以快速借鑒或應用。
* 提供72集(節)視頻錄像。本書提供了72集(節)的視頻錄像,讀者可以邊看邊學,快速提高。
本書配套資源
本書提供了內容豐富的配套資源,包括自學視頻、源程序、素材,以及模塊庫、案例庫、題庫、素材庫等多項輔助內容,讀者朋友可以通過如下方式獲取。
第1種方式:
(1)登錄清華大學出版社的網站,在網頁右上角的搜索文本框中輸入本書書名(注意區分大小寫和留出空格),或者輸入本書關鍵字,或者輸入本書ISBN號(注意去掉ISBN號間隔線「-」),單擊「搜索」按鈕。
(2)找到本書後單擊超鏈接,在該書的網頁下側單擊「網路資源」超鏈接,即可下載。
第2種方式:
訪問本書的新浪微博Javamrbook,找到配套資源的鏈接地址進行下載。
配套資源目錄如下圖所示。
本書約定
* 項目使用方法
用戶在學習本書的過程中,可以從配套資源中復製程序,去掉其只讀屬性。有些項目需要使用相應的資料庫或第三方資源,此類程序在使用前需要進行相應配置,詳細使用方式可參考本書配套資源。此外,如果用戶直接將本書內容用於商業領域,由此產生的不良後果由用戶自己承擔。
* 部分項目只給出關鍵代碼
由於篇幅限制,書中有些模塊只給出了關鍵代碼,完整代碼可參見配套資源對應程序。
讀者人群
本書非常適合以下讀者閱讀:
* 從事Java編程行業的開發人員
* 具有一定語言基礎,想進一步提高技能的人員
* 大中專院校的老師和學生
* 即將走上工作崗位的大學畢業生
* 相關培訓機構的老師和學員
* Java編程愛好者
B. Java項目案例分析的目錄
第1篇 實用項目篇
第1章 超市管理系統(Swing+
SQL Server 2005實現)
自學視頻、源程序:
配套資源mr1 2
1.1 項目設計思路 3
1.1.1 功能闡述 3
1.1.2 系統預覽 3
1.1.3 功能結構 3
1.1.4 文件組織結構 4
1.2 資料庫設計 4
1.2.1 資料庫設計 4
1.2.2 數據表設計 5
1.3 公共類設計 7
1.3.1 連接資料庫 7
1.3.2 獲取當前系統時間類 7
1.4 登錄模塊設計 8
1.4.1 登錄模塊概述 8
1.4.2 實現帶背景的窗體 8
1.4.3 登錄模塊實現過程 9
1.5 主窗體設計 12
1.5.1 主窗體概述 12
1.5.2 平移面板控制項 12
1.5.3 主窗體實現過程 16
1.6 采購訂貨模塊設計 18
1.6.1 采購訂貨模塊概述 18
1.6.2 在表格中添加按鈕 19
1.6.3 添加采購訂貨信息實現
過程 19
1.6.4 搜索采購訂貨信息實現
過程 21
1.6.5 修改采購訂貨信息實現
過程 23
1.6.6 刪除采購訂貨信息實現
過程 27
1.7 人員管理模塊設計 28
1.7.1 人員管理模塊概述 28
1.7.2 使用觸發器級聯刪除數據 28
1.7.3 顯示查詢條件實現過程 29
1.7.4 顯示員工基本信息實現
過程 31
1.7.5 添加員工信息實現過程 33
1.7.6 刪除員工信息實現過程 37
1.8 在Eclipse中實現程序打包 37
本章摘要 40
第2章 物資管理系統(Swing+Spring+
Hibernate+MySQL實現)
自學視頻、源程序:
配套資源mr2 41
2.1 項目設計思路 42
2.1.1 功能闡述 42
2.1.2 系統預覽 42
2.1.3 功能結構 43
2.1.4 文件組織結構 44
2.2 資料庫設計 44
2.2.1 資料庫設計 44
2.2.2 數據表設計 45
2.3 使用CVS進行團隊開發 46
2.3.1 安裝CVS伺服器 46
2.3.2 配置CVS伺服器 47
2.3.3 添加CVS用戶 48
2.3.4 連接CVS伺服器 49
2.3.5 共享項目到CVS伺服器 50
2.3.6 使用CVS資源庫進行
團隊開發 52
2.4 公共模塊 53
2.4.1 Spring+Hibernate整合配置 53
2.4.2 基本信息常量的介面
Constant 55
2.4.3 Hibernate操作資料庫的類
BaseDao 57
2.4.4 類型轉換與獲得信息的類
CommonUtil 59
2.4.5 獲得Spring配置實例的類
ManagerFactory 60
2.4.6 獲得事務管理策略對象的類
ManagerFactoryUtil 61
2.5 系統登錄模塊 62
2.5.1 系統登錄模塊概述 62
2.5.2 系統登錄模塊的實現過程 62
2.6 主窗體模塊 65
2.6.1 主窗體模塊概述 65
2.6.2 工具按鈕的設計 66
2.6.3 主功能區介紹 68
2.7 部門管理模塊設計 69
2.7.1 部門管理模塊概述 69
2.7.2 創建部門管理模塊的實體類
及映射文件 69
2.7.3 添加部門信息 70
2.7.4 查看部門信息 71
2.7.5 修改部門信息 73
2.7.6 刪除部門信息 74
2.8 人員管理模塊設計 76
2.8.1 人員管理模塊概述 76
2.8.2 添加人員信息 76
2.8.3 查看人員信息 77
2.8.4 修改人員信息 78
2.8.5 刪除人員信息 79
2.9 物品管理模塊設計 81
2.9.1 物品管理模塊概述 81
2.9.2 添加物品信息 81
2.9.3 查看物品信息 83
2.9.4 修改物品信息 84
2.9.5 刪除物品信息 85
2.9.6 出借物品信息 87
2.10 出借信息模塊設計 88
2.10.1 出借信息模塊概述 88
2.10.2 查看出借物品 89
2.10.3 歸還出借物品 90
2.11 歸還信息模塊設計 92
2.11.1 歸還信息模塊概述 92
2.11.2 查看歸還物品 93
2.11.3 刪除歸還物品 94
項目發布 96
本章摘要 97
第3章 網上商城系統(Struts2+
Hibernate+Spring實現)
自學視頻、源程序:
配套資源mr3 98
3.1 項目設計思路 99
3.1.1 功能闡述 99
3.1.2 系統預覽 99
3.1.3 功能結構 101
3.1.4 文件組織結構 102
3.2 資料庫設計 102
3.2.1 資料庫設計 102
3.2.2 數據表設計 103
3.3 公共模塊設計 105
3.3.1 泛型工具類 105
3.3.2 數據持久化類 106
3.3.3 分頁操作 107
3.3.4 字元串工具類 109
3.4 注冊模塊設計 109
3.4.1 用戶注冊概述 109
3.4.2 用戶注冊實現過程 110
3.5 前台商品信息查詢模塊設計 111
3.5.1 功能概述 111
3.5.2 實現分頁顯示 112
3.5.3 商品搜索的實現過程 112
3.5.4 熱門商品查詢的實現過程 114
3.6 購物車模塊設計 115
3.6.1 功能概述 115
3.6.2 取消無用訂單信息 116
3.6.3 購物車基本功能的實現
過程 117
3.6.4 訂單功能實現過程 119
3.7 後台商品管理模塊設計 122
3.7.1 功能概述 122
3.7.2 解決Struts2中文亂碼
問題 122
3.7.3 商品管理功能實現過程 123
3.7.4 商品類別管理功能
實現過程 126
3.8 後台訂單管理模塊的設計 129
3.8.1 模塊概述 129
3.8.2 按鈕的觸發事件和窗口的
自動刷新 130
3.8.3 後台訂單查詢的實現過程 131
本章摘要 133
第4章 英漢雙語詞典(Swing+
MySQL 5.5實現)
自學視頻、源程序:
配套資源mr4 134
4.1 項目設計思路 135
4.1.1 功能闡述 135
4.1.2 系統預覽 135
4.1.3 功能結構 136
4.1.4 文件組織結構 137
4.1.5 配置開發環境 137
4.2 資料庫設計 142
4.2.1 數據表設計 142
4.2.2 建立資料庫和數據表 142
4.3 公共類設計 144
4.3.1 編寫Word類封裝單詞 144
4.3.2 讀取配置文件 145
4.3.3 編寫JDBC工具類 149
4.4 主窗體設計 156
4.4.1 主窗體概述 156
4.4.2 小工具菜單實現過程 156
4.4.3 查詢功能實現過程 158
4.4.4 單詞列表實現過程 159
4.4.5 設置單詞細節樣式 160
4.5 增加單詞模塊設計 165
4.5.1 增加單詞模塊概述 165
4.5.2 限制文本控制項可用長度 165
4.5.3 顯示文本控制項可用長度 166
4.5.4 編輯音標對話框實現
過程 168
4.5.5 支持預覽圖片的文件
選擇器 169
4.5.6 增加單詞實現過程 171
4.6 修改和刪除單詞模塊設計 173
4.6.1 修改單詞模塊概述 173
4.6.2 MP3播放與圖片預覽功能
實現 173
4.6.3 刪除單詞功能實現 174
4.7 統計查詢模塊 175
4.7.1 統計查詢模塊概述 175
4.7.2 設置表格樣式 175
4.7.3 列印功能的實現過程 176
本章摘要 177
第5章 學生成績管理系統(Swing+
SQL Server 2005實現)
自學視頻、源程序:
配套資源mr5 178
5.1 項目設計思路 179
5.1.1 功能闡述 179
5.1.2 系統預覽 179
5.1.3 功能結構 180
5.1.4 文件組織結構 180
5.2 資料庫設計 181
5.2.1 資料庫設計 181
5.2.2 數據表設計 182
5.3 公共類設計 183
5.3.1 各種實體類的編寫 184
5.3.2 操作資料庫公共類的編寫 184
5.4 系統登錄模塊設計 191
5.4.1 系統登錄模塊概述 191
5.4.2 系統登錄模塊技術分析 191
5.4.3 系統登錄模塊實現過程 191
5.5 主窗體模塊設計 193
5.5.1 主窗體模塊概述 193
5.5.2 主窗體模塊技術分析 194
5.5.3 主窗體模塊實現過程 194
5.6 班級信息設置模塊設計 198
5.6.1 班級信息設置模塊概述 198
5.6.2 班級信息設置模塊技術
分析 198
5.6.3 班級信息設置模塊實現
過程 199
5.7 學生基本信息管理模塊設計 202
5.7.1 學生基本信息管理模塊
概述 202
5.7.2 學生基本信息管理模塊
技術分析 203
5.7.3 學生基本信息管理模塊
實現過程 203
5.8 學生考試成績信息管理模塊
設計 208
5.8.1 學生考試成績信息管理
模塊概述 208
5.8.2 學生考試成績信息管理
模塊技術分析 208
5.8.3 學生考試成績信息管理
模塊實現過程 209
5.9 基本信息數據查詢模塊設計 214
5.9.1 基本信息數據查詢模塊
概述 214
5.9.2 基本信息數據查詢模塊技
術分析 215
5.9.3 基本信息數據查詢模塊實
現過程 215
5.10 考試成績班級明細查詢模塊
設計 218
5.10.1 考試成績班級明細查詢模
塊概述 218
5.10.2 考試成績班級明細查詢模
塊技術分析 218
5.10.3 考試成績班級明細查詢模
塊實現過程 218
本章摘要 221
第6章 彩票預測系統(Swing+MySQL
實現)
自學視頻、源程序:
配套資源mr6 222
6.1 項目設計思路 223
6.1.1 功能闡述 223
6.1.2 系統預覽 223
6.1.3 功能結構 225
6.1.4 文件組織結構 225
6.2 資料庫設計 225
6.2.1 資料庫設計 225
6.2.2 數據表設計 226
6.2.3 連接資料庫 226
6.3 公共類設計 227
6.3.1 操作開獎信息的
HistoryDao類 227
6.3.2 操作購買彩票記錄信息的
ForecastDao類 229
6.4 主窗體設計 231
6.4.1 主窗體概述 231
6.4.2 主窗體實現過程 231
6.4.3 表格中的分頁 233
6.5 添加開獎號碼模塊設計 236
6.5.1 添加開獎號碼模塊概述 236
6.5.2 添加開獎號碼模塊窗體
設計 237
6.5.3 獲取開獎號碼的期數 239
6.5.4 驗證開獎號碼的格式 239
6.5.5 將開獎號碼信息添加到
資料庫 240
6.5.6 在主窗體更新開獎信息 241
6.5.7 判斷購買該期彩票所得的
獎金 241
6.6 修改開獎號碼模塊設計 244
6.6.1 修改開獎號碼模塊概述 244
6.6.2 判斷用戶是否選中要修改的
獎號 244
6.6.3 選取要修改的數據 244
6.6.4 修改開獎號碼 245
6.7 查看號碼走勢模塊設計 246
6.7.1 查看號碼走勢模塊概述 246
6.7.2 創建選項卡面板 246
6.7.3 用條形圖顯示號碼走勢 247
6.8 隨機選號模塊設計 248
6.8.1 隨機選號模塊概述 248
6.8.2 讓隨機選號達到滾動效果 249
6.8.3 記錄購買隨機生成的獎號 251
6.9 中獎查詢模塊設計 252
6.9.1 中獎查詢模塊概述 252
6.9.2 中獎查詢模塊窗體設計 253
6.9.3 中獎查詢模塊實現過程 254
本章摘要 256
第2篇 畢業設計篇
第7章 進銷存管理系統(Java+
SQL Server 2005實現)
自學視頻、源程序:
配套資源mr7 258
7.1 設計說明 259
7.1.1 畢業設計(論文)主要
內容 259
7.1.2 畢業設計(論文)要求 259
7.2 填寫開題報告 260
7.3 封面設計 261
7.4 書寫摘要 261
7.4.1 中文摘要 262
7.4.2 英文摘要(Abstract) 262
7.5 編排制定目錄 262
7.6 緒論 264
7.6.1 本課題的開發背景及
意義 264
7.6.2 課題研究內容 264
7.6.3 設計組織結構 265
7.7 開發工具及相關技術 265
7.7.1 常用開發工具 265
7.7.2 運行環境 267
7.7.3 相關技術 268
7.7.4 C/S系統結構 269
7.8 需求分析 270
7.8.1 可行性分析 270
7.8.2 系統需求 271
7.8.3 系統性能要求 272
7.9 系統資料庫設計 273
7.9.1 資料庫概述 273
7.9.2 系統實體E-R圖 273
7.9.3 系統數據表設計 274
7.10 系統設計 276
7.10.1 系統實現關系圖 276
7.10.2 系統功能模塊設計 276
7.11 系統功能實現 277
7.11.1 基礎信息模塊設計 277
7.11.2 查詢統計模塊設計 277
7.11.3 庫存管理模塊設計 278
7.12 結論 278
7.13 參考文獻 279
7.14 致謝 279
7.15 附錄 279
7.15.1 附錄A 主程序(JXCFrame.
java代碼) 279
7.15.2 附錄B 子程序(Dao.java
代碼) 284
本章摘要 296
第8章 圖書館管理系統(Java+
SQL Server 2005實現)
自學視頻、源程序:
配套資源mr8 297
8.1 設計說明 298
8.1.1 畢業設計(論文)主要
內容 298
8.1.2 畢業設計(論文)要求 298
8.2 填寫開題報告 299
8.3 封面設計 300
8.4 書寫摘要 300
8.4.1 中文摘要 301
8.4.2 英文摘要(Abstract) 301
8.5 編排制定目錄 301
8.6 緒論 303
8.6.1 本課題的開發背景及意義 303
8.6.2 課題研究內容 303
8.6.3 設計組織結構 304
8.7 開發工具及相關技術 304
8.7.1 常用開發工具 304
8.7.2 運行環境 306
8.7.3 相關技術 307
8.7.4 C/S系統結構 308
8.8 需求分析 309
8.8.1 可行性分析 309
8.8.2 系統需求 310
8.8.3 功能需求 310
8.8.4 性能要求 310
8.9 系統資料庫設計 311
8.9.1 資料庫概述 311
8.9.2 系統實體E-R圖 311
8.9.3 系統數據表設計 312
8.10 系統設計 315
8.10.1 系統實現關系圖 315
8.10.2 系統功能模塊設計 316
8.11 系統功能實現 316
8.11.1 基礎數據維護模塊設計 316
8.11.2 新書訂購管理模塊設計 317
8.11.3 借閱管理模塊設計 318
8.11.4 系統維護模塊設計 319
8.12 結論 319
8.13 參考文獻 320
8.14 致謝 320
8.15 附錄 320
8.15.1 附錄A 主程序(Library.java
代碼) 320
8.15.2 附錄B 子程序(MenuActions.
java代碼) 324
本章摘要 330
C. 簡述java程序設計中界面設計中的三種布局方式
1 邊框布局
JPanel panel1=(JPanel)getContentPane();
panel1.setLayout(new FlowLayout());
憑著我們現在所積累的閱讀程序的功夫,應該能夠很快地知道我們通過第一條語句創建了一個容器JPanel類的panel1。
而接下來,我們則調用了panel1.setLayout(new FlowLayout()),在上一章中,我們一直沒有對它進行相應的分析。現在該是揭開它的面紗的時候了。
單詞layout的中文意思是「布局」、「布置」,setLayout就是設置布局格式。也就是說,容器panel1的方法setLayout是用來設置布局格式的。那麼,我們一直用的是什麼模式呢?對,就是後面括弧里的FlowLayout()。顧名思義,也就是流布局方式。這是一種順其自然的方式:從左到右,一個個對象地擺放,擺不下,就擺到下一行。所以,我們就無法去控制它。
從本章開始,我們就要學習一種控制布局的方法。在Java語言中,提供了各種布局管理器,以便大家能夠達到這一目的。
通常我們最常使用的布局管理器是:Border Layout(邊框布局管理器),如下圖所示,邊框布局管理器將一個屏幕分成了五個部分:東、南、西、北、中。
如果要使用這種邊框布局管理進行布局控制,我們必須先將「容器」設置為「邊框布局控制模式」,具體來說,就是當調用setLayout方法為容器設置布局控制模式時,參數設置為BorderLayout。例如:
JPanel panel1=(JPanel)getContentPane();
panel1.setLayout(new BorderLayout());
然後,我們就可以在使用容器的add方法添加部件時,附加上位置參數,使得該部件顯示在指定的位置上。位置參數分別是:
BorderLayout.NORTH 位置為北
BorderLayout.SOUTH 位置為南
BorderLayout.EAST 位置為東
BorderLayout.WEST 位置為西
BorderLayout.NORTH 位置為中心
實例說明
下面,我們就來試一試,使用按鈕排出五個方向!請創建一個testBorder.java,輸入以下源程序:
源程序:useTextArea.java
import java.awt.*;
import javax.swing.*;
import java.applet.Applet;
public class testBorder extends JApplet
{
public void init()
{
JPanel panel1=(JPanel)getContentPane();
panel1.setLayout(new BorderLayout());
JButton north=new JButton("North");
JButton south=new JButton("South");
JButton east=new JButton("East");
JButton west=new JButton("West");
JButton center=new JButton("Center");
panel1.add(north,BorderLayout.NORTH);
panel1.add(south,BorderLayout.SOUTH);
panel1.add(east,BorderLayout.EAST);
panel1.add(west,BorderLayout.WEST);
panel1.add(center,BorderLayout.CENTER);
}
}
然後,我們使用javac編譯這個程序,然後編輯一個顯示這個Java Applet的HTML頁面。最後調用appletviewer來觀察這個頁面
為了試一下,如果並沒有在每個位置都安排一個部件,那麼會怎麼樣呢?我們修改一下程序,將panel1.add(west,BorderLayout.WEST);這一行注釋掉(就是在前面加上「//」號),也就是不顯示西邊的按鈕,看一下結果如何。正如上圖(圖10-3)所示,西邊由於沒有部件,因此「中」就朝西靠,「佔領」了它的位置。
而如果我們修改一下程序,讓中間的按鈕不顯示的話,就會出現如圖10-5的樣子,中間的區域並未並佔領,而是空在那裡了!這是與其它位置不同的地方,大家一定要記住。
一些提示:
其實這很好理解,如果中間的那一塊的位置被其它位置上的部件佔領的話,就會使得分不清「東」、「南」、「西」、「北」了。倒成了「左上角」、「左下角」、「右上角」、「右下角」了。
2 網格布局
實例說明
下面,我們來看一個新的程序!首先創建一個testGrid.java文件,並輸入以下源程序:
源程序:testGrid.java
import java.awt.*;
import javax.swing.*;
import java.applet.Applet;
public class testGrid extends JApplet
{
String buttonLabels[]={"No.1","No.2","No.3","No.4","No.5",
"No.6","No.7","No.8","No.9"};
public void init()
{
JPanel panel1=(JPanel)getContentPane();
panel1.setLayout(new GridLayout(3,3));
for (int x=0; xpanel1.add(new JButton(buttonLabels[x]));
}
}
然後,我們使用javac編譯這個程序,然後編輯一個顯示這個Java Applet的HTML頁面。最後調用appletviewer來觀察這個頁面
我們主要是關心如何擺放各個部件,而不是如何構建一個程序。所以,正如本例中的按鈕一下,這一章中的所有部件都是一個樣子,沒有任何作用。這樣也使得程序盡可能地變短了,更易於大家閱讀和理解程序。
下面,我們就一起來看一下上面的這個程序。
1) String buttonLabels[]={"No.1","No.2","No.3","No.4","No.5",
"No.6","No.7","No.8","No.9"}
我想大家都能很輕松地讀懂這條語句,我們在此定義了一個字元串數組buttonLabels,它有9個字元串成員,也就是我們後面定義的9個按鈕的標簽文本。
值得注意的是,大家一定要知道,buttonLabels[1]表示哪個字元串!如果您的答案是「No.1」的話,就錯了!正確的答案是「No.2」,這是因為在數組中索引值是從0開始的,也就是說如果要使用「No.1」的話,應該使用buttonLabels[0]。這對更好地理解後面的程序十分重要,也是基本功之一。
2) JPanel panel1=(JPanel)getContentPane();
panel1.setLayout(new GridLayout(3,3));
在這兩行程序中,我們首先定義了一個容器部件panel1。然後調用setLayout方法設置布局管理器。這里我們使用了一個新的布局管理器:GridLayout,網格布局管理器。
我們注意到GridLayout的後面帶了兩個參數:3,3。這有什麼用呢?我們一起來看一下GridLayout方法的定義:
public GridLayout (int rows,int cols)
我們看一下這兩個參數,首先它們都是int型—整型的。那麼它們分別起到什麼作用呢?我們還是採用顧名思義法吧!row的中文意思是行,col的中文意思是列,後面加上一個s,是這兩個單詞的復數形式。
好,我們現在串起來想一下,我們定義了一個網格布局,然後定了它的行、列數!這不就畫出了這個網格了嗎?如果我們在語句是使用GridLayout(5,5)的話,就會把整個容器分成25個單元
而我們在程序中使用的是GridLayout(3,3),所以它就將整個容器分成了
注意:
這種劃分是一種邏輯上的,暗藏式的劃分,而不會把格子給劃出來。另外,我們這里舉的兩個例子行、列都相等,其實完全可以不等。
3) for (int x=0; xpanel1.add(new JButton(buttonLabels[x]));
這是一個循環結構的程序。我們先看循環語句,循環的初值是「x=0」,繼續執行循環的條件語句是「xbuttonLabels.length就是用來請得字元串數組buttonLabels的長度!也就是這個字元串數組中有多少個字元串?我們知道,我們在該數組中定義了9個。
從程序中,我們可以獲知,當x=buttonLabels.length時,循環就將結束,應為它不滿足條件x別忘了,我們使用buttonLabels[0]表示第一個字元串,buttonLabels[1]表示第二個字元串……,那麼最後一個字元串就應該是buttonLabels[buttonLabels.length-1]嘛。
在循環中,我們使用容器提供的add方法,將新定義的按鈕部件添加進去。
有時,我們可能想獲得象下圖所示的布局效果!讓各個部件之間存在一些間隙。使用Java的網格布局可以實現嗎?
我很高興地回答你:「可以!」,我們可以使用GridLayout的另一種構造器方法(簽名不同)來實現:
public GridLayout (int rows,int cols,int hgap,int vgap)
在這個方法中,可以帶上四個整型參數,前兩個我們已經很熟悉了,行數與列數。後面則是兩個新的參數。
第一個是hgap,其中gap的意思是間隙、空隙的意思,而h則是horizontal(水平)的第一個字母。也就是說,我們可以通過hgap參數設置橫向的間隙。
第二個是vgap,v則是vertical(垂直)的第一個字母。也就是說,我們可以通過vgap參數設置縱向的間隙。
自測練習
1)_________________是網格布局管理器。
a.BorderLayout b.GridLayout c.ReseauLayout
d.FlowLayout
利用以下代碼回答問題2、3、4、5:
2)這個布局中有__________行?
a.7行 b.5行 c.6行 d.8行
3)這個布局中有__________列?
a.7行 b.5行 c.6行 d.8行
4)這個布局將容器分為_________個部分?
a.48個 b.35個 c.30個 d.40個
5)第5個部件位於__________位置。
a.左上角 b.左下角 c.右上角 d.右下角 e.中間
6)根據以下界面,編寫一個程序
圖10-12 練習題圖
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
7) 如果我們構建一個5*5的網格布局,但只在該容器中加入17個按鈕,將會出現什麼情況?請編寫一個程序,來試一試效果。
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
練習答案
1)b 沒什麼好解釋的,請大家記住!
2)a 第一個參數是行數,因此是7行。
3)b 第二個參數是列數,因此為5列。
4)b 7行5列,其7*5,35個部分嘛。
5)c 第5個部件是第一行的最後一個,當然在右上角嘛。
6)下面是一個實現的程序實例:
源程序:lianxi10_2.java
import java.awt.*;
import javax.swing.*;
import java.applet.Applet;
public class lianxi10_2 extends JApplet
{
String buttonLabels[]={"1","2","3","4","5","6","7",
"8","9","*","0","#"};
public void init()
{
JPanel panel1=(JPanel)getContentPane();
panel1.setLayout(new GridLayout(4,3,10,10));
for (int x=0; xpanel1.add(new JButton(buttonLabels[x]));
}
}
7)下面是一個實現的程序實例:
源程序:lianxi10_3.java
import java.awt.*;
import javax.swing.*;
import java.applet.Applet;
public class lianxi10_3 extends JApplet
{
String buttonLabels[]={"No.1","No.2","No.3","No.4","No.5",
"No.6","No.7","No.8","No.9","No.10","No.11","No.12",
"No.13","No.14","No.15","No.16","No.17"};
public void init()
{
JPanel panel1=(JPanel)getContentPane();
panel1.setLayout(new GridLayout(5,5));
for (int x=0; xpanel1.add(new JButton(buttonLabels[x]));
}
}
這個程序使用javac編譯完後,編寫一個包含這個類的HTML頁面,再用appletviewer來觀察發現運行結果如下圖所示:
圖10-13 練習答案圖
這個輸出是不是令你感到十分意外,整個程序根本不按要求分成5列,所以我們從這里得到一個使用網格布局中要十二分注意的一點:「請別忘了將網格填滿」。否則程序的輸出將不可預料。
一些提示:
如果你真的沒有那麼多東西來「佔領」網格的話,我建議你使用一個空標簽來填補這個空白的網格,使得程序的輸出如你所願。使用空標簽的方法很容易:
panel1.add(new Label(「」));
從這里,我們也看出了,我們學習過的流布局管理器、邊框布局管理器,以及我們剛學習過的網格布局管理器都比較死板,不夠高級。
10.3 組合布局
傳授新知
正如我們在上一小節結束的時候說的一樣,各種布局管理器都有自己的缺點,沒有一種能夠真正地完全滿足我們編寫GUI程序時的要求。
而在Java語言中,允許在容器中加入其他容器,這樣每個容器可以根據自己的需要採用不同的布局管理器,組合成為一個我們所需要的GUI程序界面。這種方法,我們就稱之為組合布局。
注意:
與流布局、邊框布局、網格布局不同,組合布局並不是一個新的布局管理器,它是通過結合各種布局管理器的優點,將它們組合地應用在GUI程序界面的開發中。這是一種布局管理的方法。也可以說是一種GUI程序界面的開發技巧。
當我們設計GUI程序界面時,最好先在紙上把它畫出來,然後「分塊解決」。也就是將能夠組合在一起的部件找出來,將它們放在一個容器里,並為它們選擇合適的布局管理器。然後把這些容器擺放在一起,就解決了。
設計時還要考慮到窗體大小發生改變的時候,引起的部件形體變化。這方面,請你熟記幾個設計技巧:
1) 對於那些要求扁平狀的部件,我們應該將它放置在邊框布局中的南面或北面;
2) 對於那些要求細高狀的部件,我們應該將它放置在邊框布局中的東面或西面;
3) 對於那些可以隨著窗體改變大小部分,我們可以將它放置在邊框布局的中間;
4) 如果我們要求部件保持大小相同,那麼,我們就應該採用網格布局。
下面,我們就通過幾個實際的例子,來讓大家領會和掌握這種設計的思路與方法。
實例說明
我們首先創建一個testCombination.java文件,然後輸入以下源程序:
源程序:testCombination.java
import java.awt.*;
import javax.swing.*;
import java.applet.Applet;
public class testCombination1 extends JApplet
{
public void init()
{
Frame frame1=new Frame("testCombination1");
frame1.setLayout(new BorderLayout());
TextArea text1=new TextArea();
frame1.add(text1,BorderLayout.CENTER);
JPanel panel1=(JPanel)getContentPane();
panel1.setLayout(new GridLayout(1,3,10,5));
panel1.add(new JButton("A"));
panel1.add(new JButton("B"));
panel1.add(new JButton("C"));
frame1.add(panel1,BorderLayout.SOUTH);
frame1.pack();
frame1.show();
}
}
然後,我們使用javac編譯這個程序,然後編輯一個顯示這個Java Applet的HTML頁面。最後調用appletviewer來觀察這個頁面,如下圖所示:
圖10-14 程序testCombination.java的輸出
正如上圖所示,程序的輸出與以前不同,各個部件不是顯示在Appletviewer程序框中,而是顯示在另一個窗口中。
這是怎麼回事呢?下面我們就一起來看一下這個程序!
傳授新知
在以前的程序中,我們一直都是使用容器JPanel,面板。而我們在這個程序中引進了一個新的容器Frame。使用了這個容器後,就會新創建一個窗口。這也就是為什麼程序的輸出有這么大的變化的原因。
1)Frame frame1=new Frame("testCombination");
這條語句,定義了一個Frame容器frame1,然後使用new操作符調用構造器方法,後面帶的參數「testCombination」則是這個Frame的標題。
一些提示:
其實大家能Frame所體現出來的效果是很熟悉的,它等價於Windows中的窗體。而Frame的標題就是窗體的標題。
2) frame1.setLayout(new BorderLayout());
與JPanel容器一樣,我們可以調用setLayout方法來設置Frame的布局管理器。在這里,我們將Frame容器frame1的布局管理器設置成為邊框布局管理器(也就是我們在10.1小節中學習過的布局管理器)。
3) frame1.add(text1,BorderLayout.CENTER);
緊接著,我們調用frame1的add方法,將文本區部件text1添加到frame1容器中來。注意我們設置了它的位置:BorderLayout.CENTER。
這是因為,我們希望這個文本區能夠隨著窗體的大小變化而變化。所以適合將其放在在中間位置。
4) panel1.setLayout(new GridLayout(1,3,10,5));
我們又定義了一個JPanel容器panel1,並將其的布局管理器設置為網格布局管理器。並通過指定參數,使得整個panel1被分成1行3列,水平空隙為10的網格。
5) frame1.add(panel1,BorderLayout.SOUTH);
這是組合布局方法最重要的一步,我們將panel1這個容器,加入到了frame1這個容器中去了。並且指定了panel1這個容器在整個frame1中所佔的位置:BorderLayout.SOUTH,也就是下邊。這樣,panel1中包含的三個按鈕就會永遠(不管窗體大小如何改變)呆在文本區的下邊,而且三個按鈕的大小會根據窗體大小的改變而改變。
一些提示:
這是因為,panel1中的按鈕是用網格布局的。
6) frame1.pack();
frame1.show();
與JPanel不一樣,使用Frame容器,不能夠直接顯示了出來。我們必須調用Frame容器的show方法才能使得Frame顯示出來。
而在使用show方法之前,我們還需要使用pack方法將Frame中的內容做一個整合。請記住這里的使用方法。
4 GridBag布局
實例說明
到現在為止,我們已經學習了邊框布局、網格布局以及組合布局,現在大家試一試編寫一個程序,
怎麼樣,挺有難度的吧!完成這個GUI得花很多心思去設計組合,十分討厭,下面我們就使用一個新的布局管理器GridBagLayout來解決這種難題。
首先,輸入以下源程序:
源程序:testGridBag.java
import java.awt.*;
import javax.swing.*;
import java.applet.Applet;
public class testGridBag extends JApplet
{
public void init()
{
JPanel panel1=(JPanel)getContentPane();
panel1.setLayout(new GridBagLayout());
GridBagConstraints gbdc=new GridBagConstraints();
gbdc.fill=GridBagConstraints.BOTH;
gbdc.weightx=1;
gbdc.weighty=1;
panel1.add(new JButton("No.1"),gbdc);
panel1.add(new JButton("No.2"),gbdc);
panel1.add(new JButton("No.3"),gbdc);
gbdc.gridwidth=2;
gbdc.gridx=0;
panel1.add(new JButton("No.4"),gbdc);
gbdc.gridx=2;
gbdc.gridwidth=1;
gbdc.gridheight=2;
panel1.add(new JButton("No.5"),gbdc);
gbdc.gridx=0;
gbdc.gridheight=1;
panel1.add(new JButton("No.6"),gbdc);
gbdc.gridx=1;
panel1.add(new JButton("No.7"),gbdc);
gbdc.gridx=0;
gbdc.gridwidth=2;
panel1.add(new JButton("No.8"),gbdc);
gbdc.gridx=2;
gbdc.gridwidth=1;
panel1.add(new JButton("No.9"),gbdc);
}
}
在這個程序中,我們使用了GridBagLayout輕松地完成了這個界面的設計,允分凸現出它的強大。可以這么說,GridBagLayout是Java語言中最強大的布局管理器。
GridBagLayout,從名字上看,就知道它與GridLayout有一定的淵源,是的,GridBagLayout的確與其十分類似,也是使用網格來進行布局管理的。但與GridLayout(網格布局)不同的是,GridBagLayout不像網格布局一相,需要所有的部件的大小、形狀相同。而且還可以將某一個部件放在一個固定的位置上。
下面,我們一起來分析一下testGridBag.java程序。
1) panel1.setLayout(new GridBagLayout());
在調用setLayout方法時,指定為GridBagLaoyout,使panel1使用GridBag布局管理。
2) GridBagConstraints gbdc=new GridBagConstraints();
GridBagLayout布局管理器是通過一個GridBagConstraints類來實現神奇的效果的。所以,我們要在使用時先定義一個GridBagConstraints類的對象。在這個程序中,我們定義了一個GridBagConstraints類的對象gbdc。
3) gbdc.fill=GridBagConstraints.BOTH;
由於網格單元可能比該單元中的部件大,如果是這樣,部件放置在單元格內會有一些多餘空間。在默認情況下,部件不會擴張,也就是不會填充這個多餘空間。
GridBagConstraints提供了一個fill屬性,我們可以通過設置它的值來起到不同的效果。
¨ GridBagConstraints.NONE:不擴張,是默認值;
¨ GridBagConstraints.HORIZONTAL:部件水平擴張,將水平方向的多餘空間填滿;
¨ GridBagConstraints.VERTICAL:部件垂直擴張,將垂直方向的多餘空間填滿;
¨ GridBagConstraints.BOTH:部件同時向兩個方向擴張,將單元格填滿。
4) gbdc.weightx=1;
gbdc.weighty=1;
weightx和weighty是GridBagConstraints提供的另一對屬性。它的取值是整型數,默認值為0。用來設置行(weightx)、列(weighty)的「重量」,如果值為0的話,所有的部件都會緊收在一起,聚集在中央,如圖10-17所示。
而如果取值為其它數字,則會根據值來分配空間。
5) panel1.add(new JButton("No.1"),gbdc);
panel1.add(new JButton("No.2"),gbdc);
panel1.add(new JButton("No.3"),gbdc);
在沒有任何約束的時候,向gbdc中添加按鈕,這時效果與採用網格布局的效果完全一樣。一個接著一個地「佔領」單元格。
6) gbdc.gridwidth=2;
gbdc.gridx=0;
panel1.add(new JButton("No.4"),gbdc);
由於第四個按鈕(No.4)是寬度是2,在GridBagLayout中,是由gridwidth屬性來控制添加進入的部件的寬度的。
我們通過gbdc.gridwidth=2將其設置為2,這樣,再添加進入的部件就會變成為2個單元格的寬度。
另外,我們再使用gbdc.gridx=0讓其另起一行,從最左邊的單元格開始填充。
因此,我們發現第四個按鈕被加在了第二行,寬度為2個單元格。
7) gbdc.gridx=2;
gbdc.gridwidth=1;
gbdc.gridheight=2;
panel1.add(new JButton("No.5"),gbdc);
接下來,我們要擺放第五個按鈕,這個按鈕將從第3個單元開始填充,其寬度為一個單元格,而高度則為兩個單元格。
因此,我們首先使用用gbdc.gridx=2,使得下一個添加的部件(第五個按鈕)放入第3個單元格(0代表第1個單元格,因此第3個單元格應該是2)。
由於,前面我們已經將gridwidth設置為2了,因此,我們需要使用gbdc.gridwidth=1,將其值設置回1。
最後使用gdbc.gridheight=2,使得添入的部件的單元格縱向跨越兩個單元格。
8) gbdc.gridx=0;
gbdc.gridheight=1;
panel1.add(new JButton("No.6"),gbdc);
我想這一段程序,大家應該都不會再有什麼理解上的問題了吧!主要提醒大家注意的是,我們使用gbdc.gridheight=1將單元格縱向跨度改成了默認值1。這是因為我們在前面需要時將其改成了2,所以在此需要將其改回來。
實例說明
為了更好地理解這個強大的GridBagLayout布局管理器,我們再來做一個有趣的實驗。首先,我們創建一個testGridBag2.java
源程序:testGridBag2.java
import java.awt.*;
import javax.swing.*;
import java.applet.Applet;
public class testGridBag2 extends JApplet
{
public void init()
{
JPanel panel1=(JPanel)getContentPane();
panel1.setLayout(new GridBagLayout());
GridBagConstraints gbdc=new GridBagConstraints();
panel1.add(new JButton("No.1"),gbdc);
panel1.add(new JButton("No.2"),gbdc);
setSize(300,200);
}
}
然後我們使用滑鼠改變一下窗口的大小,看一下,這兩個按鈕有什麼變化?你會驚奇地發現,窗口改變了大小,兩個按鈕的大小卻沒有變,而且一直呆在正中央的位置。
一些說明:
在這個程序中,我們使用了一個以前沒有用過的語句:setsize(300.200),它的功能是在程序中設置窗口的大小。
現在我們對這個程序做一些修改!將添加兩個按鈕的兩條語句:
panel1.add(new JButton("No.1"),gbdc);
panel1.add(new JButton("No.2"),gbdc);
將它們擴展為:
gbdc.weightx=1;
panel1.add(new JButton("No.1"),gbdc);
gbdc.weightx=3;
panel1.add(new JButton("No.2"),gbdc);
為什麼會得到這個效果呢?我們在程序中添加按鈕的程序段前加入一句:
gbdc.fill=GridBagConstraints.HORIZONTAL;
再重新編譯一下程序,再看看程序的輸出有什麼變化!
在得到結果之前,自己可以想像一下結果,然後看一下程序的實際輸出與你的想法是否相吻合。
我們驚奇地發現,第二個按鈕,突然變得十分寬起來(如圖10-20所示)。這是因為放置第二個按鈕的單元格的寬度是3,而第一個按鈕的寬度是1。而且,我們又讓第二個按鈕橫向擴展,因此得到了這樣的輸出結果。
相信實驗做到這里,不須我說,大家也會有一些心得體會了。但是GridBagLayout遠不止這一點,大家應該多做試驗,才能夠在真實的使用環境中有效地掌握GridBagLayout。
D. java nio 開發實例
首先了解下所謂的java nio是個什麼東西!
傳統的並發型伺服器設計是利用阻塞型網路I/O 以多線程的模式來實現的 然而由
於系統常常在進行網路讀寫時處於阻塞狀態 會大大影響系統的性能 自Java 開始引入
了NIO(新I/O) API 通過使用非阻塞型I/O 實現流暢的網路讀寫操作 為開發高性能並發
型伺服器程序提供了一個很好的解決方案 這就罩笑答是java nio
首先來看下傳統的阻塞型網路 I/O的不足
Java 平台傳統的I/O 系統都是基於Byte(位元組)和Stream(數據流)的 相應的I/O 操
作都是阻塞型的 所以伺服器程序也採用阻塞型I/O 進行數據的讀 寫操作 本文以TCP
長連接模式來討論並發型伺服器的相關設計 為了實現伺服器程序的並發性要求 系統由一
個單獨的主線程來監聽用戶發起的連接請求 一直處於阻塞狀態 當有用戶連接請求到來時
程序都會啟一個新的線程來統一處理用戶數據的讀 寫操作
這種模式的優點是簡單 實用 易管理 然而缺點也是顯而易見的 由於是為每一個客
戶端分配一個線程來處理輸入 輸出數據 其線程與客戶機的比例近似為 隨著線程
數量的不斷增加 伺服器啟動了大量的並發線程 會大大加大系統對線程的管理開銷 這將
成為吞吐量瓶頸的主要原因 其次由於底層的I/O 操作採用的同步模式 I/O 操作的阻塞管
理粒度是以服務於請求的線程為單位的 有可能大量的線程會閑置 處於盲等狀態升派 造成I/O
資源利用率不高 影響整個系統的性能
對於並發型伺服器 系統用在阻塞型I/O 等待和線程間切換的時間遠遠多於CPU 在內
存中處理數據的時間 因此傳統的阻塞型物慧I/O 已經成為制約系統性能的瓶頸 Java 版本
後推出的NIO 工具包 提供了非阻塞型I/O 的非同步輸入輸出機制 為提高系統的性能提供
了可實現的基礎機制
NIO 包及工作原理
針對傳統I/O 工作模式的不足 NIO 工具包提出了基於Buffer(緩沖區) Channel(通
道) Selector(選擇器)的新模式 Selector(選擇器) 可選擇的Channel(通道)和
SelectionKey(選擇鍵)配合起來使用 可以實現並發的非阻塞型I/O 能力
NIO 工具包的成員
Buffer(緩沖器)
Buffer 類是一個抽象類 它有 個子類分別對應於七種基本的數據類型 ByteBuffer
CharBuffer DoubleBuffer FloatBuffer IntBuffer LongBuffer 和ShortBuffer 每一個Buffer
對象相當於一個數據容器 可以把它看作內存中的一個大的數組 用來存儲和提取所有基本
類型(boolean 型除外)的數據 Buffer 類的核心是一塊內存區 可以直接對其執行與內存有關
的操作 利用操作系統特性和能力提高和改善Java 傳統I/O 的性能
Channel(通道)
Channel 被認為是NIO 工具包的一大創新點 是(Buffer)緩沖器和I/O 服務之間的通道
具有雙向性 既可以讀入也可以寫出 可以更高效的傳遞數據 我們這里主要討論
ServerSocketChannel 和SocketChannel 它們都繼承了SelectableChannel 是可選擇的通道
分別可以工作在同步和非同步兩種方式下(這里的可選擇不是指可以選擇兩種工作方式 而是
指可以有選擇的注冊自己感興趣的事件) 當通道工作在同步方式時 它的功能和編程方法
與傳統的ServerSocket Socket 對象相似 當通道工作在非同步工作方式時 進行輸入輸出處
理不必等到輸入輸出完畢才返回 並且可以將其感興趣的(如 接受操作 連接操作 讀出
操作 寫入操作)事件注冊到Selector 對象上 與Selector 對象協同工作可以更有效率的支
持和管理並發的網路套接字連接
Selector(選擇器)和SelectionKey(選擇鍵)
各類 Buffer 是數據的容器對象 各類Channel 實現在各類Buffer 與各類I/O 服務間傳輸
數據 Selector 是實現並發型非阻塞I/O 的核心 各種可選擇的通道將其感興趣的事件注冊
到Selector 對象上 Selector 在一個循環中不斷輪循監視這各些注冊在其上的Socket 通道
SelectionKey 類則封裝了SelectableChannel 對象在Selector 中的注冊信息 當Selector 監測
到在某個注冊的SelectableChannel 上發生了感興趣的事件時 自動激活產生一個SelectionKey
對象 在這個對象中記錄了哪一個SelectableChannel 上發生了哪種事件 通過對被激活的
SelectionKey 的分析 外界可以知道每個SelectableChannel 發生的具體事件類型 進行相應的
處理
NIO 工作原理
通過上面的討論 我們可以看出在並發型伺服器程序中使用NIO 實際上是通過網路事
件驅動模型實現的 我們應用Select 機制 不用為每一個客戶端連接新啟線程處理 而是將
其注冊到特定的Selector 對象上 這就可以在單線程中利用Selector 對象管理大量並發的網
絡連接 更好的利用了系統資源 採用非阻塞I/O 的通信方式 不要求阻塞等待I/O 操作完
成即可返回 從而減少了管理I/O 連接導致的系統開銷 大幅度提高了系統性能
當有讀或寫等任何注冊的事件發生時 可以從Selector 中獲得相應的
SelectionKey 從SelectionKey 中可以找到發生的事件和該事件所發生的具體的
SelectableChannel 以獲得客戶端發送過來的數據 由於在非阻塞網路I/O 中採用了事件觸
發機制 處理程序可以得到系統的主動通知 從而可以實現底層網路I/O 無阻塞 流暢地讀
寫 而不像在原來的阻塞模式下處理程序需要不斷循環等待 使用NIO 可以編寫出性能更
好 更易擴展的並發型伺服器程序
並發型伺服器程序的實現代碼
應用 NIO 工具包 基於非阻塞網路I/O 設計的並發型伺服器程序與以往基於阻塞I/O 的
實現程序有很大不同 在使用非阻塞網路I/O 的情況下 程序讀取數據和寫入數據的時機不
是由程序員控制的 而是Selector 決定的 下面便給出基於非阻塞網路I/O 的並發型伺服器
程序的核心代碼片段
import java io * //引入Java io包
import * //引入包
import java nio channels * //引入Java nio channels包
import java util * //引入Java util包
public class TestServer implements Runnable
{
/**
* 伺服器Channel對象 負責接受用戶連接
*/
private ServerSocketChannel server
/**
* Selector對象 負責監控所有的連接到伺服器的網路事件的發生
*/
private Selector selector
/**
* 總的活動連接數
*/
private int activeSockets
/**
* 伺服器Channel綁定的埠號
*/
private int port
/**
*
* 構造函數
*/
public TestServer()throws IOException
{
activeSockets=
port= //初始化伺服器Channel綁定的埠號為
selector= Selector open() //初始化Selector對象
server=ServerSocketChannel open() //初始化伺服器Channel對象
ServerSocket socket=server socket() //獲取伺服器Channel對應的//ServerSocket對象
socket bind(new InetSocketAddress(port)) //把Socket綁定到監聽埠 上
nfigureBlocking(false) //將伺服器Channel設置為非阻塞模式
server register(selector SelectionKey OP_ACCEPT) //將伺服器Channel注冊到
Selector對象 並指出伺服器Channel所感興趣的事件為可接受請求操作
}
public void run()
{
while(true)
{
try
{
/**
*應用Select機制輪循是否有用戶感興趣的新的網路事件發生 當沒有
* 新的網路事件發生時 此方法會阻塞 直到有新的網路事件發生為止
*/
selector select()
}
catch(IOException e)
{
continue //當有異常發生時 繼續進行循環操作
}
/**
* 得到活動的網路連接選擇鍵的集合
*/
Set<SelectionKey> keys=selector selectedKeys()
activeSockets=keys size() //獲取活動連接的數目
if(activeSockets== )
{
continue //如果連接數為 則繼續進行循環操作
}
/**
/**
* 應用For—Each循環遍歷整個選擇鍵集合
*/
for(SelectionKey key :keys)
{
/**
* 如果關鍵字狀態是為可接受 則接受連接 注冊通道 以接受更多的*
事件 進行相關的伺服器程序處理
*/
if(key isAcceptable())
{
doServerSocketEvent(key)
continue
}
/**
* 如果關鍵字狀態為可讀 則說明Channel是一個客戶端的連接通道
* 進行相應的讀取客戶端數據的操作
*/
if(key isReadable())
{
doClientReadEvent(key)
continue
}
/**
* 如果關鍵字狀態為可寫 則也說明Channel是一個客戶端的連接通道
* 進行相應的向客戶端寫數據的操作
*/
if(key isWritable())
{
doClinetWriteEvent(key)
continue
}
}
}
}
/**
* 處理伺服器事件操作
* @param key 伺服器選擇鍵對象
*/
private void doServerSocketEvent(SelectionKey key)
{
SocketChannel client=null
try
{
ServerSocketChannel server=(ServerSocketChannel)key channel()
client=server accept()
if(client==null)
{
return
}
nfigureBlocking(false) //將客戶端Channel設置為非阻塞型
/**
/**
* 將客戶端Channel注冊到Selector對象上 並且指出客戶端Channel所感
* 興趣的事件為可讀和可寫
*/
client register(selector SelectionKey OP_READ|SelectionKey OP_READ)
}catch(IOException e)
{
try
{
client close()
}catch(IOException e ){}
}
}
/**
* 進行向客戶端寫數據操作
* @param key 客戶端選擇鍵對象
*/
private void doClinetWriteEvent(SelectionKey key)
{
代碼實現略
}
/**
* 進行讀取客戶短數據操作
* @param key 客戶端選擇鍵對象
*/
private void doClientReadEvent(SelectionKey key)
{
代碼實現略
}
}
從上面對代碼可以看出 使用非阻塞性I/O進行並發型伺服器程序設計分三個部分
向Selector對象注冊感興趣的事件 從Selector中獲取所感興趣的事件 根據不同的事件進
行相應的處理
結語
通過使用NIO 工具包進行並發型伺服器程序設計 一個或者很少幾個Socket 線程就可
以處理成千上萬個活動的Socket 連接 大大降低了伺服器端程序的開銷 同時網路I/O 採取
非阻塞模式 線程不再在讀或寫時阻塞 操作系統可以更流暢的讀寫數據並可以更有效地向
CPU 傳遞數據進行處理 以便更有效地提高系統的性能
看到這里相信你看了不止 分鍾了吧 我說 分鍾其實就是想讓大家能夠輕松的讀下去(雞蛋 )
好了 到這里大家應該對java nio有個初步的了解了吧~~~
lishixin/Article/program/Java/hx/201311/27190
E. Java多線程程序設計詳細解析
一、理解多線程
多線程是這樣一種機制,它允許在程序中並發執行多個指令流,每個指令流都稱為一個線程,彼此間互相獨立。
線程又稱為輕量級進程,它和進程一樣擁有獨立的執行控制,由操作系統負責調度,區別在於線程沒有獨立的存儲空間,而是和所屬進程中的其它線程共享一個存儲空間,這使得線程間的通信遠較進程簡單。
多個線程的執行是並發的,也就是在邏輯上「同時」,而不管是否是物理上的「同時」。如果系統只有一個CPU,那麼真正的「同時」是不可能的,但是由於CPU的速度非常快,用戶感覺不到其中的區別,因此我們也不用關心它,只需要設想各個線程是同時執行即可。
多線程和傳統的單線程在程序設計上最大的區別在於,由於各個線程的控制流彼此獨立,使得各個線程之間的代碼是亂序執行的,由此帶來的線程調度,同步等問題,將在以後探討。
二、在Java中實現多線凱液慎程
我們不妨設想,為了創建一個新的線程,我們需要做些什麼?很顯然,我們必須指明這個線程所要執行的代碼,而這就是在Java中實現多線程我們所需要做的一切!
真是神奇!Java是如何做到這一點的?通過類!作為一個完全面向對象的語言,Java提供了類java.lang.Thread來方便多線程編程,這個類提供了大量的方法來方便我們控制自己的各個線程,我們以後的討論都將圍繞這個類進行。
那麼如何提供給 Java 我們要線程執行的代碼呢?讓我們來看一看 Thread 類。Thread 類最重要的方法是run(),它為Thread類的方法start()所調用,提供我們的線程所要執行的代碼。為了指定我們自己的代碼,只需要覆蓋它!
方法一:繼承 Thread 類,覆蓋方法 run(),我們在創建的 Thread 類的子類中重寫 run() ,加入線程所要執行的代碼即可。下面是一個例子:
public class MyThread extends Thread
{
int count= 1, number;
public MyThread(int num)
{
number = num;
System.out.println
("創建線程 " + number);
}
public void run() {
while(true) {
System.out.println
("線程 " + number + ":計數 " + count);
if(++count== 6) return;
}
}
public static void main(String args[])
{
for(int i = 0;
i 〈 5; i++) new MyThread(i+1).start();
}
}
這種方法簡單明了,符合大家的習慣,但是,它也有一個很大的缺點,那就是如果我們的類已經從一個類繼承(如小程序必須繼承自 Applet 類),則無法再繼承 Thread 類,這時如果我們又不想建立一個新的類,應該怎麼辦呢?
我們不妨來探索一種新的方法:我們不創建Thread類的子類,而是直接使用它,那麼我們只能將我們的方法作為參數傳遞給 Thread 類的實例,有點類似回調函數。但是 Java 沒有指針,我們只能傳遞一個包含這個方法的類的實例。
那麼如何限制這個類盯敬必須包含這一方法呢?當然是使用介面!(雖然抽象類也可滿足,但是需要繼承,而我們之所以要採用這種新方法,不就是為了避免繼承帶來的限制嗎?)
Java 提供了介面 java.lang.Runnable 來支持這種方法。
方法二:實現 Runnable 介面
Runnable介面只有一個方法run(),我們聲明自己的類實現Runnable介面並提供這一方法,將我們的線程代碼寫入其中,就完成了這一部分的任務。但是Runnable介面並沒有任何對線程的支持,我們還必須創建Thread類的實例,這一點通過Thread類的構造函數public Thread(Runnable target);來實現。下面埋禪是一個例子:
public class MyThread implements Runnable
{
int count= 1, number;
public MyThread(int num)
{
number = num;
System.out.println("創建線程 " + number);
}
public void run()
{
while(true)
{
System.out.println
("線程 " + number + ":計數 " + count);
if(++count== 6) return;
}
}
public static void main(String args[])
{
for(int i = 0; i 〈 5;
i++) new Thread(new MyThread(i+1)).start();
}
}
嚴格地說,創建Thread子類的實例也是可行的,但是必須注意的是,該子類必須沒有覆蓋 Thread 類的 run 方法,否則該線程執行的將是子類的 run 方法,而不是我們用以實現Runnable 介面的類的 run 方法,對此大家不妨試驗一下。
使用 Runnable 介面來實現多線程使得我們能夠在一個類中包容所有的代碼,有利於封裝,它的缺點在於,我們只能使用一套代碼,若想創建多個線程並使各個線程執行不同的代碼,則仍必須額外創建類,如果這樣的話,在大多數情況下也許還不如直接用多個類分別繼承 Thread 來得緊湊。
綜上所述,兩種方法各有千秋,大家可以靈活運用。
下面讓我們一起來研究一下多線程使用中的一些問題。
三、線程的四種狀態
1. 新狀態:線程已被創建但尚未執行(start() 尚未被調用)。
2. 可執行狀態:線程可以執行,雖然不一定正在執行。CPU 時間隨時可能被分配給該線程,從而使得它執行。
3. 死亡狀態:正常情況下 run() 返回使得線程死亡。調用 stop()或 destroy() 亦有同樣效果,但是不被推薦,前者會產生異常,後者是強制終止,不會釋放鎖。
4. 阻塞狀態:線程不會被分配 CPU 時間,無法執行。
四、線程的優先順序
線程的優先順序代表該線程的重要程度,當有多個線程同時處於可執行狀態並等待獲得 CPU 時間時,線程調度系統根據各個線程的優先順序來決定給誰分配 CPU 時間,優先順序高的線程有更大的機會獲得 CPU 時間,優先順序低的線程也不是沒有機會,只是機會要小一些罷了。
你可以調用 Thread 類的方法 getPriority() 和 setPriority()來存取線程的優先順序,線程的優先順序界於1(MIN_PRIORITY)和10(MAX_PRIORITY)之間,預設是5(NORM_PRIORITY)。
五、線程的同步
由於同一進程的多個線程共享同一片存儲空間,在帶來方便的同時,也帶來了訪問沖突這個嚴重的問題。Java語言提供了專門機制以解決這種沖突,有效避免了同一個數據對象被多個線程同時訪問。
由於我們可以通過 private 關鍵字來保證數據對象只能被方法訪問,所以我們只需針對方法提出一套機制,這套機制就是 synchronized 關鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。
1. synchronized 方法:通過在方法聲明中加入 synchronized關鍵字來聲明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制對類成員變數的訪問:每個類實例對應一把鎖,每個 synchronized 方法都必須獲得調用該方法的類實例的鎖方能執行,否則所屬線程阻塞,方法一旦執行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此後被阻塞的線程方能獲得該鎖,重新進入可執行狀態。
這種機制確保了同一時刻對於每一個類實例,其所有聲明為 synchronized 的成員函數中至多隻有一個處於可執行狀態(因為至多隻有一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變數的訪問沖突(只要所有可能訪問類成員變數的方法均被聲明為 synchronized)。
在 Java 中,不光是類實例,每一個類也對應一把鎖,這樣我們也可將類的靜態成員函數聲明為 synchronized ,以控制其對類的靜態成員變數的訪問。
synchronized 方法的缺陷:若將一個大的方法聲明為synchronized 將會大大影響效率,典型地,若將線程類的方法 run() 聲明為 synchronized ,由於在線程的整個生命期內它一直在運行,因此將導致它對本類任何 synchronized 方法的調用都永遠不會成功。當然我們可以通過將訪問類成員變數的代碼放到專門的方法中,將其聲明為 synchronized ,並在主方法中調用來解決這一問題,但是 Java 為我們提供了更好的解決辦法,那就是 synchronized 塊。
2. synchronized 塊:通過 synchronized關鍵字來聲明synchronized 塊。語法如下:
synchronized(syncObject)
{
//允許訪問控制的代碼
}
#p#副標題#e#
synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實例或類)的鎖方能執行,具體機制同前所述。由於可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。
六、線程的阻塞為了解決對共享存儲區的訪問沖突,Java 引入了同步機制,現在讓我們來考察多個線程對共享資源的訪問,顯然同步機制已經不夠了,因為在任意時刻所要求的資源不一定已經准備好了被訪問,反過來,同一時刻准備好了的資源也可能不止一個。為了解決這種情況下的訪問控制問題,Java 引入了對阻塞機制的支持。
阻塞指的是暫停一個線程的執行以等待某個條件發生(如某資源就緒),學過操作系統的同學對它一定已經很熟悉了。Java 提供了大量方法來支持阻塞,下面讓我們逐一分析。
1. sleep() 方法:sleep() 允許 指定以毫秒為單位的一段時間作為參數,它使得線程在指定的時間內進入阻塞狀態,不能得到CPU 時間,指定的時間一過,線程重新進入可執行狀態。典型地,sleep() 被用在等待某個資源就緒的情形:測試發現條件不滿足後,讓線程阻塞一段時間後重新測試,直到條件滿足為止。
2. suspend() 和 resume() 方法:兩個方法配套使用,suspend()使得線程進入阻塞狀態,並且不會自動恢復,必須其對應的resume() 被調用,才能使得線程重新進入可執行狀態。典型地,suspend() 和 resume() 被用在等待另一個線程產生的結果的情形:測試發現結果還沒有產生後,讓線程阻塞,另一個線程產生了結果後,調用 resume() 使其恢復。
3. yield() 方法:yield() 使得線程放棄當前分得的 CPU 時間,但是不使線程阻塞,即線程仍處於可執行狀態,隨時可能再次分得 CPU 時間。調用 yield() 的效果等價於調度程序認為該線程已執行了足夠的時間從而轉到另一個線程。
4. wait() 和 notify() 方法:兩個方法配套使用,wait() 使得線程進入阻塞狀態,它有兩種形式,一種允許 指定以毫秒為單位的一段時間作為參數,另一種沒有參數,前者當對應的 notify() 被調用或者超出指定時間時線程重新進入可執行狀態,後者則必須對應的 notify() 被調用。
初看起來它們與 suspend() 和 resume() 方法對沒有什麼分別,但是事實上它們是截然不同的。區別的核心在於,前面敘述的所有方法,阻塞時都不會釋放佔用的鎖(如果佔用了的話),而這一對方法則相反。
上述的核心區別導致了一系列的細節上的區別。
首先,前面敘述的所有方法都隸屬於 Thread 類,但是這一對卻直接隸屬於 Object 類,也就是說,所有對象都擁有這一對方法。初看起來這十分不可思議,但是實際上卻是很自然的,因為這一對方法阻塞時要釋放佔用的鎖,而鎖是任何對象都具有的,調用任意對象的 wait() 方法導致線程阻塞,並且該對象上的鎖被釋放。
而調用 任意對象的notify()方法則導致因調用該對象的 wait() 方法而阻塞的線程中隨機選擇的一個解除阻塞(但要等到獲得鎖後才真正可執行)。
其次,前面敘述的所有方法都可在任何位置調用,但是這一對方法卻必須在 synchronized 方法或塊中調用,理由也很簡單,只有在synchronized 方法或塊中當前線程才佔有鎖,才有鎖可以釋放。
同樣的道理,調用這一對方法的對象上的鎖必須為當前線程所擁有,這樣才有鎖可以釋放。因此,這一對方法調用必須放置在這樣的 synchronized 方法或塊中,該方法或塊的上鎖對象就是調用這一對方法的對象。若不滿足這一條件,則程序雖然仍能編譯,但在運行時會出現IllegalMonitorStateException 異常。
wait() 和 notify() 方法的上述特性決定了它們經常和synchronized 方法或塊一起使用,將它們和操作系統的進程間通信機製作一個比較就會發現它們的相似性:synchronized方法或塊提供了類似於操作系統原語的功能,它們的執行不會受到多線程機制的干擾,而這一對方法則相當於 block 和wakeup 原語(這一對方法均聲明為 synchronized)。
它們的結合使得我們可以實現操作系統上一系列精妙的進程間通信的演算法(如信號量演算法),並用於解決各種復雜的線程間通信問題。
關於 wait() 和 notify() 方法最後再說明兩點:
第一:調用 notify() 方法導致解除阻塞的線程是從因調用該對象的 wait() 方法而阻塞的線程中隨機選取的,我們無法預料哪一個線程將會被選擇,所以編程時要特別小心,避免因這種不確定性而產生問題。
第二:除了 notify(),還有一個方法 notifyAll() 也可起到類似作用,唯一的區別在於,調用 notifyAll() 方法將把因調用該對象的 wait() 方法而阻塞的所有線程一次性全部解除阻塞。當然,只有獲得鎖的那一個線程才能進入可執行狀態。
談到阻塞,就不能不談一談死鎖,略一分析就能發現,suspend() 方法和不指定超時期限的 wait() 方法的調用都可能產生死鎖。遺憾的是,Java 並不在語言級別上支持死鎖的避免,我們在編程中必須小心地避免死鎖。
以上我們對 Java 中實現線程阻塞的各種方法作了一番分析,我們重點分析了 wait() 和 notify()方法,因為它們的功能最強大,使用也最靈活,但是這也導致了它們的效率較低,較容易出錯。實際使用中我們應該靈活使用各種方法,以便更好地達到我們的目的。
七、守護線程
守護線程是一類特殊的線程,它和普通線程的區別在於它並不是應用程序的核心部分,當一個應用程序的所有非守護線程終止運行時,即使仍然有守護線程在運行,應用程序也將終止,反之,只要有一個非守護線程在運行,應用程序就不會終止。守護線程一般被用於在後台為其它線程提供服務。
可以通過調用方法 isDaemon() 來判斷一個線程是否是守護線程,也可以調用方法 setDaemon() 來將一個線程設為守護線程。
八、線程組
線程組是一個 Java 特有的概念,在 Java 中,線程組是類ThreadGroup 的對象,每個線程都隸屬於唯一一個線程組,這個線程組在線程創建時指定並在線程的整個生命期內都不能更改。
你可以通過調用包含 ThreadGroup 類型參數的 Thread 類構造函數來指定線程屬的線程組,若沒有指定,則線程預設地隸屬於名為 system 的系統線程組。
在 Java 中,除了預建的系統線程組外,所有線程組都必須顯式創建。在 Java 中,除系統線程組外的每個線程組又隸屬於另一個線程組,你可以在創建線程組時指定其所隸屬的線程組,若沒有指定,則預設地隸屬於系統線程組。這樣,所有線程組組成了一棵以系統線程組為根的樹。
Java 允許我們對一個線程組中的所有線程同時進行操作,比如我們可以通過調用線程組的相應方法來設置其中所有線程的優先順序,也可以啟動或阻塞其中的所有線程。
Java 的線程組機制的另一個重要作用是線程安全。線程組機制允許我們通過分組來區分有不同安全特性的線程,對不同組的線程進行不同的處理,還可以通過線程組的分層結構來支持不對等安全措施的採用。
Java 的 ThreadGroup 類提供了大量的方法來方便我們對線程組樹中的每一個線程組以及線程組中的每一個線程進行操作。
九、總結
在本文中,我們講述了 Java 多線程編程的方方面面,包括創建線程,以及對多個線程進行調度、管理。我們深刻認識到了多線程編程的復雜性,以及線程切換開銷帶來的多線程程序的低效性,這也促使我們認真地思考一個問題:我們是否需要多線程?何時需要多線程?
多線程的核心在於多個代碼塊並發執行,本質特點在於各代碼塊之間的代碼是亂序執行的。我們的程序是否需要多線程,就是要看這是否也是它的內在特點。
假如我們的程序根本不要求多個代碼塊並發執行,那自然不需要使用多線程;假如我們的程序雖然要求多個代碼塊並發執行,但是卻不要求亂序,則我們完全可以用一個循環來簡單高效地實現,也不需要使用多線程;只有當它完全符合多線程的特點時,多線程機制對線程間通信和線程管理的強大支持才能有用武之地,這時使用多線程才是值得的。
#p#副標題#e#
F. java基礎與案例分析 第二章最後一個題 結合換行 制位表 空格在控制台列印出由「*」所組成的心形效果圖
湊合著看吧,剛實現了一個,代碼如下:
public class Test {
public static void main(String args[]) {
System.out.println("this is XX!");
System.err.println(" ** ** ");
System.err.println(" ** ** ** ** ");
System.err.println(" ** ** ** ");
System.err.println(" ** ** ");
System.err.println(" ** ** ");
System.err.println(" ** ** ");
System.err.println(" ** ");
}
}
復制到網路裡面有點亂,你要是會用MyEclipse的話直接Ctrl+Shift+F;
如果喜歡的話希望採納@~
G. java 項目 框架 說明文檔
你好,一般的web應用結構分為三層,採用經典的MVC設計模式,在程序的目版錄架構上有如下分層:權資料庫<->model->->service。分析:
1、資料庫和model中的實體類是相互對應的,一個實體類,就對應這資料庫中的一張表。
2、是一個service和model聯系的橋梁,封裝了操作數據的具體實現,你可以在中使用jdbc,也可以使用hibernate。但是這些對service層都是透明的。
3、service層通過調用層,直接取得需要的數據或者要執行的操作,並且加入相應的業務邏輯,如檢查、判斷。
4、當一個應用為web應用時,就可以在servlet中調用service層的方法,來完成不同的功能。常見的增刪查改。
5、servlet處理完成後,就結果轉發給視圖層進行顯示。
有上面可以看出:1、2就是M層,3、4就是C層,jsp、html就是V層。