作????者 | (美)David M?Geary | 譯????者 | 李建森/等 |
---|---|---|---|
出版社 | 機械工業(yè)出版社 | 出版時間 | 2000年02月 |
頁????數 | 982 頁 | 定????價 | 108 元 |
裝????幀 | 平裝 | ISBN | 9787111077749 |
譯者序
序
前言
第一部分 Swing基礎
第1章 簡介
1.1Swing的歷史
1.2輕量組件與重量組件的比較
1.3Swing組件
1.3.1AWT的替代組件
1.3.2Swing增加的組件
1.4J組件
1.5Swing包概覽
1.6Swing與AWT
1.7開始學習
1.8Swing資源
1.9本章回顧
第2章 Swing的基本知識
2.1小應用程序與應用程序
2.1.1小應用程序
2.1.2JApplet類
2.1.3應用程序
2.1.4JFrame類
2.1.5小應用程序/應用程序的組合
2.2GJApp
2.3混合使用Swing組件和AWT組件
2.3.1層序
2.3.2Swing彈出式菜單
2.3.3滾動
2.3.4內部窗體
2.4Swing和線程
2.4.1Swing單線程設計的結果
2.4.2SwingUtilities 類的invokeLater
和invokeAndWait方法
2.5本章回顧
第3章 Swing組件的體系結構
3.1典型的“模型-視圖-控制器”體系
結構
3.1.1插入式視圖和控制器
3.1.2視圖更新
3.2SwingMVC
3.2.1Swing組件
3.2.2靜態(tài)認識
3.2.3動態(tài)認識
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調試圖形
4.1.5自動滾動
4.1.6工具提示
4.1.7鍵擊處理和客戶屬性
4.2JComponent類結構
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調試圖形
4.6自動滾動
4.7工具提示
4.7.1基于鼠標位置的工具提示
4.7.2工具提示的首選位置
4.7.3定制工具提示的行為
4.7.4定制工具提示的界面樣式
4.8鍵擊處理
4.9客戶屬性
4.10焦點管理
4.10.1JComponent的焦點屬性
4.10.2焦點管理器
4.11支持可訪問性
4.12本章回顧
第5章 邊框、圖標和動作
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替換內置邊框
5.1.9實現定制邊框
5.2圖標
5.2.1把圖標與組件相關聯
5.2.2在組件中共享圖標
5.2.3圖像圖標
5.2.4動畫的圖像圖標
5.3動作
5.3.1作為控制中心點的動作
5.3.2動作常量
5.4本章回顧
第6章 實用工具
6.1計時器
6.2事件監(jiān)聽器列表
6.3Swing實用工具
6.4Swing常量
6.5BoxLayout和Box 類
6.5.1BoxLayout類
6.5.2Box類
6.6進度監(jiān)視器
6.6.1ProgressMonitor
6.6.2Progress MonitorInputStream
6.7撤消/重復
6.7.1一個簡單的撤消/重復樣例
6.7.2UndoableEditSupport
6.7.3組合編輯
6.7.4UndoManager
6.7.5狀態(tài)編輯
6.8本章回顧
第7章 插入式界面樣式
7.1界面樣式結構
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章 標簽與按鈕
8.1JLabel與JButton
8.2JLabel
8.2.1內容排列
8.2.2文本的位置
8.2.3圖標/文本間隙
8.2.4許可狀態(tài)
8.2.5JLabel屬 性
8.2.6JLabel事件
8.2.7JLabel類總結
8.3按鈕
8.4JButton
8.4.1JButton屬性
8.4.2JButton事件
8.4.3JButton類總結
8.4.4AWT兼容
8.5本章回顧
第9章 反轉按鈕、復選框和單選鈕
9.1JToggleButton類
9.1.1JToggleButton屬性
9.1.2JToggleButton事件
9.1.3JToggleButton類總結
9.1.4AWT兼容
9.2按鈕組
9.3復選框
9.3.1JCheckBox屬性
9.3.2JCheckBox事件
9.3.3JCheckBox類總結
9.4單選鈕
9.4.1JRadioButton屬性
9.4.2JRadioButton 事件
9.4.3JRadioButton類總結
9.4.4AWT兼容
9.5本章回顧
第10章 菜單和工具條
10.1菜單、菜單欄和工具條
10.2菜單和彈出式菜單
10.3JMenuItem
10.3.1菜單項快捷鍵和助記符鍵
10.3.2JMenuItem屬性
10.3.3JMenuItem事件
10.3.4JMenuItem類總結
10.3.5AWT兼容
10.4JCheckBoxMenuItem
10.4.1JCheckBoxMenuItem屬性
10.4.2JCheckBoxMenuItem事件
10.4.3JCheckBoxMenuItem類總結
10.4.4AWT兼容
10.5JRadioButtonMenuItem
10.5.1JRadioButt onMe nuItem 屬性
10.5.2JRadioButtonMenuItem事件
10.5.3JRadioButtonMenuItem類
總結
10.5.4AWT兼容
10.6JMenu
10.6.1動態(tài)修改菜單
10.6.2右拉式菜單
10.6.3JMenu屬性
10.6.4JMenu事件
10.6.5JMenu類總結
10.6.6AWT兼容
10.7菜單元素
10.8JPopu pMe nu
10.8.1彈出式菜單觸發(fā)器
10.8.2輕量/中量/重量彈出式
菜單
10.8.3彈出式菜單調用者
10.8.4JPopupMenu屬性
10.8.5JPopupMenu事件
10.8.6JPopupMenu類總結
10.8.7AWT兼容
10.9JMenuBar
10.9.1菜單欄菜單和組件
10.9.2JMenuBar屬性
10.9.3JMenuBar事件
10.9.4JMenuBar類總結
10.9.5AWT兼容
10.10JToolBar
10.10.1滾過式工具條
10.10.2在工具條中使用動作
10.10.3浮動工具條
10.10.4位置固定的工具提示
10.10.5JToolBar屬性
10.10.6JToolBar事件
10.10.7JToolBar類總結
10.10.8AWT兼容
10.11本章回顧
第11章 進度條、滑桿和分隔條
11.1JProgessBar
11.1.1進度條與線程
11.1.2JProges sBar屬性
11.1.3JProgessBar事件
11.1.4JProgessBar類總結
11.1.5AWT兼容
11.2JSlider
11.2.1填充的滑桿
11.2.2滑桿間隔標記
11.2.3滑桿標簽
11.2.4反轉滑桿值
11.2.5滑桿的外延值
11.2.6JSlider屬性
11.2.7JSlider事件
11.2.8JSlider類總結
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類總結
12.1.4AWT兼容
12.2JRootPane
12.2.1RootPaneCotainer接口
12.2.2玻璃窗格
12.2.3內容窗格
12.2.4JRootPane屬性
12.2.5JRooPane事件
12.2.6JRootPane類總結
12.2.7AWT兼容
12.3JLaye redPane
12.3.1回顧輕量組件的層序
12.3.2為組件分配層
12.3.3指定同一層中組件的位置
12.3.4使用拖動層
12.3.5JLay eredPane屬性
12.3.6JLayeredPane類總結
12.3.7AWT兼容
12.4JTabbedPane
12.4.1選項卡的位置
12.4.2JTabbedPane的屬性
12.4.3JTabbedPane事件
12.4.4JTabbedPane類總結
12.5JSplitPane類
12.5.1JSplitPane屬性
12.5.2JSplitPane事件
12.5.3JSplitPane類總結
12.5.4AWT兼容
12.6本章回顧
第13章 滾動
13.1JViewport
13.1.1拖動視口中的視圖
13.1.2使用scrollRectToV isible
方法
13.1.3JViewport屬性
13.1.4JViewport事件
13.1.5JViewport類總結
13.1.6AWT兼容
13.2JScrollPane
13.2.1滾動窗格的頭部
13.2.2滾動窗格的角部
13.2.3JScrollPane屬性
13.2.4JScrollPane事件
13.2.5JScrollPane類總結
13.2.6AWT兼容
13.3Scrollable接口
13.4JScrollBar
13.4.1使用Swing的JScrollBar類進
行手動滾動
13.4.2塊增量和單元增量
13.4.3JScrollBar屬性
13.4.4JScrollBar事件
13.4.5JScrollBar類總結
13.4.6AWT兼容
13.5本章回顧
第14章 窗口和對話框
14.1JWindow
14.1.1JWindow屬性
14.1.2JWindow類總結
14.1.3AWT兼容
14.2JDialog
14.2.1JDialog屬性
14.2.2JDialog類總結
14.2.3AWT兼容
14.3JOptionPane
14.3.1內部窗體
14.3.2用JOptionPane靜態(tài)方法創(chuàng)建
對話框
14.3.3消息對話框
14.3.4確認對話框
14.3.5輸入對話框
14.3.6選項對話框
14.3.7JOptionPane屬性
14.3.8JOptionPane事件
14.3.9JOptionPane類總結
14.3.10AWT兼容
14.4本章回顧
第15章 內部窗體和桌面窗格
15.1JInternalFrame
15.1.1jintertnalFrame屬性
15.1.2JInternalFrame事件
15.1.3AWT兼容
15.2JDesktopPane
15.2.1JDesktopPane屬性
15.2.2JDesktopPane事件
15.2.3JDesktopPane類總結
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類總結
16.1.9AWT兼容
16.2JColorChooser
16.2.1在對話框中顯示顏色
選取器
16.2.2定制顏色選取器
16.2.3JColorChooser屬性
16.2.4JColorChooser事件
16.2.5JColorChooser類總結
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類總結
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類總結
18.6.4AWT兼容
18.7本章回顧
第19章 表格
19.1表格和滾動
19.2表格模型
19.2.1表格數據模型
19.2.2TableModel接口
19.2.3AbstractTableModel
19.2.4DefaultTableModel
19.2.5表格模型、缺省繪制器
和缺省編輯器
19.3表格列
19.3.1列調整大小模式
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實現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類總結
19.9.9AWT兼容
19.10本章回顧
第20章 樹
20.1創(chuàng)建樹
20.2樹節(jié)點
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é)點和根句柄
20.7樹單元編輯
20.7.1擴展DefaultCellEditor
20.7.2DefaultTreeCellEditor
20.8繪制和編輯:學習一個樣例
20.8.1Test類
20.8.2SelectableFile類和FileNode
類
20.8.3繪制器
20.8.4編輯器
20.8.5JTree屬性
20.8.6樹事件
20.8.7JTree類總結
20.8.8AWT兼容
20.9本章回顧
第21章 文本基礎
21.1Swing文本組件
21.2動作
21.2.1文本動作
21.2.2動作和編輯工具包
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撤銷/恢復
21.7JTextComponent
21.8本章回顧
第22章 文本組件
22.1JTexlField
22.1.1水平可視性和滾動偏移
22.1.2布局單行文本域
22.1.3使單行文本域有效
22.1.4JTextField組件總結
22.1.5JTextField屬性
22.1.6JTextField事件
22.1.7JTextField類總結
22.1.8AWT兼容
22.2JPasswordField
22.2.1JPasswordField組件總結
22.2.2JPasswordFi eld屬性
22.2.3JPasswordField類總結
22.3JTextArea
22.3.1JTextArea組件總結
22.3.2JTextArea屬性
22.3.3JTextArea類總結
22.3.4AWT兼容
22.4JEditorPane
22.4.1JEditorPane屬性
22.4.2JEditorPane事件
22.4.3JEditorPane類總結
22.5JTextPane
22.5.1嵌入圖標和組件
22.5.2用屬性標記內容
22.5.3JTextPane屬性
22.5.4JTextPane類總結
22.6AWT兼容
22.7本章回顧
第23章 定制文本組件
23.1概覽
23.2屬性集和風格常量
23.3定制動作
23.4視圖
23.5風格和風格的相關內容
23.6元素
23.7本章回顧
第三部分 附錄
附錄A 類圖
附錄B 插入式界面樣式常量
2100433B
Swing是一流的Java圖形用戶界面開發(fā)工具。本書詳細介紹了Swing的設計思想、體系結構、使用技巧,內容豐富、深入細致、分析透徹。本書用大量實例代碼介紹了每個組件的用法,使初學者能很快入門;用大量圖示分析了Swing組件的特點、結構及相互關系,使有經驗的編程人員能高效利用Swing的強大功能。本書對掌握Swing技術提供了最全面的參考。
一句話:專業(yè)的最好,不管是CRT還LCD,專業(yè)顯示器才是好的。不過價格實在太天文了而已。
一般作圖都推薦瓏管的純平顯示器,液晶也有適合作圖的,但是價格離譜也比較難買的到,一般就用純平。三菱的鉆石瓏和索尼的特麗瓏都已經在幾年前停止了21寸以下的顯像管生產,所以市面上現在很難找到還做瓏管顯示器...
JavaWeb應用開發(fā)框架實例
一、 概述
Web 應用架構可以劃分為兩大子系統(tǒng):前端子系統(tǒng)和后臺子系統(tǒng)。
前端子系統(tǒng):
1. 基礎技術: Html/Java/CSS / Flash
2. 開發(fā)框架: jQuery, Extjs , Flex 等;
后臺子系統(tǒng):
1. 基礎技術: Java Servlet;
2. 開發(fā)框架: Struts, Spring, Hibernate, ibatis 等;
3. 應用服務器: Tomcat / Jetty
編程模型: B/S 模型。 客戶端向服務器端發(fā)送請求, 服務器經過處理后返回響應, 然后客戶端根據響應及需求繪制前端展現。
在用戶客戶端和實際提供功能的Web 服務器之間還可能存在著代理服務器, 負載均衡服務器, 不過那些屬于錦上添花的事物,暫時不在考慮范圍內。
客戶端應用理念: 客戶端承擔大量的交互邏輯及渲染工作,服務器端主要是處理請求和返回數據。
前后端系統(tǒng)耦合: 客戶端和服務器端各自處理自己內部的子系統(tǒng)耦合;而客戶端與服務器端的耦合簡化為一個通信與數據通道。該通道用來傳輸通信請求和返回數據。
請求通信: 采用 Http / Tcp 協(xié)議
數據通道: 采用 Json, xml , 文本字符串,字節(jié)。 內部系統(tǒng)一般采用 Json 作為數據交換格式;系統(tǒng)間的互操作則采用XML 來規(guī)范;文本字符串是最一般的形式, 字節(jié)是最底層的形式。
JavaWeb應用開發(fā)框架實例
二、 架構演變
最輕的架構: jQuery + Servlet + ajax 在客戶端使用 jQuery發(fā)送 ajax 請求給Java 服務端的 Servlet 進行處理, Servlet 僅僅返回數據給客戶端進行渲染。
該架構有效地分離了前端展示和后臺請求處理,同時又保持了最輕的復雜性, 只需要學會編寫 Servlet 及使用 jQuery , 就能構建簡單的應用。
如果只是做個人創(chuàng)意演示, 可以采用該架構, 快速實現自己的創(chuàng)意功能。 Servlet 是Java web 應用的基礎技術,jQuery 則是前端開發(fā)的簡單易用的利器。
后臺架構演變:
1. 邏輯與頁面的分離: JSP/Servlet
JSP 實現了頁面邏輯與外觀的分離,但是, 前端子系統(tǒng)與后臺子系統(tǒng)仍然是緊密耦合的; 前端設計人員實際上只需要服務端返回的數據, 就可設計出非常專業(yè)的界面顯示。
2. MVC 架構:Struts2(含Servlet,MVC) + JDBC
用Servlet 來添加服務器功能是基本的選擇,但在web.xml中配置大量的 Servlet 卻不是最佳的選擇。
Struts2 在服務端實現了更豐富的MVC 模式, 將本來由應用決定的控制器從web容器中分離。
3. SSH 架構: Struts2(含Servlet, MVC) + Spring (Ioc) + Hibernate (ORM,對象-關系映射)
通常, 應用系統(tǒng)中需要預先創(chuàng)建一些單例對象, 比如 Controller, Service, Dao, 線程池等, 可以引入 Spring Ioc 來有效地創(chuàng)建、管理和推送這些對象;使用 Hibernate 來實現關系數據庫的行與面向對象的屬性之間的映射與聯接,以更好地簡化和管理應用系統(tǒng)的數據庫操作。SSH 可以說是 JavaWeb應用系統(tǒng)開發(fā)的三劍客。
4. SI 架構: SpringMVC(含Servlet, Ioc, MVC, Rest) + iBatis (Semi-ORM)
過于復雜的架構會將人搞暈。因此,在適應需求的情況下, 盡量選擇簡單的架構,是明智之選。 這種架構使用面向資源的理念,著重使用Spring作為MVC及應用基礎服務設施, 同時使用 iBatis 來實現更簡單靈活的ORM映射, 使之在可以理解和維護的范圍內。
前端架構:
1. Flash 架構: Flex + jQuery + JSP
這是一種比較傳統(tǒng)的前端架構,采用同步模式, Flex 承擔大量的頁面渲染工作, 并采用AMF協(xié)議與Java端進行通信, 而JSP 則可以用于更快速的頁面顯示。優(yōu)點是: 經過考驗的結構, 通常是值得信賴的; 缺點是, 由于采用同步模式, 在交互效果上可能不夠流暢, 需要進行比較耗時的編譯過程;此外, Flex 基于瀏覽器插件運行,在調試方面有些麻煩。
2. MVC 架構: Extjs + jQuery
這是一種比較現代的前端架構, 采用異步模式, Extjs4 可以實現前端子系統(tǒng)的MVC 分離, 對于可維護性是非常不錯的支持;此外, jQuery 可以作為有效的補充。
優(yōu)點: 異步, 快速, 對于企業(yè)內部的后臺管理系統(tǒng)是非常好的選擇。
缺點: Extjs4 的可定制性、可適應性可能難以適應各種特殊的需求,需要用其它組件來補充, 比如大數據量的繪制。對于互聯網應用, 速度可能是致命傷。
三、 架構的選擇
不要去詢問哪種架構更好,更需要做的是清晰地定位項目目標,根據自己的具體情況來選擇和定制架構。反復地嘗試、觀察和改進,反復磨煉技藝,這樣才有助于設計水平的提升。
架構的選擇通常有四種關注點:
1. 適用性: 是否適合你的項目需求。 架構有大有小, 小項目用小架構, 大項目用大架構。
2. 可擴展性: 該架構在需要添加新功能時,是否能夠以常量的成本添加到現有系統(tǒng)中, 所做的改動在多大程度上會影響現有功能的實現(基本不影響,還是要大面積波及)。
3. 便利性: 使用該架構是否易于開發(fā)功能和擴展功能, 學習、開發(fā)和測試成本有多大。
4. 復雜性: 使用該架構后,維護起來的成本有多大。你自然希望能夠寫一條語句做很多事,使用各種成熟的組件是正確的方式,同時,在項目中混雜各種組件,也會提升理解和維護系統(tǒng)的復雜度。便利性和復雜性需要達到較好的平衡。
特殊的關注點:
譬如,應用需要支持高并發(fā)的情況, 需要建立一個底層的并發(fā)基礎設施, 并向上層提供簡單易用的接口,屏蔽其復雜性。
四、 架構演進的基本手段
架構并不是一成不變的, 在做出最初的架構之后,隨著開發(fā)的具體情況和需求的變更, 需要對最初架構做出變更和改進。
架構演進的基本手段:
一致性, 隔離與統(tǒng)一管理, 螺旋式重構改進, 消除重復, 借鑒現有方案。
1. 一致性: 確保使用統(tǒng)一模式來處理相同或相似的功能; 解決一次, 使用多次。
2. 模塊化、隔離與統(tǒng)一管理: 對于整體的應用, 分而治之,將其劃分為隔離性良好的模塊,提供必要的通信耦合;對于特定的功能模塊, 采用隔離手段,將其隔離在局部統(tǒng)一管理,避免分散在系統(tǒng)的各處。
3. 不斷重構改進, 一旦發(fā)現更好的方式, 馬上替換掉原有方式。
4. 盡可能重用,消除重復。
5. 盡可能先借鑒系統(tǒng)中已有方案并復用之;如果有更好方案可替換之;
有一條設計準則是: 預先設計, 但不要過早設計。
意思是說, 需要對需求清楚的部分進行仔細的設計, 但是對于未知不清楚的需求,要堅持去理解它,但不要過早地去做出“預測性設計”;設計必須是明確的、清晰的、有效的, 不能針對含糊的東西來設計。可以在后期通過架構演進來獲得對后續(xù)需求的適應能力。
我們對Java的探索從客戶端Web應用開始,其代碼也在瀏覽器提供的引擎上執(zhí)行。為了打好后續(xù)對Java語言和瀏覽器平臺的學習基礎,首先我們要理解Web應用的生命周期,尤其要理解Java代碼執(zhí)行在生命周期的所有環(huán)節(jié)。
本文會完整探索客戶端Web應用程序的生命周期,從頁面請求開始,到用戶不同種類的交互,最后至頁面被關閉。首先我們來看看頁面是如何從HTML代碼建立的。然后我們將集中探討Java代碼的執(zhí)行,它給我們的頁面提供了大量交互。最后我們會看看為了響應用戶的動作,事件是如何被處理的。在這一些列過程中,我們將探索很多Web應用的基礎概念,例如DOM(Web頁面的一種結構化表示方式)和事件循環(huán)(它決定了應用如何處理事件)。讓我們開始學習吧!
你知道嗎?
瀏覽器是否總是會根據給定的HTML來渲染頁面呢? Web應用一次能處理多少個事件? 為什么瀏覽器使用事件隊列來處理事件?1.1 生命周期概覽典型客戶端Web應用的生命周期從用戶在瀏覽器地址欄輸入一串URL,或單擊一個鏈接開始。例如,我們想去Google的主頁查找一個術語。首先我們輸入了URL,www.google.com,其過程如圖1.1所示。
圖1.1 客戶端Web應用的周期從用戶指定某個網站地址(或單擊某個鏈接)開始,
其由兩個步驟組成:頁面構建和事件處理
從用戶的角度來說,瀏覽器構建了發(fā)送至服務器(序號2)的請求,該服務器處理了請求(序號3)并形成了一個通常由HTML、CSS和Java代碼所組成的響應。當瀏覽器接收了響應(序號4)時,我們的客戶端應用開始了它的生命周期。 由于客戶端Web應用是圖形用戶界面(GUI)應用,其生命周期與其他的GUI應用相似(例如標準的桌面應用或移動應用),其執(zhí)行步驟如下所示:
1.頁面構建——創(chuàng)建用戶界面;
2.事件處理——進入循環(huán)(序號5)從而等待事件(序號6)的發(fā)生,發(fā)生后調用事件處理器。
應用的生命周期隨著用戶關掉或離開頁面(序號7)而結束?,F在讓我們一起看一個簡單的示例程序:每當用戶移動鼠標或單擊頁面就會顯示一條消息。本文會始終使用這個示例,如清單1.1所示。
清單 1.1 一個帶有GUI的Web應用小程序,其描述了對事件的響應
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 } ?--- 定義一個函數用于向一個元素增加一條信息
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附上鼠標移動事件處理函數
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兩個元素的文字顏色(從而使我們方便地區(qū)分兩者)。隨后用first這個id定義了一個列表元素:1<ul id="first"></ul>然后定義一個addMessage函數,每當調用該函數都會創(chuàng)建一個新的列表項元素,為其設置文字內容,然后將其附加到一個現有的元素上:
1function addMessage(element, message){
2 var messageElement = document.("li");
3 messageElement.textContent = message;
4 element.(messageElement);
5}
如下所示,通過使用內置的方法getElementById來從文檔中獲取ID為first的元素,然后為該元素添加一條信息,用于告知頁面正在加載中:
1var first = document.getElementById("first");
2addMessage(first, "Page loading");
然后我們又定義了一個列表元素,這次給該列表賦予的ID屬性為second:1<ul id="second"></ul>最后將這兩個事件處理器附加到Web頁面的body上。每當用戶移動鼠標,鼠標移動事件處理器就會被執(zhí)行,然后該處理器調用addMessage方法,為第二個列表元素加上一句話“Event: mousemove”。
1document.body.addEventListener("mousemove", function() {
2 var second = document.getElementById("second");
3 addMessage(second, "Event: mousemove");
4});
我們還注冊了一個單擊事件處理器,每當用戶單擊頁面就會輸出該消息“Event: click”,并添加至第二個列表元素中。
1document.body.addEventListener("click", function(){
2 var second = document.getElementById("second");
3 addMessage(second, "Event: click");
4});
該應用的運行結果和交互如圖1.2所示。
我們還會用這個例子來展示Web應用生命周期階段之間的不同之處。讓我們從頁面構建階段開始講起。
圖1.2 清單1.1中的代碼運行后,用戶的動作會被記錄為消息
1.2 頁面構建階段當Web應用能被展示或交互之前,其頁面必須根據服務器獲取的響應(通常是HTML、CSS和Java代碼)來構建。頁面構建階段的目標是建立Web應用的UI,其主要包括兩個步驟:
1.解析HTML代碼并構建文檔對象模型 (DOM);
2.執(zhí)行Java代碼。
步驟1會在瀏覽器處理HTML節(jié)點的過程中執(zhí)行,步驟二會在HTML解析到一種特殊節(jié)點——腳本節(jié)點(包含或引用Java代碼的節(jié)點)時執(zhí)行。頁面構建階段中,這兩個步驟會交替執(zhí)行多次,如圖1.3所示。
圖1.3 頁面構建階段從瀏覽器接收頁面代碼開始。其執(zhí)行分為兩個步驟:HTML解析和DOM構建,以及Java代碼的執(zhí)行
1.2.1 HTML解析和DOM構建頁面構建階段始于瀏覽器接收HTML代碼時,該階段為瀏覽器構建頁面UI的基礎。通過解析收到的HTML代碼,構建一個個HTML元素,構建DOM。在這種對HTML結構化表示的形式中,每個HTML元素都被當作一個節(jié)點。如圖1.4所示,直到遇到第一個腳本元素,示例頁面都在構建DOM。
注意圖1.4中的節(jié)點是如何組織的,除了第一個節(jié)點——html根節(jié)點(序號1)以外,所有節(jié)點都只有一個父節(jié)點。例如,head節(jié)點(序號2)父節(jié)點為html節(jié)點(序號1)。同時,一個節(jié)點可以有任意數量的子節(jié)點。例如,html節(jié)點(序號1)有兩個孩子節(jié)點:head節(jié)點(序號2)和body節(jié)點。同一個元素的孩子節(jié)點被稱作兄弟節(jié)點。(head節(jié)點和body節(jié)點是兄弟節(jié)點)盡管DOM是根據HTML來創(chuàng)建的,兩者緊密聯系,但需要強調的是,它們兩者并不相同。你可以把HTML代碼看作瀏覽器頁面UI構建初始DOM的藍圖。為了正確構建每個DOM,瀏覽器還會修復它在藍圖中發(fā)現的問題。讓我們看下面的示例,如圖1.5所示。
圖1.4 當瀏覽器遇到第一個腳本元素時,它已經用多個HTML元素(右邊的節(jié)點)創(chuàng)建了一個DOM樹
圖1.5展示了一個簡單的錯誤HTML代碼示例,頁面中的head元素中錯誤地包含了一個paragraph元素。head元素的一般用途是展示頁面的總體信息,例如,頁面標題、字符編碼和外部樣式腳本,而不是用于類似本例中的定義頁面內容。故而這里出現了錯誤,瀏覽器靜默修復錯誤,將段落元素放入了理應放置頁面內容的body元素中,構造了正確的DOM(如圖1.5右側)。
圖1.5 瀏覽器修正了錯誤的HTML代碼
HTML規(guī)范和DOM規(guī)范
當前HTML的版本是HTML5, 可以通過 https://html.spec.whatwg.org/ 查看當前版本中有哪些可用特性。你若需要更易讀的文檔,我們向你推薦Mozilla的HTML5指南,可通過https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5 查看。而另一方面,DOM的發(fā)展則相對緩慢。當前的DOM版本是DOM3,可以通過 https://dom.spec.whatwg.org/ 查看該標準。同樣,Mozilla也為DOM提供了一份報告,可以通過https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model 進行查看。在頁面構建階段,瀏覽器會遇到特殊類型的HTML元素——腳本元素,該元素用于包括Java代碼。每當解析到腳本元素時,瀏覽器就會停止從HTML構建DOM,并開始執(zhí)行Java代碼。
1.2.2 執(zhí)行Java代碼所有包含在腳本元素中的Java代碼由瀏覽器的Java引擎執(zhí)行,例如,Firefox的Spidermonkey引擎, Chrome 和 Opera和 V8引擎和Edge的(IE的)Chakra引擎。由于代碼的主要目的是提供動態(tài)頁面,故而瀏覽器通過全局對象提供了一個API 使Java引擎可以與之交互并改變頁面內容。
Java中的全局對象瀏覽器暴露給Java 引擎的主要全局對象是window對象,它代表了包含著一個頁面的窗口。window對象是獲取所有其他全局對象、全局變量(甚至包含用戶定義對象)和瀏覽器API的訪問途徑。全局window對象最重要的屬性是document,它代表了當前頁面的DOM。通過使用這個對象,Java代碼就能在任何程度上改變DOM,包括修改或移除現存的節(jié)點,以及創(chuàng)建和插入新的節(jié)點。
讓我們看看清單1.1中所示的代碼片段:
1var first = document.getElementById("first");
這個示例中使用全局document對象來通過ID選擇一個元素,然后將該元素賦值給變量first。隨后我們就能在該元素上用Java代碼來對其作各種操作,例如改變其文字內容,修改其屬性,動態(tài)創(chuàng)建和增加新孩子節(jié)點,甚至可以從DOM上將該元素移除。
瀏覽器API 本文自始至終都會描述一系列瀏覽器內置對象和函數(例如,window和document)。不過很遺憾,瀏覽器所支持的全部特性已經超出本文探討Java的范圍。幸好Mozilla為我們提供支持,通過https://developer.mozilla.org/en-US/docs/Web/API,你可以查找到WebAPI接口的當前狀態(tài)。對瀏覽器提供的基本全局對象有了基本了解后,我們可以開始看看Java代碼中兩種不同類型的定義方式。
Java代碼的不同類型我們已能大致區(qū)分出兩種不同類型的Java代碼:全局代碼和函數代碼。清單1.2會幫你理解這兩種類型代碼的不同。
清單1.2 Java全局代碼和函數代碼
1<>
2 function addMessage(element, message){
3 var messageElement = document.("li");
4 messageElement.textContent = message; ?--- 函數代碼指的是包含在函數中的代碼
5 element.(messageElement);
6 }
7 var first = document.getElementById("first");
8 addMessage(first, "Page loading"); ?--- 全局代碼指的是位于函數之外的代碼
9</>
這兩類代碼的主要不同是它們的位置:包含在函數內的代碼叫作函數代碼,而在所有函數以外的代碼叫作全局代碼。
這兩種代碼在執(zhí)行中也有不同(隨后你將能看到一些其他的不同)。全局代碼由Java引擎(后續(xù)會作更多解釋)以一種直接的方式自動執(zhí)行,每當遇到這樣的代碼就一行接一行地執(zhí)行。例如,在清單1.2中,定義在addMessage函數中的全局代碼片段使用內置方法getElementById來獲取ID為first的元素,然后再調用addMessage函數,如圖1.6所示,每當遇到這些代碼就會一個個執(zhí)行。
圖1.6 執(zhí)行Java代碼時的程序執(zhí)行流
反過來,若想執(zhí)行函數代碼,則必須被其他代碼調用:既可以是全局代碼(例如,由于全局代碼的執(zhí)行過程中執(zhí)行了addMessage函數代碼,所以addMessage函數得意被調用),也可以是其他函數,還可以由瀏覽器調用(后續(xù)會作更多解釋)。
在頁面構建階段執(zhí)行Java代碼當瀏覽器在頁面構建階段遇到了腳本節(jié)點,它會停止HTML到DOM的構建,轉而開始執(zhí)行Java代碼,也就是執(zhí)行包含在腳本元素的全局Java 代碼 (以及由全局代碼執(zhí)行中調用的函數代碼)。讓我們看看清單1.1中的示例。
圖1.7顯示了在全局Java代碼被執(zhí)行后DOM的狀態(tài)。讓我們仔細看看這個執(zhí)行過程。首先定義了一個addMessage函數:
1<>
2 function addMessage(element, message){
3 var messageElement = document.("li");
4 messageElement.textContent = message; ?--- 函數代碼指的是包含在函數中的代碼
5 element.(messageElement);
6 }
7 var first = document.getElementById("first");
8 addMessage(first, "Page loading"); ?--- 全局代碼指的是位于函數之外的代碼
9</>
然后通過全局document對象上的getElementById方法從DOM上獲取了一個元素:1var first = document.getElementById("first");
這段代碼后緊跟著對函數addMessage 的調用:
1addMessage(first, "Page loading");
這條代碼創(chuàng)建了一個新的li元素,然后修改了其中的文字內容,最后將其插入DOM中。
圖1.7 當執(zhí)行了腳本元素中的Java代碼后,頁面中的DOM結構
這個例子中,Java通過創(chuàng)建一個新元素并將其插入DOM節(jié)點修改了當前的DOM結構。一般來說,Java 代碼能夠在任何程度上修改DOM結構:它能創(chuàng)建新的接單或移除現有DOM節(jié)點。但它依然不能做某些事情,例如選擇和修改還沒被創(chuàng)建的節(jié)點。這就是為什么要把元素放在頁面底部的原因。如此一來,我們就不必擔心是否某個HTML元素已經加載為DOM。
一旦Java引擎執(zhí)行到了腳本元素中(如圖1.5中的addMessage函數返回)Java代碼的最后一行,瀏覽器就退出了Java執(zhí)行模式,并繼將余下的HTML構建為DOM節(jié)點。在這期間,如果瀏覽器再次遇到腳本元素,那么從HTML到DOM的構建再次暫停,Java運行環(huán)境開始執(zhí)行余下的Java代碼。需要重點注意:Java應用在此時依然會保持著全局狀態(tài)。所有在某個Java代碼執(zhí)行期間用戶創(chuàng)建的全局變量都能正常地被其他腳本元素中的Java代碼所訪問到。其原因在于全局window對象會存在于整個頁面的生存期之間,在它上面存儲著所有的Java變量。只要還有沒處理完的HTML元素和沒執(zhí)行完的Java代碼,下面兩個步驟都會一直交替執(zhí)行。
1.將HTML構建為DOM。
2.執(zhí)行Java代碼。
最后,當瀏覽器處理完所有HTML元素后,頁面構建階段就結束了。隨后瀏覽器就會進入應用生命周期的第二部分:事件處理。
1.3 事件處理客戶端Web 應用是一種GUI應用,也就是說這種應用會對不同類型的事件作響應,如鼠標移動、單擊和鍵盤按壓等。因此,在頁面構建階段執(zhí)行的Java代碼,除了會影響全局應用狀態(tài)和修改DOM外,還會注冊事件監(jiān)聽器(或處理器)。這類監(jiān)聽器會在事件發(fā)生時,由瀏覽器調用執(zhí)行。有了這些事件處理器,我們的應用也就有了交互能力。在詳細探討注冊事件處理器之前,讓我們先從頭到尾看一遍事件處理器的總體 思想。
1.3.1 事件處理器概覽瀏覽器執(zhí)行環(huán)境的核心思想基于:同一時刻只能執(zhí)行一個代碼片段,即所謂的單線程執(zhí)行模型。想象一下在銀行柜臺前排隊,每個人進入一支隊伍等待叫號并“處理”。但Java則只開啟了一個營業(yè)柜臺!每當輪到某個顧客時(某個事件),只能處理該位顧客。
你所需要的僅僅是一個在營業(yè)柜臺(所有人都在這個柜臺排隊!)的職員為你處理工作,幫你訂制全年的財務計劃。當一個事件抵達后,瀏覽器需要執(zhí)行相應的事件處理函數。這里不保證用戶總會極富耐心地等待很長時間,直到下一個事件觸發(fā)。所以,瀏覽器需要一種方式來跟蹤已經發(fā)生但尚未處理的事件。為實現這個目標,瀏覽器使用了事件隊列,如圖1.8所示。
所有已生成的事件(無論是用戶生成的,例如鼠標移動或鍵盤按壓,還是服務器生成的,例如Ajax事件)都會放在同一個事件隊列中,以它們被瀏覽器檢測到的順序排列。如圖1.8的中部所示,事件處理的過程可以描述為一個簡單的流程圖。
瀏覽器檢查事件隊列頭; 如果瀏覽器沒有在隊列中檢測到事件,則繼續(xù)檢查; 如果瀏覽器在隊列頭中檢測到了事件,則取出該事件并執(zhí)行相應的事件處理器(如果存在)。在這個過程中,余下的事件在事件隊列中耐心等待,直到輪到它們被處理。由于一次只能處理一個事件,所以我們必須格外注意處理所有事件的總時間。執(zhí)行需要花費大量時間執(zhí)行的事件處理函數會導致Web應用無響應!(如果聽起來還不太明確,不要擔心,以后我們還會學習事件循環(huán),再看看它是如何損害Web應用在感受上的性能的)。
圖1.8 客戶端Web應用的周期從用戶指定某個網站地址(或單擊某個鏈接)開始。
其由兩個步驟組成:頁面構建和事件處理
重點注意瀏覽器在這個過程中的機制,其放置事件的隊列是在頁面構建階段和事件處理階段以外的。這個過程對于決定事件何時發(fā)生并將其推入事件隊列很重要,這個過程不會參與事件處理線程。
事件是異步的事件可能會以難以預計的時間和順序發(fā)生(強制用戶以某個順序按鍵或單擊是非常奇怪的)。我們對事件的處理,以及處理函數的調用是異步的。如下類型的事件會在其他類型事件中發(fā)生。
瀏覽器事件,例如當頁面加載完成后或無法加載時; 網絡事件,例如來自服務器的響應(Ajax事件和服務器端事件); 用戶事件,例如鼠標單擊、鼠標移動和鍵盤事件; 計時器事件,當timeout時間到期或又觸發(fā)了一次時間間隔。Web應用的Java代碼中,大部分內容都是對上述事件的處理!
事件處理的概念是Web應用的核心,你在本文中的例子會反復看到:代碼的提前建立是為了在之后的某個時間點執(zhí)行。除了全局代碼,頁面中的大部分代碼都將作為某個事件的結果執(zhí)行。
在事件能被處理之前,代碼必須要告知瀏覽器我們要處理特定事件。接下來看看如何注冊事件處理器。
1.3.2 注冊事件處理器前面已經講過了,事件處理器是當某個特定事件發(fā)生后我們希望執(zhí)行的函數。為了達到這個目標,我們必須告知瀏覽器我們要處理哪個事件。這個過程叫作注冊事件處理器。在客戶端Web應用中,有兩種方式注冊事件。
通過把函數賦給某個特殊屬性; 通過使用內置addEventListener方法。例如,編寫如下代碼,將一個函數賦值給window對象上的某個特定屬性:
1window. = function(){};
通過這種方式,事件處理器就會注冊到load事件上(當DOM已經就緒并全部構建完成,就會觸發(fā)這個事件)。(如果你對賦值操作符右邊的記法有些困惑,不要擔心,隨后的章節(jié)中我們會細致地講述函數)類似,如果我們想要為在文檔中body元素的單擊事件注冊處理器,我們可以輸入下述代碼:
1document.body.onclick = function(){};
把函數賦值給特殊屬性是一種簡單而直接的注冊事件處理器方式。但是,我們并不推薦你使用這種方式來注冊事件處理器,這是因為這種做法會帶來缺點:對于某個事件只能注冊一個事件處理器。也就是說,一不小心就會將上一個事件處理器改寫掉。幸運的是,還有一種替代方案:addEventListener方法讓我們能夠注冊盡可能多的事件,只要我們需要。如下清單使用了清單1.3中的示例,向你展示這種便捷的用法。
清單1.3 注冊事件處理器
1<>
2 document.body.addEventListener("mousemove", function() { ?--- 為mousemove事件注冊處理器
3 var second = document.getElementById("second");
4 addMessage(second, "Event: mousemove");
5 });
6 document.body.addEventListener("click", function(){ ?--- 為click事件注冊處理器
7 var second = document.getElementById("second");
8 addMessage(second, "Event: click");
9 });
10</>
本例中使用了某個HTML元素上的內置的方法addEventListener,并在函數中指定了事件的類型(mousemove事件或click)和事件的處理器。這意味著當鼠標從頁面上移動后,瀏覽器會調用該函數添加一條消息到ID位second的list元素上,"Event: mousemove"(類似,當body被單擊時,"Event: click"也會被添加到同樣的元素上)。 現在你學習了如何創(chuàng)建事件處理器,讓我們回憶下前面看到的簡單流程圖,然后仔細看看事件是如何被處理的。
1.3.3 處理事件事件處理背后的的主要思想是:當事件發(fā)生時,瀏覽器調用相應的事件處理器。如前面提到的,由于單線程執(zhí)行模型,所以同一時刻只能處理一個事件。任何后面的事件都只能在當前事件處理器完全結束執(zhí)行后才能被處理!
讓我們回到清單1.1中的應用。圖1.9展示了在用戶快速移動和單擊鼠標時的執(zhí)行情況。
讓我們看看這里發(fā)生了什么。為了響應用戶的動作,瀏覽器把鼠標移動和單擊事件以它們發(fā)生的次序放入事件隊列:第一個是鼠標移動事件,第二個是單擊事件序號1。
在事件處理階段中,事件循環(huán)會檢查隊列,其發(fā)現隊列的前面有一個鼠標移動事件,然后執(zhí)行了相應的事件處理器序號2。當鼠標移動事件處理器處理完畢后,輪到了等待在隊列中的單擊事件。當鼠標移動事件處理器函數的最后一行代碼執(zhí)行完畢后,Java引擎退出事件處理器函數,鼠標移動事件完整地處理了序號3,事件循環(huán)再次檢查隊列。這一次,在隊列的最前面,事件循環(huán)發(fā)現了鼠標單擊事件并處理了該事件。一旦單擊處理器執(zhí)行完成,隊列中不再有新的事件,事件循環(huán)就會繼續(xù)循環(huán),等待處理新到來的事件。這個循環(huán)會一直執(zhí)行到用戶關閉了Web應用。
圖1.9 兩個事件——鼠標移動和單擊中的事件處理階段示例
現在我們有了個總體的認識,理解了事件處理階段的所有步驟。讓我們看看這個過程是如何影響DOM的(如圖1.10所示)。執(zhí)行鼠標移動處理器時會選擇第二個列表元素,其ID為second。
圖1.10 當鼠標移動和鼠標點擊事件都處理完成后,實例應用的DOM樹結構
然后通過使用addMessage,使用文字“Event: mousemove”添加了一個新的列表項元素序號1。一旦鼠標移動處理器結束后,事件循環(huán)執(zhí)行單擊事件處理器,從而創(chuàng)建了另一個列表元素序號2,并附加在ID為second的第二個列表元素后。
對Web應用客戶端的生命周期有了清晰的理解后,本文的下一部分,我們會開始聚焦于Java語言,理清函數的來龍去脈。
1.4 小結瀏覽器接收的HTML代碼用作創(chuàng)建DOM的藍圖,它是客戶端Web應用結構的內部展示階段。 我們使用Java代碼來動態(tài)地修改DOM以便給Web應用帶來動態(tài)行為。 客戶端Web應用的執(zhí)行分為兩個階段。頁面構建代碼是用于創(chuàng)建DOM的,而全局Java代碼是遇到節(jié)點時執(zhí)行的。在這個執(zhí)行過程中,Java代碼能夠以任意程度改變當前的DOM,還能夠注冊事件處理器——事件處理器是一種函數,當某個特定事件(例如,一次鼠標單擊或鍵盤按壓)發(fā)生后會被執(zhí)行。注冊事件處理器很容易:使用內置的addEventListener方法。 事件處理——在同一時刻,只能處理多個不同事件中的一個,處理順序是事件生成的順序。事件處理階段大量依賴事件隊列,所有的事件都以其出現的順序存儲在事件隊列中。事件循環(huán)會檢查實踐隊列的隊頭,如果檢測到了一個事件,那么相應的事件處理器就會被調用。1.5 練習1.客戶端Web應用的兩個生命周期階段是什么?
2.相比將事件處理器賦值給某個特定元素的屬性上,使用addEventListener方法來注冊事件處理器的優(yōu)勢是什么?
3.Java引擎在同一時刻能處理多少個事件?
4.事件隊列中的事件是以什么順序處理的?
1 </div>
2 <p id="copyright-declare">
3 本文僅用于學習和交流目的,不代表異步社區(qū)觀點。非商業(yè)轉載請注明作譯者、出處,并保留本文的原始鏈接。
4 </p>
5 </div>
本文摘自《Java忍者秘籍 第2版》
《Java忍者秘籍 第2版》
[美] John,Resig(萊西格),Bear,Bibeault(貝比奧特),Josip ... 著
點擊封面購買紙書
Java語言非常重要,相關的技術圖書也很多,但至今市面沒有一本對Java語言的重要部分(函數、閉包和原型)進行深入、全面介紹的圖書,也沒有一本講述跨瀏覽器代碼編寫的圖書。而本書彌補了這一空缺,是由jQuery庫創(chuàng)始人編寫的一本深入剖析Java語言的書。《Java 忍者秘籍(第2版)》使用實際的案例清晰地詮釋每一個核心概念和技術。本書向讀者介紹了如何掌握 Java 核心的概念,諸如函數、閉包、對象、原型和 promise,同時還介紹了 Java API, 包括 DOM、事件和計時器。你將學會測試、跨瀏覽器開發(fā),所有這些都是高級Java開發(fā)者應該掌握的技能。
延伸推薦2018年1月重磅新書小學生開始學Python,最接近AI的編程語言:安利一波Python書單政策升溫:大家都在學大數據,一大波好書推薦一本基于Python語言的Selenium自動化測試書 8本新書,送出一本你喜歡的AI經典書單| 入門人工智能該讀哪些書?點擊關鍵詞閱讀更多新書:Python|機器學習|Kotlin|Java|移動開發(fā)|機器人|有獎活動|Web前端|書單
在“異步圖書”后臺回復“關注”,即可免費獲得2000門在線視頻課程;推薦朋友關注根據提示獲取贈書鏈接,免費得異步圖書一本。趕緊來參加哦!
掃一掃上方二維碼,回復“關注”參與活動!
點擊閱讀原文購買《Java忍者秘籍 第2版》
閱讀原文
內部類不是很好理解,但說白了其實也就是一個類中還包含著另外一個類。
如同一個人是由大腦、肢體、器官等身體結果組成,而內部類相當于其中的某個器官之一,例如心臟:它也有自己的屬性和行為(血液、跳動)。
顯然,此處不能單方面用屬性或者方法表示一個心臟,而需要一個類。
而心臟又在人體當中,正如同是內部類在外部內當中。
實例1:內部類的基本結構
//外部類 class Out { private int age = 12; //內部類 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(); */ } }運行結果:12
從上面的例子不難看出,內部類其實嚴重破壞了良好的代碼結構,但為什么還要使用內部類呢?
因為內部類可以隨意使用外部類的成員變量(包括私有)而不用生成外部類的對象,這也是內部類的唯一優(yōu)點。
如同心臟可以直接訪問身體的血液,而不是通過醫(yī)生來抽血
程序編譯過后會產生兩個.class文件,分別是Out.class和Out$In.class。
其中$代表了上面程序中Out.In中的那個 。
Out.In in = new Out().new In()可以用來生成內部類的對象,這種方法存在兩個小知識點需要注意:
1、開頭的Out是為了標明需要生成的內部類對象在哪個外部類當中;
2、必須先有外部類的對象才能生成內部類的對象,因為內部類的作用就是為了訪問外部類中的成員變量。
實例2:內部類中的變量訪問形式
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("內部類變量:" + 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(); } }運行結果:局部變量:14;內部類變量:13;外部類變量:12
從實例1中可以發(fā)現,內部類在沒有同名成員變量和局部變量的情況下,內部類會直接訪問外部類的成員變量,而無需指定Out.this.屬性名。
否則,內部類中的局部變量會覆蓋外部類的成員變量。
而訪問內部類本身的成員變量可用this.屬性名,訪問外部類的成員變量需要使用Out.this.屬性名。
實例3:靜態(tà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(); } }運行結果:12
可以看到,如果用static 將內部內靜態(tài)化,那么內部類就只能訪問外部類的靜態(tài)成員變量,具有局限性。
其次,因為內部類被靜態(tài)化,因此Out.In可以當做一個整體看,可以直接new 出內部類的對象(通過類名訪問static,生不生成外部類對象都沒關系)。
實例4:私有內部類
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(); } }運行結果:12
如果一個內部類只希望被外部類中的方法操作,那么可以使用private聲明內部類。
上面的代碼中,我們必須在Out類里面生成In類的對象進行操作,而無法再使用Out.In in = new Out().new In() 生成內部類的對象。
也就是說,此時的內部類只有外部類可控制。
如同是,我的心臟只能由我的身體控制,其他人無法直接訪問它。
實例5:方法內部類
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); } }運行結果:3;12
在上面的代碼中,我們將內部類移到了外部類的方法中,然后在外部類的方法中再生成一個內部類對象去調用內部類方法。
如果此時我們需要往外部類的方法中傳入參數,那么外部類的方法形參必須使用final定義。
至于final在這里并沒有特殊含義,只是一種表示形式而已。
最后,覺得不錯,大家一起分享給其他的同學吧
Java新人自學交流群:202250194