选择特殊符号

选择搜索类型

热门搜索

首页 > 百科 > 建设工程百科

Java 2图形设计――卷Ⅱ:SWING(附CD)

《Java 2图形设计――卷Ⅱ:SWING(附CD)》是2000年2月机械工业出版社出版的图书,作者是(美)David M・Geary。 

Java 2图形设计――卷Ⅱ:SWING(附CD)基本信息

Java 2图形设计――卷Ⅱ:SWING(附CD)作品目录

译者序

前言

第一部分 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

查看详情

Java 2图形设计――卷Ⅱ:SWING(附CD)造价信息

  • 市场价
  • 信息价
  • 询价

上位机图形软件

  • V1.0
  • 迅捷
  • 13%
  • 深圳迅捷光通科技有限公司
  • 2022-12-08
查看价格

CRT图形显示系统软件

  • CRT-B-2
  • 北大青鸟
  • 13%
  • 佛山青鸟环宇消防设备有限公司
  • 2022-12-08
查看价格

设计系列盖板

  • 品种:分线盒盖;类别:设计系列边框;产品型号:MTN391960;产品组:EAT;库存类型:IND;交货期(工作日):40;最小起订量(个)
  • 施耐德
  • 13%
  • 上海随乐贸易有限公司
  • 2022-12-08
查看价格

设计系列盖板

  • 品种:分线盒盖;类别:设计系列边框;产品型号:MTN391919;产品组:EAT;库存类型:IND;交货期(工作日):40;最小起订量(个)
  • 施耐德
  • 13%
  • 上海随乐贸易有限公司
  • 2022-12-08
查看价格

钢质单片帘片

  • F2型跨度≤9m,配件另计
  • 中霍
  • 13%
  • 广东霍曼实业有限公司
  • 2022-12-08
查看价格

导线

  • DSJ23-122
  • 台班
  • 汕头市2012年2季度信息价
  • 建筑工程
查看价格

导线

  • DSJ23-122
  • 台班
  • 汕头市2011年3季度信息价
  • 建筑工程
查看价格

导线

  • DSJ23-122
  • 台班
  • 汕头市2011年1季度信息价
  • 建筑工程
查看价格

导线

  • DSJ23-122
  • 台班
  • 广州市2010年4季度信息价
  • 建筑工程
查看价格

导线

  • DSJ23-122
  • 台班
  • 汕头市2010年4季度信息价
  • 建筑工程
查看价格

专业CD

  • Tascam CD-160MK
  • 9636台
  • 1
  • 中档
  • 不含税费 | 含运费
  • 2015-05-28
查看价格

CD碟机CD2300

  • CD碟机CD2300
  • 1台
  • 1
  • 含税费 | 含运费
  • 2011-08-01
查看价格

CD-2A触发器

  • CD-2A触发器
  • 1个
  • 1
  • 亚明
  • 普通
  • 含税费 | 含运费
  • 2017-04-27
查看价格

CD

  • 播放格式:CD, CD-R, CD-RW, MP3-CD,
  • 1台
  • 1
  • PHILIPS
  • 中档
  • 含税费 | 含运费
  • 2021-11-29
查看价格

盆式支座GPZ()2SX

  • GPZ()2SX
  • 2个
  • 1
  • 不含税费 | 不含运费
  • 2011-07-07
查看价格

Java 2图形设计――卷Ⅱ:SWING(附CD)内容介绍

Swing是一流的Java图形用户界面开发工具。本书详细介绍了Swing的设计思想、体系结构、使用技巧,内容丰富、深入细致、分析透彻。本书用大量实例代码介绍了每个组件的用法,使初学者能很快入门;用大量图示分析了Swing组件的特点、结构及相互关系,使有经验的编程人员能高效利用Swing的强大功能。本书对掌握Swing技术提供了最全面的参考。

查看详情

Java 2图形设计――卷Ⅱ:SWING(附CD)常见问题

查看详情

Java 2图形设计――卷Ⅱ:SWING(附CD)文献

图形设计 图形设计

图形设计

格式:pdf

大小:2.4MB

页数: 24页

图形设计

图形设计论文 图形设计论文

图形设计论文

格式:pdf

大小:2.4MB

页数: 7页

图形设计论文

JavaWeb应用开发框架实例

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. 尽可能先借鉴系统中已有方案并复用之;如果有更好方案可替换之;

有一条设计准则是: 预先设计, 但不要过早设计。

意思是说, 需要对需求清楚的部分进行仔细的设计, 但是对于未知不清楚的需求,要坚持去理解它,但不要过早地去做出“预测性设计”;设计必须是明确的、清晰的、有效的, 不能针对含糊的东西来设计。可以在后期通过架构演进来获得对后续需求的适应能力。

查看详情

干货:JavaScript页面构建

点击关注 异步图书,置顶公众号每天与你分享 IT好书 技术干货 职场知识本文包括以下内容:Web应用的生命周期步骤 从HTML代码到Web页面的处理过程 Java代码的执行顺序 与事件交互 事件循环

我们对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版》

阅读原文

查看详情

Java中的内部类总结

内部类不是很好理解,但说白了其实也就是一个类中还包含着另外一个类。

如同一个人是由大脑、肢体、器官等身体结果组成,而内部类相当于其中的某个器官之一,例如心脏:它也有自己的属性和行为(血液、跳动)。

显然,此处不能单方面用属性或者方法表示一个心脏,而需要一个类。

而心脏又在人体当中,正如同是内部类在外部内当中。

实例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

查看详情

相关推荐

立即注册
免费服务热线: 400-888-9639