赞
踩
分类号_______________ 密级________________
UDC _______________ 学号_ _
毕业设计(论文)
论文题目 | Java扫雷游戏的设计与实现 |
---|---|
Thesis Topic | Design and Implementation of Mine Game |
毕业设计(论文)任务书
毕业设计(论文)题目:Java扫雷游戏的设计与实现毕业设计(论文)要求及原始数据(资料):1.学习和掌握计算机编程相关的基本知识;2.了解和运用JAVA面向对象的特征;3.熟悉JAVA的可移植性,跨平台性等多种特性;4.设计并实现扫雷游戏的基本功能;5.深入分析扫雷游戏的算法实现;6.训练检索文献资料和利用文献资料的能力;7.训练撰写技术文档与学位论文的能力。 |
---|
毕业设计(论文)主要内容:1.进行可行性分析以及需求分析;2.论述毕业设计所需开发环境以及开发工具;3.实现程序的概要设计; 4.在概要设计的基础上进行详细设计,编写实现各个类;5. 展示Java编程的扫雷游戏源代码;6.进行系统测试。学生应交出的设计文件(论文):1.内容完整、层次清晰、叙述流畅、排版规范的毕业设计论文;2.包括毕业设计论文、源程序等内容在内的毕业设计电子文档及其它相关材料。 |
---|
主要参考文献(资料):秦亮. 利用java实现扫雷游戏的算法解析. 软件开发与设计(2011年06期)张洪斌. Java2高级程序设计[M]. 中科多媒体出版社,2010孙鑫. Java Web 开发详解[M]. 电子工业出版社, 2006.189-274萨师煊,王珊. 数据库系统概论(第三版)[M]. 北京:高等教育出版社,2009.王家华. 软件工程[M]. 东北大学出版社,2012尹伟民. Java程序设计之网络编程. 北京:中国电力出版社, 2009赵生慧. Java面向对象程序设计. 北京:高等教育出版社, 2010王梅. Java并发程序—设计原则与模式. 北京:中国电力出版社, 2008Jon Titus. ECN Technical Editor: “The Eclipse of stand[J]. Journal of Zhongkai Agrotechnical College” , Vol.19,No.2, 2006.W.Clay,Richardson,Donald,”Avondolio. The Java high class weaves a distance: JDK 5”, Scientific & Technology Book Review,No.3,2006Alice Woudhuysen.China internet:The long march toward e-commerce[J].theeconomist intelligence unit. 2007 |
---|
Java扫雷游戏的设计与实现
扫雷这款游戏有着很长的历史,从扫雷被开发出来到现在进行了无数次的优化,这款游戏变得越来越让人爱不释手了,简单的玩法在加上一个好看的游戏界面,每一处的细节都体现了扫雷的魅力。所以本次的毕业设计我将开发一款扫雷游戏。
本次毕业设计是以JAVA语言作为开发环境,使用Eclipse设计并开发一个类似Windows扫雷的游戏,实现其基本功能。论文首先介绍了课题背景,其次进行了需求分析及可行性分析;然后设计游戏流程,介绍雷区中的雷怎么安放和产生雷的随机算法;最后介绍游戏中可能会触发的各种时间,比如鼠标点击时间和清理掉没有雷的格子,其中鼠标事件包括点击到或没有点击到雷触发的事件和点击到重新开始以及菜单触发的事件,清理掉没有雷的格子就需要使用“递归”的方法来使该功能可以简单的实现。
关键词:扫雷;Eclipse;事件;递归
Design and Implementation of Mine Game
Mine the game has a long history, from the mine was developed to now numerous optimization, the game is becoming more and more let a person fondle admiringly, simple style with a nice game interface, every detail reflects the charm of mine. So I'm going to develop a minesweeper game.
The graduate design study was designed with the JAVA language as a development environment, using Eclipse to design and develop a game like Windows minesweeper to implement its basic functions. The paper firstly introduces the background of the project and analyzes the requirements and feasibility analysis. Then the game process was designed to introduce the random algorithm of how the thunder in the minefield was placed and produced. Finally introduced the game may trigger a variety of time, such as a mouse click on the clear time and there is no ray of diamonds, mouse events including click to or no click to ray trigger events and click to restart and menu trigger events, clearing out squares no ray will need to use the "recursive" methods to make the function can be simple to implement.
Key words: Mine game; Visual Basic 6.0; Affairs; Recursion
摘 要i
Abstractii
1绪论1
1.1课题背景及意义1
1.2开发工具的选用及介绍1
1.3选题目的和意义2
1.4本文主要研究的内容2
2需求分析3
2.1可行性分析3
2.2扫雷游戏功能描述3
2.3扫雷游戏用例图4
2.4扫雷游戏功能需求4
2.5扫雷游戏界面需求5
2.6扫雷游戏功能模块6
3游戏的概要分析与设计7
3.1设计构想7
3.2流程规划8
3.3界面规划9
3.4算法思想9
4游戏的详细设计11
4.1游戏初始化11
4.2雷区的布置12
4.3游戏中主要模块的介绍与使用13
4.3.1鼠标事件13
4.3.2地雷及雷区表面探测情况14
4.3.3清除未靠近地雷的格子15
4.3.4游戏难度的选择16
4.3.5菜单栏的功能16
4.4游戏的判断16
4.4.1游戏成功完成16
4.4.2游戏失败16
4.5类设计17
4.5.1MineGame类17
4.5.2Block类18
4.5.3BlockView类19
4.5.4Record类20
4.5.5ShowRecord类21
4.5.6MineArea类22
4.5.7LayMines类23
5游戏实现25
5.1游戏难度自定义25
5.2扫雷28
5.2.1玩家通过右键进行扫雷,并显示小红旗28
5.2.2玩家因触碰到雷而导致游戏结束29
5.2.3玩家扫雷成功29
5.2.4玩家游戏数据显示30
5.3程序打包发布过程32
6游戏测试结果33
6.1游戏难度自定义测试33
6.2扫雷测试35
6.3玩家游戏数据显示测试37
6.4游戏数据显示区测试39
6.5递归算法测试43
结 论44
参考文献45
致 谢47
外文原文48
中文翻译55
图2-1 扫雷游戏用例图
从上述扫雷游戏功能描述以及用例图可以分析出,整个扫雷游戏中,玩家所能进行的操作有:选择难度、开始游戏、重新开始游戏和记录查询等。
图3-2 整体规划图
②
③
⑤
④
⑥
图3-3 规划样图
说明如下:
①:游戏主界面(Interface)。
②:菜单(Menu)。
③:地雷数显示区(MineNumberArea)
④:重新开始(Restart)。
⑤:扫雷用时显示区(TimeArea)。
⑥:地雷区(MineArea)。
扫雷数据显示区
地雷区
图4-1 游戏初始界面
游戏界面相对计算机自带的扫雷游戏比较简洁,主要由游戏数据显示区域以及雷区构成。
我们使用ImageIcon对象来存放地雷的图标,可以图标大小最好要小一些,否则会在格子上显示不全。
对地雷区的初始化是很重要的,我们使用一个循环即可实现初始化。玩家点开游戏之后可以点击菜单栏选择扫雷游戏的难度,不同的难度就代表了不同的地雷总数和尺寸,自定义的难度也会设置雷数和尺寸,设置的这些数据全部存放在几个变量之中,当雷区要初始化的时候程序会调用这么变量属性,然后通过一个循环来完成初始化。主要代码如下:
而之前所说的循环初始化其实就是循环的加载组件,雷区就是由一个个组件构成的,通过循环将所有的格子都布置到雷区,然后再随机产生地雷并布置到雷区上。
组件位置的摆放是按照其在数组中的位置来摆放的,所有的格子组件都存放在一个二维数组之中,组件在这个数组中的下标就是他在布局中的位置。
菜单栏是由菜单组件构成的,也就是JMenu组件,这个是菜单组件,用来定义未点击时的菜单样式,这个组件有可以设置JMenauItem组件,这是菜单项的意思,是当玩家点击了菜单后展示出来的子菜单栏。
扫雷游戏的计时区以及计数区又是使用另一种组件完成的,叫做JTextField
,这个组件是文本显示的组件,在设置了不可编辑的属性后,该组件在显示的外观上会有变化,边框会变灰一些,意味着该组件不能够点击或者输入。然后使用一个方法随着时间动态的在时间区以一秒为单位增加,计数区则会随着用户标记雷而减一,可以减到负数。
在设置界面的时候还需要进行一些设置,比如设置属性来让玩家在关闭游戏窗口时,系统会自动释放资源,并关闭窗口。
判断为左键且该格子为未探测状态:
布置地雷:
判断地雷应该被布置的位置:
组件名称:
这两个原型中的参数,Button参数值分别表示玩家用鼠标的左键或者右键进行点击的。其意义如下:
1:左键 2:右键
如果同时按两个按键,那么系统就会传回3,因为点左键是1,点右键是2,一起点就是两个值相加了。
在本次的扫雷设计中,我们会用鼠标的MouseUp事件来响应玩家的操作,其中鼠标左键用来点开玩家认为没有雷的格子,鼠标右键用来标记玩家认为下面藏有雷的格子。使用MouseaUp事件而不用MouseaDown事件的原因是,前者是在当鼠标按键抬起是触发,而后者是当按键按下就触发。假如玩家在点击某一个格子的时候突然发现好像点错了,此时玩家只要继续按下鼠标右键,再抬起的时候就会发现格子被标记成地雷了,现在的游戏一般都有这个功能,这可以有效的提升玩家的游戏体验。
在人机交互的界面上,鼠标的操作是很重要的,但是程序的设计不合理会使鼠标无法发挥应有的功能。在鼠标点击事件中使用MouseUp来响应事件在上面也进行了解释,这样就可以很巧妙的让玩家体会到游戏的贴心之处,可以有效的提升玩家的游戏体验。
当鼠标左键点击格子时:
当鼠标右击标识有雷的格子时:
图4-2地雷随机生成
图4-3 地雷被标记
玩家通过单击格子来点开格子,右击格子标记地雷。由于使用了两个数组记录地雷的位置以及被点开的信息,所以可以通过两层嵌套循环来判断该格子周围雷的数目,而且系统会点开该格子周围8个格子中没有雷的格子,这又需要用到递归调用来一直进行这个点开格子并验证的过程,直到不符合条件为止。利用这种嵌套循环以及递归调用,都会使一个耗时耗力的问题变得容易解决起来,使得代码结构也变得清晰明了,并且会提高系统的计算速度。
图4-4 MineGame类的类图
(1)成员变量
bar 是窗体的菜单栏变量。
fileMenu1 是扫雷游戏的菜单,叫做“游戏”。
fileMenu2 是扫雷的另一个菜单,叫做“帮助”。
初级 是扫雷游戏的难度变量。
中级 是扫雷游戏的难度变量。
高级 是扫雷游戏的难度变量。
扫雷榜 是扫雷的统计信息变量。
mineArea 是MineArea雷的对象,是设计雷区的变量。
file 是文件变量,用来读取扫雷记录。
hashtable 是用来临时存放扫雷记录。
showHeroRecord 是用来显示扫雷记录的变量。
(2)成员方法
MiwneGaame()通过该方法设置了游戏整体的位置和布局,并实现菜单栏功能,初始化统计信息。
actionPerformed(ActionEvent e)是响应点击菜单项的方法,通过该方法可以实现选择不同难度调整游戏的雷数和尺寸。玩家点选不同的菜单项会让该方法执行不同的操作。
main()方法是游戏开始的方法,通过new了一个MineGame的对象来实现游戏的开始。
图4-5 Block类的类图
图4-6 BlockView类的类图
图4-7 Record类的类图
ShowRecord类是显示扫雷记录的类。展示该类的UML图如图4-8所示。
图4-8 ShowRecord类的类图
ShowRecord() 方法是该类的构造方法,初始化成员变量。
readAndShow() 方法会把在文档中记录的玩家通关信息筛选出来(按游戏完成是花费的时间最短为条件筛选),并显示到弹出的对话框中。
actionPerformed(ActionEvent e)方法是响应玩家操作的方法,当玩家想要显示游戏记录的时候系统会响应玩家的要求并调用显示成绩的方法,在弹框中刷新成绩。
MineArea类主要用于雷区的初始化以及鼠标事件的响应。展示该类的UML图如图4-9所示。
图4-9 MineArea类的类图
MineArea()方法是该类的构造方法,初始化组件和一下对象,设置字体颜色、边框颜色等,并调用初始化雷区的方法来初始化雷区。
initMineArea()方法用来设置雷数和游戏界面所有格子的数量,初始化雷区。
initMine()方法是真正执行初始化的方法。
setRow(int row)方法用来设置行数。
setColum(int colum)方法用来设置列数。
setMineCount(int mineCount)方法用来设置地雷总数。
setGrade(int grade)方法用来设置难度。
actionPerformed(ActionEvent)方法是响应鼠标点击格子的事件的方法,当点击的格子下面没有雷,该方法会显示该格子周围雷的数目,有雷则显示雷的图标。
show()方法是显示格子内容,并且使用递归调用来显示周围格子的内容,直到这个格子周围有雷。
mousePressed(MouseEvent)方法响应玩家对格子进行的鼠标左键单击事件和鼠标右键点击事件。
inquireWin()方法是在玩家在游戏过程中满足获胜条件是触发的方法,同时会调用其他方法弹框提示玩家游戏结束。
LayMines是计算不是雷的格周围雷个数的类,以及设置点选之后的图片样式。标明该类的UML图如图4-10所示。
图4-10 LayMines类的类图
LayMines() 构造方法,定义图片资源。
layMinesForBlock(Block block[][], int mineCount) 方法会判断是否是第一次点击格子,如果玩家是第一次点击格子,开始随机产生雷,但是雷不会布置到第一次点的格子上。如果不是第一次点击且该格子没有雷时会计算周围8个格子中有雷的数目并显示出来。
图5-1 选择难度以及游戏自定义
图5-2 初级难度
图5-3 中级难度
图5-4 高级难度
图5-5 自定义难度
如图5-1所示,玩家可以自己选择游戏难度,其中有初级,中级,高级以及自定义四种难度,所对应的游戏界面的尺寸也不相同,以及地雷的总数也不相同,雷数越多则难度越大,同时玩家可以自定义游戏难度,通过输入行列数以及地雷数设置游戏界面的大小以及难度。
如图5-2、图5-3、图5-4、图5-5所示,分别展示了初级、中级、高级以及自定义难度的游戏界面尺寸和雷数。
图5-6 扫雷图
玩家在游戏过程中会对某一格周围是否有雷进行判断,当玩家认为格子下有雷时就可以通过使用鼠标右键点击该格子,将该格子标识为小红旗如图5-6所示。
图5-7 扫雷失败图
如图5-7所示,当玩家在点开了有雷的格子时,游戏失败,系统会自动将所有的地雷显示出来,并且会弹出一个窗口提示玩家:“你输了,请继续努力!”。除此以外还有两个选项,当玩家选择重新开始按钮时,游戏界面初始化,雷区中的雷全部取消,等待玩家第一次点击格子后开始布雷;如果用户点的是取消的话,就可以关掉这个提示的弹框。
图5-8 扫雷成功图
如图5-8所示,当所有的地雷用都被玩家用小红旗标识出来,并且非雷的格子也被点开时游戏结束,计时停止,系统弹框提醒玩家“您真厉害,请输入您的名字,记录上榜!”,玩家可以在这个界面输入自己的名字来保存扫雷成绩。
图5-9扫雷榜图
如图5-9所示,每当玩家成功扫完所有的雷后,系统会自动的记录本次的成绩,保存在本地的txt文档中,内容是三种难度,玩家姓名以及对应难度玩家所用的最少游戏时间。当玩家点击查看英雄榜的时候系统默认只显示三种难度和匿名。
图5-10 最佳成绩显示图
图5-11 重新记分显示图
如图5-10所示,玩家点击关闭时此窗口会关掉这个弹窗,当点击重新记分时,统计信息会清空,点击显示成绩就好刷新并将历史最好的成绩显示出来。界面也会发生变化,可以显示出游戏用时。
将清单文件保存到E:\workspace\扫雷\bin\com\zx\mine中,也就是和编写的扫雷游戏程序生成的字节码存放的位置相同,保证这些文件放在一个目录下。
需要注意的是,清单文件中的所有内容均为键值对的形式存放,键值直接必须空一个空格。
预期结果 | 选择不同的难度后可以正确的初始化雷区 |
---|---|
输入条件 | 分别点击初级、中级、高级和自定义 |
测试结果 | 点选不同难度后系统会自动改变雷区大小和地雷数 |
图6-1 游戏难度以及自定义测试
图6-2 初级难度测试
图6-3中级难度测试
图6-4 高级难度测试
图6-5 自定义难度测试
如图6-1所示,本次扫雷游戏设计将游戏的难度设定为初、中、高以及自定义四种难度,前三种难度对应的游戏界面的尺寸以及地雷总数是不同的,难度越大雷数越多,尺寸也越大,玩家扫雷时就需要更加的动脑思考才能通关。除了固定的难度以外还可以自定义难度,玩家需要自己输入尺寸与雷数,系统根据玩家的要求生产新的游戏,难度可以更加的灵活,这样可以让玩家更满意。
图6-2、图6-3、图6-4、图6-5测试了四种难度的选择,由图可以看出难度的选择功能正常,可以自由变换尺寸和地雷总数。
预期结果 | 成功的将所有地雷标记出来后,游戏可以立马结束且会弹框提示输入玩家名字 |
---|---|
输入条件 | 将所有地雷标记并将非雷格子点开 |
测试结果 | 游戏结束并且显示了要求玩家输入名字的弹框 |
扫雷失败测试用例:
表6-3 扫雷失败测试用例表
预期结果 | 点击了有雷的格子后游戏可以立马结束且弹框提示玩家游戏失败 |
---|---|
输入条件 | 点击有雷的格子 |
测试结果 | 游戏正确的结束且弹框提示了游戏失败的提示 |
图6-6 游戏完成并且扫雷成功测试
如图6-6所示,玩家可以通过左键单击格子点开格子,右键单击标识地雷的方式完成游戏。当玩家将所有的地雷都标识出来且其他的格子都被点开的情况下,游戏胜利。系统会弹框提示玩家成功完成了游戏任务“您真厉害,请输入您的名字,记录上榜!”。玩家可以在弹出的弹框中输入自己的名字,若不输入则默认为匿名,然后系统会保存玩家的游戏记录。当玩家点击扫雷榜的时候可以查看自己用时最短的游戏记录。
图6-7 游戏完成但是扫雷失败测试
如图6-7所示,玩家可以通过左键单击格子点开格子,右键单击标识地雷的方式完成游戏。当玩家在进行扫雷游戏的时候,不小心点击了有雷的格子,这时候地雷被引爆了,所有未被标记的和已被标记的都被引爆,游戏结束。此时系统会自动将所有的地雷显示出来,方便玩家查看之前标记的正确与否,可以总结经验。然后系统弹框提示“你输了,请继续努力!”,玩家可以点击重新开始的按钮重开游戏,继续挑战扫雷游戏,也可以选择结束游戏。
预期结果 | 点击“最佳成绩”按钮时会显示玩家最佳的游戏记录 |
---|---|
输入条件 | 打开游戏的扫雷榜,点击“最佳成绩”按钮 |
测试结果 | 游戏可以正确的显示玩家的游戏记录 |
重新记分测试用例:
表6-5 重新记分测试用例表
预期结果 | 点击“重新记分”按钮时会清除玩家以往的游戏记录 |
---|---|
输入条件 | 打开游戏的扫雷榜,点击“重新记分”按钮 |
测试结果 | 点击按钮后游戏记录消失,再点击“最佳成绩”按钮只会显示默认的成绩 |
图6-8 扫雷榜测试
图6-9 最佳成绩测试
图6-10 重新记分测试
如图6-8和图6-9所示,玩家在进行游戏时,如果成功的扫完雷,系统会提示并保存游戏记录。在扫雷榜上玩家可以进行查看。但是当玩家反复通关后,系统会自动的将用时最短的扫雷记录显示出来,所以玩家在查看记录的时候只能看到最快一次通关的记录。
通过图6-10可以看出,当玩家点击了“重新记分”按钮后,之前游戏记录都会被清除掉,只会显示默认的数据:时间为999秒,名字是匿名。
预期结果 | 第一次点击雷区的格子时,计时区开始计时,游戏结束时计时停止,重新开始游戏时,计时归零 |
---|---|
输入条件 | 点击雷区的任意一格,然后结束游戏,最后点击“重新开始”按钮 |
测试结果 | 当点击雷区任意一格后计时开始,计时正常。然后点击有雷的格子结束游戏发现计时停止。点击“重新开始”后计时区的计时正确归零 |
图6-11 开始计时测试
图6-12 计时停止测试
图6-13 重新计时测试
如图6-11,图6-12,图6-13所示,当点击雷区中的任意一格后,计时区开始正常计时。当游戏结束时计时区的计时就会停止,最后点击了“重新开始”后计时区的计时归零。
地雷数显示区测试用例:
表6-7 地雷数显示区测试用例表
预期结果 | 标记地雷后计数区的数字会减1,如果标记数超过地雷数,计数区会用负数显示 |
---|---|
输入条件 | 先标记一颗地雷,然后将所有的地雷标记出来 |
测试结果 | 标记一颗地雷后,计数区可以正确的减少1个,然后当所有的地雷全被标记后,如果继续标记,计数区可以正确的以负数表示 |
图6-14 计数区测试
图6-15 计数区负数显示测试
如图6-14,图6-15所示,当标记了一颗地雷后计数区中的数字会相应的减少一个,表示有一颗雷被找到了。但是如果标记数超过了地雷的数目,计数区会以负数显示,表示有的地雷标记错了
。
递归调用测试用例:
表6-8 递归调用测试用例表
预期结果 | 当点击一个周围八个格子中都没有雷的格子时,系统会将这8个格子也点开 |
---|---|
输入条件 | 点开一个周围没有雷的格子 |
测试结果 | 系统正确的将周围的八个格子点开,如果这些格子中周围的格子没有雷的话还会自动点开 |
图6-16 递归算法测试
如图6-16所示,当点击一个周围八个格子中都没有雷的格子时,系统会将周围的格子点开,被点开的格子也会显示周围的地雷数。如果被点开的格子周围还是没有地雷,系统就会递归调用再把周围的格子点开。
扫雷游戏的代码编写完成,且基本功能也实现了,虽然与电脑上自带的扫雷游戏相比在界面上还是有着一些差距,一些小细节也没有完善,但已经让我很满意了,我会在以后继续完善这些细节,让这个扫雷游戏变得愈来愈好玩。在简单的测试后,该游戏也达到了我的预期,所以本次的设计就到此结束了。
毕业设计对于我们来说并不是简单的一个毕业考核而已,这是我们的一个机会,一个将大学四年所学到的知识结合起来,通过自己的构思完成一个系统的机会。在完成这个系统的过程中,一定会遇到许许多多的问题,这些问题就犹如大山挡在我们的面前,如何去解决这些问题也是我们要思考的。而当我们把困难都解决掉,成功完成毕业设计的时候,就说明我们成功的摆脱了只会理论知识,不会解决实际问题的状态,锻炼了我们学习新事物并加以运用以及发现问题并找到方法去解决的能力。
虽然这次的扫雷游戏看似简单,但是其中却涉及了GUI设计,这一部分在学校是没有深讲的,需要我自己去学习。其中有各种组件的调用,各种控件的使用等,我通过本次的毕业设计又学到了许多的新知识。
通过编写这次的扫雷游戏,我深刻的认识到了细节问题的重要性,这些问题看似无关紧要但是如果忽视会养成不好的编程习惯,所以我们要着眼于细节,善于去发现身边的一些小事情,让自己的水平不断提高。而且我的编程能力在不断的编写代码的过程中也有了显著的提高。很多时候人们都认为知识学过了就行,但是当实际去用的时候就会发现这些知识是多么的陌生。所以我认为学过的知识就要多用,用实际操作来让知识真正变成自己的。
[1] 耿祥义.Java大学实用教程[M].北京:清华大学出版社,2009.
[2] 冯锋,王运坚.Visual Basic 程序设计基础教程[M].西安:电子工业出版社,1999。
[3] 王鹏.JavaSwing图形界面开发与案例详解[M].北京:清华大学出版社,2008。
[4] 李怀明.Visual Basic 6.0 中文版 参考详解[M].北京:清华大学出版社,1999。
[5] 丁振凡.Java语言实验教程[M].北京:北京邮电大学出版社,2005。
[6] 郑莉.Java语言程序设计[M].北京:清华大学出版社,2006。
[7] 伍俊良.VB课程设计与系统开发案例[M].北京:清华大学出版社,2002。
[8] 孙全党,王吴迪,赵枫朝.Java程序设计应用教程[M].北京:电子工业出版社,2006。
[9] 雷之宇.Java项目开发实践-网络篇[M].北京:中国铁道出版社,2005。
[10] 赵玉阳.Java从入门到精通[M].北京:清华大学出版社,2006。
[11] 何斌,刘醒.Visual Basic 6.0 应用指南[M].成都:四川大学出版社,1998。
[12] 毕广吉.Java程序设计实例教程[M].北京:冶金工业出版社,2007年。
[13] 李善茂.Visual Basic 6.0 高级编程技巧[M].西安:电子工业出版社,1999。
[14] 王保罗.Java面向对象程序设计[M].北京:清华大学出版社,2003年.人民邮电出版社,2004
[15] 刘腾红,孙细明.信息系统分析与设计[M].北京:科学出版社,2003年。
[16] 林邦杰,彻底研究java[M].北京:电子工业出版社,2002年。
[17] 刘京华. Java Web整合开发王者归来[M].北京:清华大学出版社,2010。
[18] 唐任仲.工程应用软件开发技术[M].北京:化学工业出版社,1999。
[19] 谭浩强.Visual Basic 6.0 中文版 提高与应用[M].西安:电子工业出版社,1999。
[20] (美)阿诺德,Ken Arnold,等. Java程序设计语言[M].北京:人民邮电出版社,2006.
[21] Bruce Eckel. Thinking in Java[M]. Upper Saddle River, New Jersey, USA: Prentice Hall, 2006.
[22] Holger Eichelberger,Klaus Schhmid.Flexible resource monitoring ofJava programs.[J].The Journal of Systems & Software,2014 Elsevier.
[23] Joshua Bloch. Effective Java[M]. Piscataway, N.J: IEEE Press, 2009.
[24] oug Twilleager,Jeff Kesselman,Athomas Goldberger,Daniel Petersen,Juan Carlos,Soto,Chris Melissinos.Java Technologies for games.[J].Computers in Entertainment(CIE),2004,Vol.2(2),pp.18-18 ACM.
感谢杰普基地的老师以及校内指导老师,在这次我的毕业设计过程中,老师们对我的悉心的指导,从一开始的开题报告与选择到后来的系统设计,从程序设计到论文的编写,他们纠正了我许多的不成熟的想法和考虑,给我提出了许多宝贵的建议,而且在程序设计与开发过程中遇到的最困难的时候给予我继续努力完成设计的信心。老师们严谨的作风和对我的严格要求,使我克服了懒惰和投机取巧的毛病,能过不断战胜自己,取得进步。
通过这次毕业设计的整个开发过程,我系统开发过程从需求分析到具体功能实现,再到最终测试和维护的理解有了很大的进步,让我对系统开发有了更深层次的认识。现在我的动手能力和独立解决问题的能力也得到了很大的锻炼和提高,这是这次毕业设计最好的收获。
最后,在整个系统开发过程中,我身边的同学和朋友给了我很多的建议,让我很快的确定了系统的业务逻辑。在次,我衷心的向他们表示感谢。
在论文完成过程中,本人还得到了其他老师和许多同学的热心帮助,本人向他们表示深深的谢意!
最后向在百忙之中评审本文的各位专家、老师表示衷心的感谢!
Thinking in Java
Learning Java
At about the same time that my first book, Using C++ (Osborne/McGraw-Hill, 1989), came out, I began teaching that language. Teaching programming ideas has become my profession; I’ve seen nodding heads, blank faces, and puzzled expressions in audiences all over the world
since 1987. As I began giving in-house training with smaller groups of people, I discovered something during the exercises. Even those people who were smiling and nodding were confused about many issues. I found out, by creating and chairing the C++ track at the Software Development Conference for a number of years (and later creating and chairing the Java track), that I and other speakers tended to give the typical audience too many topics too quickly. So eventually, through both variety in the audience level and the way that I presented the material, I would end up losing some portion of the audience. Maybe it’s asking too much, but because I am one of those people resistant to traditional lecturing (and for most people, I believe, such resistance results from boredom), I wanted to try to keep everyone up to speed. For a time, I was creating a number of different presentations in fairly short order. Thus, I ended up learning by experiment and iteration (a technique that also works well in program design). Eventually, I developed a course using everything I had learned from my teaching experience. My company, MindView, Inc., now gives this as the public and in-house Thinking in Java seminar; this is our main introductory seminar that provides the foundation for our more advanced seminars. You can find details at www.MindView.net. (The introductory seminar is also available as the Hands-On Java CD ROM. Information is available at the same Web site.)
The feedback that I get from each seminar helps me change and refocus the material until I think it works well as a teaching medium. But this book isn’t just seminar notes; I tried to pack as much information as I could within these pages, and structured it to draw you through into the next subject. More than anything, the book is designed to serve the solitary reader who is struggling with a new programming language.
The singly rooted hierarchy
One of the issues in OOP that has become especially prominent since the introduction of C++ is whether all classes should ultimately be inherited from a single base class. In Java (as with virtually all other OOP languages except for C++) the answer is yes, and the name of this ultimate base class is simply Object. It turns out that the benefits of the singly rooted hierarchy are many.
All objects in a singly rooted hierarchy have an interface in common, so they are all ultimately the same fundamental type. The alternative (provided by C++) is that you don’t know that everything is the same basic type. From a backward-compatibility standpoint this fits the model of C better and can be thought of as less restrictive, but when you want to do full-on objectoriented programming you must then build your own hierarchy to provide the same convenience that’s built into other OOP languages. And in any new class library you acquire, some other incompatible interface will be used. It requires effort (and possibly multiple inheritance) to work the new interface into your design. Is the extra “flexibility” of C++ worth it? If you need it—if you have a large investment in C—it’s quite valuable. If
you’re starting from scratch, other alternatives such as Java can often be more productive.
All objects in a singly rooted hierarchy can be guaranteed to have certain functionality. You know you can perform certain basic operations on every object in your system. All objects can easily be created on the heap, and argument passing is greatly simplified. A singly rooted hierarchy makes it much easier to implement a garbage collector, which is one of the fundamental improvements of Java over C++. And since information about the type of an object is guaranteed to be in all objects, you’ll never end up with an object whose type you cannot determine. This is especially important with system-level operations, such as exception handling, and to allow greater flexibility in programming.
Containers
In general, you don’t know how many objects you’re going to need to solve a particular problem, or how long they will last. You also don’t know how to store those objects. How can
you know how much space to create if that information isn’t known until run time?
The solution to most problems in object-oriented design seems flippant: You create another type of object. The new type of object that solves this particular problem holds references to other objects. Of course, you can do the same thing with an array, which is available in most
languages. But this new object, generally called a container (also called a collection, but the Java library uses that term in a different sense so this book will use “container”), will expand itself whenever necessary to accommodate everything you place inside it. So you don’t need to know how many objects you’re going to hold in a container. Just create a container object
and let it take care of the details.
Fortunately, a good OOP language comes with a set of containers as part of the package. In C++, it’s part of the Standard C++ Library and is often called the Standard Template Library(STL). Smalltalk has a very complete set of containers. Java also has numerous containers inits standard library. In some libraries, one or two generic containers is considered good enough for all needs, and in others (Java, for example) the library has different types of containers for different needs: several different kinds of List classes (to hold sequences), Maps (also known as associative arrays, to associate objects with other objects), Sets (to hold one of each type of object), and more components such as queues, trees, stacks, etc.
From a design standpoint, all you really want is a container that can be manipulated to solve your problem. If a single type of container satisfied all of your needs, there’d be no reason to have different kinds. There are two reasons that you need a choice of containers. First, containers provide different types of interfaces and external behavior. A stack has a different interface and behavior than a queue, which is different from a set or a list. One of these might provide a more flexible solution to your problem than the other. Second, different containers have different efficiencies for certain operations. For example, there are two basic types of List: ArrayList and LinkedList. Both are simple sequences that can have identical interfaces and external behaviors. But certain operations can have significantly different costs. Randomly accessing elements in an ArrayList is a constant-time operation; it takes the same amount of time regardless of the element you select. However, in a LinkedList it is expensive to move through the list to randomly select an element, and it takes longer to find an element that is farther down the list. On the other hand, if you want to insert an element in the middle of a sequence, it’s cheaper in a LinkedList than in an ArrayList. These and other
operations have different efficiencies depending on the underlying structure of the sequence. You might start building your program with a LinkedList and, when tuning for performance,change to an ArrayList. Because of the abstraction via the interface List, you can change from one to the other with minimal impact on your code.
Object creation & lifetime
One critical issue when working with objects is the way they are created and destroyed. Each object requires resources, most notably memory, in order to exist. When an object is no longer needed it must be cleaned up so that these resources are released for reuse. In simple programming situations the question of how an object is cleaned up doesn’t seem too challenging: You create the object, use it for as long as it’s needed, and then it should be destroyed. However, it’s not hard to encounter situations that are more complex.
Suppose, for example, you are designing a system to manage air traffic for an airport. (The same model might also work for managing crates in a warehouse, or a video rental system, or a kennel for boarding pets.) At first it seems simple: Make a container to hold airplanes, then create a new airplane and place it in the container for each airplane that enters the air-trafficcontrol zone. For cleanup, simply clean up the appropriate airplane object when a plane leaves the zone.
But perhaps you have some other system to record data about the planes; perhaps data that doesn’t require such immediate attention as the main controller function. Maybe it’s a record of the flight plans of all the small planes that leave the airport. So you have a second container of small planes, and whenever you create a plane object you also put it in this second container if it’s a small plane. Then some background process performs operations on the objects in this container during idle moments.
Now the problem is more difficult: How can you possibly know when to destroy the objects? When you’re done with the object, some other part of the system might not be. This same
problem can arise in a number of other situations, and in programming systems (such as C++) in which you must explicitly delete an object when you’re done with it this can become quite complex.
Where is the data for an object and how is the lifetime of the object controlled? C++ takes the approach that control of efficiency is the most important issue, so it gives the programmer a choice. For maximum runtime speed, the storage and lifetime can be determined while the program is being written, by placing the objects on the stack (these are sometimes called
automatic or scoped variables) or in the static storage area. This places a priority on the speed of storage allocation and release, and this control can be very valuable in some situations. However, you sacrifice flexibility because you must know the exact quantity, lifetime, and type of objects while you’re writing the program. If you are trying to solve a more general problem such as computer-aided design, warehouse management, or air-traffic control, this is too restrictive.
The second approach is to create objects dynamically in a pool of memory called the heap. In this approach, you don’t know until run time how many objects you need, what their lifetime is, or what their exact type is. Those are determined at the spur of the moment while the program is running. If you need a new object, you simply make it on the heap at the point that you need it. Because the storage is managed dynamically, at run time, the amount of time required to allocate storage on the heap can be noticeably longer than the time to create storage on the stack. Creating storage on the stack is often a single assembly instruction to move the stack pointer down and another to move it back up. The time to create heap storage depends on the design of the storage mechanism.
The dynamic approach makes the generally logical assumption that objects tend to be complicated, so the extra overhead of finding storage and releasing that storage will not have an important impact on the creation of an object. In addition, the greater flexibility is essential to solve the general programming problem.
Java uses dynamic memory allocation, exclusively. Every time you want to create an object, you use the new operator to build a dynamic instance of that object. There’s another issue, however, and that’s the lifetime of an object. With languages that allow objects to be created on the stack, the compiler determines how long the object lasts and can automatically destroy it. However, if you create it on the heap the compiler has no knowledge of its lifetime. In a language like C++, you must determine programmatically when to destroy the object, which can lead to memory leaks if you don’t do it correctly (and this is a common problem in C++
programs). Java provides a feature called a garbage collector that
automatically discovers when an object is no longer in use and destroys it. A garbage collector is much more convenient because it reduces the number of issues that you must track and the code you must write. More importantly, the garbage collector provides a much
higher level of insurance against the insidious problem of memory leaks, which has brought many a C++ project to its knees.
With Java, the garbage collector is designed to take care of the problem of releasing the memory (although this doesn’t include other aspects of cleaning up an object). The garbage collector “knows” when an object is no longer in use, and it then automatically releases the memory for that object. This, combined with the fact that all objects are inherited from the single root class Object and that you can create objects only one way—on the heap—makes
the process of programming in Java much simpler than programming in C++. You have far fewer decisions to make and hurdles to overcome.
Java编程思想
Java的学习
在我第一本书《Using C++》面市的几乎同一时间(Osborne/McGraw-Hill 于 1989 年出版),我开始教授那种语言。程序设计语言的教授已成为我的专业。自 1989 年以来,我便在世界各地见过许多昏昏欲睡、满脸茫然以及困惑不解的面容。开始在室内面向较少的一组人授课以后,我从作业中发现了一些特别的问题。即使
那些上课面带会心的微笑或者频频点头的学生,对许多问题也存在认识上的混淆。在过去几年间的“软件开发会议”上,由我主持 C++分组讨论会(现在变成了 Java 讨论会)。有的演讲人试图在很短的时间内向听众灌输过多的主题。所以到最后,尽管听众的水平都还可以,而且提供的材料也很充足,但仍然损失了一部分
听众。这可能是由于问得太多了,但由于我是那些采取传统授课方式的人之一,所以很想使每个人都能跟上讲课进度。
有段时间,我编制了大量教学简报。经过不断的试验和修订(或称“反复”,这是在 Java 程序设计中非常有用的一项技术),最后成功地在一门课程中集成了从我的教学经验中总结出来的所有东西——我在很长一段时间里都在使用。其中由一系列离散的、易于消化的小步骤组成,而且每个小课程结束后都有一些适当的练习。我目前已在 Java 公开研讨会上公布了这一课程,大家可到 http://www.BruceEckel.com 了解详情(对研讨会的介绍也以 CD-ROM 的形式提供,具体信息可在同样的 Web 站点找到)。 从每一次研讨会收到的反馈都帮助我修改及重新制订学习材料的重心,直到我最后认为它成为一个完善的教学载体为止。但本书并非仅仅是一本教科书——我尝试在其中装入尽可能多的信息,并按照主题进行了有序的分类。无论如何,这本书的主要宗旨是为那些独立学习的人士服务,他们正准备深入一门新的程序设计语言,而没有太大的可能参加此类专业研讨会。
单根结构
在面向对象的程序设计中,由于 C++的引入而显得尤为突出的一个问题是:所有类最终是否都应从单独一个基础类继承。在 Java 中(与其他几乎所有 OOP 语言一样),对这个问题的答案都是肯定的,而且这个终级基础类的名字很简单,就是一个“Object”。这种“单根结构”具有许多方面的优点。
单根结构中的所有对象都有一个通用接口,所以它们最终都属于相同的类型。另一种方案(就象 C++那样)是我们不能保证所有东西都属于相同的基本类型。从向后兼容的角度看,这一方案可与 C 模型更好地配合,而且可以认为它的限制更少一些。但假期我们想进行纯粹的面向对象编程,那么必须构建自己的结构,以期获得与内建到其他 OOP 语言里的同样的便利。需添加我们要用到的各种新类库,还要使用另一些不兼容的接口。理所当然地,这也需要付出额外的精力使新接口与自己的设计方案配合(可能还需要多重继承)。为得到 C++额外的“灵活性”,付出这样的代价值得吗,当然,如果真的需要——如果早已是 C 专家,如果对 C有难舍的情结——那么就真的很值得。但假如你是一名新手,首次接触这类设计,象 Java 那样的替换方案也许会更省事一些。 单根结构中的所有对象(比如所有 Java 对象)都可以保证拥有一些特定的功能。在自己的系统中,我们知道对每个对象都能进行一些基本操作。一个单根结构,加上所有对象都在内存堆中创建,可以极大简化参数的传递(这在 C++里是一个复杂的概念)。 利用单根结构,我们可以更方便地实现一个垃圾收集器。与此有关的必要支持可安装于基础类中,而垃圾收集器可将适当的消息发给系统内的任何对象。如果没有这种单根结构,而且系统通过一个句柄来操纵对象,那么实现垃圾收集器的途径会有很大的不同,而且会面临许多障碍。
由于运行期的类型信息肯定存在于所有对象中,所以永远不会遇到判断不出一个对象的类型的情况。这对系统级的操作来说显得特别重要,比如违例控制;而且也能在程序设计时获得更大的灵活性。但大家也可能产生疑问,既然你把好处说得这么天花乱坠,为什么 C++没有采用单根结构呢,事实上,这是早期在效率与控制上权衡的一种结果。单根结构会带来程序设计上的一些限制。而且更重要的是,它加大了新程序与原有代C码兼容的难度。尽管这些限制仅在特定的场合会真的造成问题,但为了获得最大的灵活程度,C++最终决定放弃采用单根结构这一做法。而 Java 不存在上述的问题,它是全新设计的一种语言,不必
与现有的语言保持所谓的“向后兼容”。所以很自然地,与其他大多数面向对象的程序设计语言一样,单根结构在 Java 的设计方案中很快就落实下来。
集合与继承器
针对一个特定问题的解决,如果事先不知道需要多少个对象,或者它们的持续时间有多长,那么也不知道如何保存那些对象。既然如此,怎样才能知道那些对象要求多少空间呢,事先上根本无法提前知道,除非进入运行期。
在面向对象的设计中,大多数问题的解决办法似乎都有些轻率——只是简单地创建另一种类型的对象。用于解决特定问题的新型对象容纳了指向其他对象的句柄。当然,也可以用数组来做同样的事情,那是大多数语言都具有的一种功能。但不能只看到这一点。这种新对象通常叫作“集合”(亦叫作一个“容器”,但 AWT
在不同的场合应用了这个术语,所以本书将一直沿用“集合”的称呼。在需要的时候,集合会自动扩充自己,以便适应我们在其中置入的任何东西。所以我们事先不必知道要在一个集合里容下多少东西。只需创建一个集合,以后的工作让它自己负责好了。 幸运的是,设计优良的 OOP 语言都配套提供了一系列集合。在 C++中,它们是以“标准模板库”(STL)的形式提供的。 Object Pascal 用自己的“可视组件库”(VCL)提供集合。 Smalltalk 提供了一套非常完整的集合。而 Java 也用自己的标准库提供了集合。在某些库中,一个常规集合便可满足人们的大多数要求;而在另一些库中(特别是C++的库),则面向不同的需求提供了不同类型的集合。例如,可以用一个矢量统一对所有元素的访问方式;一个链接列表则用于保证所有元素的插入统一。所以我们能根据自己的需要选择适当的类型。其中包括集、队列、散列表、树、堆栈等等。 所有集合都提供了相应的读写功能。将某样东西置入集合时,采用的方式是十分明显的。有一个叫作“推”(Push)、“添加”(Add)或其他类似名字的函数用于做这件事情。但将数据从集合中取出的时候,方式却并不总是那么明显。如果是一个数组形式的实体,比如一个矢量(Vector),那么也许能用索引运算符或函
数。但在许多情况下,这样做往往会无功而返。此外,单选定函数的功能是非常有限的。如果想对集合中的一系列元素进行操纵或比较,而不是仅仅面向一个,这时又该怎么办呢,
办法就是使用一个“继续器”(Iterator),它属于一种对象,负责选择集合内元素,并把它们提供给继承器的用户。作为一个类,它也提供了一级抽象。利用这一级抽象,可将集合细节与用于访问那个集合的代码隔离开。通过继承器的作用,集合被抽象成一个简单的序列。继承器允许我们遍历那个序列,同时毋需关心基础结构是什么——换言之,不管它是一个矢量、一个链接列表、一个堆栈,还是其他什么东西。这样一来,我们就可以灵活地改变基础数据,不会对程序里的代码造成干扰。 Java 最开始(在1.0和 1.1版中)提供的是一个标准继承器,名为 Enumeration(枚举),为它的所有集合类提供服务。 Java 1.2 新增一个更复杂的集合库,其中包含了一个名为 Iterator 的继承器,可以做比老式的 Enumeration 更多的事情。
从设计角度出发,我们需要的是一个全功能的序列。通过对它的操纵,应该能解决自己的问题。如果一种类型的序列即可满足我们的所有要求,那么完全没有必要再换用不同的类型。有两方面的原因促使我们需要对集合作出选择。首先,集合提供了不同的接口类型以及外部行为。堆栈的接口与行为与队列的不同,而队列的接口与行为又与一个集(Set)或列表的不同。利用这个特征,我们解决问题时便有更大的灵活性。 其次,不同的集合在进行特定操作时往往有不同的效率。最好的例子便是矢量( Vector)和列表( List )的区别。它们都属于简单的序列,拥有完全一致的接口和外部行为。但在执行一些特定的任务时,需要的开销却是完全不同的。对矢量内的元素进行的随机访问(存取)是一种常时操作;无论我们选择的选择是什么,需要的时间量都是相同的。但在一个链接列表中,若想到处移动,并随机挑选一个元素,就需付出“惨重”的代价。而且假设某个元素位于列表较远的地方,找到它所需的时间也会长许多。但在另一方面,如果想在序列中部插入一个元素,用列表就比用矢量划算得多。这些以及其他操作都有不同的执行效率,具体取决于序列的基础结构是什么。在设计阶段,我们可以先从一个列表开始。最后调整性能的时候,再根据情况把它换成矢量。由于抽象是通过继承器进行的,所以能在两者方便地切换,对代码的影响则显得微不足道。最后,记住集合只是一个用来放置对象的储藏所。如果那个储藏所能满足我们的所有需要,就完全没必要关心它具体是如何实现的(这是大多数类型对象的一个基本概念)。如果在一个编程环境中工作,它由于其他因素(比如在 Windows 下运行,或者由垃圾收集器带来了开销)产生了内在的开销,那么矢量和链接列表之间在系统开销上的差异就或许不是一个大问题。我们可能只需要一种类型的序列。甚至可以想象有一个“完美”的集合抽象,它能根据自己的使用方式自动改变基层的实现方式。
对象的创建和存在时间
每个对象都要求资源才能“生存”,其中最令人注目的资源是内存。如果不再需要使用一个对象,就必须将其清除,以便释放这些资源,以便其他对象使用。如果要解决的是非常简单的问题,如何清除对象这个问题并不显得很突出:我们创建对象,在需要的时候调用它,然后将其清除或者“破坏”。但在另一方面,我们平时遇到的问题往往要比这复杂得多。
举个例子来说,假设我们要设计一套系统,用它管理一个机场的空中交通(同样的模型也可能适于管理一个仓库的货柜、或者一套影带出租系统、或者宠物店的宠物房。这初看似乎十分简单:构造一个集合用来容纳飞机,然后创建一架新飞机,将其置入集合。
对进入空中交通管制区的所有飞机都如此处理。至于清除,在一架飞机离开这个区域的时候把它简单地删去即可。
但事情并没有这么简单,可能还需要另一套系统来记录与飞机有关的数据。当然,和控制器的主要功能不同,这些数据的重要性可能一开始并不显露出来。例如,这条记录反映的可能是离开机场的所有小飞机的飞行计划。所以我们得到了由小飞机组成的另一个集合。一旦创建了一个飞机对象,如果它是一架小飞机,那
么也必须把它置入这个集合。然后在系统空闲时期,需对这个集合中的对象进行一些后台处理。
问题现在显得更复杂了:如何才能知道什么时间删除对象呢,用完对象后,系统的其他某些部分可能仍然要发挥作用。同样的问题也会在其他大量场合出现,而且在程序设计系统中(如 C++),在用完一个对象之后必须明确地将其删除,所以问题会变得异常复杂。
最重要的问题之一是对象的创建及破坏方式。对象需要的数据位于哪儿,如何控制对象的“存在时间”呢,针对这个问题,解决的方案是各异其趣的。 C++认为程序的执行效率是最重要的一个问题,所以它允许程序员作出选择。为获得最快的运行速度,存储以及存在时间可在编写程序时决定,只需将对象放置在堆栈(有时也叫作自动或定域变量)或者静态存储区域即可。这样便为存储空间的分配和释放提供了一个优先级。某些情况下,这种优先级的控制是非常有价值的。然而,我们同时也牺牲了灵活性,因为在编写程序时,必须知道对象的准确的数量、存在时间、以及类型。如果要解决的是一个较常规的问题,如计算机辅助设计、仓储管理或者空中交通控制,这一方法就显得太局限了。 第二个方法是在一个内存池中动态创建对象,该内存池亦叫“堆”或者“内存堆”。若采用这种方式,除非进入运行期,否则根本不知道到底需要多少个对象,也不知道它们的存在时间有多长,以及准确的类型是什么。这些参数都在程序正式运行时才决定的。若需一个新对象,只需在需要它的时候在内存堆里简单地创建
它即可。由于存储空间的管理是运行期间动态进行的,所以在内存堆里分配存储空间的时间比在堆栈里创建的时间长得多(在堆栈里创建存储空间一般只需要一个简单的指令,将堆栈指针向下或向下移动即可)。由于动态创建方法使对象本来就倾向于复杂,所以查找存储空间以及释放它所需的额外开销不会为对象的创建造成明显的影响。除此以外,更大的灵活性对于常规编程问题的解决是至关重要的。
动态方法使该对象对于的一般逻辑的假设往往是复杂的,所以找到存储和释放该内存的额外空间不会对创建对象产生重要的影响。此外,更多的灵活性对于解决一般的规划问题是至关重要的。
Java 对象不具备与主类型一样的存在时间。对于用 new 创建的对象,只要我们愿意,它们就会一直保留下去。这个编程问题在 C和 C++里特别突出。看来在 C++里遇到的麻烦最大:由于不能从语言获得任何帮助,所以在需要对象的时候,根本无法确定它们是否可用。而且更麻烦的是,在 C++里,一旦工作完成,必须保证将对象清除。这样便带来了一个有趣的问题。假如 Java 让对象依然故我,怎样才能防止它们大量充斥内存,并最终造成程序的“凝固”呢。在 C++里,这个问题最令程序员头痛。但 Java 以后,情况却发生了改观。 Java 有一个特别的“垃圾收集器”,它会查找用 new 创建的所有对象,并辨别其中哪些不再被引用。随后,它会自动释放由那些闲置对象占据的内存,以便能由新对象使用。这意味着我们根本不必操心内存的回收问题。只需简单地创建对象,一旦不再需要它们,它们就会自动离去。这样做可防止在 C++里很常见的一个编程问题:由于程序员忘记释放内存造成的“内存溢出”。
在 Java 中,垃圾收集器在设计时已考虑到了内存的释放问题(尽管这并不包括清除一个对象涉及到的其他方面)。垃圾收集器“知道”一个对象在什么时候不再使用,然后会自动释放那个对象占据的内存空间。采用这种方式,另外加上所有对象都从单个根类 Object 继承的事实,而且由于我们只能在内存堆中以一种方式创建对象,所以 Java 的编程要比 C++的编程简单得多。我们只需要作出少量的抉择,即可克服原先存在的大量障碍。
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。