选择特殊符号
选择搜索类型
请输入搜索
译者序
序
前言
第一部分 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静态认识
3.2.3动态认识
3.2.4模型
3.2.5UI代表
3.2.6组件UI的案例
3.2.7监听器
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事件监听器列表
6.3Swing实用工具
6.4Swing常量
6.5BoxLayout和Box 类
6.5.1BoxLayout类
6.5.2Box类
6.6进度监视器
6.6.1ProgressMonitor
6.6.2Progress MonitorInputStream
6.7撤消/重复
6.7.1一个简单的撤消/重复样例
6.7.2UndoableEditSupport
6.7.3组合编辑
6.7.4UndoManager
6.7.5状态编辑
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许可状态
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动态修改菜单
10.6.2右拉式菜单
10.6.3JMenu属性
10.6.4JMenu事件
10.6.5JMenu类总结
10.6.6AWT兼容
10.7菜单元素
10.8JPopu pMe nu
10.8.1弹出式菜单触发器
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静态方法创建
对话框
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创建树
20.2树节点
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根节点和根句柄
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文档监听器
21.5加字符与加重器
21.5.1加字符
21.5.2加字符监听器
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图形用户界面开发工具。本书详细介绍了Swing的设计思想、体系结构、使用技巧,内容丰富、深入细致、分析透彻。本书用大量实例代码介绍了每个组件的用法,使初学者能很快入门;用大量图示分析了Swing组件的特点、结构及相互关系,使有经验的编程人员能高效利用Swing的强大功能。本书对掌握Swing技术提供了最全面的参考。
《便衣警察》 《死于青春》 《一场风花雪月的事》 《海岩文集》 《永不瞑目》 《海岩散文》 《玉观音》 《海岩中篇选》 《你的生命如此多情》 《海岩长篇经典全集》 《拿什么拯救你,我的爱人》 《煽》 ...
徐韦华,在台湾很有影响力的著名拼布作家,从事 拼布材料包 设计多年,经验非常丰富。《一缝就成的拼布小物》是一本实用的拼布书,以面纸套、手机袋、卡片夹、零钱包、笔袋、相机包等非常实用的拼布小物的制作讲解...
前言第1章 概述1.1 传统的图解展开法1.2 程编计算公式法展开放样1.3 计算器的程编计算应用第2章 圆柱面构件的展开2.1 被平面斜截后的圆柱管构件2.2 被圆柱面截切后的圆柱管构件2.3 被椭...
JavaWeb应用开发框架实例
一、 概述
Web 应用架构可以划分为两大子系统:前端子系统和后台子系统。
前端子系统:
1. 基础技术: Html/Java/CSS / Flash
2. 开发框架: jQuery, Extjs , Flex 等;
后台子系统:
1. 基础技术: Java Servlet;
2. 开发框架: Struts, Spring, Hibernate, ibatis 等;
3. 应用服务器: Tomcat / Jetty
编程模型: B/S 模型。 客户端向服务器端发送请求, 服务器经过处理后返回响应, 然后客户端根据响应及需求绘制前端展现。
在用户客户端和实际提供功能的Web 服务器之间还可能存在着代理服务器, 负载均衡服务器, 不过那些属于锦上添花的事物,暂时不在考虑范围内。
客户端应用理念: 客户端承担大量的交互逻辑及渲染工作,服务器端主要是处理请求和返回数据。
前后端系统耦合: 客户端和服务器端各自处理自己内部的子系统耦合;而客户端与服务器端的耦合简化为一个通信与数据通道。该通道用来传输通信请求和返回数据。
请求通信: 采用 Http / Tcp 协议
数据通道: 采用 Json, xml , 文本字符串,字节。 内部系统一般采用 Json 作为数据交换格式;系统间的互操作则采用XML 来规范;文本字符串是最一般的形式, 字节是最底层的形式。
JavaWeb应用开发框架实例
二、 架构演变
最轻的架构: jQuery + Servlet + ajax 在客户端使用 jQuery发送 ajax 请求给Java 服务端的 Servlet 进行处理, Servlet 仅仅返回数据给客户端进行渲染。
该架构有效地分离了前端展示和后台请求处理,同时又保持了最轻的复杂性, 只需要学会编写 Servlet 及使用 jQuery , 就能构建简单的应用。
如果只是做个人创意演示, 可以采用该架构, 快速实现自己的创意功能。 Servlet 是Java web 应用的基础技术,jQuery 则是前端开发的简单易用的利器。
后台架构演变:
1. 逻辑与页面的分离: JSP/Servlet
JSP 实现了页面逻辑与外观的分离,但是, 前端子系统与后台子系统仍然是紧密耦合的; 前端设计人员实际上只需要服务端返回的数据, 就可设计出非常专业的界面显示。
2. MVC 架构:Struts2(含Servlet,MVC) + JDBC
用Servlet 来添加服务器功能是基本的选择,但在web.xml中配置大量的 Servlet 却不是最佳的选择。
Struts2 在服务端实现了更丰富的MVC 模式, 将本来由应用决定的控制器从web容器中分离。
3. SSH 架构: Struts2(含Servlet, MVC) + Spring (Ioc) + Hibernate (ORM,对象-关系映射)
通常, 应用系统中需要预先创建一些单例对象, 比如 Controller, Service, Dao, 线程池等, 可以引入 Spring Ioc 来有效地创建、管理和推送这些对象;使用 Hibernate 来实现关系数据库的行与面向对象的属性之间的映射与联接,以更好地简化和管理应用系统的数据库操作。SSH 可以说是 JavaWeb应用系统开发的三剑客。
4. SI 架构: SpringMVC(含Servlet, Ioc, MVC, Rest) + iBatis (Semi-ORM)
过于复杂的架构会将人搞晕。因此,在适应需求的情况下, 尽量选择简单的架构,是明智之选。 这种架构使用面向资源的理念,着重使用Spring作为MVC及应用基础服务设施, 同时使用 iBatis 来实现更简单灵活的ORM映射, 使之在可以理解和维护的范围内。
前端架构:
1. Flash 架构: Flex + jQuery + JSP
这是一种比较传统的前端架构,采用同步模式, Flex 承担大量的页面渲染工作, 并采用AMF协议与Java端进行通信, 而JSP 则可以用于更快速的页面显示。优点是: 经过考验的结构, 通常是值得信赖的; 缺点是, 由于采用同步模式, 在交互效果上可能不够流畅, 需要进行比较耗时的编译过程;此外, Flex 基于浏览器插件运行,在调试方面有些麻烦。
2. MVC 架构: Extjs + jQuery
这是一种比较现代的前端架构, 采用异步模式, Extjs4 可以实现前端子系统的MVC 分离, 对于可维护性是非常不错的支持;此外, jQuery 可以作为有效的补充。
优点: 异步, 快速, 对于企业内部的后台管理系统是非常好的选择。
缺点: Extjs4 的可定制性、可适应性可能难以适应各种特殊的需求,需要用其它组件来补充, 比如大数据量的绘制。对于互联网应用, 速度可能是致命伤。
三、 架构的选择
不要去询问哪种架构更好,更需要做的是清晰地定位项目目标,根据自己的具体情况来选择和定制架构。反复地尝试、观察和改进,反复磨炼技艺,这样才有助于设计水平的提升。
架构的选择通常有四种关注点:
1. 适用性: 是否适合你的项目需求。 架构有大有小, 小项目用小架构, 大项目用大架构。
2. 可扩展性: 该架构在需要添加新功能时,是否能够以常量的成本添加到现有系统中, 所做的改动在多大程度上会影响现有功能的实现(基本不影响,还是要大面积波及)。
3. 便利性: 使用该架构是否易于开发功能和扩展功能, 学习、开发和测试成本有多大。
4. 复杂性: 使用该架构后,维护起来的成本有多大。你自然希望能够写一条语句做很多事,使用各种成熟的组件是正确的方式,同时,在项目中混杂各种组件,也会提升理解和维护系统的复杂度。便利性和复杂性需要达到较好的平衡。
特殊的关注点:
譬如,应用需要支持高并发的情况, 需要建立一个底层的并发基础设施, 并向上层提供简单易用的接口,屏蔽其复杂性。
四、 架构演进的基本手段
架构并不是一成不变的, 在做出最初的架构之后,随着开发的具体情况和需求的变更, 需要对最初架构做出变更和改进。
架构演进的基本手段:
一致性, 隔离与统一管理, 螺旋式重构改进, 消除重复, 借鉴现有方案。
1. 一致性: 确保使用统一模式来处理相同或相似的功能; 解决一次, 使用多次。
2. 模块化、隔离与统一管理: 对于整体的应用, 分而治之,将其划分为隔离性良好的模块,提供必要的通信耦合;对于特定的功能模块, 采用隔离手段,将其隔离在局部统一管理,避免分散在系统的各处。
3. 不断重构改进, 一旦发现更好的方式, 马上替换掉原有方式。
4. 尽可能重用,消除重复。
5. 尽可能先借鉴系统中已有方案并复用之;如果有更好方案可替换之;
有一条设计准则是: 预先设计, 但不要过早设计。
意思是说, 需要对需求清楚的部分进行仔细的设计, 但是对于未知不清楚的需求,要坚持去理解它,但不要过早地去做出“预测性设计”;设计必须是明确的、清晰的、有效的, 不能针对含糊的东西来设计。可以在后期通过架构演进来获得对后续需求的适应能力。
我们对Java的探索从客户端Web应用开始,其代码也在浏览器提供的引擎上执行。为了打好后续对Java语言和浏览器平台的学习基础,首先我们要理解Web应用的生命周期,尤其要理解Java代码执行在生命周期的所有环节。
本文会完整探索客户端Web应用程序的生命周期,从页面请求开始,到用户不同种类的交互,最后至页面被关闭。首先我们来看看页面是如何从HTML代码建立的。然后我们将集中探讨Java代码的执行,它给我们的页面提供了大量交互。最后我们会看看为了响应用户的动作,事件是如何被处理的。在这一些列过程中,我们将探索很多Web应用的基础概念,例如DOM(Web页面的一种结构化表示方式)和事件循环(它决定了应用如何处理事件)。让我们开始学习吧!
你知道吗?
浏览器是否总是会根据给定的HTML来渲染页面呢? Web应用一次能处理多少个事件? 为什么浏览器使用事件队列来处理事件?1.1 生命周期概览典型客户端Web应用的生命周期从用户在浏览器地址栏输入一串URL,或单击一个链接开始。例如,我们想去Google的主页查找一个术语。首先我们输入了URL,www.google.com,其过程如图1.1所示。
图1.1 客户端Web应用的周期从用户指定某个网站地址(或单击某个链接)开始,
其由两个步骤组成:页面构建和事件处理
从用户的角度来说,浏览器构建了发送至服务器(序号2)的请求,该服务器处理了请求(序号3)并形成了一个通常由HTML、CSS和Java代码所组成的响应。当浏览器接收了响应(序号4)时,我们的客户端应用开始了它的生命周期。 由于客户端Web应用是图形用户界面(GUI)应用,其生命周期与其他的GUI应用相似(例如标准的桌面应用或移动应用),其执行步骤如下所示:
1.页面构建——创建用户界面;
2.事件处理——进入循环(序号5)从而等待事件(序号6)的发生,发生后调用事件处理器。
应用的生命周期随着用户关掉或离开页面(序号7)而结束。现在让我们一起看一个简单的示例程序:每当用户移动鼠标或单击页面就会显示一条消息。本文会始终使用这个示例,如清单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 规则,即#first和#second,其指定了ID为first和second两个元素的文字颜色(从而使我们方便地区分两者)。随后用first这个id定义了一个列表元素:1<ul id="first"></ul>然后定义一个addMessage函数,每当调用该函数都会创建一个新的列表项元素,为其设置文字内容,然后将其附加到一个现有的元素上:
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上。每当用户移动鼠标,鼠标移动事件处理器就会被执行,然后该处理器调用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.执行Java代码。
步骤1会在浏览器处理HTML节点的过程中执行,步骤二会在HTML解析到一种特殊节点——脚本节点(包含或引用Java代码的节点)时执行。页面构建阶段中,这两个步骤会交替执行多次,如图1.3所示。
图1.3 页面构建阶段从浏览器接收页面代码开始。其执行分为两个步骤:HTML解析和DOM构建,以及Java代码的执行
1.2.1 HTML解析和DOM构建页面构建阶段始于浏览器接收HTML代码时,该阶段为浏览器构建页面UI的基础。通过解析收到的HTML代码,构建一个个HTML元素,构建DOM。在这种对HTML结构化表示的形式中,每个HTML元素都被当作一个节点。如图1.4所示,直到遇到第一个脚本元素,示例页面都在构建DOM。
注意图1.4中的节点是如何组织的,除了第一个节点——html根节点(序号1)以外,所有节点都只有一个父节点。例如,head节点(序号2)父节点为html节点(序号1)。同时,一个节点可以有任意数量的子节点。例如,html节点(序号1)有两个孩子节点:head节点(序号2)和body节点。同一个元素的孩子节点被称作兄弟节点。(head节点和body节点是兄弟节点)尽管DOM是根据HTML来创建的,两者紧密联系,但需要强调的是,它们两者并不相同。你可以把HTML代码看作浏览器页面UI构建初始DOM的蓝图。为了正确构建每个DOM,浏览器还会修复它在蓝图中发现的问题。让我们看下面的示例,如图1.5所示。
图1.4 当浏览器遇到第一个脚本元素时,它已经用多个HTML元素(右边的节点)创建了一个DOM树
图1.5展示了一个简单的错误HTML代码示例,页面中的head元素中错误地包含了一个paragraph元素。head元素的一般用途是展示页面的总体信息,例如,页面标题、字符编码和外部样式脚本,而不是用于类似本例中的定义页面内容。故而这里出现了错误,浏览器静默修复错误,将段落元素放入了理应放置页面内容的body元素中,构造了正确的DOM(如图1.5右侧)。
图1.5 浏览器修正了错误的HTML代码
HTML规范和DOM规范
当前HTML的版本是HTML5, 可以通过 https://html.spec.whatwg.org/ 查看当前版本中有哪些可用特性。你若需要更易读的文档,我们向你推荐Mozilla的HTML5指南,可通过https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5 查看。而另一方面,DOM的发展则相对缓慢。当前的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,并开始执行Java代码。
1.2.2 执行Java代码所有包含在脚本元素中的Java代码由浏览器的Java引擎执行,例如,Firefox的Spidermonkey引擎, Chrome 和 Opera和 V8引擎和Edge的(IE的)Chakra引擎。由于代码的主要目的是提供动态页面,故而浏览器通过全局对象提供了一个API 使Java引擎可以与之交互并改变页面内容。
Java中的全局对象浏览器暴露给Java 引擎的主要全局对象是window对象,它代表了包含着一个页面的窗口。window对象是获取所有其他全局对象、全局变量(甚至包含用户定义对象)和浏览器API的访问途径。全局window对象最重要的属性是document,它代表了当前页面的DOM。通过使用这个对象,Java代码就能在任何程度上改变DOM,包括修改或移除现存的节点,以及创建和插入新的节点。
让我们看看清单1.1中所示的代码片段:
1var first = document.getElementById("first");
这个示例中使用全局document对象来通过ID选择一个元素,然后将该元素赋值给变量first。随后我们就能在该元素上用Java代码来对其作各种操作,例如改变其文字内容,修改其属性,动态创建和增加新孩子节点,甚至可以从DOM上将该元素移除。
浏览器API 本文自始至终都会描述一系列浏览器内置对象和函数(例如,window和document)。不过很遗憾,浏览器所支持的全部特性已经超出本文探讨Java的范围。幸好Mozilla为我们提供支持,通过https://developer.mozilla.org/en-US/docs/Web/API,你可以查找到WebAPI接口的当前状态。对浏览器提供的基本全局对象有了基本了解后,我们可以开始看看Java代码中两种不同类型的定义方式。
Java代码的不同类型我们已能大致区分出两种不同类型的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</>
这两类代码的主要不同是它们的位置:包含在函数内的代码叫作函数代码,而在所有函数以外的代码叫作全局代码。
这两种代码在执行中也有不同(随后你将能看到一些其他的不同)。全局代码由Java引擎(后续会作更多解释)以一种直接的方式自动执行,每当遇到这样的代码就一行接一行地执行。例如,在清单1.2中,定义在addMessage函数中的全局代码片段使用内置方法getElementById来获取ID为first的元素,然后再调用addMessage函数,如图1.6所示,每当遇到这些代码就会一个个执行。
图1.6 执行Java代码时的程序执行流
反过来,若想执行函数代码,则必须被其他代码调用:既可以是全局代码(例如,由于全局代码的执行过程中执行了addMessage函数代码,所以addMessage函数得意被调用),也可以是其他函数,还可以由浏览器调用(后续会作更多解释)。
在页面构建阶段执行Java代码当浏览器在页面构建阶段遇到了脚本节点,它会停止HTML到DOM的构建,转而开始执行Java代码,也就是执行包含在脚本元素的全局Java 代码 (以及由全局代码执行中调用的函数代码)。让我们看看清单1.1中的示例。
图1.7显示了在全局Java代码被执行后DOM的状态。让我们仔细看看这个执行过程。首先定义了一个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");
这条代码创建了一个新的li元素,然后修改了其中的文字内容,最后将其插入DOM中。
图1.7 当执行了脚本元素中的Java代码后,页面中的DOM结构
这个例子中,Java通过创建一个新元素并将其插入DOM节点修改了当前的DOM结构。一般来说,Java 代码能够在任何程度上修改DOM结构:它能创建新的接单或移除现有DOM节点。但它依然不能做某些事情,例如选择和修改还没被创建的节点。这就是为什么要把元素放在页面底部的原因。如此一来,我们就不必担心是否某个HTML元素已经加载为DOM。
一旦Java引擎执行到了脚本元素中(如图1.5中的addMessage函数返回)Java代码的最后一行,浏览器就退出了Java执行模式,并继将余下的HTML构建为DOM节点。在这期间,如果浏览器再次遇到脚本元素,那么从HTML到DOM的构建再次暂停,Java运行环境开始执行余下的Java代码。需要重点注意:Java应用在此时依然会保持着全局状态。所有在某个Java代码执行期间用户创建的全局变量都能正常地被其他脚本元素中的Java代码所访问到。其原因在于全局window对象会存在于整个页面的生存期之间,在它上面存储着所有的Java变量。只要还有没处理完的HTML元素和没执行完的Java代码,下面两个步骤都会一直交替执行。
1.将HTML构建为DOM。
2.执行Java代码。
最后,当浏览器处理完所有HTML元素后,页面构建阶段就结束了。随后浏览器就会进入应用生命周期的第二部分:事件处理。
1.3 事件处理客户端Web 应用是一种GUI应用,也就是说这种应用会对不同类型的事件作响应,如鼠标移动、单击和键盘按压等。因此,在页面构建阶段执行的Java代码,除了会影响全局应用状态和修改DOM外,还会注册事件监听器(或处理器)。这类监听器会在事件发生时,由浏览器调用执行。有了这些事件处理器,我们的应用也就有了交互能力。在详细探讨注册事件处理器之前,让我们先从头到尾看一遍事件处理器的总体 思想。
1.3.1 事件处理器概览浏览器执行环境的核心思想基于:同一时刻只能执行一个代码片段,即所谓的单线程执行模型。想象一下在银行柜台前排队,每个人进入一支队伍等待叫号并“处理”。但Java则只开启了一个营业柜台!每当轮到某个顾客时(某个事件),只能处理该位顾客。
你所需要的仅仅是一个在营业柜台(所有人都在这个柜台排队!)的职员为你处理工作,帮你订制全年的财务计划。当一个事件抵达后,浏览器需要执行相应的事件处理函数。这里不保证用户总会极富耐心地等待很长时间,直到下一个事件触发。所以,浏览器需要一种方式来跟踪已经发生但尚未处理的事件。为实现这个目标,浏览器使用了事件队列,如图1.8所示。
所有已生成的事件(无论是用户生成的,例如鼠标移动或键盘按压,还是服务器生成的,例如Ajax事件)都会放在同一个事件队列中,以它们被浏览器检测到的顺序排列。如图1.8的中部所示,事件处理的过程可以描述为一个简单的流程图。
浏览器检查事件队列头; 如果浏览器没有在队列中检测到事件,则继续检查; 如果浏览器在队列头中检测到了事件,则取出该事件并执行相应的事件处理器(如果存在)。在这个过程中,余下的事件在事件队列中耐心等待,直到轮到它们被处理。由于一次只能处理一个事件,所以我们必须格外注意处理所有事件的总时间。执行需要花费大量时间执行的事件处理函数会导致Web应用无响应!(如果听起来还不太明确,不要担心,以后我们还会学习事件循环,再看看它是如何损害Web应用在感受上的性能的)。
图1.8 客户端Web应用的周期从用户指定某个网站地址(或单击某个链接)开始。
其由两个步骤组成:页面构建和事件处理
重点注意浏览器在这个过程中的机制,其放置事件的队列是在页面构建阶段和事件处理阶段以外的。这个过程对于决定事件何时发生并将其推入事件队列很重要,这个过程不会参与事件处理线程。
事件是异步的事件可能会以难以预计的时间和顺序发生(强制用户以某个顺序按键或单击是非常奇怪的)。我们对事件的处理,以及处理函数的调用是异步的。如下类型的事件会在其他类型事件中发生。
浏览器事件,例如当页面加载完成后或无法加载时; 网络事件,例如来自服务器的响应(Ajax事件和服务器端事件); 用户事件,例如鼠标单击、鼠标移动和键盘事件; 计时器事件,当timeout时间到期或又触发了一次时间间隔。Web应用的Java代码中,大部分内容都是对上述事件的处理!
事件处理的概念是Web应用的核心,你在本文中的例子会反复看到:代码的提前建立是为了在之后的某个时间点执行。除了全局代码,页面中的大部分代码都将作为某个事件的结果执行。
在事件能被处理之前,代码必须要告知浏览器我们要处理特定事件。接下来看看如何注册事件处理器。
1.3.2 注册事件处理器前面已经讲过了,事件处理器是当某个特定事件发生后我们希望执行的函数。为了达到这个目标,我们必须告知浏览器我们要处理哪个事件。这个过程叫作注册事件处理器。在客户端Web应用中,有两种方式注册事件。
通过把函数赋给某个特殊属性; 通过使用内置addEventListener方法。例如,编写如下代码,将一个函数赋值给window对象上的某个特定属性:
1window. = function(){};
通过这种方式,事件处理器就会注册到load事件上(当DOM已经就绪并全部构建完成,就会触发这个事件)。(如果你对赋值操作符右边的记法有些困惑,不要担心,随后的章节中我们会细致地讲述函数)类似,如果我们想要为在文档中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"也会被添加到同样的元素上)。 现在你学习了如何创建事件处理器,让我们回忆下前面看到的简单流程图,然后仔细看看事件是如何被处理的。
1.3.3 处理事件事件处理背后的的主要思想是:当事件发生时,浏览器调用相应的事件处理器。如前面提到的,由于单线程执行模型,所以同一时刻只能处理一个事件。任何后面的事件都只能在当前事件处理器完全结束执行后才能被处理!
让我们回到清单1.1中的应用。图1.9展示了在用户快速移动和单击鼠标时的执行情况。
让我们看看这里发生了什么。为了响应用户的动作,浏览器把鼠标移动和单击事件以它们发生的次序放入事件队列:第一个是鼠标移动事件,第二个是单击事件序号1。
在事件处理阶段中,事件循环会检查队列,其发现队列的前面有一个鼠标移动事件,然后执行了相应的事件处理器序号2。当鼠标移动事件处理器处理完毕后,轮到了等待在队列中的单击事件。当鼠标移动事件处理器函数的最后一行代码执行完毕后,Java引擎退出事件处理器函数,鼠标移动事件完整地处理了序号3,事件循环再次检查队列。这一次,在队列的最前面,事件循环发现了鼠标单击事件并处理了该事件。一旦单击处理器执行完成,队列中不再有新的事件,事件循环就会继续循环,等待处理新到来的事件。这个循环会一直执行到用户关闭了Web应用。
图1.9 两个事件——鼠标移动和单击中的事件处理阶段示例
现在我们有了个总体的认识,理解了事件处理阶段的所有步骤。让我们看看这个过程是如何影响DOM的(如图1.10所示)。执行鼠标移动处理器时会选择第二个列表元素,其ID为second。
图1.10 当鼠标移动和鼠标点击事件都处理完成后,实例应用的DOM树结构
然后通过使用addMessage,使用文字“Event: mousemove”添加了一个新的列表项元素序号1。一旦鼠标移动处理器结束后,事件循环执行单击事件处理器,从而创建了另一个列表元素序号2,并附加在ID为second的第二个列表元素后。
对Web应用客户端的生命周期有了清晰的理解后,本文的下一部分,我们会开始聚焦于Java语言,理清函数的来龙去脉。
1.4 小结浏览器接收的HTML代码用作创建DOM的蓝图,它是客户端Web应用结构的内部展示阶段。 我们使用Java代码来动态地修改DOM以便给Web应用带来动态行为。 客户端Web应用的执行分为两个阶段。页面构建代码是用于创建DOM的,而全局Java代码是遇到节点时执行的。在这个执行过程中,Java代码能够以任意程度改变当前的DOM,还能够注册事件处理器——事件处理器是一种函数,当某个特定事件(例如,一次鼠标单击或键盘按压)发生后会被执行。注册事件处理器很容易:使用内置的addEventListener方法。 事件处理——在同一时刻,只能处理多个不同事件中的一个,处理顺序是事件生成的顺序。事件处理阶段大量依赖事件队列,所有的事件都以其出现的顺序存储在事件队列中。事件循环会检查实践队列的队头,如果检测到了一个事件,那么相应的事件处理器就会被调用。1.5 练习1.客户端Web应用的两个生命周期阶段是什么?
2.相比将事件处理器赋值给某个特定元素的属性上,使用addEventListener方法来注册事件处理器的优势是什么?
3.Java引擎在同一时刻能处理多少个事件?
4.事件队列中的事件是以什么顺序处理的?
1 </div>
2 <p id="copyright-declare">
3 本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。
4 </p>
5 </div>
本文摘自《Java忍者秘籍 第2版》
《Java忍者秘籍 第2版》
[美] John,Resig(莱西格),Bear,Bibeault(贝比奥特),Josip ... 著
点击封面购买纸书
Java语言非常重要,相关的技术图书也很多,但至今市面没有一本对Java语言的重要部分(函数、闭包和原型)进行深入、全面介绍的图书,也没有一本讲述跨浏览器代码编写的图书。而本书弥补了这一空缺,是由jQuery库创始人编写的一本深入剖析Java语言的书。《Java 忍者秘籍(第2版)》使用实际的案例清晰地诠释每一个核心概念和技术。本书向读者介绍了如何掌握 Java 核心的概念,诸如函数、闭包、对象、原型和 promise,同时还介绍了 Java API, 包括 DOM、事件和计时器。你将学会测试、跨浏览器开发,所有这些都是高级Java开发者应该掌握的技能。
延伸推荐2018年1月重磅新书小学生开始学Python,最接近AI的编程语言:安利一波Python书单政策升温:大家都在学大数据,一大波好书推荐一本基于Python语言的Selenium自动化测试书 8本新书,送出一本你喜欢的AI经典书单| 入门人工智能该读哪些书?点击关键词阅读更多新书:Python|机器学习|Kotlin|Java|移动开发|机器人|有奖活动|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
从上面的例子不难看出,内部类其实严重破坏了良好的代码结构,但为什么还要使用内部类呢?
因为内部类可以随意使用外部类的成员变量(包括私有)而不用生成外部类的对象,这也是内部类的唯一优点。
如同心脏可以直接访问身体的血液,而不是通过医生来抽血
程序编译过后会产生两个.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中可以发现,内部类在没有同名成员变量和局部变量的情况下,内部类会直接访问外部类的成员变量,而无需指定Out.this.属性名。
否则,内部类中的局部变量会覆盖外部类的成员变量。
而访问内部类本身的成员变量可用this.属性名,访问外部类的成员变量需要使用Out.this.属性名。
实例3:静态内部类
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 将内部内静态化,那么内部类就只能访问外部类的静态成员变量,具有局限性。
其次,因为内部类被静态化,因此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