譯者序
序
前言
第一部分 Swing基礎(chǔ)
第1章 簡介
1.1Swing的歷史
1.2輕量組件與重量組件的比較
1.3Swing組件
1.3.1AWT的替代組件
1.3.2Swing增加的組件
1.4J組件
1.5Swing包概覽
1.6Swing與AWT
1.7開始學(xué)習(xí)
1.8Swing資源
1.9本章回顧
第2章 Swing的基本知識(shí)
2.1小應(yīng)用程序與應(yīng)用程序
2.1.1小應(yīng)用程序
2.1.2JApplet類
2.1.3應(yīng)用程序
2.1.4JFrame類
2.1.5小應(yīng)用程序/應(yīng)用程序的組合
2.2GJApp
2.3混合使用Swing組件和AWT組件
2.3.1層序
2.3.2Swing彈出式菜單
2.3.3滾動(dòng)
2.3.4內(nèi)部窗體
2.4Swing和線程
2.4.1Swing單線程設(shè)計(jì)的結(jié)果
2.4.2SwingUtilities 類的invokeLater
和invokeAndWait方法
2.5本章回顧
第3章 Swing組件的體系結(jié)構(gòu)
3.1典型的“模型-視圖-控制器”體系
結(jié)構(gòu)
3.1.1插入式視圖和控制器
3.1.2視圖更新
3.2SwingMVC
3.2.1Swing組件
3.2.2靜態(tài)認(rèn)識(shí)
3.2.3動(dòng)態(tài)認(rèn)識(shí)
3.2.4模型
3.2.5UI代表
3.2.6組件UI的案例
3.2.7監(jiān)聽器
3.3本章回顧
第4章 JComponent類
4.1JComponent類概覽
4.1.1邊框
4.1.2可訪問性
4.1.3雙緩存
4.1.4調(diào)試圖形
4.1.5自動(dòng)滾動(dòng)
4.1.6工具提示
4.1.7鍵擊處理和客戶屬性
4.2JComponent類結(jié)構(gòu)
4.2.1Swing組件是AWT容器
4.2.2最小尺寸 最大尺寸和首選
尺寸
4.3繪制JComponent組件
4.3.1Swing組件中的定制繪制
4.3.2在AWT組件中重載繪制方法
4.3.3在Swing組件中重載繪制方法
4.3.4painT、repaint和update方法
4.3.5validate、invalidate和revalidate
方法
4.3.6不透明組件與透明組件的比較
4.3.7立即繪制Swing組件
4.4雙緩存
4.5調(diào)試圖形
4.6自動(dòng)滾動(dòng)
4.7工具提示
4.7.1基于鼠標(biāo)位置的工具提示
4.7.2工具提示的首選位置
4.7.3定制工具提示的行為
4.7.4定制工具提示的界面樣式
4.8鍵擊處理
4.9客戶屬性
4.10焦點(diǎn)管理
4.10.1JComponent的焦點(diǎn)屬性
4.10.2焦點(diǎn)管理器
4.11支持可訪問性
4.12本章回顧
第5章 邊框、圖標(biāo)和動(dòng)作
5.1邊框
5.1.1邊框和邊襯
5.1.2Swing的邊框類型
5.1.3不透明與透明之間的比較
5.1.4邊框包
5.1.5邊框接口
5.1.6AbstracBorder類
5.1.7邊框庫――共享邊框
5.1.8替換內(nèi)置邊框
5.1.9實(shí)現(xiàn)定制邊框
5.2圖標(biāo)
5.2.1把圖標(biāo)與組件相關(guān)聯(lián)
5.2.2在組件中共享圖標(biāo)
5.2.3圖像圖標(biāo)
5.2.4動(dòng)畫的圖像圖標(biāo)
5.3動(dòng)作
5.3.1作為控制中心點(diǎn)的動(dòng)作
5.3.2動(dòng)作常量
5.4本章回顧
第6章 實(shí)用工具
6.1計(jì)時(shí)器
6.2事件監(jiān)聽器列表
6.3Swing實(shí)用工具
6.4Swing常量
6.5BoxLayout和Box 類
6.5.1BoxLayout類
6.5.2Box類
6.6進(jìn)度監(jiān)視器
6.6.1ProgressMonitor
6.6.2Progress MonitorInputStream
6.7撤消/重復(fù)
6.7.1一個(gè)簡單的撤消/重復(fù)樣例
6.7.2UndoableEditSupport
6.7.3組合編輯
6.7.4UndoManager
6.7.5狀態(tài)編輯
6.8本章回顧
第7章 插入式界面樣式
7.1界面樣式結(jié)構(gòu)
7.1.1界面樣式
7.1.2界面樣式缺省值
7.1.3UI管理器
7.1.4UI資源
7.2Java界面樣式
7.2.1客戶屬性
7.2.2主題
7.3附加UI
7.4本章回顧
第二部分Swing組件
第8章 標(biāo)簽與按鈕
8.1JLabel與JButton
8.2JLabel
8.2.1內(nèi)容排列
8.2.2文本的位置
8.2.3圖標(biāo)/文本間隙
8.2.4許可狀態(tài)
8.2.5JLabel屬 性
8.2.6JLabel事件
8.2.7JLabel類總結(jié)
8.3按鈕
8.4JButton
8.4.1JButton屬性
8.4.2JButton事件
8.4.3JButton類總結(jié)
8.4.4AWT兼容
8.5本章回顧
第9章 反轉(zhuǎn)按鈕、復(fù)選框和單選鈕
9.1JToggleButton類
9.1.1JToggleButton屬性
9.1.2JToggleButton事件
9.1.3JToggleButton類總結(jié)
9.1.4AWT兼容
9.2按鈕組
9.3復(fù)選框
9.3.1JCheckBox屬性
9.3.2JCheckBox事件
9.3.3JCheckBox類總結(jié)
9.4單選鈕
9.4.1JRadioButton屬性
9.4.2JRadioButton 事件
9.4.3JRadioButton類總結(jié)
9.4.4AWT兼容
9.5本章回顧
第10章 菜單和工具條
10.1菜單、菜單欄和工具條
10.2菜單和彈出式菜單
10.3JMenuItem
10.3.1菜單項(xiàng)快捷鍵和助記符鍵
10.3.2JMenuItem屬性
10.3.3JMenuItem事件
10.3.4JMenuItem類總結(jié)
10.3.5AWT兼容
10.4JCheckBoxMenuItem
10.4.1JCheckBoxMenuItem屬性
10.4.2JCheckBoxMenuItem事件
10.4.3JCheckBoxMenuItem類總結(jié)
10.4.4AWT兼容
10.5JRadioButtonMenuItem
10.5.1JRadioButt onMe nuItem 屬性
10.5.2JRadioButtonMenuItem事件
10.5.3JRadioButtonMenuItem類
總結(jié)
10.5.4AWT兼容
10.6JMenu
10.6.1動(dòng)態(tài)修改菜單
10.6.2右拉式菜單
10.6.3JMenu屬性
10.6.4JMenu事件
10.6.5JMenu類總結(jié)
10.6.6AWT兼容
10.7菜單元素
10.8JPopu pMe nu
10.8.1彈出式菜單觸發(fā)器
10.8.2輕量/中量/重量彈出式
菜單
10.8.3彈出式菜單調(diào)用者
10.8.4JPopupMenu屬性
10.8.5JPopupMenu事件
10.8.6JPopupMenu類總結(jié)
10.8.7AWT兼容
10.9JMenuBar
10.9.1菜單欄菜單和組件
10.9.2JMenuBar屬性
10.9.3JMenuBar事件
10.9.4JMenuBar類總結(jié)
10.9.5AWT兼容
10.10JToolBar
10.10.1滾過式工具條
10.10.2在工具條中使用動(dòng)作
10.10.3浮動(dòng)工具條
10.10.4位置固定的工具提示
10.10.5JToolBar屬性
10.10.6JToolBar事件
10.10.7JToolBar類總結(jié)
10.10.8AWT兼容
10.11本章回顧
第11章 進(jìn)度條、滑桿和分隔條
11.1JProgessBar
11.1.1進(jìn)度條與線程
11.1.2JProges sBar屬性
11.1.3JProgessBar事件
11.1.4JProgessBar類總結(jié)
11.1.5AWT兼容
11.2JSlider
11.2.1填充的滑桿
11.2.2滑桿間隔標(biāo)記
11.2.3滑桿標(biāo)簽
11.2.4反轉(zhuǎn)滑桿值
11.2.5滑桿的外延值
11.2.6JSlider屬性
11.2.7JSlider事件
11.2.8JSlider類總結(jié)
11.2.9AWT兼容
11.3JSeparator
11.3.1分隔條與框
11.3.2JSeparator 屬性
11.3.3JSeparator事件
11.3.4AWT兼容
11.4本章回顧
第12章 輕量容器
12.1JPan el
12.1.1JPanel的屬性
12.1.2JPanel的事件
12.1.3JPanel類總結(jié)
12.1.4AWT兼容
12.2JRootPane
12.2.1RootPaneCotainer接口
12.2.2玻璃窗格
12.2.3內(nèi)容窗格
12.2.4JRootPane屬性
12.2.5JRooPane事件
12.2.6JRootPane類總結(jié)
12.2.7AWT兼容
12.3JLaye redPane
12.3.1回顧輕量組件的層序
12.3.2為組件分配層
12.3.3指定同一層中組件的位置
12.3.4使用拖動(dòng)層
12.3.5JLay eredPane屬性
12.3.6JLayeredPane類總結(jié)
12.3.7AWT兼容
12.4JTabbedPane
12.4.1選項(xiàng)卡的位置
12.4.2JTabbedPane的屬性
12.4.3JTabbedPane事件
12.4.4JTabbedPane類總結(jié)
12.5JSplitPane類
12.5.1JSplitPane屬性
12.5.2JSplitPane事件
12.5.3JSplitPane類總結(jié)
12.5.4AWT兼容
12.6本章回顧
第13章 滾動(dòng)
13.1JViewport
13.1.1拖動(dòng)視口中的視圖
13.1.2使用scrollRectToV isible
方法
13.1.3JViewport屬性
13.1.4JViewport事件
13.1.5JViewport類總結(jié)
13.1.6AWT兼容
13.2JScrollPane
13.2.1滾動(dòng)窗格的頭部
13.2.2滾動(dòng)窗格的角部
13.2.3JScrollPane屬性
13.2.4JScrollPane事件
13.2.5JScrollPane類總結(jié)
13.2.6AWT兼容
13.3Scrollable接口
13.4JScrollBar
13.4.1使用Swing的JScrollBar類進(jìn)
行手動(dòng)滾動(dòng)
13.4.2塊增量和單元增量
13.4.3JScrollBar屬性
13.4.4JScrollBar事件
13.4.5JScrollBar類總結(jié)
13.4.6AWT兼容
13.5本章回顧
第14章 窗口和對(duì)話框
14.1JWindow
14.1.1JWindow屬性
14.1.2JWindow類總結(jié)
14.1.3AWT兼容
14.2JDialog
14.2.1JDialog屬性
14.2.2JDialog類總結(jié)
14.2.3AWT兼容
14.3JOptionPane
14.3.1內(nèi)部窗體
14.3.2用JOptionPane靜態(tài)方法創(chuàng)建
對(duì)話框
14.3.3消息對(duì)話框
14.3.4確認(rèn)對(duì)話框
14.3.5輸入對(duì)話框
14.3.6選項(xiàng)對(duì)話框
14.3.7JOptionPane屬性
14.3.8JOptionPane事件
14.3.9JOptionPane類總結(jié)
14.3.10AWT兼容
14.4本章回顧
第15章 內(nèi)部窗體和桌面窗格
15.1JInternalFrame
15.1.1jintertnalFrame屬性
15.1.2JInternalFrame事件
15.1.3AWT兼容
15.2JDesktopPane
15.2.1JDesktopPane屬性
15.2.2JDesktopPane事件
15.2.3JDesktopPane類總結(jié)
15.2.4AWT兼容
15.3DesktopManager
15.4本章回顧
第16章 選取器
16.1JFileChooser
16.1.1文件選取器類型
16.1.2可訪問組件
16.1.3過濾文件類型
16.1.4文件視圖
16.1.5多文件選取
16.1.6JFileCHOoser屬性
16.1.7JFileChooser事件
16.1.8JFileChooser類總結(jié)
16.1.9AWT兼容
16.2JColorChooser
16.2.1在對(duì)話框中顯示顏色
選取器
16.2.2定制顏色選取器
16.2.3JColorChooser屬性
16.2.4JColorChooser事件
16.2.5JColorChooser類總結(jié)
16.2.6AWT兼容
16.3本章回顧
第17章 列表
17.1列表模型
17.1.1AbstractListModel
17.1.2DefaultListModel
17.2列表選取
17.3列表單元繪制器
17.3.1JList屬性
17.3.2JList事件
17.3.3JList類總結(jié)
17.3.4AWT兼容
17.4本章回顧
第18章 組合框
181JComboBox與JList的比較
18.2JComboBox組件
18.3組合框模型
18.3.1ComboBoxModel
18.3.2MutableComboBoxModel
18.3.3DefaultComboBoxModel
18.4組合框單元繪制器
18.5組合框鍵選取管理器
18.5.1使用缺省鍵選取管理器
18.5.2定制鍵選取管理器
18.5.3程序式的鍵選取
18.6組合框編輯器
18.6.1JComboBox屬性
18.6.2JCombo Box 事件
18.6.3JComboBox類總結(jié)
18.6.4AWT兼容
18.7本章回顧
第19章 表格
19.1表格和滾動(dòng)
19.2表格模型
19.2.1表格數(shù)據(jù)模型
19.2.2TableModel接口
19.2.3AbstractTableModel
19.2.4DefaultTableModel
19.2.5表格模型、缺省繪制器
和缺省編輯器
19.3表格列
19.3.1列調(diào)整大小模式
19.3.2列寬度
19.4表格列模型
19.4.1DefaultTableColumnModel類
19.4.2列邊距
19.4.3隱藏列
19.4.4鎖定左邊列
19.5表格選取
19.6繪制和編輯
19.6.1使用表格單元繪制器和編
輯器
19.6.2表格單元繪制器
19.6.31DefaultTableCellRenderer
類
19.6.4表格格式化繪制器
19.6.5單元編輯器
19.6.6表格單元編輯器
19.6.7實(shí)現(xiàn)TableCellEditor接口
19.7表格行
19.7.1行高
19.7.2繪制行
19.8表格裝飾器
19.9表格頭部
19.9.1JTableHeader
19.9.2列頭部繪制器和頭部工具
提示
19.9.3JTable屬性
19.9.4表格事件
19.9.5表格模型事件
19.9.6TableColumnModel事件
19.9.7列表選取事件
19.9.8JTable類總結(jié)
19.9.9AWT兼容
19.10本章回顧
第20章 樹
20.1創(chuàng)建樹
20.2樹節(jié)點(diǎn)
20.2.1TreeNode接口
20.2.2MutableTreeNode接口
20.2.3DefaultMutableTreeNode類
20.3樹路徑
20.4樹模型
20.5樹選取
20.6樹單元繪制
20.6.1DefaultTreeCellRenderer
20.6.2Metal界面樣式
20.6.3根節(jié)點(diǎn)和根句柄
20.7樹單元編輯
20.7.1擴(kuò)展DefaultCellEditor
20.7.2DefaultTreeCellEditor
20.8繪制和編輯:學(xué)習(xí)一個(gè)樣例
20.8.1Test類
20.8.2SelectableFile類和FileNode
類
20.8.3繪制器
20.8.4編輯器
20.8.5JTree屬性
20.8.6樹事件
20.8.7JTree類總結(jié)
20.8.8AWT兼容
20.9本章回顧
第21章 文本基礎(chǔ)
21.1Swing文本組件
21.2動(dòng)作
21.2.1文本動(dòng)作
21.2.2動(dòng)作和編輯工具包
21.3鍵映射
21.4文檔
21.4.1定制文檔
21.4.2文檔監(jiān)聽器
21.5加字符與加重器
21.5.1加字符
21.5.2加字符監(jiān)聽器
21.5.3定制加字符
21.5.4加重器
21.6撤銷/恢復(fù)
21.7JTextComponent
21.8本章回顧
第22章 文本組件
22.1JTexlField
22.1.1水平可視性和滾動(dòng)偏移
22.1.2布局單行文本域
22.1.3使單行文本域有效
22.1.4JTextField組件總結(jié)
22.1.5JTextField屬性
22.1.6JTextField事件
22.1.7JTextField類總結(jié)
22.1.8AWT兼容
22.2JPasswordField
22.2.1JPasswordField組件總結(jié)
22.2.2JPasswordFi eld屬性
22.2.3JPasswordField類總結(jié)
22.3JTextArea
22.3.1JTextArea組件總結(jié)
22.3.2JTextArea屬性
22.3.3JTextArea類總結(jié)
22.3.4AWT兼容
22.4JEditorPane
22.4.1JEditorPane屬性
22.4.2JEditorPane事件
22.4.3JEditorPane類總結(jié)
22.5JTextPane
22.5.1嵌入圖標(biāo)和組件
22.5.2用屬性標(biāo)記內(nèi)容
22.5.3JTextPane屬性
22.5.4JTextPane類總結(jié)
22.6AWT兼容
22.7本章回顧
第23章 定制文本組件
23.1概覽
23.2屬性集和風(fēng)格常量
23.3定制動(dòng)作
23.4視圖
23.5風(fēng)格和風(fēng)格的相關(guān)內(nèi)容
23.6元素
23.7本章回顧
第三部分 附錄
附錄A 類圖
附錄B 插入式界面樣式常量
2100433B
Swing是一流的Java圖形用戶界面開發(fā)工具。本書詳細(xì)介紹了Swing的設(shè)計(jì)思想、體系結(jié)構(gòu)、使用技巧,內(nèi)容豐富、深入細(xì)致、分析透徹。本書用大量實(shí)例代碼介紹了每個(gè)組件的用法,使初學(xué)者能很快入門;用大量圖示分析了Swing組件的特點(diǎn)、結(jié)構(gòu)及相互關(guān)系,使有經(jīng)驗(yàn)的編程人員能高效利用Swing的強(qiáng)大功能。本書對(duì)掌握Swing技術(shù)提供了最全面的參考。
《便衣警察》 《死于青春》 《一場(chǎng)風(fēng)花雪月的事》 《海巖文集》 《永不瞑目》 《海巖散文》 《玉觀音》 《海巖中篇選》 《你的生命如此多情》 《海巖長篇經(jīng)典全集》 《拿什么拯救你,我的愛人》 《煽》 ...
徐韋華,在臺(tái)灣很有影響力的著名拼布作家,從事 拼布材料包 設(shè)計(jì)多年,經(jīng)驗(yàn)非常豐富?!兑豢p就成的拼布小物》是一本實(shí)用的拼布書,以面紙?zhí)住⑹謾C(jī)袋、卡片夾、零錢包、筆袋、相機(jī)包等非常實(shí)用的拼布小物的制作講解...
前言第1章 概述1.1 傳統(tǒng)的圖解展開法1.2 程編計(jì)算公式法展開放樣1.3 計(jì)算器的程編計(jì)算應(yīng)用第2章 圓柱面構(gòu)件的展開2.1 被平面斜截后的圓柱管構(gòu)件2.2 被圓柱面截切后的圓柱管構(gòu)件2.3 被橢...
格式:pdf
大?。?span id="4zsnld7" class="single-tag-height">2.4MB
頁數(shù): 24頁
評(píng)分: 4.4
圖形設(shè)計(jì)
格式:pdf
大?。?span id="qeasu6h" class="single-tag-height">2.4MB
頁數(shù): 7頁
評(píng)分: 4.3
圖形設(shè)計(jì)論文
JavaWeb應(yīng)用開發(fā)框架實(shí)例
一、 概述
Web 應(yīng)用架構(gòu)可以劃分為兩大子系統(tǒng):前端子系統(tǒng)和后臺(tái)子系統(tǒng)。
前端子系統(tǒng):
1. 基礎(chǔ)技術(shù): Html/Java/CSS / Flash
2. 開發(fā)框架: jQuery, Extjs , Flex 等;
后臺(tái)子系統(tǒng):
1. 基礎(chǔ)技術(shù): Java Servlet;
2. 開發(fā)框架: Struts, Spring, Hibernate, ibatis 等;
3. 應(yīng)用服務(wù)器: Tomcat / Jetty
編程模型: B/S 模型。 客戶端向服務(wù)器端發(fā)送請(qǐng)求, 服務(wù)器經(jīng)過處理后返回響應(yīng), 然后客戶端根據(jù)響應(yīng)及需求繪制前端展現(xiàn)。
在用戶客戶端和實(shí)際提供功能的Web 服務(wù)器之間還可能存在著代理服務(wù)器, 負(fù)載均衡服務(wù)器, 不過那些屬于錦上添花的事物,暫時(shí)不在考慮范圍內(nèi)。
客戶端應(yīng)用理念: 客戶端承擔(dān)大量的交互邏輯及渲染工作,服務(wù)器端主要是處理請(qǐng)求和返回?cái)?shù)據(jù)。
前后端系統(tǒng)耦合: 客戶端和服務(wù)器端各自處理自己內(nèi)部的子系統(tǒng)耦合;而客戶端與服務(wù)器端的耦合簡化為一個(gè)通信與數(shù)據(jù)通道。該通道用來傳輸通信請(qǐng)求和返回?cái)?shù)據(jù)。
請(qǐng)求通信: 采用 Http / Tcp 協(xié)議
數(shù)據(jù)通道: 采用 Json, xml , 文本字符串,字節(jié)。 內(nèi)部系統(tǒng)一般采用 Json 作為數(shù)據(jù)交換格式;系統(tǒng)間的互操作則采用XML 來規(guī)范;文本字符串是最一般的形式, 字節(jié)是最底層的形式。
JavaWeb應(yīng)用開發(fā)框架實(shí)例
二、 架構(gòu)演變
最輕的架構(gòu): jQuery + Servlet + ajax 在客戶端使用 jQuery發(fā)送 ajax 請(qǐng)求給Java 服務(wù)端的 Servlet 進(jìn)行處理, Servlet 僅僅返回?cái)?shù)據(jù)給客戶端進(jìn)行渲染。
該架構(gòu)有效地分離了前端展示和后臺(tái)請(qǐng)求處理,同時(shí)又保持了最輕的復(fù)雜性, 只需要學(xué)會(huì)編寫 Servlet 及使用 jQuery , 就能構(gòu)建簡單的應(yīng)用。
如果只是做個(gè)人創(chuàng)意演示, 可以采用該架構(gòu), 快速實(shí)現(xiàn)自己的創(chuàng)意功能。 Servlet 是Java web 應(yīng)用的基礎(chǔ)技術(shù),jQuery 則是前端開發(fā)的簡單易用的利器。
后臺(tái)架構(gòu)演變:
1. 邏輯與頁面的分離: JSP/Servlet
JSP 實(shí)現(xiàn)了頁面邏輯與外觀的分離,但是, 前端子系統(tǒng)與后臺(tái)子系統(tǒng)仍然是緊密耦合的; 前端設(shè)計(jì)人員實(shí)際上只需要服務(wù)端返回的數(shù)據(jù), 就可設(shè)計(jì)出非常專業(yè)的界面顯示。
2. MVC 架構(gòu):Struts2(含Servlet,MVC) + JDBC
用Servlet 來添加服務(wù)器功能是基本的選擇,但在web.xml中配置大量的 Servlet 卻不是最佳的選擇。
Struts2 在服務(wù)端實(shí)現(xiàn)了更豐富的MVC 模式, 將本來由應(yīng)用決定的控制器從web容器中分離。
3. SSH 架構(gòu): Struts2(含Servlet, MVC) + Spring (Ioc) + Hibernate (ORM,對(duì)象-關(guān)系映射)
通常, 應(yīng)用系統(tǒng)中需要預(yù)先創(chuàng)建一些單例對(duì)象, 比如 Controller, Service, Dao, 線程池等, 可以引入 Spring Ioc 來有效地創(chuàng)建、管理和推送這些對(duì)象;使用 Hibernate 來實(shí)現(xiàn)關(guān)系數(shù)據(jù)庫的行與面向?qū)ο蟮膶傩灾g的映射與聯(lián)接,以更好地簡化和管理應(yīng)用系統(tǒng)的數(shù)據(jù)庫操作。SSH 可以說是 JavaWeb應(yīng)用系統(tǒng)開發(fā)的三劍客。
4. SI 架構(gòu): SpringMVC(含Servlet, Ioc, MVC, Rest) + iBatis (Semi-ORM)
過于復(fù)雜的架構(gòu)會(huì)將人搞暈。因此,在適應(yīng)需求的情況下, 盡量選擇簡單的架構(gòu),是明智之選。 這種架構(gòu)使用面向資源的理念,著重使用Spring作為MVC及應(yīng)用基礎(chǔ)服務(wù)設(shè)施, 同時(shí)使用 iBatis 來實(shí)現(xiàn)更簡單靈活的ORM映射, 使之在可以理解和維護(hù)的范圍內(nèi)。
前端架構(gòu):
1. Flash 架構(gòu): Flex + jQuery + JSP
這是一種比較傳統(tǒng)的前端架構(gòu),采用同步模式, Flex 承擔(dān)大量的頁面渲染工作, 并采用AMF協(xié)議與Java端進(jìn)行通信, 而JSP 則可以用于更快速的頁面顯示。優(yōu)點(diǎn)是: 經(jīng)過考驗(yàn)的結(jié)構(gòu), 通常是值得信賴的; 缺點(diǎn)是, 由于采用同步模式, 在交互效果上可能不夠流暢, 需要進(jìn)行比較耗時(shí)的編譯過程;此外, Flex 基于瀏覽器插件運(yùn)行,在調(diào)試方面有些麻煩。
2. MVC 架構(gòu): Extjs + jQuery
這是一種比較現(xiàn)代的前端架構(gòu), 采用異步模式, Extjs4 可以實(shí)現(xiàn)前端子系統(tǒng)的MVC 分離, 對(duì)于可維護(hù)性是非常不錯(cuò)的支持;此外, jQuery 可以作為有效的補(bǔ)充。
優(yōu)點(diǎn): 異步, 快速, 對(duì)于企業(yè)內(nèi)部的后臺(tái)管理系統(tǒng)是非常好的選擇。
缺點(diǎn): Extjs4 的可定制性、可適應(yīng)性可能難以適應(yīng)各種特殊的需求,需要用其它組件來補(bǔ)充, 比如大數(shù)據(jù)量的繪制。對(duì)于互聯(lián)網(wǎng)應(yīng)用, 速度可能是致命傷。
三、 架構(gòu)的選擇
不要去詢問哪種架構(gòu)更好,更需要做的是清晰地定位項(xiàng)目目標(biāo),根據(jù)自己的具體情況來選擇和定制架構(gòu)。反復(fù)地嘗試、觀察和改進(jìn),反復(fù)磨煉技藝,這樣才有助于設(shè)計(jì)水平的提升。
架構(gòu)的選擇通常有四種關(guān)注點(diǎn):
1. 適用性: 是否適合你的項(xiàng)目需求。 架構(gòu)有大有小, 小項(xiàng)目用小架構(gòu), 大項(xiàng)目用大架構(gòu)。
2. 可擴(kuò)展性: 該架構(gòu)在需要添加新功能時(shí),是否能夠以常量的成本添加到現(xiàn)有系統(tǒng)中, 所做的改動(dòng)在多大程度上會(huì)影響現(xiàn)有功能的實(shí)現(xiàn)(基本不影響,還是要大面積波及)。
3. 便利性: 使用該架構(gòu)是否易于開發(fā)功能和擴(kuò)展功能, 學(xué)習(xí)、開發(fā)和測(cè)試成本有多大。
4. 復(fù)雜性: 使用該架構(gòu)后,維護(hù)起來的成本有多大。你自然希望能夠?qū)懸粭l語句做很多事,使用各種成熟的組件是正確的方式,同時(shí),在項(xiàng)目中混雜各種組件,也會(huì)提升理解和維護(hù)系統(tǒng)的復(fù)雜度。便利性和復(fù)雜性需要達(dá)到較好的平衡。
特殊的關(guān)注點(diǎn):
譬如,應(yīng)用需要支持高并發(fā)的情況, 需要建立一個(gè)底層的并發(fā)基礎(chǔ)設(shè)施, 并向上層提供簡單易用的接口,屏蔽其復(fù)雜性。
四、 架構(gòu)演進(jìn)的基本手段
架構(gòu)并不是一成不變的, 在做出最初的架構(gòu)之后,隨著開發(fā)的具體情況和需求的變更, 需要對(duì)最初架構(gòu)做出變更和改進(jìn)。
架構(gòu)演進(jìn)的基本手段:
一致性, 隔離與統(tǒng)一管理, 螺旋式重構(gòu)改進(jìn), 消除重復(fù), 借鑒現(xiàn)有方案。
1. 一致性: 確保使用統(tǒng)一模式來處理相同或相似的功能; 解決一次, 使用多次。
2. 模塊化、隔離與統(tǒng)一管理: 對(duì)于整體的應(yīng)用, 分而治之,將其劃分為隔離性良好的模塊,提供必要的通信耦合;對(duì)于特定的功能模塊, 采用隔離手段,將其隔離在局部統(tǒng)一管理,避免分散在系統(tǒng)的各處。
3. 不斷重構(gòu)改進(jìn), 一旦發(fā)現(xiàn)更好的方式, 馬上替換掉原有方式。
4. 盡可能重用,消除重復(fù)。
5. 盡可能先借鑒系統(tǒng)中已有方案并復(fù)用之;如果有更好方案可替換之;
有一條設(shè)計(jì)準(zhǔn)則是: 預(yù)先設(shè)計(jì), 但不要過早設(shè)計(jì)。
意思是說, 需要對(duì)需求清楚的部分進(jìn)行仔細(xì)的設(shè)計(jì), 但是對(duì)于未知不清楚的需求,要堅(jiān)持去理解它,但不要過早地去做出“預(yù)測(cè)性設(shè)計(jì)”;設(shè)計(jì)必須是明確的、清晰的、有效的, 不能針對(duì)含糊的東西來設(shè)計(jì)??梢栽诤笃谕ㄟ^架構(gòu)演進(jìn)來獲得對(duì)后續(xù)需求的適應(yīng)能力。
我們對(duì)Java的探索從客戶端Web應(yīng)用開始,其代碼也在瀏覽器提供的引擎上執(zhí)行。為了打好后續(xù)對(duì)Java語言和瀏覽器平臺(tái)的學(xué)習(xí)基礎(chǔ),首先我們要理解Web應(yīng)用的生命周期,尤其要理解Java代碼執(zhí)行在生命周期的所有環(huán)節(jié)。
本文會(huì)完整探索客戶端Web應(yīng)用程序的生命周期,從頁面請(qǐng)求開始,到用戶不同種類的交互,最后至頁面被關(guān)閉。首先我們來看看頁面是如何從HTML代碼建立的。然后我們將集中探討Java代碼的執(zhí)行,它給我們的頁面提供了大量交互。最后我們會(huì)看看為了響應(yīng)用戶的動(dòng)作,事件是如何被處理的。在這一些列過程中,我們將探索很多Web應(yīng)用的基礎(chǔ)概念,例如DOM(Web頁面的一種結(jié)構(gòu)化表示方式)和事件循環(huán)(它決定了應(yīng)用如何處理事件)。讓我們開始學(xué)習(xí)吧!
你知道嗎?
瀏覽器是否總是會(huì)根據(jù)給定的HTML來渲染頁面呢? Web應(yīng)用一次能處理多少個(gè)事件? 為什么瀏覽器使用事件隊(duì)列來處理事件?1.1 生命周期概覽典型客戶端Web應(yīng)用的生命周期從用戶在瀏覽器地址欄輸入一串URL,或單擊一個(gè)鏈接開始。例如,我們想去Google的主頁查找一個(gè)術(shù)語。首先我們輸入了URL,www.google.com,其過程如圖1.1所示。
圖1.1 客戶端Web應(yīng)用的周期從用戶指定某個(gè)網(wǎng)站地址(或單擊某個(gè)鏈接)開始,
其由兩個(gè)步驟組成:頁面構(gòu)建和事件處理
從用戶的角度來說,瀏覽器構(gòu)建了發(fā)送至服務(wù)器(序號(hào)2)的請(qǐng)求,該服務(wù)器處理了請(qǐng)求(序號(hào)3)并形成了一個(gè)通常由HTML、CSS和Java代碼所組成的響應(yīng)。當(dāng)瀏覽器接收了響應(yīng)(序號(hào)4)時(shí),我們的客戶端應(yīng)用開始了它的生命周期。 由于客戶端Web應(yīng)用是圖形用戶界面(GUI)應(yīng)用,其生命周期與其他的GUI應(yīng)用相似(例如標(biāo)準(zhǔn)的桌面應(yīng)用或移動(dòng)應(yīng)用),其執(zhí)行步驟如下所示:
1.頁面構(gòu)建——?jiǎng)?chuàng)建用戶界面;
2.事件處理——進(jìn)入循環(huán)(序號(hào)5)從而等待事件(序號(hào)6)的發(fā)生,發(fā)生后調(diào)用事件處理器。
應(yīng)用的生命周期隨著用戶關(guān)掉或離開頁面(序號(hào)7)而結(jié)束?,F(xiàn)在讓我們一起看一個(gè)簡單的示例程序:每當(dāng)用戶移動(dòng)鼠標(biāo)或單擊頁面就會(huì)顯示一條消息。本文會(huì)始終使用這個(gè)示例,如清單1.1所示。
清單 1.1 一個(gè)帶有GUI的Web應(yīng)用小程序,其描述了對(duì)事件的響應(yīng)
1<!DOCTYPE html>
2<html>
3 <head>
4 <title>Web app lifecycle</title>
5 <style>
6 #first { color: green;}
7 #second { color: red;}
8 </style>
9 </head>
10 <body>
11 <ul id="first"> </ul>
1213 <>
14 function addMessage(element, message){
15 var messageElement = document.("li");
16 messageElement.textContent = message;
17 element.(messageElement);
18 } ?--- 定義一個(gè)函數(shù)用于向一個(gè)元素增加一條信息
19 var first = document.getElementById("first");
20 addMessage(first, "Page loading");
21 </>
2223 <ul id="second"> </ul>
2425 <>
26 document.body.addEventListener("mousemove", function() { ?--- 為body附上鼠標(biāo)移動(dòng)事件處理函數(shù)
27 var second = document.getElementById("second");
28 addMessage(second, "Event: mousemove");
29 });
30 document.body.addEventListener("click", function(){ ?---
31 var second = document.getElementById("second");
32 addMessage(second, "Event: click");
33 });
34 </>
35 </body>
36</html>
清單1.1首先定義了兩條CSS 規(guī)則,即#first和#second,其指定了ID為first和second兩個(gè)元素的文字顏色(從而使我們方便地區(qū)分兩者)。隨后用first這個(gè)id定義了一個(gè)列表元素:1<ul id="first"></ul>然后定義一個(gè)addMessage函數(shù),每當(dāng)調(diào)用該函數(shù)都會(huì)創(chuàng)建一個(gè)新的列表項(xiàng)元素,為其設(shè)置文字內(nèi)容,然后將其附加到一個(gè)現(xiàn)有的元素上:
1function addMessage(element, message){
2 var messageElement = document.("li");
3 messageElement.textContent = message;
4 element.(messageElement);
5}
如下所示,通過使用內(nèi)置的方法getElementById來從文檔中獲取ID為first的元素,然后為該元素添加一條信息,用于告知頁面正在加載中:
1var first = document.getElementById("first");
2addMessage(first, "Page loading");
然后我們又定義了一個(gè)列表元素,這次給該列表賦予的ID屬性為second:1<ul id="second"></ul>最后將這兩個(gè)事件處理器附加到Web頁面的body上。每當(dāng)用戶移動(dòng)鼠標(biāo),鼠標(biāo)移動(dòng)事件處理器就會(huì)被執(zhí)行,然后該處理器調(diào)用addMessage方法,為第二個(gè)列表元素加上一句話“Event: mousemove”。
1document.body.addEventListener("mousemove", function() {
2 var second = document.getElementById("second");
3 addMessage(second, "Event: mousemove");
4});
我們還注冊(cè)了一個(gè)單擊事件處理器,每當(dāng)用戶單擊頁面就會(huì)輸出該消息“Event: click”,并添加至第二個(gè)列表元素中。
1document.body.addEventListener("click", function(){
2 var second = document.getElementById("second");
3 addMessage(second, "Event: click");
4});
該應(yīng)用的運(yùn)行結(jié)果和交互如圖1.2所示。
我們還會(huì)用這個(gè)例子來展示W(wǎng)eb應(yīng)用生命周期階段之間的不同之處。讓我們從頁面構(gòu)建階段開始講起。
圖1.2 清單1.1中的代碼運(yùn)行后,用戶的動(dòng)作會(huì)被記錄為消息
1.2 頁面構(gòu)建階段當(dāng)Web應(yīng)用能被展示或交互之前,其頁面必須根據(jù)服務(wù)器獲取的響應(yīng)(通常是HTML、CSS和Java代碼)來構(gòu)建。頁面構(gòu)建階段的目標(biāo)是建立Web應(yīng)用的UI,其主要包括兩個(gè)步驟:
1.解析HTML代碼并構(gòu)建文檔對(duì)象模型 (DOM);
2.執(zhí)行Java代碼。
步驟1會(huì)在瀏覽器處理HTML節(jié)點(diǎn)的過程中執(zhí)行,步驟二會(huì)在HTML解析到一種特殊節(jié)點(diǎn)——腳本節(jié)點(diǎn)(包含或引用Java代碼的節(jié)點(diǎn))時(shí)執(zhí)行。頁面構(gòu)建階段中,這兩個(gè)步驟會(huì)交替執(zhí)行多次,如圖1.3所示。
圖1.3 頁面構(gòu)建階段從瀏覽器接收頁面代碼開始。其執(zhí)行分為兩個(gè)步驟:HTML解析和DOM構(gòu)建,以及Java代碼的執(zhí)行
1.2.1 HTML解析和DOM構(gòu)建頁面構(gòu)建階段始于瀏覽器接收HTML代碼時(shí),該階段為瀏覽器構(gòu)建頁面UI的基礎(chǔ)。通過解析收到的HTML代碼,構(gòu)建一個(gè)個(gè)HTML元素,構(gòu)建DOM。在這種對(duì)HTML結(jié)構(gòu)化表示的形式中,每個(gè)HTML元素都被當(dāng)作一個(gè)節(jié)點(diǎn)。如圖1.4所示,直到遇到第一個(gè)腳本元素,示例頁面都在構(gòu)建DOM。
注意圖1.4中的節(jié)點(diǎn)是如何組織的,除了第一個(gè)節(jié)點(diǎn)——html根節(jié)點(diǎn)(序號(hào)1)以外,所有節(jié)點(diǎn)都只有一個(gè)父節(jié)點(diǎn)。例如,head節(jié)點(diǎn)(序號(hào)2)父節(jié)點(diǎn)為html節(jié)點(diǎn)(序號(hào)1)。同時(shí),一個(gè)節(jié)點(diǎn)可以有任意數(shù)量的子節(jié)點(diǎn)。例如,html節(jié)點(diǎn)(序號(hào)1)有兩個(gè)孩子節(jié)點(diǎn):head節(jié)點(diǎn)(序號(hào)2)和body節(jié)點(diǎn)。同一個(gè)元素的孩子節(jié)點(diǎn)被稱作兄弟節(jié)點(diǎn)。(head節(jié)點(diǎn)和body節(jié)點(diǎn)是兄弟節(jié)點(diǎn))盡管DOM是根據(jù)HTML來創(chuàng)建的,兩者緊密聯(lián)系,但需要強(qiáng)調(diào)的是,它們兩者并不相同。你可以把HTML代碼看作瀏覽器頁面UI構(gòu)建初始DOM的藍(lán)圖。為了正確構(gòu)建每個(gè)DOM,瀏覽器還會(huì)修復(fù)它在藍(lán)圖中發(fā)現(xiàn)的問題。讓我們看下面的示例,如圖1.5所示。
圖1.4 當(dāng)瀏覽器遇到第一個(gè)腳本元素時(shí),它已經(jīng)用多個(gè)HTML元素(右邊的節(jié)點(diǎn))創(chuàng)建了一個(gè)DOM樹
圖1.5展示了一個(gè)簡單的錯(cuò)誤HTML代碼示例,頁面中的head元素中錯(cuò)誤地包含了一個(gè)paragraph元素。head元素的一般用途是展示頁面的總體信息,例如,頁面標(biāo)題、字符編碼和外部樣式腳本,而不是用于類似本例中的定義頁面內(nèi)容。故而這里出現(xiàn)了錯(cuò)誤,瀏覽器靜默修復(fù)錯(cuò)誤,將段落元素放入了理應(yīng)放置頁面內(nèi)容的body元素中,構(gòu)造了正確的DOM(如圖1.5右側(cè))。
圖1.5 瀏覽器修正了錯(cuò)誤的HTML代碼
HTML規(guī)范和DOM規(guī)范
當(dāng)前HTML的版本是HTML5, 可以通過 https://html.spec.whatwg.org/ 查看當(dāng)前版本中有哪些可用特性。你若需要更易讀的文檔,我們向你推薦Mozilla的HTML5指南,可通過https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5 查看。而另一方面,DOM的發(fā)展則相對(duì)緩慢。當(dāng)前的DOM版本是DOM3,可以通過 https://dom.spec.whatwg.org/ 查看該標(biāo)準(zhǔn)。同樣,Mozilla也為DOM提供了一份報(bào)告,可以通過https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model 進(jìn)行查看。在頁面構(gòu)建階段,瀏覽器會(huì)遇到特殊類型的HTML元素——腳本元素,該元素用于包括Java代碼。每當(dāng)解析到腳本元素時(shí),瀏覽器就會(huì)停止從HTML構(gòu)建DOM,并開始執(zhí)行Java代碼。
1.2.2 執(zhí)行Java代碼所有包含在腳本元素中的Java代碼由瀏覽器的Java引擎執(zhí)行,例如,F(xiàn)irefox的Spidermonkey引擎, Chrome 和 Opera和 V8引擎和Edge的(IE的)Chakra引擎。由于代碼的主要目的是提供動(dòng)態(tài)頁面,故而瀏覽器通過全局對(duì)象提供了一個(gè)API 使Java引擎可以與之交互并改變頁面內(nèi)容。
Java中的全局對(duì)象瀏覽器暴露給Java 引擎的主要全局對(duì)象是window對(duì)象,它代表了包含著一個(gè)頁面的窗口。window對(duì)象是獲取所有其他全局對(duì)象、全局變量(甚至包含用戶定義對(duì)象)和瀏覽器API的訪問途徑。全局window對(duì)象最重要的屬性是document,它代表了當(dāng)前頁面的DOM。通過使用這個(gè)對(duì)象,Java代碼就能在任何程度上改變DOM,包括修改或移除現(xiàn)存的節(jié)點(diǎn),以及創(chuàng)建和插入新的節(jié)點(diǎn)。
讓我們看看清單1.1中所示的代碼片段:
1var first = document.getElementById("first");
這個(gè)示例中使用全局document對(duì)象來通過ID選擇一個(gè)元素,然后將該元素賦值給變量first。隨后我們就能在該元素上用Java代碼來對(duì)其作各種操作,例如改變其文字內(nèi)容,修改其屬性,動(dòng)態(tài)創(chuàng)建和增加新孩子節(jié)點(diǎn),甚至可以從DOM上將該元素移除。
瀏覽器API 本文自始至終都會(huì)描述一系列瀏覽器內(nèi)置對(duì)象和函數(shù)(例如,window和document)。不過很遺憾,瀏覽器所支持的全部特性已經(jīng)超出本文探討Java的范圍。幸好Mozilla為我們提供支持,通過https://developer.mozilla.org/en-US/docs/Web/API,你可以查找到WebAPI接口的當(dāng)前狀態(tài)。對(duì)瀏覽器提供的基本全局對(duì)象有了基本了解后,我們可以開始看看Java代碼中兩種不同類型的定義方式。
Java代碼的不同類型我們已能大致區(qū)分出兩種不同類型的Java代碼:全局代碼和函數(shù)代碼。清單1.2會(huì)幫你理解這兩種類型代碼的不同。
清單1.2 Java全局代碼和函數(shù)代碼
1<>
2 function addMessage(element, message){
3 var messageElement = document.("li");
4 messageElement.textContent = message; ?--- 函數(shù)代碼指的是包含在函數(shù)中的代碼
5 element.(messageElement);
6 }
7 var first = document.getElementById("first");
8 addMessage(first, "Page loading"); ?--- 全局代碼指的是位于函數(shù)之外的代碼
9</>
這兩類代碼的主要不同是它們的位置:包含在函數(shù)內(nèi)的代碼叫作函數(shù)代碼,而在所有函數(shù)以外的代碼叫作全局代碼。
這兩種代碼在執(zhí)行中也有不同(隨后你將能看到一些其他的不同)。全局代碼由Java引擎(后續(xù)會(huì)作更多解釋)以一種直接的方式自動(dòng)執(zhí)行,每當(dāng)遇到這樣的代碼就一行接一行地執(zhí)行。例如,在清單1.2中,定義在addMessage函數(shù)中的全局代碼片段使用內(nèi)置方法getElementById來獲取ID為first的元素,然后再調(diào)用addMessage函數(shù),如圖1.6所示,每當(dāng)遇到這些代碼就會(huì)一個(gè)個(gè)執(zhí)行。
圖1.6 執(zhí)行Java代碼時(shí)的程序執(zhí)行流
反過來,若想執(zhí)行函數(shù)代碼,則必須被其他代碼調(diào)用:既可以是全局代碼(例如,由于全局代碼的執(zhí)行過程中執(zhí)行了addMessage函數(shù)代碼,所以addMessage函數(shù)得意被調(diào)用),也可以是其他函數(shù),還可以由瀏覽器調(diào)用(后續(xù)會(huì)作更多解釋)。
在頁面構(gòu)建階段執(zhí)行Java代碼當(dāng)瀏覽器在頁面構(gòu)建階段遇到了腳本節(jié)點(diǎn),它會(huì)停止HTML到DOM的構(gòu)建,轉(zhuǎn)而開始執(zhí)行Java代碼,也就是執(zhí)行包含在腳本元素的全局Java 代碼 (以及由全局代碼執(zhí)行中調(diào)用的函數(shù)代碼)。讓我們看看清單1.1中的示例。
圖1.7顯示了在全局Java代碼被執(zhí)行后DOM的狀態(tài)。讓我們仔細(xì)看看這個(gè)執(zhí)行過程。首先定義了一個(gè)addMessage函數(shù):
1<>
2 function addMessage(element, message){
3 var messageElement = document.("li");
4 messageElement.textContent = message; ?--- 函數(shù)代碼指的是包含在函數(shù)中的代碼
5 element.(messageElement);
6 }
7 var first = document.getElementById("first");
8 addMessage(first, "Page loading"); ?--- 全局代碼指的是位于函數(shù)之外的代碼
9</>
然后通過全局document對(duì)象上的getElementById方法從DOM上獲取了一個(gè)元素:1var first = document.getElementById("first");
這段代碼后緊跟著對(duì)函數(shù)addMessage 的調(diào)用:
1addMessage(first, "Page loading");
這條代碼創(chuàng)建了一個(gè)新的li元素,然后修改了其中的文字內(nèi)容,最后將其插入DOM中。
圖1.7 當(dāng)執(zhí)行了腳本元素中的Java代碼后,頁面中的DOM結(jié)構(gòu)
這個(gè)例子中,Java通過創(chuàng)建一個(gè)新元素并將其插入DOM節(jié)點(diǎn)修改了當(dāng)前的DOM結(jié)構(gòu)。一般來說,Java 代碼能夠在任何程度上修改DOM結(jié)構(gòu):它能創(chuàng)建新的接單或移除現(xiàn)有DOM節(jié)點(diǎn)。但它依然不能做某些事情,例如選擇和修改還沒被創(chuàng)建的節(jié)點(diǎn)。這就是為什么要把元素放在頁面底部的原因。如此一來,我們就不必?fù)?dān)心是否某個(gè)HTML元素已經(jīng)加載為DOM。
一旦Java引擎執(zhí)行到了腳本元素中(如圖1.5中的addMessage函數(shù)返回)Java代碼的最后一行,瀏覽器就退出了Java執(zhí)行模式,并繼將余下的HTML構(gòu)建為DOM節(jié)點(diǎn)。在這期間,如果瀏覽器再次遇到腳本元素,那么從HTML到DOM的構(gòu)建再次暫停,Java運(yùn)行環(huán)境開始執(zhí)行余下的Java代碼。需要重點(diǎn)注意:Java應(yīng)用在此時(shí)依然會(huì)保持著全局狀態(tài)。所有在某個(gè)Java代碼執(zhí)行期間用戶創(chuàng)建的全局變量都能正常地被其他腳本元素中的Java代碼所訪問到。其原因在于全局window對(duì)象會(huì)存在于整個(gè)頁面的生存期之間,在它上面存儲(chǔ)著所有的Java變量。只要還有沒處理完的HTML元素和沒執(zhí)行完的Java代碼,下面兩個(gè)步驟都會(huì)一直交替執(zhí)行。
1.將HTML構(gòu)建為DOM。
2.執(zhí)行Java代碼。
最后,當(dāng)瀏覽器處理完所有HTML元素后,頁面構(gòu)建階段就結(jié)束了。隨后瀏覽器就會(huì)進(jìn)入應(yīng)用生命周期的第二部分:事件處理。
1.3 事件處理客戶端Web 應(yīng)用是一種GUI應(yīng)用,也就是說這種應(yīng)用會(huì)對(duì)不同類型的事件作響應(yīng),如鼠標(biāo)移動(dòng)、單擊和鍵盤按壓等。因此,在頁面構(gòu)建階段執(zhí)行的Java代碼,除了會(huì)影響全局應(yīng)用狀態(tài)和修改DOM外,還會(huì)注冊(cè)事件監(jiān)聽器(或處理器)。這類監(jiān)聽器會(huì)在事件發(fā)生時(shí),由瀏覽器調(diào)用執(zhí)行。有了這些事件處理器,我們的應(yīng)用也就有了交互能力。在詳細(xì)探討注冊(cè)事件處理器之前,讓我們先從頭到尾看一遍事件處理器的總體 思想。
1.3.1 事件處理器概覽瀏覽器執(zhí)行環(huán)境的核心思想基于:同一時(shí)刻只能執(zhí)行一個(gè)代碼片段,即所謂的單線程執(zhí)行模型。想象一下在銀行柜臺(tái)前排隊(duì),每個(gè)人進(jìn)入一支隊(duì)伍等待叫號(hào)并“處理”。但Java則只開啟了一個(gè)營業(yè)柜臺(tái)!每當(dāng)輪到某個(gè)顧客時(shí)(某個(gè)事件),只能處理該位顧客。
你所需要的僅僅是一個(gè)在營業(yè)柜臺(tái)(所有人都在這個(gè)柜臺(tái)排隊(duì)?。┑穆殕T為你處理工作,幫你訂制全年的財(cái)務(wù)計(jì)劃。當(dāng)一個(gè)事件抵達(dá)后,瀏覽器需要執(zhí)行相應(yīng)的事件處理函數(shù)。這里不保證用戶總會(huì)極富耐心地等待很長時(shí)間,直到下一個(gè)事件觸發(fā)。所以,瀏覽器需要一種方式來跟蹤已經(jīng)發(fā)生但尚未處理的事件。為實(shí)現(xiàn)這個(gè)目標(biāo),瀏覽器使用了事件隊(duì)列,如圖1.8所示。
所有已生成的事件(無論是用戶生成的,例如鼠標(biāo)移動(dòng)或鍵盤按壓,還是服務(wù)器生成的,例如Ajax事件)都會(huì)放在同一個(gè)事件隊(duì)列中,以它們被瀏覽器檢測(cè)到的順序排列。如圖1.8的中部所示,事件處理的過程可以描述為一個(gè)簡單的流程圖。
瀏覽器檢查事件隊(duì)列頭; 如果瀏覽器沒有在隊(duì)列中檢測(cè)到事件,則繼續(xù)檢查; 如果瀏覽器在隊(duì)列頭中檢測(cè)到了事件,則取出該事件并執(zhí)行相應(yīng)的事件處理器(如果存在)。在這個(gè)過程中,余下的事件在事件隊(duì)列中耐心等待,直到輪到它們被處理。由于一次只能處理一個(gè)事件,所以我們必須格外注意處理所有事件的總時(shí)間。執(zhí)行需要花費(fèi)大量時(shí)間執(zhí)行的事件處理函數(shù)會(huì)導(dǎo)致Web應(yīng)用無響應(yīng)!(如果聽起來還不太明確,不要擔(dān)心,以后我們還會(huì)學(xué)習(xí)事件循環(huán),再看看它是如何損害Web應(yīng)用在感受上的性能的)。
圖1.8 客戶端Web應(yīng)用的周期從用戶指定某個(gè)網(wǎng)站地址(或單擊某個(gè)鏈接)開始。
其由兩個(gè)步驟組成:頁面構(gòu)建和事件處理
重點(diǎn)注意瀏覽器在這個(gè)過程中的機(jī)制,其放置事件的隊(duì)列是在頁面構(gòu)建階段和事件處理階段以外的。這個(gè)過程對(duì)于決定事件何時(shí)發(fā)生并將其推入事件隊(duì)列很重要,這個(gè)過程不會(huì)參與事件處理線程。
事件是異步的事件可能會(huì)以難以預(yù)計(jì)的時(shí)間和順序發(fā)生(強(qiáng)制用戶以某個(gè)順序按鍵或單擊是非常奇怪的)。我們對(duì)事件的處理,以及處理函數(shù)的調(diào)用是異步的。如下類型的事件會(huì)在其他類型事件中發(fā)生。
瀏覽器事件,例如當(dāng)頁面加載完成后或無法加載時(shí); 網(wǎng)絡(luò)事件,例如來自服務(wù)器的響應(yīng)(Ajax事件和服務(wù)器端事件); 用戶事件,例如鼠標(biāo)單擊、鼠標(biāo)移動(dòng)和鍵盤事件; 計(jì)時(shí)器事件,當(dāng)timeout時(shí)間到期或又觸發(fā)了一次時(shí)間間隔。Web應(yīng)用的Java代碼中,大部分內(nèi)容都是對(duì)上述事件的處理!
事件處理的概念是Web應(yīng)用的核心,你在本文中的例子會(huì)反復(fù)看到:代碼的提前建立是為了在之后的某個(gè)時(shí)間點(diǎn)執(zhí)行。除了全局代碼,頁面中的大部分代碼都將作為某個(gè)事件的結(jié)果執(zhí)行。
在事件能被處理之前,代碼必須要告知瀏覽器我們要處理特定事件。接下來看看如何注冊(cè)事件處理器。
1.3.2 注冊(cè)事件處理器前面已經(jīng)講過了,事件處理器是當(dāng)某個(gè)特定事件發(fā)生后我們希望執(zhí)行的函數(shù)。為了達(dá)到這個(gè)目標(biāo),我們必須告知瀏覽器我們要處理哪個(gè)事件。這個(gè)過程叫作注冊(cè)事件處理器。在客戶端Web應(yīng)用中,有兩種方式注冊(cè)事件。
通過把函數(shù)賦給某個(gè)特殊屬性; 通過使用內(nèi)置addEventListener方法。例如,編寫如下代碼,將一個(gè)函數(shù)賦值給window對(duì)象上的某個(gè)特定屬性:
1window. = function(){};
通過這種方式,事件處理器就會(huì)注冊(cè)到load事件上(當(dāng)DOM已經(jīng)就緒并全部構(gòu)建完成,就會(huì)觸發(fā)這個(gè)事件)。(如果你對(duì)賦值操作符右邊的記法有些困惑,不要擔(dān)心,隨后的章節(jié)中我們會(huì)細(xì)致地講述函數(shù))類似,如果我們想要為在文檔中body元素的單擊事件注冊(cè)處理器,我們可以輸入下述代碼:
1document.body.onclick = function(){};
把函數(shù)賦值給特殊屬性是一種簡單而直接的注冊(cè)事件處理器方式。但是,我們并不推薦你使用這種方式來注冊(cè)事件處理器,這是因?yàn)檫@種做法會(huì)帶來缺點(diǎn):對(duì)于某個(gè)事件只能注冊(cè)一個(gè)事件處理器。也就是說,一不小心就會(huì)將上一個(gè)事件處理器改寫掉。幸運(yùn)的是,還有一種替代方案:addEventListener方法讓我們能夠注冊(cè)盡可能多的事件,只要我們需要。如下清單使用了清單1.3中的示例,向你展示這種便捷的用法。
清單1.3 注冊(cè)事件處理器
1<>
2 document.body.addEventListener("mousemove", function() { ?--- 為mousemove事件注冊(cè)處理器
3 var second = document.getElementById("second");
4 addMessage(second, "Event: mousemove");
5 });
6 document.body.addEventListener("click", function(){ ?--- 為click事件注冊(cè)處理器
7 var second = document.getElementById("second");
8 addMessage(second, "Event: click");
9 });
10</>
本例中使用了某個(gè)HTML元素上的內(nèi)置的方法addEventListener,并在函數(shù)中指定了事件的類型(mousemove事件或click)和事件的處理器。這意味著當(dāng)鼠標(biāo)從頁面上移動(dòng)后,瀏覽器會(huì)調(diào)用該函數(shù)添加一條消息到ID位second的list元素上,"Event: mousemove"(類似,當(dāng)body被單擊時(shí),"Event: click"也會(huì)被添加到同樣的元素上)。 現(xiàn)在你學(xué)習(xí)了如何創(chuàng)建事件處理器,讓我們回憶下前面看到的簡單流程圖,然后仔細(xì)看看事件是如何被處理的。
1.3.3 處理事件事件處理背后的的主要思想是:當(dāng)事件發(fā)生時(shí),瀏覽器調(diào)用相應(yīng)的事件處理器。如前面提到的,由于單線程執(zhí)行模型,所以同一時(shí)刻只能處理一個(gè)事件。任何后面的事件都只能在當(dāng)前事件處理器完全結(jié)束執(zhí)行后才能被處理!
讓我們回到清單1.1中的應(yīng)用。圖1.9展示了在用戶快速移動(dòng)和單擊鼠標(biāo)時(shí)的執(zhí)行情況。
讓我們看看這里發(fā)生了什么。為了響應(yīng)用戶的動(dòng)作,瀏覽器把鼠標(biāo)移動(dòng)和單擊事件以它們發(fā)生的次序放入事件隊(duì)列:第一個(gè)是鼠標(biāo)移動(dòng)事件,第二個(gè)是單擊事件序號(hào)1。
在事件處理階段中,事件循環(huán)會(huì)檢查隊(duì)列,其發(fā)現(xiàn)隊(duì)列的前面有一個(gè)鼠標(biāo)移動(dòng)事件,然后執(zhí)行了相應(yīng)的事件處理器序號(hào)2。當(dāng)鼠標(biāo)移動(dòng)事件處理器處理完畢后,輪到了等待在隊(duì)列中的單擊事件。當(dāng)鼠標(biāo)移動(dòng)事件處理器函數(shù)的最后一行代碼執(zhí)行完畢后,Java引擎退出事件處理器函數(shù),鼠標(biāo)移動(dòng)事件完整地處理了序號(hào)3,事件循環(huán)再次檢查隊(duì)列。這一次,在隊(duì)列的最前面,事件循環(huán)發(fā)現(xiàn)了鼠標(biāo)單擊事件并處理了該事件。一旦單擊處理器執(zhí)行完成,隊(duì)列中不再有新的事件,事件循環(huán)就會(huì)繼續(xù)循環(huán),等待處理新到來的事件。這個(gè)循環(huán)會(huì)一直執(zhí)行到用戶關(guān)閉了Web應(yīng)用。
圖1.9 兩個(gè)事件——鼠標(biāo)移動(dòng)和單擊中的事件處理階段示例
現(xiàn)在我們有了個(gè)總體的認(rèn)識(shí),理解了事件處理階段的所有步驟。讓我們看看這個(gè)過程是如何影響DOM的(如圖1.10所示)。執(zhí)行鼠標(biāo)移動(dòng)處理器時(shí)會(huì)選擇第二個(gè)列表元素,其ID為second。
圖1.10 當(dāng)鼠標(biāo)移動(dòng)和鼠標(biāo)點(diǎn)擊事件都處理完成后,實(shí)例應(yīng)用的DOM樹結(jié)構(gòu)
然后通過使用addMessage,使用文字“Event: mousemove”添加了一個(gè)新的列表項(xiàng)元素序號(hào)1。一旦鼠標(biāo)移動(dòng)處理器結(jié)束后,事件循環(huán)執(zhí)行單擊事件處理器,從而創(chuàng)建了另一個(gè)列表元素序號(hào)2,并附加在ID為second的第二個(gè)列表元素后。
對(duì)Web應(yīng)用客戶端的生命周期有了清晰的理解后,本文的下一部分,我們會(huì)開始聚焦于Java語言,理清函數(shù)的來龍去脈。
1.4 小結(jié)瀏覽器接收的HTML代碼用作創(chuàng)建DOM的藍(lán)圖,它是客戶端Web應(yīng)用結(jié)構(gòu)的內(nèi)部展示階段。 我們使用Java代碼來動(dòng)態(tài)地修改DOM以便給Web應(yīng)用帶來動(dòng)態(tài)行為。 客戶端Web應(yīng)用的執(zhí)行分為兩個(gè)階段。頁面構(gòu)建代碼是用于創(chuàng)建DOM的,而全局Java代碼是遇到節(jié)點(diǎn)時(shí)執(zhí)行的。在這個(gè)執(zhí)行過程中,Java代碼能夠以任意程度改變當(dāng)前的DOM,還能夠注冊(cè)事件處理器——事件處理器是一種函數(shù),當(dāng)某個(gè)特定事件(例如,一次鼠標(biāo)單擊或鍵盤按壓)發(fā)生后會(huì)被執(zhí)行。注冊(cè)事件處理器很容易:使用內(nèi)置的addEventListener方法。 事件處理——在同一時(shí)刻,只能處理多個(gè)不同事件中的一個(gè),處理順序是事件生成的順序。事件處理階段大量依賴事件隊(duì)列,所有的事件都以其出現(xiàn)的順序存儲(chǔ)在事件隊(duì)列中。事件循環(huán)會(huì)檢查實(shí)踐隊(duì)列的隊(duì)頭,如果檢測(cè)到了一個(gè)事件,那么相應(yīng)的事件處理器就會(huì)被調(diào)用。1.5 練習(xí)1.客戶端Web應(yīng)用的兩個(gè)生命周期階段是什么?
2.相比將事件處理器賦值給某個(gè)特定元素的屬性上,使用addEventListener方法來注冊(cè)事件處理器的優(yōu)勢(shì)是什么?
3.Java引擎在同一時(shí)刻能處理多少個(gè)事件?
4.事件隊(duì)列中的事件是以什么順序處理的?
1 </div>
2 <p id="copyright-declare">
3 本文僅用于學(xué)習(xí)和交流目的,不代表異步社區(qū)觀點(diǎn)。非商業(yè)轉(zhuǎn)載請(qǐng)注明作譯者、出處,并保留本文的原始鏈接。
4 </p>
5 </div>
本文摘自《Java忍者秘籍 第2版》
《Java忍者秘籍 第2版》
[美] John,Resig(萊西格),Bear,Bibeault(貝比奧特),Josip ... 著
點(diǎn)擊封面購買紙書
Java語言非常重要,相關(guān)的技術(shù)圖書也很多,但至今市面沒有一本對(duì)Java語言的重要部分(函數(shù)、閉包和原型)進(jìn)行深入、全面介紹的圖書,也沒有一本講述跨瀏覽器代碼編寫的圖書。而本書彌補(bǔ)了這一空缺,是由jQuery庫創(chuàng)始人編寫的一本深入剖析Java語言的書?!禞ava 忍者秘籍(第2版)》使用實(shí)際的案例清晰地詮釋每一個(gè)核心概念和技術(shù)。本書向讀者介紹了如何掌握 Java 核心的概念,諸如函數(shù)、閉包、對(duì)象、原型和 promise,同時(shí)還介紹了 Java API, 包括 DOM、事件和計(jì)時(shí)器。你將學(xué)會(huì)測(cè)試、跨瀏覽器開發(fā),所有這些都是高級(jí)Java開發(fā)者應(yīng)該掌握的技能。
延伸推薦2018年1月重磅新書小學(xué)生開始學(xué)Python,最接近AI的編程語言:安利一波Python書單政策升溫:大家都在學(xué)大數(shù)據(jù),一大波好書推薦一本基于Python語言的Selenium自動(dòng)化測(cè)試書 8本新書,送出一本你喜歡的AI經(jīng)典書單| 入門人工智能該讀哪些書?點(diǎn)擊關(guān)鍵詞閱讀更多新書:Python|機(jī)器學(xué)習(xí)|Kotlin|Java|移動(dòng)開發(fā)|機(jī)器人|有獎(jiǎng)活動(dòng)|Web前端|書單
在“異步圖書”后臺(tái)回復(fù)“關(guān)注”,即可免費(fèi)獲得2000門在線視頻課程;推薦朋友關(guān)注根據(jù)提示獲取贈(zèng)書鏈接,免費(fèi)得異步圖書一本。趕緊來參加哦!
掃一掃上方二維碼,回復(fù)“關(guān)注”參與活動(dòng)!
點(diǎn)擊閱讀原文購買《Java忍者秘籍 第2版》
閱讀原文
內(nèi)部類不是很好理解,但說白了其實(shí)也就是一個(gè)類中還包含著另外一個(gè)類。
如同一個(gè)人是由大腦、肢體、器官等身體結(jié)果組成,而內(nèi)部類相當(dāng)于其中的某個(gè)器官之一,例如心臟:它也有自己的屬性和行為(血液、跳動(dòng))。
顯然,此處不能單方面用屬性或者方法表示一個(gè)心臟,而需要一個(gè)類。
而心臟又在人體當(dāng)中,正如同是內(nèi)部類在外部內(nèi)當(dāng)中。
實(shí)例1:內(nèi)部類的基本結(jié)構(gòu)
//外部類 class Out { private int age = 12; //內(nèi)部類 class In { public void print() { System.out.println(age); } } } public class Demo { public static void main(String[] args) { Out.In in = new Out().new In(); in.print(); //或者采用下種方式訪問 /* Out out = new Out(); Out.In in = out.new In(); in.print(); */ } }運(yùn)行結(jié)果:12
從上面的例子不難看出,內(nèi)部類其實(shí)嚴(yán)重破壞了良好的代碼結(jié)構(gòu),但為什么還要使用內(nèi)部類呢?
因?yàn)閮?nèi)部類可以隨意使用外部類的成員變量(包括私有)而不用生成外部類的對(duì)象,這也是內(nèi)部類的唯一優(yōu)點(diǎn)。
如同心臟可以直接訪問身體的血液,而不是通過醫(yī)生來抽血
程序編譯過后會(huì)產(chǎn)生兩個(gè).class文件,分別是Out.class和Out$In.class。
其中$代表了上面程序中Out.In中的那個(gè) 。
Out.In in = new Out().new In()可以用來生成內(nèi)部類的對(duì)象,這種方法存在兩個(gè)小知識(shí)點(diǎn)需要注意:
1、開頭的Out是為了標(biāo)明需要生成的內(nèi)部類對(duì)象在哪個(gè)外部類當(dāng)中;
2、必須先有外部類的對(duì)象才能生成內(nèi)部類的對(duì)象,因?yàn)閮?nèi)部類的作用就是為了訪問外部類中的成員變量。
實(shí)例2:內(nèi)部類中的變量訪問形式
class Out { private int age = 12; class In { private int age = 13; public void print() { int age = 14; System.out.println("局部變量:" + age); System.out.println("內(nèi)部類變量:" + this.age); System.out.println("外部類變量:" + Out.this.age); } } } public class Demo { public static void main(String[] args) { Out.In in = new Out().new In(); in.print(); } }運(yùn)行結(jié)果:局部變量:14;內(nèi)部類變量:13;外部類變量:12
從實(shí)例1中可以發(fā)現(xiàn),內(nèi)部類在沒有同名成員變量和局部變量的情況下,內(nèi)部類會(huì)直接訪問外部類的成員變量,而無需指定Out.this.屬性名。
否則,內(nèi)部類中的局部變量會(huì)覆蓋外部類的成員變量。
而訪問內(nèi)部類本身的成員變量可用this.屬性名,訪問外部類的成員變量需要使用Out.this.屬性名。
實(shí)例3:靜態(tài)內(nèi)部類
class Out { private static int age = 12; static class In { public void print() { System.out.println(age); } } } public class Demo { public static void main(String[] args) { Out.In in = new Out.In(); in.print(); } }運(yùn)行結(jié)果:12
可以看到,如果用static 將內(nèi)部內(nèi)靜態(tài)化,那么內(nèi)部類就只能訪問外部類的靜態(tài)成員變量,具有局限性。
其次,因?yàn)閮?nèi)部類被靜態(tài)化,因此Out.In可以當(dāng)做一個(gè)整體看,可以直接new 出內(nèi)部類的對(duì)象(通過類名訪問static,生不生成外部類對(duì)象都沒關(guān)系)。
實(shí)例4:私有內(nèi)部類
class Out { private int age = 12; private class In { public void print() { System.out.println(age); } } public void outPrint() { new In().print(); } } public class Demo { public static void main(String[] args) { //此方法無效 /* Out.In in = new Out().new In(); in.print(); */ Out out = new Out(); out.outPrint(); } }運(yùn)行結(jié)果:12
如果一個(gè)內(nèi)部類只希望被外部類中的方法操作,那么可以使用private聲明內(nèi)部類。
上面的代碼中,我們必須在Out類里面生成In類的對(duì)象進(jìn)行操作,而無法再使用Out.In in = new Out().new In() 生成內(nèi)部類的對(duì)象。
也就是說,此時(shí)的內(nèi)部類只有外部類可控制。
如同是,我的心臟只能由我的身體控制,其他人無法直接訪問它。
實(shí)例5:方法內(nèi)部類
class Out { private int age = 12; public void Print(final int x) { class In { public void inPrint() { System.out.println(x); System.out.println(age); } } new In().inPrint(); } } public class Demo { public static void main(String[] args) { Out out = new Out(); out.Print(3); } }運(yùn)行結(jié)果:3;12
在上面的代碼中,我們將內(nèi)部類移到了外部類的方法中,然后在外部類的方法中再生成一個(gè)內(nèi)部類對(duì)象去調(diào)用內(nèi)部類方法。
如果此時(shí)我們需要往外部類的方法中傳入?yún)?shù),那么外部類的方法形參必須使用final定義。
至于final在這里并沒有特殊含義,只是一種表示形式而已。
最后,覺得不錯(cuò),大家一起分享給其他的同學(xué)吧
Java新人自學(xué)交流群:202250194