程序设计就是创建(或者开发)软件,软件也称为程序。
简言之,软件包含了指令,告诉计算机(或者计算机设备)做什么。
软件开发人员在称为程序设计语言的强大工具的帮助下创建软件。
每种语言都是为了实现某个特定的目的而发明的,比如,构建在以前语言的长处上,或者为程序员提供一套全新和独特的工具。
关键是学习如何使用程序设计方法来解决问题,这是本书的主旨。
概念:
计算机的组成:
计算机包括硬件(hardware)和软件(software)两部分。
一般来说,硬件包括计算机种可以看得见的物理部分,而软件提供看不见的指令,这些指令控制硬件并且使得硬件完成特定的任务。
总线:
概念:
CPU组成:
CPU通常由两部分组成:控制单元(control unit)和算数/逻辑单元(arithmetic/logic unit)。
控制单元用于控制和协调其他组件的动作。
算数/逻辑单元用于完成数值运算(加法、减法、乘法、除法)和逻辑运算(比较)。
现在的CPU都是构建在一块小小的硅半导体芯片上,这块芯片上包含数百万称为晶体管的小电路开关,用于处理信息。
时钟:
每台计算机都有一个内部时钟,该时钟以固定速度发射电子脉冲。这些脉冲用于控制和同步各种操作的步调。时钟速度越快,在给定时间段内执行的指令就越多。时钟速度的单位是赫兹(hertz,Hz),1赫兹相当于每秒1个脉冲。20世纪90年代计算机的时钟速度通常是以兆兹(MHz)来表示的(1MHz就是100万Hz)。随着CPU的速度不断提高,目前计算机的时钟速度通常以千兆兹(GHz)来表述。
Intel公司最新处理器的运行速度大约是3GHz。
核:
摩尔定律(Moore’s Law):
安迪-比尔定律(Andy and Bill’s Law):
反摩尔定律(Reverse Moore’s Law):
计算机发展史上的鼻祖:
根据冯·诺伊曼体系结构构成的计算机,必须具有如下功能:
概念:
比特的概念:
字节的概念:
编码模式的概念:
程序员不需要关心数据的编码和解码,这些都是系统根据编码模式(schema)来自动完成的。
编码模式是一系列的规则,控制计算机将字符、数字和符号翻译成计算机可以实际工作的数据。
大多数模式将每个字符翻译成预先确定的一个比特串。
存储单位:
内存概念:
内存与CPU的区别:
RAM的缺点:
存储设备的概念:
存储设备主要的三种类型:
驱动器的解释:
驱动器(drive)是对存储介质进行操作的设备。存储介质物理地存储数据和程序指令。驱动器从介质读取数据并将数据写在介质上。
1、磁盘
2、光盘和数字化视频磁盘
CD的全称是致密的盘片(compact disc)。
光盘驱动器的类型有两种:只读光盘(CD-R)和可读写光盘(CD-RW)。
只读光盘上的信息只能用于读取,内容一旦记录到光盘上,用户是不能修改它们的。
可读写光盘可以像硬盘一样使用。也就是说,可以将数据写到光盘上,然后用新的数据覆盖掉这些数据。
单张光盘的容量可以达到700MB。
DVD的全称是数字化多功能碟片或者数字化视频磁盘。
DVD和CD看起来很像,可以使用任意一种来存储数据。
一张DVD上可以保存的信息要比一张CD上可以保存的信息多。
一张标准的DVD的存储容量是4.7GB。
有两种类型的DVD:DVD-R(只读)和DVD-RW(可重写)。
3、USB闪存驱动器
通用串行总线(Universal serial Bus,USB)接口允许用户将多种外部设备连接到计算机上。
USB闪存驱动器(flash drive)适用于存储和传输数据的设备。
概念:
1、键盘
键盘是用于输入的设备。
功能键(function key)位于键盘的最上边,而且都是以F为前缀。它们的功能取决于当前所使用的软件。
修饰符键(numeric keypad)位于键盘的右下角,是一套独立的类似计算器风格的按键集合,用于快速输入数字。
方向键(arrow key)位于主键盘和数字小键盘之间,在各种程序中用于上下左右地移动光标。
插入键(Insert)、删除键(Delete)、向上翻页键(Page Up)和向下翻页键(Page Down)分别用于在字处理和其他程序中完成插入文本和对象、删除文本和对象以及向上和向下翻页的功能。
2、鼠标
鼠标(mouse)是定点设备,用来在屏幕上移动一个称为光标的图形化的指针(通常以一个箭头的形状),或者用于单击屏幕上的对象(如一个按钮)来触发它以执行动作。
3、显示器
显示器(monitor)显示信息(文本和图形)。
计算机可以通过通信设备进行联网。
通信设备种类:
计算机程序概念:
什么是计算机语言:
计算机的原生语言因计算机类型的不同而有差异,计算机的原生语言就是机器语言(machine language),即一套内嵌的原子指令集。因为这些指令都是以二进制代码的形式存在,所以,为了以机器原生语言的形式给计算机指令,必须以二进制代码输入指令。
简介:
汇编器:
汇编语言与机器语言:
简介:
20世纪50年代,新一代编程语言即众所周知的高级语言出现了。
它们是平台独立的,这意味着可以使用高级语言编程,然后在各种不同类型的机器上运行。
高级语言中的指令称为语句。
用高级语言编写的程序称为源程序(Source Program)或源代码(source code)。由于计算机不能运行源程序,源程序必须被翻译成可执行的机器代码。
流行的高级语言:
TIOBE是一个流行编程语言排行榜。每月更新。排名权重基于世界范围内:工程师数量、课程数量和第三方供应商数量。Google Bing Yahoo!Wikipedia Amazon Yotube和百度这些主流的搜索引擎,也将作为排名权重的参考指标。声明。TIOBE排名既无关最好的编程语言,也无关被书写了最多行代码的编程语言。
解释器与编译器:
简介:
操作系统的主要任务有:
操作系统执行基本的任务,例如,识别来自键盘的输入,向显示器发送输出结果,跟踪存储在设备中的文件和文件夹的动态,控制类似硬盘驱动器和打印机这样的外部设备。
操作系统还要确保不同的程序和用户同时使用计算机时不会互相干扰。
另外,操作系统还负责安全处理,以确保未经授权的用户和程序无权访问系统。
操作系统负责确定一个程序需要使用哪些计算机资源,并进行资源分配和调配以运行程序。
操作系统负责调度程序的活动,以便有效地利用系统资源。
为了提高系统的性能,目前许多操作系统都支持多道程序设计(multiprogramming)、多线程(multithreading)和多处理(multiprocessing)这样的技术。
Java简介:
Java是一种功能强大和多用途的编程语言,可用于开发运行在移动设备、台式计算机以及服务器端的软件。
Java是由James Gosling在Sun公司领导的小组开发的。
2010年Sun公司被Oracle公司收购。
Java最初被称为Orak(橡树),是1991年为消费类电子产品的嵌入式芯片而设计的。
1995年更名为Java,并重新设计用于开发Web应用程序。
关于Java的历史,参见www.java.com/en/javahistory/index.jsp。
关于Java特性的剖析,参见www.cs.armstrong/liang/JavaCharacteristics.pdf。
Java是功能完善的通用程序设计语言,可以用来开发健壮的任务关键的应用程序。
万维网:
B/S和C/S:
HTML:
Java的其他作用:
职业发展与提升:
Java语言规范:
Java库:
JDK:
IDE:
Java的三个版本:
Java标准版(Java Standard Edition,Java SE)可以用来开发客户端的应用程序。应用程序可以独立运行或作为applet在Web浏览器中运行。
Java 企业版(Java Enterprise Edition,JavaEE)可以用来开发服务器端的应用程序,例如,Java servlet和JavaServer Pages(JSP),以及JavaServer Faces(JSF)。
Java微型版(Java MicroEdition,JavaME)用来开发移动设备的应用程序。
软件开发:
人机交互方式:
Java是从类中的main方法开始执行的。
Java源程序是区分大小写的。
控制台:
类:
方法:
字符串:
语句结束符:
保留字和关键字:
注释:
类块和方法块:
特殊字符:
创建:
Java源程序保存为java文件,编译为class文件。class文件由java虚拟机(JVM)执行。
可以使用任何一个文本编辑器或者集成开发环境来创建和编辑Java源代码文件。
从命令窗口,可以使用文本编辑器比如记事本(NotePad)来创建Java源代码。
**注意:**源文件的扩展名必须是.java,而且文件名必须与公共类名完全相同。
编译:
字节码:
Java语言是高级语言,而Java字节码是低级语言。字节码类似于机器指令,但它是体系中立的,是可以在任何带Java虚拟机(JVM)平台上运行的。
虚拟机不是物理机,而是一个解释Java字节码的程序。
Java字节码可以在不同的硬件平台和操作系统上运行。Java源代码编译成Java字节码,然后Java字节码被JVM解释执行。你的Java代码可能要用到Java库中的代码。JVM将执行你的程序代码以及库中的代码。
执行:
执行Java代码就是运行程序的字节码,可以在任何一个装有JVM的平台上运行字节码,解释Java字节码。解释的过程就是一次将字节码中单独的一步翻译为目标机器语言代码,而不是将整个程序翻译成单独的一块。翻译完一步就立即执行这一步。
**警告:**在命令行执行程序时,不要使用扩展名.class。
**提示:**如果要运行一个不存在的类,就会出现NoClassDefFoundError的错误。如果执行的类文件中没有main方法或者敲错了main方法,则会出现提示NoSuchMethodError。
注意:在执行一个Java程序时,JVM首先会用一个称为类加载器(class loader)的程序将类的字节码加载到内存中。如果你的程序中使用其他类,类加载程序会在需要它们之前动态地加载它们。当加载该类后,JVM使用一个称为字节码验证器(bytecode verifier)的程序来检验字节码的合法性,确保字节码不会违反Java的安全规范。Java强制执行严格的安全规范,以确保来自网络的Java程序不会篡改和危害你的计算机。
简介:
程序设计风格:
程序设计风格(programming style)决定程序的外观。
如果把整个程序写在一行,它也会被正确地编译和运行,但是这是非常不好地程序设计风格,因为程序的可读性很差。
文档:
良好的程序设计风格和适当的文档可以减少出错的机率,并且提高程序的可读性。
在程序的开头写一个总结,解释一下这个程序是做什么的、其主要特点以及所用到的独特技术。
在较长的程序中还要加上注释,介绍每一个主要步骤并解释每个难以读懂之处。
除了行注释和块注释之外,Java还支持一种称为Java文档注释(javadoc comment)的特殊注释形式。javadoc注释以/**开始,以*/结尾。它们能使用JDK的javadoc命令提取一个HTML文件。
使用javadoc注释(/***/)来注释整个类或整个方法。为了将这些注释提取出来放在一个javadocHTML文件中,这些注释必须放在类或者方法头的前面。
要注释方法中的某一步骤,使用行注释。
缩进(identation)用于描述程序中组成部分或语句之间的结构性关系。
即使将程序的所有语句都写在一行中,Java也可以读懂这样的程序。
在嵌套结构中,每个内层的组成部分或语句应该比外层缩进两格。
二元操作符的两边应该各加一个空格。
块是由花括号围起来的一组语句。
块的写法有两种常用方式:次行(next-line)风格和行尾(end-of-line)风格。
次行风格将括号垂直对其,因而使程序容易阅读。而行尾风格更节省空间,并有助于避免犯一些细小的程序设计错误。
程序设计错误可以分为三类:语法错误、运行时错误和逻辑错误。
在编程过程中出现的错误称为语法错误(syntax error)或编译错误(compile error)。语法错误是由创建代码时的错误引起的。这些错误通常很容易检测到,因为编译器会告诉你这些错误在哪儿,以及是什么原因造成的。
由于一个错误常常会显示很多行的编译错误,因此,从最上面的行开始向下纠正错误是一个很好的习惯。
运行时错误(runtime error)是引起程序非正常中断的错误。
运行应用程序时,当环境检测到一个不可能执行的操作时,就会出现运行时错误。
输入错误是典型的运行时错误。另一个常见的运行时错误是0作除数。
当程序没有按预期的方式执行时就会发生逻辑错误(logic error)。这种错误发生的原因有很多种。
Java中,整数相除是返回除法的整数部分,即小数部分被截掉。
通常情况下,因为编译器可以明确指出错误的位置以及出错的原因,所以语法错误是很容易发现和纠正的。运行时错误也不难找,因为在程序异常中止时,错误的原因和位置都会显示在控制台上。然而,查找逻辑错误就很富有挑战性。
1、遗漏右括号:括号用来标识程序中的块。每个左括号必须有一个右括号匹配。常见的错误是遗漏右括号号。为避免这个错误,任何时候输入左括号的时候就输入右括号。如果使用NetBeans和Eclipse这样的IDE,IDE将自动为每个输入的左括号插入一个右括号。
2、遗漏分号:每个语句都以一个语句结束符(;)结束。通常,编程入门者会忘了在一个块的最后一行语句后加上语句结束符。
3、遗漏引号:字符串必须放在引号中。如果使用NetBeans和Eclipse这样的IDE,IDE将自动为每个输入的左括号插入一个右引号。
4、命名拼写错误:Java是大小写敏感的。
NetBeans和Eclipse是两个开发Java程序的免费的流行集成开发环境。
如果按照简单的指南学习,可以很快掌握。
1、计算机是存储和处理数据的电子设备。
2、计算机包括硬件和软件两部分。
3、硬件是计算机中可以触摸到的物理部分。
4、计算机程序,也就是通常所说的软件,是一些不可见的指令,它们控制硬件完成任务。
5、计算机程序设计就是编写让计算机执行的指令(即代码)
6、中央处理器(CPU)是计算机的大脑。它从内存获取指令并且执行这些指令。
7、计算机使用0或1,因为数字设备有两个稳定的状态,习惯上就是指0和1。
8、一个比特就是指二进制数0或1。
9、一个字节是指8比特的序列。
10、千字节大约是1000字节,兆字节大约是100万字节,千兆字节大约是10亿字节,万亿字节大约是1万亿字节。
11、内存存储CPU要执行的数据和程序指令。
12、内存单元是字节的有序序列。
13、内存是不能长久地保存在存储设备里,当计算机确实需要使用它们时被移入内存。
14、程序和数据永久地保存在存储设备里,当计算机确实是需要使用它们时被移入内存。
15、机器语言是一套内嵌在每台计算机的原始指令集。
16、汇编语言是一种低级程序设计语言,它用助记符表示每一条机器语言的指令。
17、高级语言类似英语,易于学习和编写程序。
18、用高级语言编写的程序称为源程序。
19、编译器是将源程序翻译成机器语言程序的软件。
20、操作系统(OS)是管理和控制计算机活动的程序。
21、Java是平台无关的,这意味着只需编写一次程序,就可以在任何计算机上运行。
22、Java程序可以内嵌在HTML网页内,通过Web浏览器下载,给Web客户带来生动的动画和灵活的交互性。
23、Java源程序文件名必须和程序中的公共类名一致,并且以扩展名.java结束。
24、每个类都被编译成一个独立的字节码文件,该文件名与类名相同,扩展名为.class
25、使用javac命令可以从命令行编译Java源代码文件。
26、使用java命令可以从命令行运行Java类。
27、每个Java程序都是一套类的定义集合。关键字class引入类的定义,类的内容包含在块内。
28、一个块以左括号({)开始,以右花括号(})结束。
29、方法包含在类中。每个可执行的Java程序必须有一个main方法。main方法是程序开始执行的入口。
30、Java中的每条语句都是以分号(;)结束,也称该符号为语句结束符。
31、保留字或者称关键字,对编译器而言都有特殊含义,在程序中不能用于其他目的。
32、在Java中,在单行上用两个斜杠(//)引导注释,称为行注释;在一行或多行用/和/包含注释,称为块注释或者段注释。编译器会忽略注释。
33、Java源程序是区分大小写的。
34、编程错误可以分为三类:语法错误、运行时错误和逻辑错误。编译器报告的错误称为语法错误或者编译错误。运行时错误指引起非正常结束的错误。当一个程序没有按照预期的方式执行时,产生逻辑错误。
概念:
算法:
类:
main方法:
变量:
数据类型:
为了让编译器知道变量是什么 ,需要指明它们的数据类型,即存储在变量中的数据的类型,是整数、实数,或者其他。这称为声明变量。
Java提供简单数据类型来表示整数、实数、字符以及布尔类型。这些类型称为原始数据类型或基本类型。
实数(即带小数点的数字)在计算机中使用一种浮点的方法来表示。因此,实数也称为浮点数。
Java中,可以使用关键字double来声明一个浮点变量。其表示变量是浮点数形式存储在计算机中的。
加号(+)有两种意义:一种用途是做加法,另一种用途是做字符串的连接(合并)。如果一个字符串和一个数值连接,数值将转化为字符串然后再和另外一个字符串连接。
**警告:**在源代码中,字符串常量不能跨行。
Scanner类:
Scanner input = new Scanner(System.in);
//代码解释:
语法new Scanenr(System.in)表明创建了一个Scanner类型的对象。
语法Scanner input声明input是一个Scanner类型的引用变量。
整行的Scanner input = new Scanner(System.in)表明创建了一个Scanner对象,并且将它的引用值赋值给变量input。
对象可以调用它自己的方法。调用对象的方法就是让这个对象完成某个任务。
println和print的区别:
import语句有两种类型:
明确导入(specific import)和通配符导入(wildcard import)。
明确导入是在import语句中指定单个的类。
通配符导入是指通过使用星号作为通配符,导入一个包中的所有类。
除非要在程序中使用某个类,否则关于被导入包中的这些类的信息在编译时或运行时是不被导入的。导入语句只是告诉编译器在什么地方能找到这些类。声明明确导入和声明通配符导入在性能上是没有什么差别的。
IPO:
简介:
所有的标识符必须遵从以下规则:
**提示:**不要使用 命 名 标 识 符 。 习 惯 上 , 字 符 命名标识符。习惯上,字符 命名标识符。习惯上,字符只用在机器自动产生的源代码中。
简介:
变量用于表示在程序中可能被改变的值。它们被称为变量是因为它们的值可以被改变。
变量用于表示特定类型的数据。为了使用变量,可以通过告诉编译器变量的名字及其可以存储的数据类型来声明该变量。
变量声明告诉编译器根据数据类型为变量分配合适的内存空间。(根据数据类型来分配大小,但引用类型如何确定分配的大小呢?)
如果一个变量为同一类型,允许一起声明它们。变量之间用逗号分隔开。
变量通常都有初始值。
可以一步完成变量的声明和初始化。
也可以使用简捷的方式来同时声明和初始化同一类型的变量。
在赋值给变量之前,必须声明变量。方法中声明的变量在使用之前必须被赋值。
每个变量都有使用范围。变量的使用范围是指变量可以被引用到的程序的部分。变量的作用域问题
一个变量在使用前,必须被声明和初始化。
赋值语句:
赋值表达式:
在Java中,将等号(=)作为赋值操作符(assignment operator)。
表达式(expression)表示涉及值、变量和操作符的一个运算,它们组合在一起计算出一个新值。
变量也可用在表达式中。变量也可以用于=操作符的两边。
要给一个变量赋值,变量名必须在赋值操作符的左边。
在Java中,赋值语句本质上就是计算出一个值并将它赋给操作符左边变量的一个表达式。由于这个原因,赋值语句常常称作赋值表达式(assignment expression)。.
并不能用于条件判断。
在赋值语句中,左边变量的数据类型必须与右边值的数据类型兼容。体现在多态中,子类对象赋值给父类的引用。
简介:
语法格式:
final datatype CONSTANTNAME = value;
使用常量的三个好处:
应该确保程序中为变量、常量、类和方法所选择的描述性名字是直观易懂的。
Java针对整数和浮点数有六种数值类型,以及+、-、*、/、和%等操作符。
简介:
类型名 | 范围 | 存储大小 |
---|---|---|
byte | -128~127(2^7) | 8位带符号数 |
short | -32768~32767(2^15) | 16位带符号数 |
int | -2_147_483_648~2_147_483_647(2^31) | 32位带符号数 |
long | -2^63~2^63-1 | 64位带符号数 |
float | 负数范围:-3.40282235E+38~-1.4E-45 正数范围:1.4E-45~3.4028235E+38 | 32位,标准IEEE754 |
double | ........ | 64位,标准IEEE754 |
整数类型:
浮点数类型:
方法 | 描述 |
---|---|
nextByte() | 读取一个byte类型的整数 |
nextShort() | 读取一个short类型的整数 |
nextInt() | 读取一个int类型的整数 |
nextLong() | 读取一个long类型的整数 |
nextFloat() | 读取一个float类型的整数 |
nextDouble() | 读取一个double类型的整数 |
简介:
操作符:
简介:
语法格式:
Math.pow(a,b);
一个直接量(literal)是一个程序中直接出现的常量值。
整型直接量:
注意事项:
**注意:**默认情况下,整形直接量是一个十进制数。要表示一个二进制整数直接量,使用0B或者0b(零B)开头;表示一个八进制整数直接量,就用0(零)开头,而要表示一个十六进制整数直接量,就用0x或0X(零x)开头。
浮点型直接量:
注意事项:
Java整数常量默认是int类型,当用二进制定义整数时,其第32位是符号位;当是long类型时,二进制默认占64位,第64位是符号位。
二进制整数有如下三种形式:
计算机以二进制补码的形式保存所有的整数。
科学计数法:
注意:float型和double型都是用来表示带有小数点的数。
为什么把它们称为浮点数呢?因为这些数都是以科学计数法的形式进行内部存储的。
注意:为了提高可读性,Java允许在数值直接量的两个数字间使用下划线。下划线必须置于两个数字之间。
表达式求值:
操作符优先级:
可以通过调用System.currentTimeMillis()返回当前的时间。
System类中的方法currentTimeMillis返回从GMT1970年1月1日00:00:00开始到当前时刻的毫秒数。
时间戳是时间开始记时的点,因为1970年是UNIX操作系统正式发布的时间,所以这一时间也称为UNIX时间戳(UNIX epoch)。
使用场景:
操作符+、-、*、/、%可以结合赋值操作符形式形成增强操作符。
Java允许使用增强赋值操作符来结合赋值和加法操作符的功能。
增强赋值操作符在表达式中所有其他操作符计算完后执行。
在增强赋值操作符中是没有空格的。
注意:就像赋值操作符(=)一样,增强赋值操作符既可以构成赋值语句,也可以构成赋值表达式。(不能用于条件判断,但能用于输出)
x += 2; //Statement
System.out.println(x += 2); //Expression
自增操作符(++)和自减操作符(–)是对变量进行加1和减1的操作。
++ 和 – 是对变量进行自增1和自减1的简写操作符。
++和–放在变量后面,这些操作符分别称为后置自增操作符和后置自减操作符。
++和–放在变量前面,这些操作符分别称为前置自增操作符和前置自减操作符。
操作符 | 名称 | 说明 |
---|---|---|
++var | 前置自增操作符 | 变量var的值加1且使用var增加后的新值 |
var++ | 后置自增操作符 | 变量var的值加1但使用var原来的值 |
--var | 前置自减操作符 | 变量var的值减1且使用var减少后的新值 |
var-- | 后置自减操作符 | 变量var的值减1但使用var原来的值 |
如果在一个二元运算中,其中一个操作数是整数,而另一个操作数是浮点数,Java会自动地将整数转换为浮点值。
总是可以将一个数值赋给支持更大数值范围类型的变量。但是,如果不进行类型转换,就不能将一个值赋给范围较小类型的变量。
类型转换是一种将一种数据类型的值转换为另一种数据类型的操作。
拓宽类型(widening a type):将一个小范围类型的变量转换为大范围类型的变量称为拓宽类型。
byte、char、short -->int —>long ---- float — >double
缩窄类型(narrowing a type):把大范围类型的变量转换为小范围类型的变量称为缩窄类型。
Java将自动拓宽一个类型,但是,缩窄类型必须显式完成。
类型转换的语法要求目标类型放在括号内,紧跟其后的是要转换的变量名或值。
**警告:**如果要将一个值赋给一个范围较小类型的变量,就必须进行类型转换。如果在这种情况下没有使用类型转换,就会出现编译错误。使用类型转换时必须小心,丢失的信息也许会导致不精确的结果。
当byte、char、short三种类型的变量做运算时,结果为int型。为什么?
注意:类型转换不改变被转换的变量。
注意:在增强赋值表达式中,使用的是被操作数的数据类型。
软件开生命周期是一个多阶段的过程,包括需求规范、分析、设计、实现、测试、部署和维护u。
开发一个软件产品是一个工程过程。软件产品,无论多大或者多小,具有同样的生命周期:需求规范、分析、设计、实现、测试、部署和维护。
import java.util.Scanner;
/**
*
* 程序清单2-10
*
* 将一个大额的钱分成小货币单位
*
* 假如你希望开发一个程序,将给定的钱数分成较小的货币单位。这个程序要求用户输入一个double型的值,该值是用美元和美分表示的总钱数,然后输出一个清单,列出和总钱数等价的最大数量的dollar(1美元)、quarter(2角5美分)、dime(1角)、nickel(5分)和penny(1分)的数目,按照这个顺序,从而使得硬币最少。
*
* 开发流程:
* 1、提示用户输入十进制数作为总钱数,例如11.56
* 2、将该钱数转换为1分币的个数
* 3、通过将1分币的个数除以100,求出1美元的个数。通过对1分币的个数除以100求余数,得到剩余1分币的个数。
* 4、通过将剩余的1分币的个数除以25,求出2角5分币的个数。通过对剩余的1分币的个数除以25求余数,得到剩余1分币的个数。
* 5、将剩余的1分币的个数除以10,求出1角币的个数。通过对剩余的1分币的个数除以10求余数,得到剩余1分币的个数。
* 6、将剩余的1分币的个数除以5,求出5分币的个数。通过对剩余1分币的个数除以5求余数,得到剩余1分币的个数。
* 7、剩余1分币的个数即为所求
* 8、显示结果。
*
*
*
*/
public class ComputeChange {
public static void main(String[] args) {
//Create a Scanner object
Scanner input = new Scanner(System.in);
//Receive the amount
System.out.print("Enter an amount in double,for example 11.56: ");
double amount = input.nextDouble();
//Covert amount to pennies
int remainingAmount = (int)(amount * 100);
//Find the number of one dollars
int numberOfOneDollars = remainingAmount / 100;
remainingAmount = remainingAmount % 100;
//Find the number of quarters in the remaining amount
int numberOfQuarters = remainingAmount / 25;
remainingAmount = remainingAmount % 25;
//Find the number of dime in the remaining amount
int numberOfDime = remainingAmount / 10;
remainingAmount = remainingAmount % 10;
//Find the number of nickel in the remaining amount
int numberOfNickel = remainingAmount / 5;
remainingAmount = remainingAmount % 5;
//Find the number of pennies in the remaining amount
int numberOfPennies = remainingAmount;
//Display results
System.out.println("Your amount " + amount + " consists of ");
System.out.println(" " + numberOfOneDollars + " dollars");
System.out.println(" " + numberOfQuarters + " quarters");
System.out.println(" " + numberOfDime + " Dimes");
System.out.println(" " + numberOfNickel + " nickel");
System.out.println(" " + numberOfPennies + " pennies");
/*
本例的一个严重问题是将一个double型的总数转换为int型数remainingAmount时可能会损失精度,这会导致不精确的结果。如果输入的总额值为10.03,那么10.03*100就会编程1002.99999999999999,程序会显示10个1美元和2个1美分。为了解决这个问题,应该输入用美分表示的整型值。
*/
}
}
常见的基础编程错误经常涉及未声明变量、未初始化变量、整数溢出、超出预期的整数除法,以及数值取整错误。
常见错误1、未声明、未初始化的变量和未使用的变量。
常见错误2:整数溢出
常见错误3:取整错误
常见错误4:超出预期的整数除法
常见陷阱:冗余的输入对象
1、标识符是程序中用于命名诸如变量、常量、方法、类、包之类元素的名称。
2、标识符是由字符、数字、下划线和美元符号构成的字符序列。标识符必须以字母或下划线开头,不能以数字开头。标识符不能是保留字。标识符可以为任意长度。
3、变量用于存储程序中的数据。声明变量就是告诉编译器变量可以存储何种数据类型。
4、有两种类型的import语句:明确导入和通配符导入。明确导入是在import语句中指定导入单个类;通配符导入将包中所有的类导入。
5、在Java中,等号被用作赋值操作符。
6、方法中声明的变量必须在使用前被赋值。
7、命名常量(或简称为常量)表示从不改变的永久数据。
8、用关键字final声明命名常量。
9、Java提供四种整数类型(byte、short、int、long)表示四种不同长度范围的整数。
10、Java提供两种浮点类型(float、double)表示两种不同精度的浮点数。
11、Java提供操作符完成数值运算:加号、减号、乘号、除号和求余符号
12、整数运算(/)得到的结果是一个整数。
13、Java表达式中的数值操作符和算数表达中的使用方法是完全一致的。
14、Java提供扩展赋值操作符:+=(加法赋值)、-=(减法赋值)、*=乘法赋值、/=除法赋值以及%=(求余赋值)。
15、自增操作符(++)和自减操作符(–)分别对变量加1或减1.
16、当计算的表达式中有不同类型的值,Java会自动地将操作数转换为恰当的类型。
17、可以使用(type)value这样的表示法显式地将数值从一个类型转换到另一个类型。
18、将一个较小范围地变量转换为较大范围类型地变量称为拓宽类型。
19、将一个较大范围的变量转换为较小范围的变量称为缩窄类型。
20、扩宽类型不需要显式转换,可以自动完成。缩窄类型必须显式完成。
21、在计算机科学中 ,1970年1月1日午夜零点称为UNIX时间戳。
和所有高级程序设计语言一样,Java也提供选择语句:在可选择的执行路径中做出选择的语句。
选择语句要用到采用布尔表达式的条件。
布尔表达式是计算结果为Boolean值:true或者false的表达式。
boolean数据类型声明一个具有值true或者false的变量。
Java提供六种关系操作符(relational operator)(也称为比较操作符(compaarison operator)),用于两个值的比较。
Java操作符 | 名称 |
---|---|
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
== | 等于 |
!= | 不等于 |
**警告:**相等的关系操作符是两个等号(==),而不是一个等号(=),后者是指赋值操作符。
具有布尔值的变量称为布尔变量(boolean variable),boolean数据类型用于声明布尔型变量。
boolean型变量可以是以下这两个值中的一个:true和false。
true和false都是直接量,就像10这样的数字。它们被当作保留字一样,不能用做程序中的标识符。
简介:
单分支语句:
if(布尔表达式){
语句(组);
}
流程图:
**注意:**省略花括号可以让代码更加简短,但是容易产生错误。当你返回去修改略去代码的时候,容易忘记加上括号。这是一个常犯的错误。
简介:
if(布尔表达式){
布尔表达式为真时执行的语句(组);
}
else{
布尔表达式为假时执行的语句(组);
}
if或if-else语句中的语句可以是任意合法的Java语句,甚至可以是其他的if或if-else语句。
内层if语句称为是嵌套在外层if语句里的。
内层if语句还可以包含其他的if语句;事实上,对嵌套的深度没有限制。
嵌套的if语句可用于实现多重选择。
注意:只有在前面的所有条件都为false时才测试下一个条件。
常见错误1:忘记必要的括号
常见错误2:在if行出现错误的分号
常见错误3:对布尔值的冗余测试
常见错误4:悬空else出现的歧义
常见错误5:两个浮点数值的相等测试
常见陷阱1:简化布尔变量赋值
if(number%2==0)
even = true;
else
even = false;
boolean even = numnber%2 == 0;
常见陷阱2:避免不同情形中的重复代码
编程新手经常会在不同情形下写重复的代码,这些代码应该写在一处的。
if(inState){
tuition=5000;
System.out.println("The tuition is " + tuition);
}else{
tuition = 15000;
System.out.println("The tuition is " + tuition);
}
//-------------------------------------------------
if(inState){
tuition = 5000;
}else{
tuition = 15000;
}
System.out.println("The tuition is " + tuition);
你可以使用Math.random()来获得一个0.0到1.0之间的随机double值,不包括1.0调用这个方法会返回一个双精度的随机值d且满足0.0<=d<1.0。
System.exit(status)是在System类中定义的, 调用这个方法可以终止程序。参数status为0表明程序是正常结束。一个非0的状态代码表示非正常结束。结束正在运行的JVM虚拟机。
如果变量变量赋值语句在if语句中,编译器认为这些语句可能不会执行,因此会报告一个编译错误。所以变量需要在条件判断语句外初始化。
System.exit和break和continue的区别是什么?
所有程序都应该先编写少量代码然后进行测试,之后再继续添加更多的代码。这个过程称为递进式开发和测试(incremental development and testing)。这种方法使得调式变得更加容易,因为错误很可能就在你刚刚添加进去的新代码中。
简介:
逻辑操作符!、&&、||和^可以用于产生复合布尔表达式。
有时候,是否执行一条语句是由几个条件的组合来决定的。可以使用逻辑操作符组合这些条件。
逻辑操作符(logical operator)也称为布尔操作符(boolean operator),是对布尔值进行的运算,它会创建新的布尔值。
逻辑操作符说明:
非操作符(!)对true取反是false,而false取反之后则是true。
与操作(&&)。当且仅当两个操作数都为true时,这两个布尔型操作数的与(&&)为true。如果操作符&&的操作数之一为false,那么表达式就是false;
或操作符(||),当至少有一个操作数为true时,两个布尔型操作数的或(||)为true。如果操作符||的操作数之一为true,那么表达式就是true。
异或(^)操作符:当且仅当两个操作数具有不同的布尔值时,两个布尔型 操作数的异或(^)才为true。
操作符 | 名称 | 说明 |
---|---|---|
! | 非 | 逻辑非 |
&& | 与 | 逻辑与 |
|| | 或 | 逻辑或 |
^ | 异或 | 逻辑异或 |
德模佛定理
是以印度出生的英国数学家和逻辑学家奥古斯都·德·模佛来命名的(1806-1871),这个定理可以用来简化表达式。
内容:
在编程术语中,&&和||被称为短路或者懒惰操作符;Java也提供了无条件AND(&)和OR(|)操作符。
import java.util.Scanner;
/**
*
* 程序清单3-7
*
* 如果某年你可以被4整除而不能被100整除,或者可以被400整除,那么这一年就是闰年
*
* 可以使用下面的表达式判定某年是否为闰年。
*
* //A leap Year is divisible by4
* boolean isLeanYear = ( year % 4 == 0 )
*
* //A leap Year is divisible by 4 but not by 100
* isLearYear = isLeapYear && (year % 100 != 0);
*
* //A leap Year is divisible by 4 but not by 1000 or divisible by 400
* is LeapYear = isLeapYear || ( year % 400 == 0)
*
*/
public class LeapYear {
public static void main(String[] args) {
//Create a Scanner object
Scanner input = new Scanner(System.in);
//Promtp the user to enter an Integer for years
System.out.print("Enter a year");
int year = input.nextInt();
//Check if the year is a leap year
boolean isLeapYear = (year % 4 == 0 ) || (year % 400 == 0);
//Display the result
System.out.println(year + " is a leap year? " + isLeapYear);
}
}
import java.util.Scanner;
/**
*
* 程序清单3-8
*
* 彩票程序涉及产生随机数、比较数字各位,以及运用布尔操作符。
*
*
* 假设你想开发一个玩彩票的游戏,程序随机地产生一个两位数的彩票,提示用户输入一个两位数,然后按照下面的规则判定用户是否能赢:
* 1、如果用户的输入数匹配彩票的实际顺序,奖金为10000美元。
* 2、如果用户输入的所有数字匹配彩票的所有数字,奖金为3000美元.
* 3、如果用户输入的一个数字匹配彩票的一个数字,奖金为1000美元。
*
* 注意,两位数字的位可能为0.如果一个数小于10,我们假设这个数字以0开始,从而构建一个两位数。
* 例如,程序中数字8被作为08处理,数字00作为00处理。
*
*
*/
public class Lottery {
public static void main(String[] args) {
//Generate a lottery number
int lottery = (int)(Math.random()* 100);
//Prompt the user to enter a guess
Scanner input = new Scanner(System.in);
System.out.print("Enter your lottery pick(two digits): ");
int guess = input.nextInt();
//Get digits form lottery
int lotteryDigit1 = lottery / 10;
int lotteryDIgit2 = lottery % 10;
//Get digits form guess
int guessDigits1 = guess / 10;
int guessDigits2 = guess % 10;
//Print the lottery number
System.out.println("The lottery number is " + lottery);
//Check the guess
if(guess == lottery){
System.out.println("Exact match: you win $10,000");
}else if(guessDigits2 == lotteryDigit1 && guessDigits1 == lotteryDIgit2){
System.out.println("Match all digits: you win $3,000");
}else if(guessDigits1 == lotteryDigit1
|| guessDigits1 == lotteryDIgit2
|| guessDigits2 == lotteryDigit1
|| guessDigits2 == lotteryDIgit2)
System.out.println("Match one digit: you win $1,000");
else
System.out.println("Sorry,no match");
}
}
简介:
switch语句遵从以下规则:
JDK14:
为了避免程序设计错误,提高代码的可维护性,如果故意省略break,在case子句后添加注释是一个好的做法。
default语句的顺序没有硬性规定。可以放在最前面。
简介:
条件表达式基于一个条件计算表达式的值。
有时可能需要给有特定条件限制的变量赋值。
条件表达式是一种完全不同的风格,在语句中没有明确出现if。
如果布尔表达式的值为true,则条件表达式的结果为expression1;否则,结果为表达式expression2.
符号?和:在条件表达式中同时出现。它们构成一种条件操作符,因为操作数有三个,所以称为三元操作符(ternary operator)。它是Java中唯一的三元操作符。
语法格式:
boolean-expression ? expression1 : expression2(布尔表达式?表达式1:表达式2)
优先级:
操作符的优先级和结合规则确定了操作符计算的顺序。
应该首先计算括号中的表达式(括号可以嵌套,在嵌套的情况下,先计算里层括号中的表达式)。当计算没有括号的表达式时,操作符会按照优先级规则和结合规则进行运算。
优先级规则定义了操作符的先后次序:逻辑操作符的优先级比关系操作符的低,而关系操作符的优先级比比算数操作符的低。
结合规则:
优先级
|
运算符
|
名称或含义
|
使用形式
|
结合方向
|
说明
|
1
|
后置++ |
后置自增运算符
|
变量名++
|
左到右
|
|
后置-- | 后置自减运算符 | 变量名-- | |||
[ ]
|
数组下标 |
数组名[整型表达式]
|
|||
( )
|
圆括号
|
(表达式)/函数名(形参表)
|
|||
.
|
成员选择(对象)
|
对象.成员名
|
|||
->
|
成员选择(指针)
|
对象指针->成员名
|
|||
2
|
-
|
负号运算符
|
-表达式
|
右到左
|
单目运算符
|
(类型)
|
强制类型转换
|
(数据类型)表达式
|
|||
前置++
|
前置自增运算符
|
++变量名
|
单目运算符
|
||
前置--
|
前置自减运算符
|
--变量名
|
单目运算符
|
||
*
|
取值运算符
|
*指针表达式
|
单目运算符
|
||
&
|
取地址运算符
|
&左值表达式
|
单目运算符
|
||
!
|
逻辑非运算符
|
!表达式
|
单目运算符
|
||
~
|
按位取反运算符
|
~表达式
|
单目运算符
|
||
sizeof
|
长度运算符
|
sizeof 表达式/sizeof(类型)
|
|||
3
|
/
|
除
|
表达式/表达式
|
左到右
|
双目运算符
|
*
|
乘
|
表达式*表达式
|
双目运算符
|
||
%
|
余数(取模)
|
整型表达式%整型表达式
|
双目运算符
|
||
4
|
+
|
加
|
表达式+表达式
|
左到右
|
双目运算符
|
-
|
减
|
表达式-表达式
|
双目运算符
|
||
5
|
<<
|
左移
|
表达式<<表达式
|
左到右
|
双目运算符
|
>>
|
右移
|
表达式>>表达式
|
双目运算符
|
||
6
|
>
|
大于
|
表达式>表达式
|
左到右
|
双目运算符
|
>=
|
大于等于
|
表达式>=表达式
|
双目运算符
|
||
<
|
小于
|
表达式<表达式
|
双目运算符
|
||
<=
|
小于等于
|
表达式<=表达式
|
双目运算符
|
||
7
|
==
|
等于
|
表达式==表达式
|
左到右
|
双目运算符
|
!=
|
不等于
|
表达式!= 表达式
|
双目运算符
|
||
8
|
&
|
按位与
|
整型表达式&整型表达式
|
左到右
|
双目运算符
|
9
|
^
|
按位异或
|
整型表达式^整型表达式
|
左到右
|
双目运算符
|
10
|
|
|
按位或
|
整型表达式|整型表达式
|
左到右
|
双目运算符
|
11
|
&&
|
逻辑与
|
表达式&&表达式
|
左到右
|
双目运算符
|
12
|
||
|
逻辑或
|
表达式||表达式
|
左到右
|
双目运算符
|
13
|
?:
|
条件运算符
|
表达式1? 表达式2: 表达式3
|
右到左
|
三目运算符
|
14
|
=
|
赋值运算符
|
变量=表达式
|
右到左
|
|
/=
|
除后赋值
|
变量/=表达式
|
|||
*=
|
乘后赋值
|
变量*=表达式
|
|||
%=
|
取模后赋值
|
变量%=表达式
|
|||
+=
|
加后赋值
|
变量+=表达式
|
|||
-=
|
减后赋值
|
变量-=表达式
|
|||
<<=
|
左移后赋值
|
变量<<=表达式
|
|||
>>=
|
右移后赋值
|
变量>>=表达式
|
|||
&=
|
按位与后赋值
|
变量&=表达式
|
|||
^=
|
按位异或后赋值
|
变量^=表达式
|
|||
|=
|
按位或后赋值
|
变量|=表达式
|
|||
15
|
,
|
逗号运算符
|
表达式,表达式,…
|
左到右
|
从左向右顺序运算
|
概念:
因为编译器可以明确指出错误的位置以及出错的原因,所以语法错误是很容易发现和纠正的。
运行时错误也不难找,因为在程序异常中止时,错误的原因和位置都会显示在控制台上。
然而,查找逻辑错误就很富有挑战性。
逻辑错误也称为臭虫(bug)。查找和改正错误的过程称为调试(debugging)。
调试方法:
调试工具:
JDK包含了一个命令行调试器jdb,结合一个类名来调用该命令。Jdb本身也是一个java程序,运行自身的一个Java解析器的拷贝。
所有的Java IDE工具,比如Eclipse和NetBeans包含集成的调试器。
调试器应用让你可以跟踪一个程序的执行。它们因系统而不同。
调试器应用的特征:
一次执行一条语句:
跟踪进入或者一步运行过一个方法:
设置断点:
显示变量:
显示调用堆栈:
修改变量:
1、boolean类型变量可以存储值true或false。
2、关系操作符(<、<=、==、!=、>、>=)产生一个布尔值。
3、选择语句用于可选择的动作路径的编程。选择语句有以下几种类型:单分支if语句、双分支if-else语句、嵌套if语句、多分支if-else语句、switch语句和条件表达式。
4、各种if语句都是基于布尔表达式来控制决定的。根据表达式的值是true或false,这些语句选择两种可能路径中的一种。
5、布尔操作符&&、||、|和^对布尔值和布尔变量进行计算。
6、当对p1&&p2求值时,Java先求p1的值,如果p1为true,再对p2求值;入宫p1为false,就不再对p2求值。当对p1||p2求值时,Java先求p1的值,如果p1为false,再对p2求值;如果p1为true,就不再对p2求值。因此,&&也称为条件与操作符或短路与操作符,而||也称为条件或操作符或短路或操作符。
7、switch语句根据char、byte、short、int或者String类型的switch表达式来进行控制决定。
8、在switch语句中,关键字break是可选的,但它通常用在每个分支的结尾,以中止执行switch语句的剩余部分。如果没有出现break语句,则执行接下来的case语句。
9、表达式中的操作符按照括号、操作符优先级以及操作符结合规则所确定的次序进行求值。
10、括号用于强制求值的顺序以任何顺序进行。
11、具有更高优先权的操作符更早地进行操作。对于同样优先级地操作符,它们的结合规则确定操作符的顺序。
12、除开赋值操作符的所有二元操作符都是左结合的,赋值操作符是右结合的。
Java在Math类中提供了许多实用的方法,来计算常用的数学函数。
方法是一组语句,用于执行一个特定的任务。
Math中的方法分为三类:
除了这些方法之外,Math类还提供了两个很有用的double型常量,PI和E(自然对数的底)。可以在任意程序中用Math.PI和Math.E的形式来使用这两个常量。
sin、cos和tan的参数都是以弧度为单位的角度。asin和atan的返回值是-PI/2到PI/2的一个弧度值,acos的返回值在0到PI之间。1°相当于PI/180弧度,90°相当于PI/2弧度,30°相当于PI/6弧度。
Math类包括五个取整方法。
求最小值
求最大值
求绝对值
Math.random()生成大于等于0.0且小于1.0的double型随机数。可以使用它编写简单的表达式,生成任意范围的随机数。
Math类在程序中使用,但是并没有导入,因为它在java.lang包中。在一个Java程序中,java.lang包中的所有类是隐式导入的。
import java.util.Scanner;
/**
* 程序清单4-1
*
*
* 例子:
* 给定一个三角形的三条边,可以通过以下公式计算角度。
* A = acos( (a * a - b * b -c * c) / (-2 * b *c))
* B = acos( (b * b - a * a -c * c) / (-2 * a *c))
* C = acos( (c * c - b * b -a * a) / (-2 * a *b))
*
* 提示:两点之间的距离公式:sprt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) )
*
*
* 提示用户输入三角形三个顶点的x和y坐标值,然后显示三个角
*/
public class ComputeArea {
public static void main(String[] args) {
//Create a Scanner Object
Scanner input = new Scanner(System.in);
//Prompt the user to enter three points
System.out.println("Enter three points: ");
double x1 = input.nextDouble();
double y1 = input.nextDouble();
double x2 = input.nextDouble();
double y2 = input.nextDouble();
double x3 = input.nextDouble();
double y3 = input.nextDouble();
//Compute three sides
double a = Math.sqrt((x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3));
double b = Math.sqrt((x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3));
double c = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
//Compute three angles
double A = Math.toDegrees((a * a - b * b - c * c) / (-2 * b * c));
double B = Math.toDegrees((b * b - a * a - c * c) / (-2 * a * c));
double C = Math.toDegrees((c * c - b * b - a * a) / (-2 * a * b));
//Display results
System.out.println("The three angles are " +
Math.round(A * 100) / 100.0 + " " +
Math.round(B * 100) / 100.0 + " " +
Math.round(C * 100) / 100.0);
}
}
字符数据类型表示单个字符。
除了处理数值之外,Java还可以处理字符。
字符数据类型用char用来表示单个字符。
字符型直接量用单引号括住。
字符串直接两必须括在双引号中。而字符直接量是括在单引号中的单个字符。
计算机内部使用二进制。一个字符在计算机中是以0和1构成的序列的形式来存储的。
编码:
Unicode:
Java支持Unicode码,Unicode码是由Unicode协会建立的一种编码方案,它支持使用世界各种语言所书写的文本的交换、处理和显示。
Unicode码一开始被设计为16位字符编码。基本数据类型char试图通过提供一种能够存放任意字符的简单数据类型来利用设计,但是,一个16位的编码能产生的字符只有65536个,它是不足以表示全世界所有字符的。因此,Unicode标准被扩展为1112064个字符。这些字符都远远超过了原来16位的限制,它们称为补充字符(supplementary character)。Java支持这些补充字符。这些做法都可以存储在一个char型变量中。
一个16位Unicode码占两个字节,用以\u开头的4位十六进制数表示。
自增自减操作符也可以用在char型变量上,这会得到该字符之前或之后的Unicode字符。
Unicode码包括ASCII码。
ASCII:
Java中可以使用ASCII码,也可以使用Unicode码。
概念:
Java定义了一种特殊的标记来表示特殊字符,这种标记称为转义序列。
转义序列由反斜杠后面加上一个字符或者一些数字位组成。
转义序列中的序列号作为一个整体翻译,而不是分开翻译。
一个转义序列被当作一个字符。
反斜杠\被称为转义字符。它是以一个特殊字符。要显示这个字符,需要使用转义序列\。
转义序列 | 名称 | Unicode码 | 十进制值 |
---|---|---|---|
\b | 退格键 | \u0008 | 8 |
\t | Tab键 | \u0009 | 9 |
\n | 换行符 | \u000A | 10 |
\f | 换页符 | \u000C | 12 |
\r | 回车符 | \u000D | 13 |
\\ | 反斜杠 | \u005C | 92 |
\" | 双引号 | \u0022 | 34 |
字符:
转换:
要将一个浮点值转换成char型时,首先将浮点值转换成int型,然后将这个整形值转换为char型。
当一个char型数据转换成数值型时,这个字符的Unicode码就被转换成某个特定的数值类型。
如果转换结果适用于目标变量,就可以使用隐式转换方式;否则,必须使用显式转换方式。
0~FFFF的任何一个十六进制正整数都可以隐式地转换成字符型数据。而不在此范围内的任何其他数值都必须显式地转换为char型。
所有数值操作符都可以用在char型操作数上。
如果另一个操作数是一个数字或字符,那么char型操作数就会被自动转换成一个数字。如果另一个操作数是一个字符串,字符就会与该字符串相连。
两个字符可以使用关系操作符进行比较,如同比较两个数字一样。这是通过比较两个字符的Unicode值实现的。
小写字母的Unicode码是连续的整数,同理,对于大写字母和数字字符也是这样的。这个特征可以用于编写测试字符的编码。
Java的Character类中用于进行字符测试的方法:
方法 | 描述 |
---|---|
isDigit(ch) | 如果指定的字符是一个数字,返回true |
isLetter(ch) | 如果指定的字符是一个字母,返回true |
isLetterOrDigit(ch) | 如果指定的字符是一个字母或者数字,返回true |
isLowerCase(ch) | 如果指定的字符是一个小写字母,返回true |
isUpperCase(ch) | 如果指定的字符是一个大写字母,返回true |
toLowerCase(ch) | 返回指定的字符的小写形式 |
toUpperCase(ch) | 返回指定的字符的大写形式 |
概念:
字符串是一个字符序列。
char类型只能表示一个字符。为了表示一串字符,使用称为String(字符串)的数据类型。
String实际上与System类和Scanner类一样,都是Java库中一个预定义的类。
String类型不是基本类型,而是引用类型(reference type)。
任何Java类都可以将变量表示为引用类型。
使用引用类型声明的变量称为引用变量,它引用一个对象。
方法 | 描述 |
---|---|
length() | 返回字符串中的字符数 |
charAt(index) | 返回字符串s中指定位置的字符 |
concat(s1) | 将本字符串和字符串s1连接,返回一个新字符串 |
toUpperCase() | 返回一个新字符串,其中所有的字母大写 |
toLowerCase() | 返回一个新字符串, 其中所有的字母小写 |
trim() | 返回一个新字符串,去掉了两边的空白字符。 |
调用实例方法的语法:一个方法可以有多个参数或者无参。
调用静态方法的语法:
可以调用字符串的length()方法获取它的长度。
注意:使用一个字符串时,往往是知道它的直接量值的。为了方便起见,Java允许在不创建新变量的情况下,使用字符串直接量直接引用字符串。引用字符串常量池中的对象。?????
s.charAt(index)此方法可用于提取字符串s中的某个特定字符,其中下标index的取值范围在0~s.length()-1之间。
**警告:**在字符串S中越界访问字符是一种常见的程序设计错误。为了避免此类错误,要确保使用的下标不会超过s.length()-1。
可以使用concat方法连接两个字符串。
可以使用加号(+)连接两个或多个字符串。
若要用加号实现连接功能,至少要有一个操作数必须为字符串。如果操作数之一不是字符串,非字符串值转换为字符串,并于另外一个字符串连接。如果操作数都不是字符串,加号(+)是一个将两个数字相加的加法操作符。
增强的+=操作符也可以用于字符串连接。
方法toLowerCase()返回一个新字符串,其中所有字母小写;
方法toUpperCase()返回一个新字符串,其中所有字母大写。
方法trim()通过删除字符串两端的空白字符返回一个新字符串。
字符’ '、\t、\f、\r、或者\n被称为空白字符。
为了从控制台读取字符串,调用Scanner对象上的next()方法。
next()方法读取以空白字符结束的字符串(即’‘、’\t’、‘\f’、‘\r’或’\n’)。
可以用nextLine()方法读取一整行文本。nextLine()方法读取以按下回车键为结束标志的字符串。
为了从控制台读取字符,调用nextLine()方法读取一个字符串,然后在字符串上调用charAt(0)来返回一个字符。
方法 | 描述 |
---|---|
equals(s1) | 如果该字符串等于字符串s1,返回true。 |
equalsIgnoreCase(s1) | 如果该字符串等于字符串s1,返回true;不区分大小写 |
compareTo(s1) | 返回一个大于0、等于0、小于0的整数,表明一个字符串是否大于、等于或者小于s1 |
compareToIgnoreCase(s1) | 和compareTo一样,除了比较是区分大小写的之外。 |
startWith(prefix) | 如果字符串以特定的前缀开始,返回true |
endsWith(suffix) | 如果字符串以特定的后缀结束,返回true |
contains(s1) | 如果s1是该字符串的子字符串,返回true |
equals用常量调用方法比较,避免空指针异常,变量有可能记录的是null。
compareTo方法也用来对两个字符串进行比较。如果s1于s2相等,那么该方法返回值0;如果按字典顺序(即Unicode码的顺序)s1小于s2,那么方法返回值小于0;如果按字典顺序s1大于s2,方法返回值大于0.方法compareTo返回的实际值是依据s1和s2从左到右第一个不同字符之间的距离得出的。
注意:如果两个字符串相等,equals方法返回true;如果它们不等,方法返回false。compareTo方法会根据一个字符串是否等于、大于或小于另一个字符串,分别返回0、正整数或负整数。
String类还提供了对字符串进行比较的方法equalsIgnoreCase和compareToIgnoreCase。当比较两个字符串时,方法equalsIgnoreCase和compareToIgnoreCase忽略字母的大小写。
还可以使用str.startsWith(prefix)来检测字符串是否以特定的前缀(prefix)开始,使用str.endWith(suffix)来检测字符串str是否以特定的后缀(suffix)结束,
并且可以使用str.contains(s1)来检测是否字符串str包含字符串s1.
警告:如果使用像>、>=、<、或<=这样的比较操作符比较两个字符串,就会发生语法错误。
方法s.charAt(index)可用于提取字符串s中的某个特定字符。也可以使用String类中的substring方法从字符串中提取子串。
方法 | 描述 |
substring(beginIndex) | 返回该字符串的子串,从特定位置beginIndex的字符开始到字符串的结尾。 |
substring(beginIndex,endIndex | 返回该字符串的子串,从特定位置beginIndex的字符开始到下标为denIndex-1的字符。注意,位于endIndex位置的字符不属于该子字符串的一部分。 |
String类提供了几个版本的indexOf和lastIndexOf方法,它们可以在字符串中找出一个字符或一个子串。
方法 | 描述 |
---|---|
indexOf(ch) | 返回字符串中出现的第一个ch的下标。如果没有匹配的,返回-1 |
indexOf(ch,fromIndex) | 返回字符串中fromIndex之后出现的第一个ch的下标。如果没有匹配的,返回-1 |
indexOf(s) | 返回字符串中出现的第一个字符串s的下标。如果没有匹配的,返回-1 |
indexOf(s,fromIndex) | 返回字符串中出现的第一个字符串s的下标。如果没有匹配的,返回-1 |
lastIndex(ch) | 返回字符串中出现的最后一个ch的下标。如果没有匹配的,返回-1 |
lastIndex(ch,fromIndex) | 返回字符串中fromIndex之前出现的最后一个ch的下标。如果没有匹配的,返回-1 |
lastIndex(s) | 返回字符串中出现的最后一个字符串s的下标。如果没有匹配的,返回-1 |
lastIndexOf(s,fromIndex) | 返回字符串中fromIndex之前出现的最后一个字符串s的下标。如果没有匹配的,返回-1 |
可以将数值型字符串转换为数值。要将字符串转换为int值,使用Interget.parseInt方法。
格式如下:
int intValue = Integer.parseInt(intString);
//intString是一个数值型字符串"123"
要将字符串转换为double值,使用Double.parseDouble方法。
格式如下:
double doubleValue = Double.parseDouble(doubleString);
//doubleString是一个数值型字符串,例如"123.54"
如果字符串不是数值型字符串,转换将导致一个运行时错误。Integer和Double类都包含在java.lang包中,因此它们是自动导入的。
可以将数值转换为字符串,只需要简单使用字符串的连接操作符。
String s = number + "";
字符串在编程中是非常基础的内容。使用字符串进行编程的能力对于学习Java编程非常关键。
import java.util.Scanner;
/**
*
* 程序清单4-3
*
* 可以通过询问5个问题,找到他出生在一个月的哪一天。每个问题都询问生日是否是5个数字集合中的一个。
*
* {1 3 5 7,
* 9 11 13 15,
* 17 19 21 23,
* 25 27 29 31}
*
* {2 3 6 7,
* 10 11 14 15,
* 18 19 22 23,
* 26 27 30 31}
*
* {4 5 6 7,
* 12 13 14 15,
* 20 21 22 23,
* 28 29 30 31}
*
* {8 9 10 11,
* 12 13 14 15,
* 24 25 26 27,
* 28 29 30 31}
*
* {16 17 18 19,
* 20 21 22 23,
* 24 25 26 27,
* 28 29 30 31}
*
*
* 生日是出现这一天的每个集合的第一个数字的和。例如:如果生日是19,那么它会出现在集合1、集合2和集合5中。这三个集合的第一个数字分别是1、2和16.它们的和就是19.
*
* 提示用户回答该天是否在集合1中,是否在集合2中,是否在集合3中,是否在集合4中,是否在集合5中。如果这个数字在某个集合中,程序就将该集合的第一个数字加到day中去。
*
*
*/
public class GuessBirthday {
public static void main(String[] args) {
String set1 =
"1 3 5 7\n" +
"9 11 13 15\n" +
"17 19 21 23\n" +
" 25 27 29 31";
String set2 =
"2 3 6 7\n" +
"10 11 14 15\n" +
"18 19 22 23\n" +
"26 27 30 31";
String set3 =
"4 5 6 7\n" +
"12 13 14 15\n" +
"20 21 22 23\n" +
"28 29 30 31";
String set4 =
"8 9 10 11\n" +
"12 13 14 15\n" +
"24 25 26 27\n" +
"28 29 30 31";
String set5 =
"16 17 18 19\n" +
"20 21 22 23\n" +
"24 25 26 27\n" +
"28 29 30 31";
int day = 0;
//Create a Scanne object
Scanner input = new Scanner(System.in);
//Prompt the user to answer questions
System.out.print("Is your birthday in Set1?\n");
System.out.print(set1);
System.out.print("\nEnter 0 for No and 1 for Yes:");
int answer = input.nextInt();
if(answer == 1)
day += 1;
//Prompt the user to answer question
System.out.print("Is your birthday in Set2?\n");
System.out.print(set2);
System.out.print("\nEnter 0 for No and 1 for Yes:");
answer = input.nextInt();
if(answer ==1)
day += 2;
//Prompt the user to answer question
System.out.print("Is your birthday in Set3?\n");
System.out.print(set3);
System.out.print("\nEnter 0 for No and 1 for Yes: ");
answer = input.nextInt();
if(answer ==1)
day += 4;
//Prompt the user to answer question
System.out.print("Is your birthday in Set4?\n");
System.out.print(set4);
System.out.print("\nEter 0 for No and 1 for Yes:");
answer = input.nextInt();
if(answer == 1)
day += 8;
//Prompt the user to answer question
System.out.print("Is your birthday in Set5?\n");
System.out.print(set5);
System.out.print("\nEnter 0 for No and 1 for Yes:");
answer = input.nextInt();
if(answer ==1)
day += 16;
System.out.println("\nYour birthday is " + day + "!");
/*
原理:
实际上,这个游戏背后的数学知识是非常简单的。这些数字不是随意组成一组的。它们放在5个集合中的方式是经过深思熟虑的。这5个集合的第一个数分别是1、2、4、8、16,它们分别对应二进制数的1、10、100、1000和10000。从1到31的十进制数最多用5个二进制数就可以表示,假设它是b5b4b3b2b1,那么b5b4b3b2b1 = b50000 + b4000 + b300 +b20 + b1,如果某天的二进制数在bk位为1,那么该数就该出现在Setk中。例如:数字19的二进制形式是1001,所以它就该出现在集合1、集合2和集合5中。它就是二进制数1+10+10000=1011或者十进制数1+2+16 = 19.数字31的二进制形式是1111,所以它就会出现在集合1、集合2、集合3、集合4和集合5中。它就是二进制数1+10+100+1000+10000=11111,或者十进制数1+2+4+8+16 = 31。
*/
}
}
当两个字符执行数值运算的时候,计算机中使用的是字符的Unicode码。
import java.util.Scanner;
/**
* 十六进制计数系统有16个数字:0~9,A~F。字母A、B、C、D、E和F对应于十进制数字10、11、12、13、14和15.我们现在写一个程序,提示用户输入一个十六进制的数字,显示它对应的十进制数。
*/
public class HexDigit2Dec {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("Enter a hex digit: ");
String hexString = input.nextLine();
//Check if the hex string has exactly one character
if(hexString.length() != 1){
System.out.println("You must enter exactly one character");
System.exit(1);
}
//Display decimal value for the hex digit
char ch = Character.toUpperCase(hexString.charAt(0));
if(ch <='F' && ch >= 'A'){
int value = ch - 'A'+ 10; //如果是十六进制字母,则将字母的Unicode码值减去10加上10
System.out.println("The decimal value for hex digit " + ch + " is " + ch);
}else if (Character.isDigit(ch)){
System.out.println("The decimal value for hex digit " + ch + " is " + ch);
}else{
System.out.println(ch + " is an invalid input"); //invalid无效
}
}
}
import java.util.Scanner;
/**
* 程序清单4-5
*
* 程序清单3-8中的彩票程序产生一个随机的两位数字,提示用户输入一个两位数字,根据以下规则确定用户是否中彩票:
* 1)如果用户输入的数字完全匹配彩票中的数字,奖金为10000美元
* 2)如果用户输入的所有数字匹配彩票中的所有数字,奖金为3000美元
* 3)如果用户输入的一个数字匹配彩票中的一个数字,奖金为1000美元。
*
* 程序清单3-8中的程序使用整数来存储数值。
* 程序清单4-5给出一个新程序,产生一个随机的两位字符串,而不是数字,并且使用字符串而不是数字来接收用户输入。
*
*
*/
public class LotteryUsingStrings {
public static void main(String[] args) {
/*
程序产生两个随机数,并且将它们连接成一个字符串lottery。这样,lottery包含两个随机数字。
程序提示用户以两位字符串形式输入一个猜测值,并且按照以下顺序,对照彩票数字检测用户的猜测值:
首先检测给出的猜测值是否完全匹配彩票
如果不匹配,检测猜测值的逆序是否匹配彩票
如果不匹配,检测是否有一个数字在彩票中。
如果以上条件都不成立,显示“Sorry,no match”
*/
//Generate a lottery as a two-digit string
String lottery = "" + (int)(Math.random() * 10) + (int)(Math.random() * 10);
//Prompt the user to enter a guess
Scanner input = new Scanner(System.in);
System.out.print("Enter your lottery pick(two digits): ");
String guess = input.nextLine();
//Get digits form lottery
char lotteryDigit1 = lottery.charAt(0);
char lotteryDigit2 = lottery.charAt(1);
//Get digits form guess
char guessDigit1 = guess.charAt(0);
char guessDigit2 = guess.charAt(1);
System.out.println("The lottery number is " + lottery);
//Check the guess
if(guess.equals(lottery))
System.out.println("Exact match: you win $10,000.");
else if(guessDigit1 ==lotteryDigit2 && guessDigit2 == lotteryDigit1)
System.out.println("Exact match: you win $3,000.");
else if(guessDigit1 == lotteryDigit1 || guessDigit1 == lotteryDigit2 || guessDigit2 == lotteryDigit1 || guessDigit2 == lotteryDigit2)
System.out.println("Exact match: you win $1,000.");
else
System.out.println("Sorry,no match.");
}
}
可以使用System.out.printf方法在控制台上显示格式化输出。
%4.2f:
调用这个方法的语法是:
System.out.printf(format,item1,item2,.....,itemk);
这里的format是指一个由子串和格式标识符构成的字符串。
格式标识符指定每个条目应该如何显示。这里的条目可以是数值、字符、布尔值或字符串。
简单的格式标识符是以百分号(%)开头的转换码。
标识符 | 输出 | 举例 |
---|---|---|
%b | 布尔值 | true或false |
%c | 字符 | 'a' |
%d | 十进制数 | 200 |
%f | 浮点数 | 45.46000000 |
%e | 标准科学计数法形式的数 | 4.556000e+01 |
%s | 字符串 | "Java is cool" |
在格式标识符中,默认情况下,浮点值显示小数点后6位数字。
可以在标识符中指定宽度和精度。
如果一个条目需要比指定宽度更多的空间,宽度自动增加。
默认情况下,输出是右对齐的。可以在格式标识符中放一个负号(-),表明该条目在特定区域中的输出是左对齐的。
**警告:**条目与格式标识符必须在类型上严格匹配。对于格式标识符%f或%e的条目必须是浮点型值。因此,int型变量不能匹配%f或%e。
**提示:**使用符号%来标记格式标识符,要在格式字符串中使用直接量%,需要使用%%。
1、Java提供了在Math类中的数学方法sin、cos、tan、asin、acos、atan、toRadius、toDegree、exp、log、log10、pow、sqrt、cell、floor、rint、round、min、max、abs以及random,用于执行数学函数。
2、字符类型char表示单个字符
3、转义序列包含反斜杠\以及后面的字符或者数字组合。
4、字符\称为转义字符
5、字符’ '、\t、\f、\r和\n都称为空白字符。
6、字符可以基于它们的Unicode码使用关系操作符进行比较。
7、Character类包含方法isDigit、isLetter、isLetterOrDigit、isLowerCase、isUpperCase,用于判断一个字符是否是数字、字母、小写字母还是大写字母。它也包含toLowerCase和toupperCase方法返回小写字母或大写字母。
8、字符串是一个字符序列。字符串的值包含在一对匹配的双引号(")中。字符串的值包含在一对匹配的单引号中。
9、字符串在Java中是对象。只能通过一个指定对象调用的方法称为实例方法。非实例方法称为静态方法,可以不使用对象来调用。
10、可以调用字符串的length()方法获取它的长度,使用charAt(index)方法从字符串中提取特定下标位置的字符,使用indexOf和lastIndexOf方法找出一个字符串中的某个字符或某个子串。
11、可以使用concat方法连接两个字符串,或者使用加号(+)连接两个或多个字符串。
12、可以使用substring发给发从字符串中提取子串。
13、可以使用equals和compareTo方法比较字符串。如果两个字符串相等,equals方法返回true;如果它们不等,则返回false。compareTo方法根据一个字符串大于、等于或小于另一个字符串,分别返回0、正整数或负整数。
14、printf方法使用格式标识符来显示一个格式化的输出。
循环简介:
循环可以用于让一个程序重复地执行语句。
Java提供了一种称为循环(loop)的功能强大的结构,用来控制一个操作或操作序列重复执行的次数。
循环是用来控制语句块重复执行的一种结构。
循环的概念是程序设计的基础。
Java提供了三种类型的循环语句:while循环、do-while循环和for循环。
while循环在条件为真的情况下,重复地执行语句。
语法格式:
while(循环继续条件){
//循环体
语句(组);
}
循环中包含的重复执行的语句部分称为循环体(loop body)。循环体的每一次执行都被认为是一次循环的迭代(或重复)。每个循环都含有循环继续条件,循环继续条件是一个布尔表达式,控制循环体的执行。
在循环体执行前总是先计算循环条件以决定是否执行它。若条件为真,则执行循环体;若条件为false,则终止整个循环,并且程序控制转移到while循环后的下一条语句。
使用一个控制变量count来对执行次数计数。这种类型的循环称为计数器控制的循环(counter-controlled loop)。
注意:循环继续条件应该总是放在圆括号内。只有当循环体只包含一条语句或不包含语句时,循环体的花括号才可以省略。
**注意:**要保证循环继续条件最终可以变为false,以便程序能够结束。一个常见的程序设计错误是无限循环(也就是说,循环会永远执行下去)。如果程序运行了不寻常的长时间而不结束,可能其中有无限循环。如果你是从命令行窗口运行程序的,按Ctrl+C键来结束。
**警告:**程序员常犯的错误就是使循环多执行一次或少执行一次。这种情况通常称为差一错误(off-by-one error)。
//提示用户为两个个位数相加的问题给出答案。使用循环,让用户重复输入新的答案,直到答案正确为止。
import java.util.Scanenr;
public class RepeatAdditionQuiz{
public static void main(String[] args){
int number1 = (int)(Math.random()*10);
int number2 = (int)(Math.random()*10);
//Create a Scanenr
Scanner input = new Scanner(System.in);
System.out.print("What is " + number1 + " + " + numnber2 + " ? ");
int answer = input.nextInt();
while(number1 + number2 != answer){
System.out.print("Wrong answer.Try again.What is " + number1 + " + " + number2 + " ? ");
answer = input.nextInt();
}
System.out.println("You got it!");
}
}
一次增加一个步骤地渐进编码(code incrementally)是一个很好地习惯。对涉及编写循环地程序而言,如果不知道如何立即编写循环,可以编写循环只执行一次的代码,然后规划如何在循环中重复执行这些代码。
import java.util.Scanner;
/**
* 程序清单5-2
*
* 要解决的问题是猜测计算机“脑子”里想的是什么数。编写一个程序,随机产生一个0到100之间的且包含0和100的整数。程序提示用户连续输入一个数字,直到它和计算机随机产生的数字相匹配为止。对用户每次输入的数字,程序都要告诉用户该输入值是偏大了,还是偏小了,这样用户可以明智地进行下一轮的猜测。
*
* 这个魔法数在0到100之间。为了减小猜测的次数,首先输入50,如果猜测值过高,那么这个魔法数就在0到49之间。如果猜测值过低,那么这个魔法数就在51到100之间。因此,经过一次猜测之后,下一次猜测时可以少考虑一半的数字。
*
* 该如何编写编写这个程序呢?要立即开始编写吗?不!编码前的思考是非常重要的。思考一下,在没有编写程序时你会如何解决这个问题。首先需要产生一个0到100之间且包含0和100的随机数,然后提示用户输入一个猜测数,最后将这个猜测数和随机数进行比较。
*
* 一次增加一个步骤地渐进编码(code incrementally)是一个很好地习惯。对涉及编写循环地程序而言,如果不知道如何立即编写循环,可以编写循环只执行一次的代码,然后规划如何在循环中重复执行这些代码。
*/
public class GuessNumber {
public static void main(String[] args) {
//Generate a random number to be guessed
int number = (int) (Math.random() * 101); //小于1.0 * 101 那么得到的值<101,对它进行取整,最大会得到100
Scanner input = new Scanner(System.in);
System.out.println("Guess a magic number between 0 and 100");
int guess = -1; //注意:guess被初始化为-1,i昂他初始化为0到100之间的值会出错,因为它很可能就是要猜的数字。
while (guess != number) {
//Prompt the user to guess the number
System.out.print("\nEnter your guess:");
guess = input.nextInt();
if (guess == number)
System.out.println("Yes,the number is " + number);
else if (guess > number)
System.out.println("Your guess is too hight.");
else
System.out.println("Your guess is too low");
}
}
}
编写一个正确的循环对编程新手来说,并不是件容易的事。编写循环时应该考虑如下三个步骤:
import java.util.Scanner;
/**
*
* 程序清单5-4
*
* 程序清单3-3中的数学减法学习工具程序,每次运行只能产生一道题目。可以使用一个循环重复产生题目。那么如何编写能产生5道题目的代码呢?遵循循环设计策略。首先,确定需要重复的语句。这些语句包括:获取两个随机数,提示用户对两数做减法然后给试题打分。然后,将这些语句放在一个循环里。最后,增加一个循环控制变量和循环继续条件,然后执行循环五次。
*
* 给出程序可以产生5道问题,在学生回答完所有5个问题后,报告回答正确的题数。这个程序还显示该测试所花的时间,并列出所有的题目。
*/
public class SubtractionQuizLoop {
public static void main(String[] args) {
final int NUMBER_OF_QUESTIONS = 5; //Number of questions
int correctCount = 0; //Count the number of corredect answers
int count = 0; //Count the number of questions
long startTime = System.currentTimeMillis();
String output = " "; //output string is initially empty
Scanner input = new Scanner(System.in);
while (count < NUMBER_OF_QUESTIONS) {
// 1.Generate two random single-digit integers
int number1 = (int) (Math.random() * 10);
int number2 = (int) (Math.random() * 10);
//2.If number < number2,swap number1 with number2
if (number1 < number2) {
int temp = number1;
number1 = number2;
number2 = temp;
}
//3.Prompt the student to answer "What is number1 - number2?"
System.out.print("What is " + number1 + " - " + number2 + " ? ");
int answer = input.nextInt();
//4.Grade the answer and display the result
if (number1 - number2 == answer) {
System.out.println("You are correct!");
correctCount++; //Increase the correct answer count
} else
System.out.println("Your answer is wrong.\n" + number1 + " - " + number2 + " should be " + (number1 - number2));
//Increase the question count
count++;
output += "\n" + number1 + "-" + number2 + "=" + answer + ((number1 - number2 == answer) ? " correnct" : " wrong");
}
long endTime = System.currentTimeMillis();
long testTime = endTime - startTime;
System.out.println("\nCorrect count is " + correctCount + "\nTest time is " + testTime / 1000 + " seconds\n" + output);
}
}
另一种控制循环的常用技术是读取和处理一个集合的值时指派一个特殊值。这个特殊的输入值也称为标记值(sentinel value),用以表明循环的结束。如果一个循环使用标记值来控制它的执行,它就被称为标记位控制的循环(sentinel-controlled loop)。
警告:在循环控制中,不要使用浮点值来比较值是否相等。因为浮点值都是某些值的近似值,使用它们可能导致不精确的循环次数和不准确的结果。
double item = 1;double sum = 0;
while(item != 0){ // No guarantee item will be 0
sum += item;
item -= 0.1;
}
/*
变量item从1开始,每执行一次循环体就减去0.1.当item变为0时循环应该终止。但是,因为浮点数在算数上是近似的,所以不能确保item会变成真正的0.从表面上看,这个循环似乎没问题,但实际上它是以无限循环。
*/
输入重定向命令(input redirection):
输出重定向命令(output redirection):
同时使用输入/输出重定向:
do-while循环和while循环基本一样,不同的是它先执行循环体一次,然后判断循环继续条件。
do-while循环是while循环的变体。
语法格式:
do{
//循环体;
语句(组);
}while(循环继续条件);
首先执行循环体,然后计算循环继续条件。如果计算结果为true,则重复执行循环体;如果为false,则终止do-while循环。
while循环与do-while循环的差别在于:计算循环继续条件和执行循环体的先后顺序不同。
//TestDoWhile.java
import java.util.Scanner;
public class TestDoWhile{
//Main method
public static void main(String[] args){
int data;
int sum = 0;
//Create a Scanner
Scanner input = new Scanner(System.in);
//Keep reading data util the input is 0
do{
//Read the next data
System.out.print("Enter an integer(the input ends if it is 0): ");
data = input.nextInt();
sum += data;
}while(data != 0);
System.out.println("The sum is " + sum);
}
}
**提示:**如果循环中的语句至少需要执行一次,建议使用do-while循环。如果使用while循环,那么这些语句必须在循环前和循环内都出现。
for循环具有编写循环的简明语法。
for循环的语法格式:
for(初始操作;循环继续条件;每次迭代后的操作){
//循环体
语句(组);
}
for循环语句从关键字for开始,然后是用括号括住的循环控制结构体。这个结构体包括初始动作、循环继续条件和每次迭代后代的动作。控制结构体后紧跟着花括号括起来的循环体。初始动作、循环继续条件和每次迭代后的动作都要用分号分隔。
一般情况下,for循环使用一个变量来控制循环体的执行次数,以及什么时候循环终止。这个变量称为控制变量(control variable)。初始化动作是指初始化控制变量,每次迭代后的动作通常会对控制变量做自增或自减,而循环继续条件检验控制便来给你是否达到终止值。
循环继续条件是一个布尔表达式。这个表达式在初始化之后和每次迭代开始之前都要计算一次。如果条件为true,则执行循环体。如果它为false,则循环终止,并且将程序控制转移到循环后的下一行。
最终,控制变量的值应该使循环继续条件变为false,否则循环将成为无限循环。
循环体内只有一条语句,则可以省略花括号。这个可以省略花括号的底层实现是怎样的?
**提示:**控制变量必须在循环控制结构体内或循环前说明。如果循环控制变量只在循环内使用而不再其他地方使用,那么在for循环的初始动作中声明它是一个很好的编程习惯。如果在循环控制结构体内声明变量,那么在循环外不能引用它。
**注意:**for循环中的初始动作可以是0个或是多个以逗号隔开的变量声明语句或赋值表达式。
for循环中每次迭代后的动作可以是0个或多个以逗号隔开的语句。
for(int i = 0;i<100;System.out.println(i),i++)
通常,将声明和初始化/一个变量/作为初始动作,将增加或减少控制变量作为每次迭代后的操作。
**注意:**如果省略for循环中的循环继续条件,则隐含地认为循环继续条件为true。
while循环和for循环都称为前测循环(pretest loop),因为继续条件实在循环体执行之前检测的,do-while循环称为后测循环(posttest loop),因为循环条件是在循环体执行之后检测的。
三种形式的循环语句:while、do-while和for,在表达上是等价的。也就是说,可以使用这三种形式之一来编写一个循环。除了某些特殊情况外。
for(initial-action;loop-continuation-condition;action-after-each-iteration){
//Loop body;
}
//==================等价于
initial-action;
while(loop-continuation-condition){
//Loop body;
action-after-each-iteration;
}
通常,如果已经提前知道重复次数,那就采用for循环。如果无法确定重复次数,就采用while循环,就像读入一些数值直到读入0为止的这种情况。如果在检验继续条件前需要执行循环体,就用do-while循环替换while循环。
在for子句的末尾和循环体之间多写分号是一个常见的错误。循环体实际上都是为空的。
在do-while循环中,需要分号来结束这个循环。
一个循环可以嵌套在另外一个循环中。
嵌套循环是由一个外层循环和一个或多个内层循环组成的。每当重复执行一次外层循环时将再次进入内部循环,然后重新开始。
需要注意的是,嵌套循环将运行较长时间。
**要点提示:**在循环继续条件中使用浮点数将导致数值错误。
涉及浮点数的数值误差是不可避免的,因为浮点数在计算机中本身就是近似表示的浮点数本身是如何存储在计算机中的???。
for循环初始动作可以是任何语句,但是,它经常用来初始化控制变量。事实上,它可以为任意数据类型。
因为计算机使用固定位数表示浮点数,因此,它就不能精确表示某些浮点数。
使用浮点数做计数器会导致最后一次循环不会运行。根本问题就是浮点数使用近似值表示的。为了解决这个问题,使用整数计数器以确保所有循环次数都会执行。
double currentValue = 1.0;
for(int count = 0;count<100;count++){
sum += currentValue;
currentValue -= 0.01;
}
从大到小添加数字没有从小到大添加数字得到的值精确。这种现象是有限精度算数的产物。如果结果值要求的精度比便来给你可以存储的更高,那么添加一个非常小的数到一个非常大的数上可能没有什么影响。为了得到更精确的结果,仔细选择计算的顺序。在较大数之前先增加较小数是减小误差的一种方法。
在编写代码之前进行思考是非常重要的。思考让你可以在考虑如何编写代码前,生成解决问题的逻辑方案。
将逻辑方案翻译成Java代码的方式不是唯一的。
一个问题常常有多种解决方案。
最大公约数(GCD)问题就有许多解决方法。一个更有效的是使用经典的欧几里得算法。
import java.util.Scanner;
/**
*
* 程序清单5-9
*
* 提示用户输入两个正整数,然后找到它们的最大公约数
*/
public class GreatestCommonDivisor {
public static void main(String[] args) {
//Create a Scanner object
Scanner input = new Scanner(System.in);
//Prompt the user to enter two integer
System.out.print("Enter first integer: ");
int n1 = input.nextInt();
System.out.print("Enter second integer: ");
int n2 = input.nextInt();
int gcd = 1; //Initial gcd is 1 //Initial 初始
int k = 2; //Possible gcd
while(k <= n1 && k <= n2){
if(n1 % k ==0 && n2 % k ==0)
gcd = k; //Update gcd
k++;
}
System.out.println("The greatest common divisor for " + n1 + " and " + n2 + " is " + gcd);
/*
考虑到n1的除数不可能大于n1/2,所以,可以改写:
for(int k = 2;k <= n1/2&&k<=n2/2;k++){
if(n1%k ==0 && n2%k ==0)
gcd = k;
}
*/
}
}
public class FutureTuition {
public static void main(String[] args) {
double tuition = 10000; //Year 0
int year = 0;
while(tuition < 20000){
tuition = tuition * 1.07;
year++;
}
System.out.println("Tuition will be doubled in " + year + " years");
System.out.printf("Tuition will be $%.2f in %1d years",tuition,year);
}
}
将十进制数转换为十六进制数,就是找到满足以下条件的十六进制数hn,hn-1,hn-2,…h2,h1,h0:这些数可以通过不断地用d除以16直到商为零而得到。依次得到的余数是hn,hn-1,hn-2,…h2,h1,h0。十六进制数字包含十进制数字0、1、2、3、4、5、6、7、8、9以及表示十进制数字的10的A,表示十进制数字11的B,表示12的C,13的D、14的E和表示15的F。
d = hnx16n + hn-1x16n-1 + hn-2x16n-2,…h2x162 + h1x161 + h0x160
import java.util.Scanner;
/**
* 程序清单5-11
*
* 提示用户输入一个十进制数,然后将它转换为一个字符串形式的十六进制数
*
*/
public class Dec2Hex {
//Main method
public static void main(String[] args) {
//Create a Scanenr object
Scanner input = new Scanner(System.in);
//Prompt the user to enter a decimal integer
System.out.print("Enter a decimal number: ");
int decimal = input.nextInt();
//Covert decimal to hex
String hex = "";
while(decimal != 0){
int hexValue = decimal % 16;
//Convert a decimal value to a hex digit
char hexDigit = (hexValue <= 9 && hexValue >=0) ? (char)(hexValue + '0') : (char)(hexValue - 10 + 'A');
hex = hexDigit + hex;
decimal = decimal / 16;
}
System.out.println("The hex number is " + hex);
/*
程序提示用户输入一个十进制数字,将其转换为一个十六进制形式的字符串,然后显示结果。为了将十进制转换为十六进制数,程序运行循环不断地将十进制数除以16,得到其余数。余数转换为一个十六进制形式地字符串。接下来,这个表示十六进制数的字符串初始时为空。将这个十进制数除以16,就从该数中去掉一个十六进制数字。循环重复执行这些操作,直到商是0为止。
程序将0到15之间的十六进制数转换为一个十六进制字符。如果hexValue在0到9之间,那它就被转换为(char)(hexValue+'0')。回顾一下,当一个字符和一个整数相加时,计算时使用的是字符的Unicode码。利用:如果hexValue为5,那么(char)(hexValue+'0')返回5.类似地,如果hexValue在10到15之间,那么它就被转换为(char)(hexValue-10+'A')。例如,如果hexValue是11,那么(char)(hexValue-10+'A')返回B。
}
}
关键字break和continue在循环中提供了额外的控制。
关键字break和continue都可以在循环语句中使用。为循环提供额外的控制。在某些情况下,使用break和continue可以简化程序设计。但是过度使用或者不正确地使用它们会使得程序难以调试。
可以在一个循环中使用break立即终止该循环。
也可以在循环中使用关键字continue。当程序遇到continue时,它会结束当前地迭代。程序控制转向该循环体的末尾。换句话说,continue只是跳出了一次迭代,而关键字break是跳出了整个循环。
注意:continue语句总是在一个循环内。在while和do-while循环中,continue语句之后会马上计算循环继续条件;而在for循环中,continue语句之后会立即先执行每次迭代后的动作,再计算循环继续条件。
总是可以编写在循环中不使用break和continue的程序。通常,只有在能够简化代码并使程序更容易阅读的情况下,才适合使用break和continue。
**注意:**很多程序设计语言都有goto语句。goto语句可以随意地将控制转移到程序中地任意一条语句上,然后执行它。这使程序很容易出错。Java中地break语句和continue语句是不同于goto语句地。它们只能运行在循环中或者switch语句中。break语句跳出整个循环,而continue语句跳出循环的当前迭代。
如果一个字符串从前往后,以及从后往前是一样的,那么它就是一个回文。
import java.util.Scanner;
/**
* 程序清单5-14
*
* 要解决的问题是:编写一个程序,提示用户输入一个字符串,然后给出该字符串是否是回文。一个解决方案是,判断字符串的第一个字符是否和最后一个字符一样。如果是,判断第二个字符是否和倒数第二个字符一样。这个过程一直持续到找到不匹配的,或者字符串中所有的字符都进行了判断。如果字符串具有奇数个数,那么中间的字符就不需要判断了。
*
*
*/
public class Palindrome {
//Main method
public static void main(String[] args) {
//Create a Scanner object
Scanner input = new Scanner(System.in);
//Prompt the user to enter a String
System.out.println("Enter a string: ");
String s = input.nextLine();
//The index of the first character in the string
int low = 0;
//The index of the last character in the string
int high = s.length()-1;
boolean isPalindrome = true;
while(low<high){
if(s.charAt(low) != s.charAt(high)){
isPalindrome = false;
break;
}
low++;
high--;
}
if(isPalindrome)
System.out.println(s + " is a palindrome.");
else
System.out.println(s + " is not a palindromem.");
}
}
开发编程解决方案来解决这个问题或其他很多问题的关键之处在于要把问题分解成子问题,然后逐个地开发出每个子问题的解决方案。不要一开始就试图开发出一个完整的解决方案。
/**
* 本节给出了一个程序,用于分5行显示前50个素数,每行包含10个数字。
*/
public class PrimeNumber {
public static void main(String[] args) {
final int NUMBER_OF_PRIMES = 50; //Number of primes to dispaly
final int NUMBER_OF_PRIMES_PER_LINE = 10; //Display 10 per line
int count = 0; // Count the number of prime numbers
int number = 2; // A number to be teseted for primeness
System.out.println("The first 50 prime numbers are \n");
//Repeatedly find prime numbers
while(count<NUMBER_OF_PRIMES){
//Assume the number is prime
boolean isPrime = true;//Is the current number prime?
//Test whether nummber is prime
for(int divisor = 2;divisor<= number/2 ;divisor++){
if(number % divisor ==0){ // If true,number if not prime
isPrime = false; // Set is Prime to false
break;
}
}
//Display the prime number and increase the count
if(isPrime){
count++; //Increase the count
if(count%NUMBER_OF_PRIMES_PER_LINE == 0){
//Display the number and advance to the new line
System.out.println(number);
}else
System.out.print(number + " ");
}
//Check if the number is prime
number ++;
}
}
}
1、循环语句有三类:while循环、do-while循环和for循环。
2、循环中包含重复执行的语句的部分称为循环体。
3、循环体执行一次称为村换的一次迭代。
4、无限循环是指循环语句被无限次执行。
5、在设计循环时,既需要考虑循环控制结构,还需要考虑循环体。
6、while循环首先检查循环继续条件。如果条件为true,则执行循环体;如果条件为false,则循环结束。
7、do-while循环与while循环类似,只是do-while循环先执行循环体,然后再检查循环继续条件,以确定是继续还是终止。
8、while和do-while循环常用于循环次数不确定的情况。
9、标记值是一个特殊的值,用来标记循环的结束。
10、for循环一般用在循环体执行次数固定的情况。
11、for循环控制由三部分组成。第一部分是初始操作,通常用于初始化控制变量。第二部分是循环继续条件,决定是否执行循环体。第三部分是每次迭代后执行的操作,经常用于调整控制变量。通常,再控制结构中初始化和修改循环控制变量。
12、while循环和for循环都称为前测循环(pretest loop),因为在循环体执行之前,要检测一下循环继续条件。
13、do-while循环称为后测循环(posttest loop),因为在循环体执行之后,要检测一下这个条件。
14、在循环中可以使用break和continue这两个关键字。
15、关键字break立即终止包含break的最内层循环。
16、关键字continue只是终止当前迭代。
要点提示:方法可以用于定义可重用的代码以及组织和简化编码。
可以通过定义和调用方法实现一次性地编写好通用的代码而无须重新编写。
方法是为完成一个操作而组合在一起的语句组。
方法:具有独立功能的代码块,不调用就不执行。
预定义的方法都在Java库中定义。
注意事项:
示例代码:
/**
* 需求:编写计算从i1到i2的和的方法
*
*/
// 和计算方法
public static int sum(int i1, int i2){
int result = 0;
for(int i = i1; i <= i2; i++){
result += i;
}
//返回结果值
return result;
}
// 调用方法
public static void main(String[] args){
System.out.println("Sum from 1 to 10 is: " + sum(1, 10));
System.out.println("Sum from 20 to 37 is: " + sum(20, 37));
System.out.println("Sum from 35 to 49 is: " + sum(35, 49));
}
要点提示:方法的定义由方法名称、参数、返回值类型以及方法体组成。
语法格式:
修饰符 返回值类型 方法名(参数列表){
//方法体;
}
方法头(method header)是指方法的修饰符(modifier)、返回值类型(return value type)、方法名(method name)和方法的参数(parameter)。
示例:求两个整数中的最大值
//需求 找出两个整数中那个数比较大
//方法头(method header)= 方法的修饰符(modifier)+ 返回值类型(return value type) + 方法名(method name) + 方法的参数(parameter)
//修饰符:public static
//返回值类型:int
//方法名:max
//方法签名:max(int num1, int num2)
//参数列表:int num1,int num2
//形式参数:num1 num2
public static int max(int num1, int num2){
//方法体
int result;
//算法一:result = num1 > num2 ? num1 :num2;
//算法二:
if(num1 > num2){
result = num1;
}else{
result = num2;
}
//返回值
return result;
}
public static void main(String args[]){
//实际参数:x,y
int z = max(x,y);
}
带返回值的方法(value-returning method):
void方法(void method):
定义在方法头中的变量称为形式参数(formal parmeter)或者简称为形参(parameter)。
参数就像占位符,当调用方法时,就给参数传递一个值,这个值称为实际参数(actual parameter)或实参(argument)。
参数列表(parameter list)指明方法中参数的类型、顺序和个数。
方法名和参数列表一起构成方法签名(method signature)。
参数是可选的,也就是说,方法可以不包含参数。
方法体中包含一个执行方法的语句集合。
为使带返回值的方法能返回一个结果,必须要使用带关键字return的返回语句。
执行return语句时方法终止。
注意:在其他某些语言中,方法称为过程(procedure)或函数(function)。带返回值的方法称为函数,返回值类型为void的方法称为过程。
**警告:**在方法头中,需要对每一个参数进行独立的数据类型声明。
注意:定义方法和声明变量的区别:定义是指被定义的条目是什么,而声明通常是指被声明的条目分配内存来存储数据。
**要点提示:**方法的调用时执行方法中的代码。
在方法定义中,定义方法要做什么。
为了使用方法,必须调用(call或invoke)它。
根据方法是否有返回值,调用方法有两种途径:
int larger = max(3,4);
System.out.println("Welcome to java!")
**注意:**在Java中,带返回值的方法也可以当作语句调用。这种情况下,函数调用者只需忽略返回值即可。虽然这种情况很少见,但是,如果调用者对返回值不感兴趣,这样也是可以的。
当程序调用一个方法时,程序控制就转移到被调用的方法。当执行完return语句或执行到表示方法结束的右括号时,被调用的方法将程序控制返还给调用者。
方法的调用流程——内存图讲解:
//测试max方法的完整程序
public class TestMax{
// Main method
public static void main(String args[]){
int i = 5;
int j = 2;
int k = max(i, j);
System.out.println("The maximum of " + i + " and " + j + " is " + k);
}
//Return the max of two numbers
public static int max(int num1, int num2){
int result;
if(num1 > num2)
result = num1;
else
result = num2;
}
}
main方法:
main方法与其他方法很类似,区别在于它是由Java虚拟机调用的。
main方法的方法头永远都是一样的。
main方法方法头解读:修饰符public和static,返回值类型void,方法名main,String[]类型的参数。String[]表明参数是一个String类型的数组。
main中的语句可以调用main方法所在类中定义的其他方法,也可以调用别的类中定义的方法。
**警告:**对带返回值的方法而言,return语句是必需的。
如果方法的返回值类型不是void,必须使用return语句返回结果,编译器没有那么智能,编译器认为只要return语句被条件所控制,就有可能执行不到。
//编译错误,Java编译器认为该方法有可能不会返回任何值。???????????’
//如果方法的返回值类型不是void,必须使用return语句返回结果,编译器没有那么智能,编译器认为只要return语句被条件所控制,就有可能执行不到。
public static int sign(int n){
if(n > 0)
return 1;
else if(n == 0)
return 0;
else if(n < 0);
return -1;
}
//正确代码
public static int sign(int n){
if(n > 0)
return 1;
else if(n == 0)
return 0;
else //删除此处的判断,使得总是有语句可以被执行。
return -1;
}
**注意:**方法能够带来代码的共享和重用。
如果创建了一个新类,可以通过使用“类名.方法名”来调用它。
每当调用一个方法时,系统会创建一个活动记录(也称为活动框架),用于保存方法中的参数和变量。活动记录置于内存区域中,称为调用堆栈(call statck)。调用堆栈也称为执行堆栈、运行时堆栈,或者一个机器堆栈,常简称为“堆栈”。当一个方法调用另一个方法时,调用者的活动记录保持不动,一个新的活动记录被创建用于被调用的新方法。一个方法结束返回到调用者时,其相应的活动记录也被释放。
方法的值通过方法调用进行传递。
**要点提示:**void方法不返回值。
对void方法的调用必须是一条语句。
//案例:打印分数的void方法
//对void方法的调用必须是一条语句。
public class TestVoidMehod{
public static void main(String[] args){
System.out.println("The grade is ");
printGrade(78.5);
System.out.println("The grade is ");
printGrade(59.5);
}
public static void printGrade(double score){
if(score >= 90.0)
System.out.println('A');
else if(score >= 80.0)
System.out.println('B');
else if(score >= 70.0)
System.out.println('C');
else if(score >= 60.0)
System.out.println('D');
else
System.out.println('F');
}
}
//带返回值的getGrade方法
public class TestReturnGradeMethod{
public static void main(String args[]){
System.out.print("The grade is " + getGrade(78.5));
System.out.println("\nThe grade is " + getGrade(59.5));
}
public static char getGrade(double score){
if(score >= 90.0){
retun 'A';
}
else if(score >= 80.0){
return 'B';
}
else if(score >= 70.0){
return 'C';
}
else if(score >= 60.0){
return 'D';
}
else{
return 'F';
}
}
}
注意:void方法不需要return语句,但它能用于终止方法并返回到方法的调用者。它的语法是:return;。
return和break和continue的区别:
**要点提示:**调用方法的时候是通过传值的方式将实参传给形参的。
形参和实参:
方法的强大之处在于它处理参数的能力。
调用方法时,需要提供实参,它们必须与方法签名中所对应的形参次序相同。这称作参数顺序匹配(parameter order association)。
警告:实参必须与方法签名中定义的参数在次序和数量上匹配,在类型上兼容。类型兼容是指不需要经过显式的类型转换,实参的值就可以传递给形参。
当调用带参数的方法时,实参的值传递给形参,这个过程称为按值传递(pass-by-value)。
如果实参是变量而不是常量,则将该变量的值传递给形参。无论形参在方法中是否改变,该变量都不受影响。这儿的意思是说,实参传给形参的是一个值,而非地址,所以不会改变实参原来的值。
带参数的方法,本质上是在方法运行时,在栈内存该方法中创建了一个存放该参数的变量。
//实现交换两个变量的swap方法
public class TestPassByValue{
//Main Method
public static void main(String[] args){
//Declare and initialize variables
int num1 = 1;
int num2 = 2;
System.out.println("Before invoking the swap method,num1 is " + num1 + " and num2 is " + num2);
//Invoke the swap method to attempt to swap two variables
swap(num1, num2);
System.out.println("After invoking the swap method,num1 is " + num1 + " and num2 is " + num2);
}
//Swap two variables
public static void swap(int n1, int n2){
System.out.println("\tInside the swap method");
System.out.println("\t\tBefore swapping, n1 is " + n1 + " and n2 is " + n2);
//Swap n1 with n2
int temp = n1;
n1 = n2;
n2 = temp;
System.out.println("\t\tAfter swapping,n1 is " + n1 + " and n2 is " + n2);
}
}
形参和实参同名是没有任何分别的。形参是方法中具有自己存储空间的变量。局部变量是在调用方法时分配的,当方法返回到调用者后它就消失了。
**要点提示:**模块化使得代码易于维护和调试,并且使得代码可以被重用。
使用方法可以减少冗余的代码,提高代码的复用性。方法也可以用来模块化代码,以提高程序的质量。
//提示用户输入两个整数,然后显示它们的最大公约数。
import java.util.Scanner;
public class GreatestCommonDivisorMethod{
//Main method
public static void main(String[] args){
//Create a Scanner
Scanner input = new Scanner(System.in);
//Prompt the user to enter two integers
System.out.print("Enter first integer:");
int n1 = input.nextInt();
System.out.print("Enter second integer:");
int n2 = input.nextInt();
System.out.println("The greatest common divisor for " + n1 + " and " + n2 + " is " +gcd(n1,n2));
}
//Return the gcd of two integers
public static int gcd(int n1, int n2){
int gcd = 1; //Initial gcd is 1
int k = 2; //Possible gcd
while(k <= n1 && k <= n2){
if(n1 % k == 0 && n2 % k == 0){
gcd = k; // Update gcd
}
k++;
}
return gcd; //Return gcd
}
}
/*
*这个程序的3个优点:
*1、它将计算最大公约数的问题和main方法中的其他代码分隔开,这样做会使逻辑更清晰而且程序的可读性更强
*2、计算最大公约数的错误就限定在gcd方法中,这样就缩小了调试的范围。
*3、现在, 其他程序就可以重复调用gcd方法。
*/
//使用你模块化的概念来对打印前50的素数,每行包含10个数字进行改进
public class PrimeNumberMethod {
public static void main(String[] args) {
System.out.println("The first 50 prime numbers ars \n");
printPrimeNumbers(50);
}
public static void printPrimeNumbers(int numberOfPrimes) {
final int NUMBER_OF_PRIMES_PER_LINE = 10; //Display 10 per line
int count = 0; //Count the number of prime numbers
int number = 2; //A number to be tested for primeness
//Repeatedly find prime numbers
while (count < numberOfPrimes) {
//Print the prime number and increase the count
if (isPrime(number)) {
count++; //Increase the count
if (count % NUMBER_OF_PRIMES_PER_LINE == 0) {
//Print the number and advance to the new line
System.out.printf("%-5s\n", number);
} else
System.out.printf("%-5s", number);
}
//Check whether the next number is prime
number++;
}
}
//Check whether number is prime
public static boolean isPrime(int number) {
for (int divisor = 2; divisor <= number / 2; divisor++) {
if (number % divisor == 0) //If true,number is not prime
return false; //number is not a prime
}
return true; //Number is prime
}
}
霍纳算法:
import java.util.Scanner;
/**
* 程序清单6-8
*
* 使用霍纳算法,将十六进制字符串转换为十进制数的高效算法
*
*/
public class Hex2Dec {
//Main method
public static void main(String[] args) {
//Create a Scanner object
Scanner input = new Scanner(System.in);
//Prompt the user to enter a String
System.out.print("Enter a string: ");
String hex = input.nextLine();
System.out.println("The dicimal value for hex number " + hex + " is " + hexToDecimal(hex.toUpperCase()));
}
public static int hexToDecimal(String hex){
int decimalValue = 0;
for(int i = 0;i < hex.length();i++){
char hexChar = hex.charAt(i);
decimalValue = decimalValue * 16 + hexCharToDecimal(hexChar);
}
return decimalValue;
}
public static int hexCharToDecimal(char ch){
if(ch >= 'A' && ch <= 'F')
return 10 + ch - 'A';
else //ch is '0','1',...,or '9'
return ch - '0';
}
}
**要点提示:**重载方法使得你可以使用同样的名字来定义不同方法,只要它们的签名是不同的。(方法签名包含方法名,所以此处并不正确,应该是参数列表不同。)
方法重载:在一个类中有两个方法,它们具有相同的名字,但有不同的参数列表。Java编译器根据方法签名决定使用哪个方法。
调用方法时,Java编译器寻找最精确匹配的方法。
**提示:**重载方法可以使得程序更加清楚,以及更加具有可读性。执行同样功能但是具有不同参数类型的方法应该使用同样的名字。
注意:被重载的方法必须具有不同的参数列表。不能基于不同修饰符或返回值类型重载方法。
**注意:**有时调用一个方法时,会有两个或更多可能的匹配,但是,编译器无法判断那个是最精确的匹配,这称为歧义调用(ambiguous invocation)。歧义调用会产生一个编译错误。
/* 歧义调用 */
public class AmbiguousOverloading{
public static void main(String[] args){
System.out.println(max(1,2));
}
public static double max(int num1,double num2){
if(num1>num2)
return num1;
else
return num2;
}
public static double max(double num1,double num2){
if(num1>num2)
return num1;
else
return num2;
}
}
**要点提示:**变量的作用域(scope of a variable)是指变量可以在程序中引用的范围。
在方法中定义的变量称为局部变量(local variable)。
局部变量的作用域从声明变量的地方开始,直到包含该变量的块结束为止。
局部变量都必须在使用之前进行声明和赋值。
参数实际上就是一个局部变量。一个方法的参数的作用域涵盖整个方法。
可以在一个方法中的不同块里声明同名的局部变量,但是,不能在嵌套块中或同一块中两次声明同一个局部变量。
**警告:**不要再块内声明一个变量然后企图在块外使用它。
**要点提示:**字符使用整数来编码。产生一个随机字符就是产生一个随机整数。
每个字符都有一个唯一的在十六进制数0到FFFF(即十进制的65535)之间的Unicode。
注意:因为0<=Math.random()<1.0,必须给65535加上1.
小写字母的Unicode是一串连续的整数,从小写字母’a’的Unicode开始,然后是’b’、‘c’、…和’z’的Unicode。
(int)'a’到(int)'z’之间的随机整数是:
(int)((int)‘a’+Math.random()*((int)‘z’-(int)‘a’ +1)。
所有的数字操作符都可以应用到char操作数上。如果另一个操作数是数字或字符,那么char型操作数就会被转换为数字。
‘a’ + Math.random()*(‘z’-‘a’+1)
这样,随机的小写字母是:
(char)(‘a’+Math.random()*(‘z’-‘a’+1))
由此,可以生成任意两个字符ch1和ch2之间的随机字符,其中ch1 (char)(ch1+Math.random()*(ch2-ch1+1)) 要点提示:开发软件的关键在于应用抽象的概念。 方法抽象(method abstraction)是通过将方法的使用和它的实现分离来实现的。用户在不知道方法是如何实现的情况下,就可以使用方法。方法的实现细节封装在方法内,对使用该方法的用户来说是隐藏的。这就称为信息隐藏(information hiding)或封装(encapsulation)。如果决定改变方法的实现,但只要不改变方法签名,用户的程序就不受影响。 当编写一个大程序时,可以使用“分治”(divid-and-conquer)策略,也称为求精(stepwise refinement),将大问题分解成子问题。子问题又分解成更小、更容易处理的问题。 假设要编写一个程序,显示给定年月的日历。程序提示用户输入年份和月份,然后显示该月的整个日历。 对日历问题来说,先把该问题拆分成两个子问题:读取用户输入和该月的日历。打印给定月份的日历还可以分解成两个子问题:打印日历的标题和日历的主体。打印日历主体时还可以分解成:获取该月的第一天是星期几和获取该月有多少天。 通常,一个子问题对应于现实中的一个方法,即使某些子问题太简单,以至于都不需要方法来实现。需要决定哪些模块要用方法实现,而哪些模块要与其他方法结合完成。这种决策应该基于所作的选择是否使整个程序更易阅读而做出的。 “自顶向下”方法是自上而下,每次实现结构图中的一个方法。等待实现的方法可以用待完善方法代替。 待完善方法(stub)是方法的一个简单但不完整的版本。使用待完善方法可以快速地构建程序的框架。 在每个待完善方法完成后,都需要编译和测试这个程序,然后修改所有的错误。 自底向上方法是从下向上每次实现结构图中的一个方法,对每个实现的方法都写一个测试程序进行测试。 自顶向下和自底向上都是不错的方法:它们都是逐步地实现方法,这有助于分离程序设计错误,使调试变得容易。 有时,这两种方法可以一起使用。 实现打印日历的细节。 逐步求精将一个大问题分解为小的易于处理的子问题。每个子问题可以使用一个方法来实现。这种方法使得问题更加易于编写、重用、调试、修改和维护。 1、程序模块化和可重用性是软件工程的中心目标之一。Java提供了很多有助于完成这一目标的有效结构。方法就是一个这样的结构。 2、方法头指定方法的修饰符、返回值类型、方法名和参数。 3、方法可以返回一个值。返回值类型returnValueType是方法要返回的值的数据类型。如果方法不返回值,则返回值类型就是关键字void。 4、参数列表是指方法中参数的类型、次序和数量。方法名和参数列表一起构成方法签名(method signature)。参数是可选的,也就是说,一个方法可以不包含参数。 5、return语句也可以用在void方法中,用来终止方法并返回到方法的调用者。在方法中,有适用于改变正常流程控制是很有用的。 6、传递给方法的实际参数应该与方法签名中的形式参数具有相同的数目、类型和顺序。 7、当程序调用一个方法时,程序控制就转移到被调用的方法。被调用的方法执行到该方法的return语句或到达方法结束的右括号时,将程序控制还给调用者。 8、在Java中,带返回值的方法也可以当作语句调用。在这种情况下,调用函数只要忽略返回值即可。 9、方法可以重载。这就意味着两个方法可以拥有相同的方法名,只要它们的方法参数列表不同即可。 10、在方法中声明的变量称作局部变量。局部变量的作用域是从声明它的地方开始,到包含这个变量的块结束为止。局部变量在使用前必须声明和初始化。 11、方法抽象是把方法的应用和实现分离。用户可以在不知道方法是如何实现的情况下使用方法。方法的实现细节封装在方法内,对调用该方法的用户隐藏。这称为信息隐藏或封装。 12、方法抽象将程序模块化为整齐、层次分明的形式。将程序写成简洁的方法构成的集合会比其他方式更容易编写、调试、维护和修改。这种编写风格也会提高方法的可重用性。 13、当实现一个大型程序时,可以使用自顶向下或自底向上的编码方法。不要一次性编写完整个程序。这种方法似乎浪费了更多的编码时间(因为要反复编译和运行这个程序),但实际上,它会更节省时间并使调试更容易。 单个的数组变量可以引用一个大的数据集合。 在执行程序的过程中,经常需要存储大量的数据。Java和许多语言都提供了一种称作数组(array)的数据结构,可以用它来存储一个元素个数固定且元素类型相同的有序集。 一旦数组被创建,它的大小是固定的。使用一个数组引用变量,通过下标来访问数组中的元素。 数组是用来存储数据的集合,但是,通常我们会把数组看作一个存储具有相同类型的变量集合会更有用。 无须声明单个变量,只要声明一个数组变量,并且用下标来表示单个变量。 数组中存入的是什么类型的数据,取出来的还是什么类型的数据。 为了在程序中使用数组,必须声明一个引用数组的变量,并指明数组的元素类型。 声明数组变量的语法: elementType可以是任意数据类型,但是数组中所有的元素都必须具有相同的数据类型。兼容的数据类型是否可以存储 **注意:**也可以用elementType arrayRefVar[](元素类型 数组引用变量[] )声明数组变量。这种来自C/C++语言的风格被Java采纳以适用于C/C++程序员。 不同于基本数据类型变量的声明,声明一个数组变量时并不在内存中给数组分配任何空间。它只是创建一个对数组的引用的存储位置。如果变量不包含对数组的引用,那么这个变量的的值为null。除非数组已经被创建,否则不能给它分配任何元素。声明数组变量之后,可以使用下面的语法用new操作符创建数组。 创建数组语法: 声明一个数组变量、创建数组、然后将数组引用赋值给变量这三个步骤可以合并在一条语句里。 使用以下语法给这些元素赋值: 注意:一个数组变量看起来似乎是存储了一个数组,但实际上它存储的是指向数组的引用。严格的讲,一个数组变量和一个数组是不同的,但多数情况下它们的差别是可以忽略的。因此,为了简化,通常可以说myList是一个数组,而不用更长的陈述:myList是一个含有10个double型元素数组的引用变量。 当给数组分配空间时,必须指定该数组能够存储的元素个数,从而确定数组大小。 创建数组之后就不能再修改它的大小。可以用arrayRefVar.length得到数组的大小。 当创建数组后,它的元素被赋予默认值,数值型基本元素类型的默认值为0,char型的默认值为’\u0000’,boolean型的默认值为false。String型默认值为null。 数组元素可以通过下标访问。 数组下标是基于0的,也就是说,其范围从0开始到arrayRefVar.length-1结束。 数组中的每个元素都可以使用下面的语法表示,称为下标变量(indexed variable): 一些语言使用圆括号引用数组元素,例如myList(9)。而Java语言使用方括号。 创建数组后,下标变量与正常变量的使用方法相同。 Java有一个简捷的标记,称作数组初始化语法,它使用下面的语法将声明数组、创建数组和初始化数组结合到一条语句中。 警告:数组初始化语法中不使用操作符new。使用数组初始化语法时,必须将声明、创建和初始化数组都放在一条语句中。将它们分开会产生语法错误。 处理数组元素时,经常会用到for循环,理由有以下两点: **提示:**对于char[]类型的数组,可以使用一条打印语句打印。 1)(使用输入值初始化数组) 2)(使用随机数初始化数组)下面的循环使用0.0到100.0之间,但小于100.0的随机值初始化数组myList。 3)(显示数组) 4)(对所有元素求和) 5)(找出最大元素) 6)(找出最大元素的最小下标值) 7)(随机打乱) 8)(移动元素)将元素向左移动一个位置并且将第一个元素放在最后一个元素的位置。 9)(简化编码) Java支持一个简便的for循环,称为foreach循环,即步使用下标变量就可以顺序地遍历整个数组。 语法格式: 注意:变量element必须声明为与arrayRefVar中元素相同的数据类型。 但是,当需要以其他顺序遍历数组或改变数组中的元素时,还是必须使用下标变量。 **警告:**越界访问数组是经常会出现的程序设计错误,它会抛出一个运行错误ArrayIndexOutOfBoundsException。为了避免错误的发生,在使用时应确保所使用的下标不超过arrayRefVar.length-1。 程序员经常错误地使用下标1引用数组的第一个元素,但其实第一个元素的下标应该是0.这称为过1错误(off-by-one error)。它是在循环中该使用<的地方误用<=时会犯的错误。 要将一个数组中的内容复制到另外一个中,你需要将数组的每个元素复制到另外一个数组中。 (=)赋值语句并不能将数组1引用的数组内容复制给数组2,而只是将数组1的引用值复制给了数组2.在这条语句之后,数组1和数组2都指向同一个数组。数组2原先所引用的数组不能再引用,它就变成了垃圾,会被Java虚拟机自动收回(这个过程称为垃圾回收)。 在Java中,可以使用赋值语句复制基本数据类型的变量,但不能复制数组。将一个数组变量赋值给另外一个数组变量,实际上是将一个数组的引用复制给另一个变量,使两个变量都指向相同的内存地址。 复制数组有三种方法: 可以使用循环将源数组中的每个元素复制到目标数组中的对应元素。 另一种方式是使用java.lang.System类的arraycopy方法复制数组,而不是使用循环。 arraycopy的语法格式: 改写: arraycopy方法没有给目标数组分配内存空间。复制前必须创建目标数组以及分配给它的内存空间。复制完成后,sourceArray和targetArray具有相同的内容,但占有独立的内存空间。 **注意:**arraycopy方法违反了Java命名习惯。根据命名习惯,该方法应该命名为arrayCopy(即字母C大写)。 要点提示:当将一个数组传递给方法时,数组的引用被传给方法。 正如前面给方法传递基本数据类型的值一样,也可以给方法传递数组。 该数组没有显式地引用变量,这样的数组称为匿名数组(anonymous array)。 Java使用按值传递(pass-by-value)的方式将实参传递给方法。传递基本数据类型变量的值与传递数组值有很大的不同。 注意:数组在Java中是对象。JVM将对象存储在一个称作堆(heap)的内存区域中,堆用于动态内存分配。 要点提示:当从方法中返回一个数组时,数组的引用被返回。 可以在调用方法时向方法传递一个数组。方法也可以返回一个数组。 **要点提示:**具有同样类型的可变长度的参数可以传递给方法,并将作为数组对待。 可以把类型相同但个数可变的参数传递给方法。方法中的参数声明如下: 在方法声明中,指定类型后紧跟着省略号(…)。只能给方法中指定一个可变长参数,同时该参数必须是最后一个参数。任何常规参数必须在它之前。 Java将可变长参数当成数组对待。可以将一个数组或数目可变的参数传递给可变长参数。当用数目可变的参数调用方法时,Java会创建一个数组并把参数传给它。 调用可变长参数方法时,如果没有传参数,数组的长度为0。 **要点提示:**如果一个数组排好序了,对于寻找数组中的一个元素,二分查找比线性查找更加高效。 查找(searching)是在数组中寻找特定元素的过程。查找是计算机程序设计经常要完成的任务。有很多用于查找的算法和数据结构:线性查找(linear searching)和二分查找(binary searching)。 线性查找法将要查找的关键字key与数组中的元素逐个进行比较。这个过程持续到在列表中找到与关键字匹配的元素,或者查完列表也没有找到关键字为止。如果匹配成功,线性查找法返回与关键字匹配的元素在数组中的下标。如果没有成功,则返回-1. 线性查找法把关键字和数组中的每一个元素进行比较。数组中的元素可以按任意顺序排列。平均来看,如果关键字存在,那么在找到关键字之前,这种算法必须与数组中一半的元素进行比较。由于线性查找法的执行时间随着数组元素个数的增长而线性增长,所以,对于大数组而言,线性查找法的效率并不高。 二分查找法是另一种常见的对数值列表的查找方法。使用二分查找法的前提条件是数组中的元素必须已经排好序。假设数组已按升序排列。二分查找发首先将关键字与数组的中间元素进行比较。 考虑下面三种情况: 二分法在每次比较之后就排除掉一半的数组元素,有时候是去掉一半的元素,有时候是去掉一半加1个元素。假设数组有n个元素。为了方便起见,假设n是2的幂。经过第1次比较,只剩下n/2个元素需要进一步查找;经过第2次比较,剩下(n/2)/2个元素需要进步一步查找。经过k次比较之后,需要查找的元素就剩下n/2k个。当k=log2n时,数组中只剩下1个元素,就只需要再比较1次。因此,在一个已经排序的数组中用二分查找法查找一个元素,即使是最坏的情况,也只需要log2n+1次比较。对于一个1024(210)个元素的数组,在最坏情况下,二分查找发只需要比较11次,而在最坏的情况下线性查找要比较1023次。 当没有找到这个关键字时,low就是一个插入点,这个位置将插入关键字以保持列表的有序性。一种更实用的方法是返回插入点减1.这个方法必须返回一个负值,表明这个关键字不在该序列中,可以只返回-low吗?答案是:不可以。如果关键字小于list[0],那么low就是0,-0也是0.这就表明关键字匹配list[0]。一个好的选择是,如果关键字不在该序列中,方法返回-low-1.返回-low-1不仅表明关键字不在序列中,而且还给出了关键字应该插入的地方。 **注意:**线性查找法适用于在较小数组或没有排序的数组中查找,但是对大数组而言效率不高。二分查找法的效率较高,但它要求数组已经排好序。 **要点提示:**如同查找一样,排序是计算机编程中非常普遍的一个任务。对于排序已经开发出很多不同的算法。 选择排序算法:假设要按升序排列一个数列。选择排序法先找到数列中最小的数,然后将它和第一个元素交换。接下来,在剩下的数中找到最小数,将它和第二个元素交换,以此类推,直到数列中仅剩一个数为止。 java.util.Arrays类包括各种各样的静态方法,用于实现数组的排序和查找、数组的比较 和填充数组元素,以及返回数组的字符串表示。这些方法都有对所有基本类型的重载方法。 sort和paralleSort方法详解: 可以采用二分查找法(binarySearch方法)在数组中查找关键字。数组必须提前按升序排列好。如果数组中不存在关键字,方法返回-1(插入点下标+1)。 可以采用equals方法检测两个数组是否相a等。如果它们的内容相同,那么这两个数组相等。 可以使用fill方法填充整个数组或部分数组。 还可以使用toString方法来返回一个字符串,该字符串代表了数组中的所有元素。这是一个显示数组中所有元素的快捷和简便的方法。 要点提示:main方法可以从命令行接收字符串参数。 main方法的声明与众不同,它具有String[] 类型参数args。很明显,参数args是一个字符串数组。main方法就像一个带参数的普通方法。可以通过传递实参来调用一个普通方法。 main方法就和普通方法一样。此外,还可以从命令行传送参数。 运行程序时,可以从命令行给main方法传递字符串参数。 在命令行中出现字符串时,不需要放在双引号中。这些字符串用空格隔开。如果字符串包含空格,那就必须使用双引号括住。 当调用main方法时,Java解释器会创建一个数组存储命令行参数,然后将该数组的引用传递给args。 然后,Java解释器传递参数args去调用main方法。 注意:如果运行程序时没有传递字符串,那么使用new String[0]创建数组。在这种情况下,该数组是长度为0的空数组。args是对这个空数组的引用。因此,args不是null,但是args.length是0。 Integer.parseInt(args[0])将一个数字字符串转换为一个整数。该字符串必须由数字构成,否则,程序会非正常中断。 使用.符号用于乘法,而不是通常的*符号。原因是当符号*用于命令行时表示当前目录下的所有文件。在使用命令java Test * 之后,下面的程序就会显示当前目录下的所有文件。为了解决这个问题,我们需要使用其他符号来用于乘法操作。 1、使用语法elementType[] arrayRefVar(元素类型[] 数组引用变量)或elementType arrayRefVar(元素类型 数组引用变量[])声明一个数组类型的变量。尽管elementType arrRefVar[] 也是合法的,但还是推荐使用elementType[] arrayRefVar风格。 2、不同于基本数据类型变量的声明,声明数组变量并不会给数组分配任何空间。数组变量不是基本数据类型变量。数据变量包含的是对数组的引用。 3、只有创建数组后才能给数组元素赋值。可以使用new操作符创建数组,语法如下:new elementType[arraySize](数据类型[数组大小]). 4、数组中的每个元素都是使用语法arrayRefVar[index](数组引用变量[下标])表示的。下标必须是一个整数或一个整数表达式。 5、创建数组之后,它的大小就不能改变,可以使用arrayRefVar.length得到数组的大小。由于数组的下标总是从0开始,所以,最后一个下标总是arrayRefVar.length - 1。如果试图引用数组界外的元素,就会发生越界错误。 6、程序员经常会错误地用下标1访问数组的第一个元素,但是,实际上这个元素的下标应该是0.这个错误称为下标过1错误(index off-by-one error)。 7、当创建一个数组时,如果其中的元素的基本数据类型时数值型,那么赋默认值0。字符类型的默认值为’\u0000’,布尔类型的默认值为false。 8、Java有一个称为数组初始化语法(array initializer)的简捷表达式,它将数组的声明、创建和初始化合并为一条语句,其语法为: 9、将数组参数传递给方法时,实际上传递的是数组的引用;更准确地说,被调用地方法可以修改调用者的原始数组的元素。 10、如果数组是排好序的,对于查找数组中的一个元素而言,二分查找比线性查找更加高效。 11、选择排序找到列表中最小的数字,并将其和第一个数字交换。然后在剩下的数字中找到最小的,和剩下列表的第一个元素交换,继续这个步骤,直到列表中只剩下一个数字。 要点提示:表格或矩阵中的数据可以表示为二维数组。 一维数组可以存储线性的元素集合。可以使用二维数组存储矩阵或表格。 要点提示:二维数组中的元素通过行和列的下标来访问。 声明二维数组的语法: 二维数组中使用两个下标,一个表示行,另一个表示列。同一维数组一样,每个下标索引值都是int型的,从0开始。 警告:使用matrix[2,1]访问行下标为2、列标下为1的特定元素是一种常见错误。在Java中,每个下标必须放在一对方括号中。 也可以使用数组初始化来声明、创建和初始化一个二维数组。 二维数组实际上是一个数组,它的每个元素都是一个一维数组。数组x的长度是数组中元素的个数,可以用x.length获取该值。 二维数组中的每一行本身就是一个数组,因此,各行的长度就可以不同。这样的数组称为锯齿数组(ragged array)。 **注意:**使用语法new int[5][]创建数组时,必须指定第一个下标。语法new int[][]是错误的。 **要点提示:**嵌套的for循环常用于处理二维数组。 处理二维数组的例子: 1)(使用输入值初始化数组) 2)(使用随机值初始化数组) 3)(打印数组) 4)(求所有元素的和)使用名为total的变量存储和。将total初始化为0。 5)(对数组按列求和)对于每一列,使用名为total的变量存储它的和。 6)(哪一行的和最大?)使用变量maxRow和indexOfMaxRow分别跟踪和的最大值以及该行的索引值。计算每一行的和,如果计算出的新行的和最大,就更新maxRow和indexOfMaxRow。 7)(随意打乱)为了实现这个功能,对每个元素matrix[i][j],随机产生下标i1和j1,然后互换matrix[i][j]和matrix[i1][j1]。 将一个二维数组传递给方法的时候,数组的引用传递给了方法。 可以像传递一维数组一样,给方法传递二维数组。也可以从一个方法返回一个数组。
* 假设有一个集合的点,找出最接近的点对问题就是找到两个点,它们到彼此的距离最近。
* 解决这个问题的方法有好几种:
* 一种直观的方法就是计算所有点对之间的距离,并且找出最短的距离。
*/ **要点提示:**二维数组由一个一维数组的数组组成,而一个三维数组可以认为是由一个二维数组的数组所组成。 在Java中,可以创建n维数组,其中n是任意整数。 可以对二维数组变量的声明以及二维数组的创建方法进行推广,用于声明n>=3的n维数组变量和创建n维数组。 声明一个三维数组变量scores,创建一个数组并将它的引用赋值给scres: 多维数组实际上是一个数组,它的每个元素都是另一个数组。三维数组是由二维数组构成的数组,每个二维数组又是一维数组的数组。
* 给出一个猜生日的程序,可以通过用三维数组来存储5个数字集来简化程序,然后使用循环提示用户回答。
*/ 1、可以使用二维数组来存储表格。 2、可以使用以下语法来声明二维数组变量: 3、可以使用以下语法来创建二维数组变量: 4、使用下面的语法表示二维数组中的每个元素: 5、可以使用数组初始化语法来创建和初始化二维数组: 6、可以使用数组的数组构成多维数组,例如:一个三维数组变量可以声明为:/**
* 程序清单6-10
*
* 创建一个名为RandomCharacter的类,它有五个重载的方法,随机获取某种特性类型的字符。
*
*/
public class RandomCharacter {
/** Generate a random character between ch1 and ch2 */
public static char getRandomCharacter(char ch1,char ch2){
return (char)(ch1+Math.random()*(ch2-ch1+1));
}
/** Generate a random lowercase letter */
public static char getRandomLowerCaseLetter(){
return getRandomCharacter('a','z');
}
/** Generate a random uppercase letter */
public static char getRandomUppercaseLetter(){
return getRandomCharacter('A','Z');
}
/** Generate a random digit character */
public static char getRandomDigitCharacter(){
return getRandomCharacter('0','9');
}
/** Generate a random character */
public static char getRandomCharacter(){
return getRandomCharacter('\uu0000','\uFFFF');
}
}
-----------------------------------------------------
/**
* 程序清单6-11
*
* 显示175个随机的小写字母
*/
public class TestRandomCharacter {
/** Main method */
public static void main(String[] args){
final int NUMBER_OF_CHARS = 175;
final int CHARS_PER_LINE = 25;
//Print random character between 'a' and 'z',25 chars per line
for(int i = 0;i<NUMBER_OF_CHARS;i++){
char ch = RandomCharacter.getRandomLowerCaseLetter();
if((i+1) % CHARS_PER_LINE == 0)
System.out.println(ch);
else
System.out.print(ch+ "\t");
}
}
}
6.11 方法抽象和逐步求精
6.11.1 自顶向下的设计
6.11.2 自顶向下和自底向上的实现
6.11.3 实现细节
//打印日历
import java.util.Scanner;
/**
* 程序清单6-12
*/
public class PrintCalendar {
/**
* Main method
*/
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
//Prompt the user to enter year
System.out.print("Enter full year(e.g.,2012): ");
int year = input.nextInt();
//Prompt the user to enter month
System.out.print("Enter month as a number between 1 and 12: ");
int month = input.nextInt();
//Print calendar for month of the year
printMonth(year, month);
}
/**
* Print the calendar for the month in a year
*/
public static void printMonth(int year, int month) {
//Print the headings of the calendar
printMonthTitle(year, month);
//Print the body of the calendar
printMonthBody(year, month);
}
/**
* Print the month title,e.g.,March 2012
*/
public static void printMonthTitle(int year, int month) {
System.out.println(" " + getMonthName(month) + " " + year);
System.out.println("--------------------------");
System.out.println(" Sun Mon Tue Wed Thu Fri Sat");
}
/**
* Print month body
*/
public static void printMonthBody(int year, int month) {
//Get start day of the week for the first date in the month
int startDay = getStartDay(year, month);
//Get number of days in the month
int numberOfDaysInMonth = getNumberOfDaysInMonth(year, month);
//Pad space before the first day of the month
int i = 0;
for (i = 0; i < startDay; i++)
System.out.print(" ");
for(i=1;i<=numberOfDaysInMonth;i++){
System.out.printf("%4d",i);
if((i+startDay) % 7 ==0)
System.out.println();
}
System.out.println();
}
/**
* Get the English name for the month
*/
public static String getMonthName(int month) {
// return "January"; // A dummy value
String monthName = "";
switch (month) {
case 1: monthName = "January";break;
case 2: monthName = "february";break;
case 3: monthName = "March"; break;
case 4: monthName = "April"; break;
case 5:monthName = "May"; break;
case 6: monthName = "June";break;
case 7: monthName = "July";break;
case 8: monthName = "Auguts"; break;
case 9:monthName = "September";break;
case 10:monthName = "October"; break;
case 11: monthName = "November"; break;
case 12:monthName = "December";
}
return monthName;
}
/**
* Get the start day of month/1/year
*/
public static int getStartDay(int year, int month) {
final int START_DAY_FOR_JAN_1_1800 = 3;
// Get total number of days form 1/1/1800 to month/1/year
int totalNumberOfDays = getTotalNumberOfDays(year,month);
//Return the start day for month/1/year
return (totalNumberOfDays + START_DAY_FOR_JAN_1_1800) % 7;
}
/**
* Get the total number of days since January 1,1800
*/
public static int getTotalNumberOfDays(int year, int month) {
int total = 0;
//Get the total days form 1800 to 1/1/Year
for(int i = 1800;i<year;i++)
if(isLeapYear(i))
total += 366;
else
total += 365;
//Add days form Jan to the month prior to the calendar month
for(int i = 1;i<month;i++)
total = total +getNumberOfDaysInMonth(year,i);
return total;
}
/**
* Get the number of days in a month
*/
public static int getNumberOfDaysInMonth(int year, int month) {
if(month == 1||month==3||month==5||month==7||month==8||month==10||month==12)
return 31;
if(month == 4||month ==6||month==9||month==11)
return 30;
if(month==2) return isLeapYear(year)?29:28;
return 0; //If month is incorrect
}
/**
* Determine if it is leap year
*/
public static boolean isLeapYear(int year) {
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0); //A dummy value
}
}
6.11.4 逐步求精的优势
本章小结
第7章 一维数组
7.1 引言
7.2 数组的基础知识
7.2.1 声明数组变量
elementType[] arrayRefVar;(元素类型[] 数组引用变量)
7.2.2 创建数组
arrayRefVar = new elementType[arraySize];
//这条语句做了两件事:1)使用new elementType[arraySize]创建了一个数组;2)把这个新创建的数组的引用赋值给变量arrayRefVar
elementType[] arrayRefVar = new elementType[arraySize];
//元素类型[] 数组引用变量 = new 元素类型[数组大小]
elementType arrayRefVar[] = new elementType[arraySize];
//元素类型 数组引用变量 = new 元素类型[数组大小]
arrayRefVar[index] = value;
7.2.3 数组大小和默认值
7.2.4 访问数组元素
arrayRefVar[index];(数组引用变量[下标])
7.2.5 数组初始化语法
elementType[] arrayRefVar = {value0,value1,....valuek};(元素类型[] 数组引用变量 = {值0,值1,....值k};
7.2.6 处理数组
char[] city = new char[]{'d','a','l','l'};
System.out.println(city);
java.util.Scanner input = new java.util.Scanner(System.in);
System.out.println("Enter " + myList.length + " values: ");
for(int i = 0;i < myList.length;i++)
myList[i] = input.nextDouble();
for(int i = 0 ;i < myList.length; i++)
myList[i] = Math.random() * 100;
for(int i = 0;i < myList.length;i++)
System.out.print(myList[i] + " ");
double total = 0;
for(int i = 0;i < myList.length; i++)
total += myList[i];
double total = myList[0];
for(int i = 1; i < myList.length; i++)
if(myList[i]>max)max = myList[i];
double max = myList[0];
int indexOfMax = 0;
for(int i = 1; i < myList.length; i++){
if(myList[i] > max){
max = myList[i];
indexOfMax = i;
}
}
for(int i = myList.length -1 ; i > 0 ; i--){
//Generate an index j randomly with 0 <= j <= i
int j = (int)(Math.random()*(i+1));
//Swap myList[i] with myList[j]
double temp = myList[i];
myList[i] = myList[j];
myList[j] = temp;
}
double temp = myList[0]; //Retain the first element
//Shift elements left
for(int i = 1;i < myList.length;i++)
myList[i-1] = myList[i];
//Move the first element to fill in the last position
myList[myList.length -1] = temp;
String[] months = {"January","February",....,"December"};
System.out.println("Enter a month numnber(1 to 12): ");
int monthNumber = input.nextInt();
System.out.println("The month is " + months[monthNumber -1]);
7.2.7 foreach循环
for(elementType element:arrayRefVar){
//Process the element
}
7.3 示例学习:分析数字
/**
* 程序清单7-1
*
* 编写一个程序,找到大于所有项平均值的那些项
*
* 现在你可以编写程序来解决本章开始时提出的问题了。问题是,读取100个数字,计算这些数的平均值并找到大于平均值的那些项的个数。为了更加灵活地吹了任意数目地输入,我们让用户给出输入的个数,而不是将其固定为100.
*/
public class AnalyzeNumbers {
public static void main(String[] args) {
java.util.Scanner input = new java.util.Scanner(System.in);
System.out.print("Enter the number of items: ");
int n = input.nextInt();
double [] numbers = new double[n];
double sum = 0;
System.out.print("Enter the numbers: ");
for(int i = 0; i < n ; i++){
numbers[i] = input.nextDouble();
sum += numbers[i];
}
double average = sum / n;
int count = 0; //The number of elements above average
for(int i = 0; i < n; i++)
if(numbers[i] > average)
count++;
System.out.println("Average is " + average);
System.out.println("Number of elements above the average is " + count);
}
}
7.4 一副牌
/**
* 程序清单7-2
*
* 编写一个程序,从一副牌中随机选出4张牌。
*
*/
public class DeckOfCards {
public static void main(String[] args) {
int[] deck = new int[52];
String[] suits = {"Spades","Hearts","Diamonds","Clubs"};
String[] ranks = {"Ace","2","3","4","5","6","7","8","9","10","Jack","Queen","king"};
//Initialize the cards
for(int i = 0 ; i<deck.length;i++)
deck[i] = i;
//Shuffle the cards
for(int i = 0; i < deck.length; i++){
//Generate an index randomly
int index = (int)(Math.random() * deck.length);
int temp = deck[i];
deck[i] = deck[index];
deck[index] = temp;
}
//Display the first four cards
for(int i = 0;i < 4; i++){
String suit = suits[deck[i] / 13];
String rank = ranks[deck[i] % 13];
System.out.println("Card number " + deck[i] + " : " + rank + " of " + suit);
}
}
}
7.5 数组的复制
list2 = list1;
arraycopy(sourceArray,srcPos,targetArray,tarPos,length);
//参数srcPos和tarPos分别表示源数组sourceArray和目标数组targetArray中的起始位置。从sourceArray复制到targetArray中的元素个数由参数length指定。
System.arraycopy(sourceArray,0,targetArray,0,sourceArray.length);
7.6 将数组传递给方法
new elementType[]{value0,value2,....valuek};
7.7 从方法中返回数组
7.8 示例学习:统计每个字母出现的次数
/**
* 程序清单6-10
*
* 创建一个名为RandomCharacter的类,它有五个重载的方法,随机获取某种特性类型的字符。
*
*/
public class RandomCharacter {
/** Generate a random character between ch1 and ch2 */
public static char getRandomCharacter(char ch1,char ch2){
return (char)(ch1+Math.random()*(ch2-ch1+1));
}
/** Generate a random lowercase letter */
public static char getRandomLowerCaseLetter(){
return getRandomCharacter('a','z');
}
/** Generate a random uppercase letter */
public static char getRandomUppercaseLetter(){
return getRandomCharacter('A','Z');
}
/** Generate a random digit character */
public static char getRandomDigitCharacter(){
return getRandomCharacter('0','9');
}
/** Generate a random character */
public static char getRandomCharacter(){
return getRandomCharacter('\uu0000','\uFFFF');
}
}
-----------------------------------------------------------
import testProblem.RandomCharacter;
/**
* 程序清单7-4
*
* 给出一个程序,用于统计一个字符数组中每个字母出现的次数。
*
* 1、随机生成1000个小写字母并将其放入一个字符数组中。可以使用请程序清单6-10中RandomCharacter类中的getRandomLowerCaseLetter()方法获取一个随机字母。
* 2、对数组中每个字母出现的次数进行计数。为了完成这个功能,创建一个具有26个int值的数组counts,每个值存放每个字母出现的次数。
*/
public class CountLettersInArray {
/** Main mehotd */
public static void main(String[] args){
//Declare and create an array
char[] chars = createArray();
//Display the array
System.out.println("The lowercase letters are: ");
displayArray(chars);
//Count the occurrences of each letter
int[] counts = countLetters(chars);
//Display counts
System.out.println("\nThe occurrences of each letter are: ");
displayCounts(counts);
}
/** Create an array of characters */
public static char[] createArray(){
//Declare an array of characters and create it
char[] chars = new char[100];
//Create lowercase letters randomly and assign
//them to the array
for(int i = 0;i < chars.length;i++)
chars[i] = RandomCharacter.getRandomLowerCaseLetter();
//Return the array
return chars;
}
/** Display the array of characters */
public static void displayArray(char[] chars){
//Display the characters in the array 20 on each line
for (int i = 0; i < chars.length; i++) {
if((i+1) % 20 ==0)
System.out.println(chars[i]);
else
System.out.print(chars[i] + " ");
}
}
/** Count the occurrences of each letter */
public static int[] countLetters(char[] chars){
//Declare and create an array of 26 int
int[] counts = new int[26];
//For each lowercase letter in the array,count it
for(int i = 0; i < chars.length; i++)
counts[chars[i] - 'a']++;
return counts;
}
/** Display counts */
public static void displayCounts(int[] counts){
for(int i = 0 ; i < counts.length;i++){
if((i+1) % 10 == 0)
System.out.println(counts[i] + " " + (char)(i+'a'));
else
System.out.print(counts[i] + " " + (char)(i + 'a') + " ");
}
}
}
7.9可变长参数列表
typeName ... parameterNmae(类型名...参数名)
7.10 数组的查找
7.10.1 线性查找法
7.10.2 二分查找法
7.11 数组的排序
/**
* 程序清单7-8
*
* 选择排序算法
*/
public class SelectionSort {
/** The method for sorting the numbers */
public static void selectionSort(double[] list){
for (int i = 0; i < list.length-1; i++) {
//Find the minimum in the list[i...list.length-1]
double currentMin = list[i];
int currentMinIndex = i;
for (int j = i + 1; j < list.length; j++) {
if(currentMin > list[j]){
currentMin = list[j];
currentMinIndex = j;
}
}
//Swap list[i] with list[currentMinIndex] if necessary
if(currentMinIndex != i){
list[currentMinIndex] = list[i];
list[i] = currentMin;
}
}
}
}
7.12 Arrays类
7.13 命令行参数
7.13.1 向main方法传递字符串
7.13.2 示例学习:计算器
public class Calculator {
/** Main method */
public static void main(String[] args) {
//Check number of strings passed
if(args.length != 3){
System.out.println(
"Usage: Java Calculator operand1 operator operand2"
);
System.exit(0);
}
//The result of the operation
int result = 0;
//Determine the operation
switch (args[1].charAt(0)){
case '+' : result = Integer.parseInt(args[0]) +
Integer.parseInt(args[2]);break;
case '-' : result = Integer.parseInt(args[0]) -
Integer.parseInt(args[2]);break;
case '.' : result = Integer.parseInt(args[0]) *
Integer.parseInt(args[2]);break;
case '/' : result = Integer.parseInt(args[0]) /
Integer.parseInt(args[2]);break;
}
//Display result
System.out.println(args[0] + ' ' + args[1] + ' ' + args[2] + " = " + result);
}
}
本章小结
元素类型[] 数组引用变量 = {value0,value1,...,valuek};
第8章 多维数组
8.1 引言
8.2 二维数组的基础知识
8.2.1声明二维数组变量并创建二维数组
数据类型[][] 数组名;
数据类型 数组名[][];//允许这种方式,但并不推荐使用它。
8.2.2 获取二维数组的长度
8.2.3 锯齿数组
//创建锯齿数组的案例
int[][] triangleArray = new int[5][];
triangleArray[0] = new int[5];
triangleArray[0] = new int[4];
triangleArray[0] = new int[3];
triangleArray[0] = new int[2];
triangleArray[0] = new int[1];
triangleArray[0][3] = 50;
triangleArray[4][0] = 45;
8.3 处理二维数组
int[][] matrix = new int[10][10];
java.util.Scanner input = new java.util.Scanner(System.in);
System.out.println("Enter " + matrix.length + " rows and " + matrix[0].length + " columns: ");
for(int row = 0; row < matrix.length; row++){
for(int column = 0;column < matrix[row].length; column++){
matrix[row][column] = input.nextInt();
}
}
for(int row = 0;row < martix.length;row++){
for(int column = 0;column < matrix[row].length;column++){
matrix[row][column] = (int)(Math.random() * 100);
}
}
for(int row = 0;row < martix.length;row++){
for(int column = 0;column < martix[row].length;column++){
System.out.println(matrix[row][column] + " ");
}
System.out.println();
}
int total = 0;
for(int row = 0 ;row < matrix.length;row++){
for(int column = 0; column < matrix[row].length;column++){
total += matrix[row][column];
}
}
for(int column = 0;column<matrix[0].length;column++){
int total = 0;
for(int row = 0;row < matrix.length;row++)
total += matrix[row][column];
System.out.println("Sum for column " + column + " is "+ total);
}
int maxRow = 0;
int indexOfMaxRow = 0;
//Get sum of the first row in maxRow
for(int row = 0;column < matrix[0].length;column++){
maxRow += matrix[0][column];
}
for(int row =1;row < matrix.length;row++){
int totalOfThisRow = 0;
for(int column = 0;column < matrix[row].length;column++)
totalOfThisRow += matrix[row][column];
if(totalOfThiwRow > maxRow){
maxRow = totalOfThisRow;
indexOfMaxRow = row;
}
}
for(int i = 0;i < matrix.length;i++){
for(int j = 0;j < matrix[i].length;j++){
int i1 = (int)(Math.random() * matrix.length);
int j1 = (int)(Math.random() * matrix[i].length);
//Swap matrix[i][j] with matrix[i1][j1]
int temp = matrix[i][j];
matrix[i][j] = matrix[i1][j1];
matrix[i1][j1] = temp;
}
}
8.4 将二维数组传递给方法
import java.util.Scanner;
/**
* 程序清单8-1
*
* 可以像传递一维数组一样,给方法传递二维数组。也可以从一个方法返回一个数组。
* 第一个方法,getArray(),返回一个二维数组。
* 第二个方法,sum(int[][] m),返回一个矩阵中所有元素的和
*/
public class PassTwoDimensionalArray {
public static void main(String[] args) {
int[][] m = getArray(); //Get an array
//Display sum of elements
System.out.println("\nSum of all elements is "+ sum(m));
}
public static int[][] getArray(){
//Create a Scanner
Scanner input = new Scanner(System.in);
//Enter array values
int[][] m = new int[3][4];
System.out.println("Enter " + m.length + " rows and " + m[0].length + " columns: ");
for (int i = 0; i < m.length; i++) {
for (int j = 0; j < m[i].length; j++) {
m[i][j] = input.nextInt();
}
}
return m;
}
public static int sum(int[][] m) {
int total = 0;
for (int row = 0; row < m.length; row++) {
for (int column = 0; column < m[row].length; column++) {
total += m[row][column];
}
}
return total;
}
}
8.5 示例学习:多选题测验评分
/**
* 程序清单8-2
*
* 编写一个可以进行多选题测验评分的程序。
*
* 编写一个程序,对多选题测验进行评分。假设这里有8个学生和10道题目,学生的答案存储在一个二维数组中。每一行记录一名学生对所有题目的答案。
* 正确答案存储在一个一维数组中,
* 程序给测验评分并显示结果。它将每个学生的答案与正确答案进行比较,统计正确答案的个数,并将其显示出来。
*/
public class GradeExam {
/** Main Method */
public static void main(String[] args) {
//Students' answers to the questions
char[][] answers = {
{'A','B','A','C','C','D','E','E','A','D'},
{'D','B','A','B','C','A','E','E','A','D'},
{'E','D','D','A','C','B','E','E','A','D'},
{'C','B','A','E','D','C','E','E','A','D'},
{'A','B','D','C','C','D','E','E','A','D'},
{'B','B','E','C','C','D','E','E','A','D'},
{'B','B','A','C','C','D','E','E','A','D'},
{'E','B','E','C','C','D','E','E','A','D'},
};
//Key to the questions
char[] keys = {'D','B','D','C','C','D','A','E','A','D'};
//Grade all answers
for (int i = 0; i < answers.length; i++) {
//Grade one student
int correctCount = 0;
for (int j = 0; j < answers[i].length; j++) {
if(answers[i][j] == keys[j])
correctCount++;
}
System.out.println("Student " + i + "'s correct count is " + correctCount);
}
}
}
8.6 示例学习:找出距离最近的点对
import java.util.Scanner;
/**
* 程序清单8-3
*
8.7 示例学习:数独
import java.util.Scanner;
/**
* 程序清单8-4
*
* 要点提示:要解决的问题是检查一个给定的数独解答是否正确。
* 数独是一个9x9的网格,它被分为更小的3x3的盒子(也称为区域或者块)。将从1到9的数字植入一些称为固定方格(fixed cell)的格子里。该程序的目标是将从1到9的数字植入那些称为自由方格(free cell)的格子,以便能够使得每行每列以及每个3x3的盒子都包含从1到9的数字。
*
* 为了方便起见,使用值0表示自由方格,很自然就会使用二维数组表示网格。
*
* 为了找到该难题的的解决方案,必须用1到9之间合适的数字替换网格中的每个0.
* 一旦找到一个数独难题的解决方案,如何验证它是正确的呢?有两种方法:
* 1、检查是否每行都有1到9的数字以及每列都有1到9的数字,并且每个小的块都有1到9的数字。
* 2、检查每个单元格。每个单元格必须是1到9的数字,单元格数字在每行、每列,以及每个小方盒中都是唯一的。
*
* 提示用户输入一个解决方案,然后报告它是否有效。
*/
public class CheckSudoKuSolution {
public static void main(String[] args) {
// Read a Sudoku solution
int[][] grid = readASolution();
System.out.println(isValid(grid) ? "Valid solution" : "Invalid solution");
}
/** Read a Sudoku solution form the console */
public static int[][] readASolution(){
//Create a Scanner
Scanner input = new Scanner(System.in);
System.out.println("Enter a Sudokku puzzle solution: ");
int[][] grid = new int[9][9];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
grid[i][j] = input.nextInt();
}
}
return grid;
}
/** Check whether a solution is valid */
public static boolean isValid(int[][] grid){
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
if(grid[i][j] < 1 || grid[i][j] > 9 || !isValid(i,j,grid))
return false;
return true; //The solution
}
/** Check wther grid[i][j] is valid in the grid */
public static boolean isValid(int i,int j,int[][] grid){
//Check whether gird[i][j] is unique in i's row
for (int column = 0; column < 9; column++)
if(column != j && grid[i][column] == grid[i][j])
return false;
// Check whether grid[i][j] is unique in j's column
for (int row = 0; row < 9; row++)
if(row != i && grid[row][j] == grid[i][j])
return false;
//Check whether grid[i][j] is unique in the 3-by-3 box
for(int row = (i / 3) * 3;row < (i / 3) * 3 + 3;row++)
for(int col = (j / 3) * 3;col < (j / 3) * 3 + 3;col++)
if(row != i && col != j && grid[row][col] == grid[i][j])
return false;
return true;//The current value at grid[i][j] is valid
}
/**
* 程序调用readASolution()方法来读取一个数独的解决方案,并且返回一个表示数独网格的二维数组。
* isValid(grid)方法通过检查每个值是否都是从1到9的数字以及每个网格中值是否都是有效的,来确认网格中是否放入了正确的值。
* isValid(i,j,grid)方法检查grid[i][j]的值是否是有效的。它检查grid[i][j]在第i行、第j行,以及3x3的方盒中是否出现超过一次。
* 如何定位同一个方盒中的所有单元格呢?对于任意的grid[i][j],包含它的3x3的方盒的起始单元格是grid[(i/3)*3][(j/3)*3]
*/
}
8.8 多维数组
double[][][] scores = new double[6][5][2];
8.8.1 示例学习:每日温度和湿度
import java.util.Scanner;
/**
* 程序清单8-5
*
* 假设气象站每天每小时都会记录温度和湿度,并且将过去十天的数据都存储在一个名为Weather.txt的文本文件中。文件中每一行包含四个数字,分别表示日期、小时、温度和湿度。
* 注意,文件的行不一定是要按照日期和小时的升序排列。
*
* 编写任程序,计算这十天的日均温度和日均湿度。可以使用输入重定向来读取文件,并将这些数据存在一个名为data的三维数组中。data的第一个下标范围从0到9,代表10天;第二个下标范围从0到23,代表24小时;而第三个下标范围从0到1,分别代表温度和湿度。
*
* 注意:在文件中,天是从1到10编号的,而小时是从1到24编号的。因为数组下标是从0开始的,所以,data[0][0][0]存储的是第一天第一小时的温度,而data[9][23][1]存储的是第十天第二十四小时的温度。
*/
public class Weather {
public static void main(String[] args) {
final int NUMBER_OF_DAYS = 10;
final int NUMBER_OF_HOURS = 24;
double[][][] data = new double[NUMBER_OF_DAYS][NUMBER_OF_HOURS][2];
Scanner input = new Scanner(System.in);
//Read input using input redirection form a file
for(int k = 0; k < NUMBER_OF_DAYS * NUMBER_OF_HOURS; k++){
int day = input.nextInt();
int hour = input.nextInt();
double temperature = input.nextDouble();
double humidity = input.nextDouble();
data[day -1][hour -1][0] = temperature;
data[day -1][hour -1][1] = humidity;
}
//Find the average daily temperature and humidity
for(int i = 0;i < NUMBER_OF_DAYS;i++){
double dailyTemperatureTotal = 0,dailyHumidityTotal = 0;
for(int j = 0;j < NUMBER_OF_HOURS;j++){
dailyTemperatureTotal += data[i][j][0];
dailyHumidityTotal += data[i][j][1];
}
//Display result
System.out.println("Day " + i + "'s average temperature is " + dailyTemperatureTotal / NUMBER_OF_HOURS);
System.out.println("Day " + i + "'s average humidity is " + dailyHumidityTotal / NUMBER_OF_HOURS);
}
}
}
8.8.2 示例学习:猜生日
import java.util.Scanner;
/**
* 程序清单8-6
*
本章小结
元素类型[][] 数组变量
new 元素类型[行的个数][列的个数]
数组变量[行的个数][列的个数]
元素类型[][] 数组变量 = {{某行的值},....,{某行的值}}
元素类型[][][] 数组变量,
使用new 元素类型[size1][size2][size3]来创建三维数组。