丰富的线上&线下活动,深入探索云世界
做任务,得社区积分和周边
最真实的开发者用云体验
让每位学生受益于普惠算力
让创作激发创新
资深技术专家手把手带教
遇见技术追梦人
技术交流,直击现场
海量开发者使用工具、手册,免费下载
极速、全面、稳定、安全的开源镜像
开发手册、白皮书、案例集等实战精华
为开发者定制的Chrome浏览器插件
赵军吴灿铭等编著
计算机(Computer)堪称是20世纪以来人类最伟大的发明之一,对于人类的影响更甚于工业革命所带来的冲击。计算机是一种具备数据处理与计算功能的电子设备。在1946年,美国宾州大学教授埃克特与莫西利合作完成了人类第一台真空电子管计算机ENIAC。而在1945年,冯诺依曼教授首先提出了计算机存储程序的运行方式与二进制的概念,认为数据与程序可以存储在计算机的存储器再投入运行,于是拉开了程序设计语言与程序设计蓬勃发展的序幕。自从人类发明计算机,计算机就渗透到人类生活的各个领域。如图1-1所示的是计算机运用于工厂生产线与大楼自动化安保管理的例子。
2006年,美国卡内基·梅隆大学的JeannetteM.Wing教授首次提出了“计算思维”的概念,她提出计算思维是现代人的一种基本技能,所有人都应该积极学习,随后谷歌(Google)公司也为教育者开发了一套计算思维课程(ComputationalThinkingforEducators),这套课程提出了培养计算思维的4部分,分别是分解(Decomposition)、模式识别(PatternRecognition)、模式概括与抽象(PatternGeneralizationandAbstraction)以及算法(Algorithm)。虽然这并不是建立计算思维唯一的方法,不过通过这4部分我们可以更有效地进行思维能力的训练,不断使用计算方法与工具解决问题,进而逐渐养成我们的计算思维习惯。虽然这并不是建立计算思维的唯一方法,不过通过这4部分我们能更有效率地拓展思维,提高使用计算方法与工具解决问题的能力,最终建立计算思维。也就是要将这4部分进行系统的学习与组合,并使用计算机来协助问题的解决(参考图1-2)。在后面的章节我们来详细说明。
许多人在编写程序或解决问题时,对于问题的分解(Decomposition)不知道从何处着手,将问题想得太庞大,如果对一个问题不能进行有效分解,就会很难处理。将一个复杂的问题分割成许多小问题,把这些小问题各个击破,小问题全部解决之后,原本的大问题也就解决了。下面我们以一个实际的例子来说明。如果有8幅非常难画的图,我们可以把它们分成2组各4幅图来完成,如果还是觉得太复杂,继续再分成4组,每组各两幅图来完成,采用相同模式反复分割问题(如图1-3所示),这就是最简单的分治法(DivideandConquer)的核心思想。
例如我们有一台计算机的部件出现故障了,如果将整台计算机逐步分解成较小的部分,对每个部分的各个硬件部件进行检查,就容易找出有问题的部件。再例如一位警察在思考如何破案时,需要将复杂的问题细分成许多小问题。经常编写程序的人在遇到问题时会考虑所有的可能性,把问题逐步分解,久而久之,这种逻辑思维的习惯就成了这些人的思考模式了,如图1-4所示。
在把一个复杂的问题分解之后,我们常常可以发现小问题中有共同的属性以及相似之处,在计算思维中,这些属性被称为“模式”(Pattern)。模式识别是指在一组数据中找出特征(Feature)或规则(Rule),用于对数据进行识别与分类,以作为决策判断的依据。在解决问题的过程中找到模式是非常重要的,模式可以让问题的解决更为简化。当问题具有相同的特征时,它们能够被更简单地解决,因为存在共同模式时,我们可以用相同的方法解决此类问题。举例来说,在知道怎么描述一只狗之后,我们可以按照这种模式轻松地描述其他狗,例如狗都有眼睛、尾巴与4只脚,不一样的地方是每只狗或多或少地有其独特之处(如图1-5所示),识别出这种模式之后,便可用这种解决办法来应对不同的问题。因为我们知道所有的狗都有这类属性,当想要画狗的时候便可将这些共同的属性加入,这样就可以很快地画出很多只狗。
算法是计算思维4个基石中的最后一个,不但是人类使用计算机解决问题的技巧之一,也是程序设计中的精髓,算法常出现在规划和设计程序的第一步,因为算法本身就是一种计划,每一条指令与每一个步骤都是经过规划的,在这个规划中包含解决问题的每一个步骤和每一条指令。在日常生活中有许多工作可以使用算法来描述,例如员工的工作报告、宠物的饲养过程、厨师准备美食的食谱、学生的课程表等。如今我们几乎每天都要使用的各种搜索引擎都必须借助不断更新的算法来运行,如图1-7所示。
在韦氏辞典中,算法定义为:“在有限的步骤内解决数学问题的程序。”如果运用在计算机领域中,我们也可以把算法定义成:“为了解决某项工作或某个问题,所需要有限数量的机械性或重复性的指令与计算步骤。”在计算机中,算法更是不可或缺的重要一环。下面讨论的内容包括计算机程序常涉及算法的概念和定义。在认识算法的定义之后,我们再来看看算法所必须符合的5个条件(参考图1-8和表1-1)。
在认识算法的定义与条件后,我们接着来思考:该用什么方法来表达算法最为适当呢?其实算法的主要目的在于让人们了解所执行的工作的流程与步骤,换句话说,算法是描述如何解决问题的办法,因而只要能清楚地体现算法的5个条件,即可清晰地表达算法。常用的算法一般可以用中文、英文、数字等文字来描述,也就是使用文字或语言语句来说明算法的具体步骤,有些算法则是使用可读性高的高级程序设计语言(如Python、C、C++、Java等)或者伪语言(Pseudo-Language)来描述或说明的。
流程图(FlowDiagram)是一种相当通用的算法表达方式,就是使用某些特定图形符号来表示算法的执行过程。为了让流程图具有更好的可读性和一致性,目前较为通用的是ANSI(美国国家标准协会)制定的统一图形符号。假如我们要设计一个程序,让用户输入一个整数,而这个程序可以帮助用户判断输入的这个整数是奇数还是偶数,那么这个程序的流程图大致如图1-9所示。
程序设计语言其实是一种人类用来和计算机进行沟通的语言,也是用来指挥计算机进行运算或执行任务的指令集合。许多不懂计算机的人可能会把程序想象成十分深奥难懂的技术文件,其实程序只是由一系列合乎程序设计语言语法规则的指令所组成的,程序设计就是编写程序指令或程序代码来指挥计算机辅助我们人类完成各项工作。
所谓编译型语言,就是使用编译器(Compiler)将程序代码翻译为目标程序。编译器可将源程序分成几个阶段转换为机器可读的可执行文件(目标程序),不过编译器必须先把源程序读入主存储器后才可以开始编译。每当源程序被修改一次,就必须重新经过编译器的编译过程,才能保持其可执行文件为最新的。经过编译后所产生的可执行文件,在执行中不必再“翻译”,因此执行效率较高。例如C、C++、PASCAL、FORTRAN等语言都是编译型语言。如图1-10所示为编译型语言编译与执行过程的示意图。
所谓解释型语言,是指使用解释器对高级语言的源代码逐行进行“翻译”(解释),每解释完一行程序语句后,才会解释下一行程序语句,如图1-11所示。如果在解释的过程中发现了错误,解释动作就会立刻停止。由于使用解释器解释的程序每次执行时都必须再解释一次,因此执行速度较慢。BASIC、LISP、PROLOG等高级语言都是解释型语言。
有些人往往认为程序设计的主要目的就是通过“运行”得出结果,而忽略了程序运行的效率与程序日后维护的成本。学习程序开发的最终目的是学会如何组织众多程序设计人员共同参与来设计一套大型且符合用户需求的复杂系统。一个程序的产生过程可分为五大设计步骤,如表1-2所示。
至于在程序设计中要使用何种程序设计语言,通常可根据主客观环境的需要进行选择,并无特别的规定。一般从4个方面来评判程序设计语言的优劣。
以下是我们在编写程序时应该注意的三项基本原则:1.适当的缩排缩排用来区分程序的层级,使程序代码易于阅读,像是在主程序中包含子程序区块,或者子程序区块中又包含其他的子程序区块时,都可以通过缩排来区分程序代码的层级,如图1-12所示。
2.明确的注释对于程序设计人员而言,在适当的位置加入足够的注释往往可以作为评断程序设计优劣的重要依据之一。尤其当程序结构复杂而且程序语句较多时,适时在程序中加入注释不仅可提高程序的可读性,而且可以让其他程序设计人员能更清楚地理解这段程序代码的作用。如下这段程序就带有非常清楚的注释:
每位程序设计人员就像一位艺术家一样,会有不同的设计逻辑,不过由于计算机是很严谨的高科技工具,不能像人脑一样天马行空,对于一个好的程序设计人员而言,还是必须遵循某些规范,符合程序设计的逻辑概念,这样才能让程序代码具备可读性与日后的可维护性。就像早期的结构化设计,时至今日已将传统程序设计的逻辑转化成面向对象的设计逻辑,这都是程序设计人员在编写程序时要遵循的大方向。
在传统程序设计的方法中,主要有“自上而下法”与“自下而上法”两种。所谓“自下而上法”,是指程序设计人员先编写整个程序需求中最容易的部分,再逐步展开来完成整个程序。“自上而下法”则是将整个程序需求从上到下、从大到小逐步分解成较小的单元(或称为模块(Module)),这样使得程序设计人员可针对各个模块分别开发,不但减轻了设计人员的负担,而且程序的可读性更高,对于日后维护也容易许多。结构化程序设计的核心思想是“自上而下的设计”与“模块化的设计”。例如,在PASCAL语言中,这些模块被称为过程(Procedure),在C语言中被称为函数(Function)。通常“结构化程序设计”具备三种控制流程,而对于一个结构化程序,无论其结构如何复杂,都可使用这三种基本的控制流程来编写,如表1-3所示。
面向对象程序设计(Object-OrientedProgramming,OOP)的主要设计思想是将存在于日常生活中随处可见的对象(Object)概念应用在软件开发模式(SoftwareDevelopmentModel)中。面向对象程序设计让我们在程序设计中能以一种更生活化、可读性更高的设计思路来进行程序的开发和设计,并且所开发出来的程序更容易扩充、修改和维护。在现实生活中充满了形形色色的物体,每个物体都可视为一种对象。我们可以通过对象的外部行为(Behavior)和内部状态(State)来进行详细的描述。外部行为代表此对象对外所显示出来的运行方式,内部状态则代表对象内部各种特征的当前状况,如图1-13所示。
封装(Encapsulation)就是利用“类”来实现“抽象数据类型”(ADT)。类是一种用来具体描述对象状态与行为的数据类型,也可以看成一个模型或蓝图,按照这个模型或蓝图所产生或创建的实例(Instance)就称为对象。类和对象的关系如图1-15所示。
所谓“抽象”,就是将代表事物特征的数据隐藏起来,并定义一些方法(Method)来作为操作这些数据的接口,让用户只能接触到这些方法,而无法直接使用数据,也符合信息隐藏(InformationHiding)的要求,而这种自定义的数据类型就称为“抽象数据类型”。而传统程序设计的概念则必须掌握所有的来龙去脉,就程序开发的时效性而言,传统程序设计便要大打折扣。
类(Class)是具有相同结构和行为的对象集合,是许多对象共同特征的描述或对象的抽象化。例如,小明与小华都属于人这个类,他们都有出生年月日、血型、身高、体重等类的属性。类中的一个对象有时就称为该类的一个实例(Instance)。
属性(Attribute)用来描述对象的基本特征及其所属的性质,例如一个人的属性可能会包括姓名、住址、年龄、出生年月日等。
方法(Method)是对象的动作与行为,我们在此以人为例,不同的职业,其工作内容就会有所不同,例如学生的主要工作为学习,而老师的主要工作则为教书。
Java语法源于C++语言,因此它的指令和语法十分简单,我们只要能了解简单英文单词与语法的概念,就能进行程序设计并完成运算处理的工作。Java具有以下两点简单特性:(1)简化了语法:Java简化了C++中的一些用法,并舍弃了不常用的语法,如容易造成内存存取问题的指针(Pointer)和多重继承的部分。(2)垃圾回收机制(GarbageCollection):Java使用了垃圾回收机制,当程序中有不再使用的资源时,系统会自动释放其占用的内存空间,从而减少程序设计者自行管理内存资源不足的困扰。
“跨平台性”表示Java的程序不依赖于任何一个特定的硬件平台,Java程序的特点是“一次编译、到处执行”,也就是说,Java程序在编译后可以不用再经过任何更改就可以在任何支持Java的硬件设备或平台上顺利执行。基本上,无论是哪一种操作系统(Windows、UNIX/Linux或Solaris)、哪一种硬件平台(PC、个人数字设备、JavaPhone或智能家电等),只要它们搭载有JVM(JavaVirtualMachine,Java虚拟机)执行环境,即可顺利执行已事先编译的JavaBytecode(字节码),如图1-17所示。也就是说,执行Java应用程序必须先安装JavaRuntimeEnvironment(JRE),JRE内部包含JVM以及一些标准的Java类库。通过JVM才能在系统中执行Java应用程序(JavaApplication)。当程序设计人员设计和编写好的Java源程序通过不同操作系统平台上的编译器(例如Intel的编译器、MacOS的编译器、Solaris的编译器或者UNIX/Linux的编译器)进行编译后,产生相同的Java虚拟机字节码(ByteCode),然后这些Java虚拟机字节码再通过不同操作系统平台的解释器,翻译成该系统平台的机器码。因此,Java是建立在软件平台上的程序设计语言,而让Java实现跨平台运行的主要因素就是JVM(Java虚拟机)和JavaAPI。
Java源程序(JavaSource)必须通过内建的实用程序javac.exe来进行编译,把Java程序的源代码编译成目标执行环境中可识别的字节码(Bytecode),字节码是一种虚拟的机器语言,而在目标环境中的执行是通过实用程序java.exe对字节码以解释方式按序执行的,这个过程如图1-18所示。
相对于大多数C++编译器不支持垃圾回收机制,Java语言有自动垃圾回收(GarbageCollection)机制,这个特点受到许多从使用C++语言转到使用Java语言的程序设计人员的欢迎。这是因为许多C++程序设计人员在进行程序初始化操作时,必须在主机内存堆栈中分配一块内存空间,当程序运行结束后,必须通过指令的下达来释放被分配的内存空间。不过,一旦程序设计人员忘记回收这块内存空间,就会造成内存泄漏(MemoryLeak)而浪费内存空间。因为Java语言有自动垃圾回收机制,所以当一个对象没有被引用时,就会自动释放这个对象所占用的内存空间,从而避免内存泄漏的现象。
泛型程序设计(GenericProgramming)是程序设计语言的一种风格。泛型在C++中其实就是模板(Template),只是Swift、Java和C#采用了泛型(Generic)这个更广泛的概念。泛型可以让程序设计人员根据不同数据类型的需求编写出适用于任何数据类型的函数和类。我们或许可以这么说:泛型是一种类型参数化的概念,主要是为了简化程序代码,降低日后程序的维护成本。泛型语法让我们在编写Java程序时可以指定类或方法来支持泛型,而且在语法上更为简洁。Java语言中引入泛型的功能后,这项重大改变使得语言、类型系统和编译器有了许多不同以往的变化,其中许多重要的类(例如集合框架)已经成为泛型化的类了,它带来了很多好处,还提高了Java程序的类型安全。
Java的开发工具分成JDK和IDE两种:(1)Java开发工具(JavaDevelopmentKit,JDK)是一种“简易”的程序开发工具,仅提供编译(Compile)、执行(Run)及调试(Debug)功能。(2)集成开发环境(IntegratedDevelopmentEnvironment,IDE)集成了编辑、编译、执行、测试及调试功能,例如常见的BorlandJbuilder、NetBeansIDE、Eclipse、Jcreator、JavaEditor等。
在图1-19所示的Java官方网站中,单击网页右侧的“JavaSE11.0.1”链接,接着会显示出如图1-20所示的网页。
在图1-20所示的网页中单击“JavaSEDownloads”下方的“DOWNLOAD”按钮(框中的按钮),随后会显示出如图1-21所示的网页,读者可以根据网页中的提示下载JDK。要开始下载,可选中如图1-21所示的网页中的AcceptLicenseAgreement单选按钮。
笔者根据自己的操作系统下载的是Windows版本的JDK,文件名为“jdk-11.0.1_windows-x64_bin.exe”,如图1-22所示。
下载完安装文件后,单击“运行”按钮,即可执行该安装程序,如图1-23所示。
JDK11的安装向导界面如图1-24所示。
在启动的“控制面板”窗口单击“系统和安全”选项,如图1-28所示。
出现如图1-29所示的窗口后,再单击“系统”选项。
出现如图1-30所示的窗口后,单击“高级系统设置”选项。
随后就会看到如图1-31所示的“系统属性”窗口,在“高级”页签中单击“环境变量”按钮(注意:Windows7的用户可从“控制面板”中的“系统”的“高级系统设置”进入类似于图1-31所示的“系统属性”窗口)。
在如图1-32所示的“环境变量”对话框下方的“系统变量”框中选择“Path”系统变量,并单击“编辑”按钮。
显示出如图1-33所示的界面后,再单击“新建”按钮,并在最后输入“C:ProgramFilesJavajdk-11.0.1bin”路径。
如果是Windows7的用户,那么先找到系统变量的Path部分,单击“编辑”按钮,在“变量值”输入字段的末尾加上“;”,然后加上“C:ProgramFilesJavajdk-11.0.1bin”,随后依次单击三次“确定”按钮,就可以设置好JDK搜索路径对应的环境变量。如果我们要验证Path环境变量是否设置成功,可以右击Windows10操作系统左下角的Windows窗口图标,随后会出现如图1-34所示的菜单。
单击菜单中的“WindowsPowerShell”或者“命令提示符”,就会启动“WindowsPowerShell”窗口或“命令提示符”窗口,在其中输入“javac-version”命令,该命令可以显示出当前系统中javac的版本信息,如图1-35所示。
如果可以看到如图1-35所示的版本信息,就表示环境变量Path设置成功,因为当前笔者计算机的工作文件夹是“C:UsersJun”,所以在这个路径(或称为文件夹)并没有javac.exe工具程序,当操作系统在这个路径找不到javac.exe时,它就会自动根据环境变量Path所指定的路径搜索javac.exe。而我们刚才已在环境变量Path中新建了一个“C:ProgramFilesJavajdk-11.0.1bin”的路径,操作系统可以在这个路径下找到javac.exe工具程序,因此可以顺利执行“javac-version”命令,从而列出如图1-35所示的“javac11”版本信息。如果我们在设置环境变量的过程中不小心输入了错误的路径名称,当执行上述“javac-version”命令时,就会显示出如“'javac'不是内部或外部命令,也不是可运行的程序或批处理文件”的错误提示信息,这时我们就要仔细检查在设置过程中有哪个步骤设置错了,或者输入了不正确的路径名称。
接着我们将分别使用Windows内建的记事本(Notepad)应用程序来编辑一个简单的Java程序,并说明Java程序的基本结构。
01/*文件:CH01_01*/02//程序公有类03publicclassCH01_01{04//主要执行区块05publicstaticvoidmain(String[]args){06//程序语句07System.out.println("我的第一个Java程序");08}09}【程序的执行结果】首先进入“命令提示符”窗口,假设笔者的Java范例程序存放的文件夹在D驱动器中,完成的操作指令说明如下:
D:(接着按【Enter】键,表示切换到D驱动器)cdJava(接着按【Enter】键,表示切换到Java文件夹,cd命令的功能就是改变目录或文件夹)cdch01(接着按【Enter】键,表示切换到ch01文件夹,cd命令的功能是改变目录或文件夹)javacCH01_01.java(接着按【Enter】键,表示将CH01_01.java源程序编译成类文件)javaCH01_01(接着按【Enter】键,表示执行CH01_01类文件,并将执行结果输出)如图1-36所示为完整的操作步骤。
java程序名称.java前面的范例程序,即使没有事先将Java源代码程序编译成类文件,也可以通过增强的java解释器直接解释执行CH01_01.java源代码程序。命令如下:
javaCH01_01.java(接着按【Enter】键,直接执行CH01_01.java的源代码程序)如图1-37所示为完整的操作步骤。
在此特别说明一下,为了兼顾学习Java10、Java9、Java8或Java更早版本的读者也能选用本书学习Java语言,在本书的后续章节中,我们将保留通用的范例程序执行过程,即先通过javac编译器将Java源代码程序编译成类文件(.class),再通过java解释器执行这些编译生成的字节码。在了解了Java程序的执行操作之后,下面开始学习“如何编写程序”。要编写出良好的程序,第一步是了解“程序的基本结构”。【Java程序的基本结构】
System.out.println("WelcometoJavaWorld");//println()具有换行显示功能System.out.print("WelcometoJavaWorld");//print()不具有换行显示功能“//”程序注释:程序注释对于程序的编写是很重要的,注释能够使编写程序的作者或其他人清楚地了解这段程序以及整个程序的设计目的,对后续的维护有很大的帮助。其中“//”适用于单行或简短的程序注释;“/”和“/”则适用于多行或需要详细解释的注释。“;”分号:在Java程序设计中,每一行程序代码编写完毕后,在程序语句的末尾都必须加上“;”分号,以明确说明这条程序语句到此已经结束了。假如没有加上分号,在编译时就会发生错误,这是初学者经常疏忽的地方。至于程序代码编写格式的问题,Java语言其实没有一定的规范,因为Java语言属于自由格式(Free-format)的编写方式,只要程序代码容易阅读,程序语法和逻辑无误,就可以正确执行。不过,适当地将程序代码“缩排”或“换行”可以让程序结构清楚且容易理解。【用记事本(Notepad)应用程序编辑Java程序】程序代码可参考图1-38。
(varx,vary)->x.process(y)等同于下面的表达式:
(x,y)->x.process(y)有关Lambda表达式的进一步说明,请大家参阅16.6节中有关Lambda表达式的介绍。
4.请简述程序设计语言的基本分类。5.评断程序设计语言好坏的要素有哪些?6.程序编写的三项基本原则是什么?7.试简述Java语言的特性(至少三种)。8.Java的开发工具可分成哪两种?9.简述Java程序语言的起源。10.试简述面向对象程序设计的三种重要特征。11.请比较编译器的编译与解释器的解释两者之间的差异性。12.试编写一个简单的Java程序,让它输出的结果为“今日事,今日毕”,如图1-40所示。
13.试编写一个简单的Java程序,它的输出结果如图1-41所示。
14.试编写一个简单的Java程序,它的输出结果如图1-42所示。