Java编程基础教程
前言
在最受欢迎的程序设计语言排行榜上,Java语言已经连续数年位列榜首。“Write once, Run anywhere,(一次编写,到处可行)”,这是一种很有效率的编程方式。跨平台、完全面向对象、既适于单机编程也适合于Internet编程等特点,给Java语言注入了强大的生命力。而Java语言也取得了举世瞩目、全球公认的地位。
如今的时代,电子设备嵌入Java技术,Java智能卡海量发行,PC中广泛应用Java技术,电信运营商支持Java技术,Java开发人员数量也日增月长。另外,Java开发者锐意创新,将Java技术应用到各种领域,使得Java技术已经成为世界上最卓越的企业应用和移动应用开发平台之一。历史上从没有过像Java平台这样能够如此广泛地应用,许多公司和软件人员大量使用Java技术。
亲爱的读者,如果你是在校生,正在上Java程序设计这门课程,请你珍惜校园的时光,珍惜学习的机会,发挥你的聪明才智吧,Java已经是无处不在,你的努力也不会徒然。如果你是一个自学者,那跟随本书二十四讲的引领,循序渐进,你一定能从入门到进阶,假如你有困惑之处,欢迎给编者发邮件讨论。如果您是一位老师,为您的学生选择课本,您此时手中的这一本应是一个不错的选择,教、学、做几个环节,一本书通达。
Java语言入门篇—主要为零起点的读者准备。对于有C或C++语言基础的读者,该篇简单学习即可。
面向对象篇—至关重要的理论篇,为所有学习Java语言的读者准备,学习该篇时要缓进而踏实,精学多悟,可辅以上机实训加强理论理解。
数据流应用篇—不可或缺的应用篇,为所有学习Java语言的读者准备,文件的输入/输出是语言基本的功能,该篇的学习策略应以上机实训为主,从验证到设计。
Java GUI编程篇—重要应用篇,通过该篇的学习,读者可设计出视窗风格的应用程序。本篇的学习策略是实践、再实践,从小的示例到较大的项目设计。
高级应用篇—进阶选学篇,全篇内容可根据课时情况,按务实原则选学选讲。 各篇分讲合成:
每篇由3~7讲组成,同一篇中的各讲循序渐进,由浅入深,由入门到提高。面向对象篇和Java GUI编程篇中的最后一讲都可作为选学或自学的内容。高级应用篇中的第二十讲建议选学,其后的各讲可跳选。
一讲一主题,每讲可对应2学时左右。 集成实践环节:
上机实训和指导,共安排了二十四个实训,一讲对应一个实训,实训部分可根据课时选做,建议读者在课时外补充完成所有实训。若采用“教、学、做一体化”的教学方式,可将讲训联合,教和学、学和做便有机融成一体。
课程设计任务和指导,安排了一个“写字留痕”日记本的设计课题,可作为1~2周的设计实践。课题涉及知识基本在前四篇中,扩展的一小部分新知识在“指导”环节进行了说明和演示。指导环节还补充了将Java应用程序包装成可执行的Jar文件的简捷方法,该方法可在学习应用篇的过程中提前穿插学习。
一、Java语言快速入门
内容: Java的起源和发展历程 Java语言的工作原理及特点 Java开发环境的建立 第一个程序的编译和运行 一、Java的起源和发展历程
作为一名Java语言的学习者,对Java的起源和发展有个概略认识应是必要的。
1. Java的起源 。Java起源于20世纪90年代初Sun公司的一个叫Green的项目,该项目的目标是开发嵌入家用电器的分布式软件系统,使电器更加智能化。项目小组成员皆为C++的高手,便采用C++进行系统开发,但在开发过程中发现了很多问题,如C++语言过于复杂、安全性差等。于是项目小组只好另辟蹊径,在“简单的、可靠的、紧凑的并易于移植的”的框架内开发了一个小型的计算机语言,用于编写消费者的电子产品中的程序,当时这款语言命名为Oak,橡树的意思。至于这Oak怎么变成了Java......
二、Java语言的工作原理及特点
1. Java是一门怎样的语言。 Sun公司出品了Java,它对Java语言有专门的描述:“Java is a simple, object-oriented, distributed, interpreted, robust, secure, architecture neutral, portable, high-performance, multithreaded, and dynamic language.”
它告诉你Java是简单的、面向对象的、分布式的、解释的、健壮的、安全的、结构中立的、轻便的、高性能的、多线程的动态语言。所有这些形容词恰恰指明了Java语言的特点和工作原理。
2. 跨平台特性
关于Java程序,有一句口号式的经典描述—“Write once, Run anywhere”,其中文意思是“一次编写,到处运行”。这指的正是Java语言跨平台的特性。
Java源代码被编译成一种结构中立的中间文件格式,在机器上能直接执行这种中间代码,不管机器是什么型号的,操作系统是哪种。但有一个必要的前提:那台运行Java程序的机器上需要预先安装Java运行系统。Java运行系统又称为Java虚拟机(简称JVM),它可以从java.sun.com网站免费下载,不同的操作系统需要安装对应的JVM版本。而Java的跨平台特性即通过JVM实现。
3. 完全面向对象
Java语言是一门面向对象的语言,它比C++等语言新,一张白纸上可以画最美好的图画,20世纪90年代初它就是这样一张白纸,以James Gosling为首的Green项目团队给它画的那幅画是完全面向对象,一切皆为对象。
什么是对象呢?对象是可存储数据的变量和可提供操作的方法的集合。对象的核心就是两项:变量和方法。每个对象在内存中都占据独立的空间,每个对象都拥有类型,对象从类型创建而来。
4. 多线程
Java中提供了专门的类,可方便地用于多线程编程。
多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。
多线程的程序可同时执行多个任务,多线程程序具有更好的交互性、实时性。 5. 内存垃圾自动回收
在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其他对象;而在Java中,JVM的一个系统级线程可以监督对象,它可以发现对象何时不再被使用,原先分配给该对象的内存即成为了垃圾,JVM系统线程会释放该内存块,对象即被销毁,内存垃圾就被自动回收。
事实上,除了释放没用的对象,Java垃圾收集也可以清除内存碎片。JVM将经过碎片整理后的内存分配给新的对象。
6. 简洁有效
Java语言的语法大多基于C++,但Java更加严谨、简洁。这体现在如下方面: Java去除了C++中一些难以理解、容易混淆的因素,如头文件、指针、结构体等;避免了赋值语句与逻辑运算语句的混淆;避免了隐藏变量带来的困惑,如“if(a = 3)?;”,在C++中是没问题的,而在Java中是错误的;取消了多重继承这一复杂的继承机制。
Java提供了对内存的自动管理:内存分配、内存释放。 Java提供了丰富的类库,有利于软件开发的高效和标准化。 7. 健壮且安全
Java程序首先要通过编译的环节,而Java有着最严格的“编译器”,可在编译阶段尽早发现错误,只有纠错完毕才能编译成功,生成字节码文件。这是健壮性的一个保证。字节码文件通过JVM解释执行,类装入器负责装入运行一个程序需要的所有类,确定整个可执行程序的内存布局。字节码校验器对装入的代码进行检查。校验器可发现操作数栈溢出、非法数据类型转换等多种错误。解释执行的机制是又一个健壮保证。
另外解释器的沙盒运行模式,Java网络程序被限制访问本地资源,这些保障了系统的安全性。
三、Java开发环境的建立
一台计算机上安装了JVM,该台机器就可运行Java程序;而要开发Java程序,计算机上仅仅有JVM还是不够的,需要建立Java开发环境才行。
选择什么样的版本,是J2SE还是J2EE,或者是J2ME?这要看从事哪方面的Java开发应用。本书使用J2SE来讨论Java程序设计,因此需要下载和安装JavaSE开发包。
1. JavaSE的组成
JavaSE是一个包含Java开发环境和运行环境的套件,该套件由如下三项集成: Java Development kit(JDK),Java程序开发环境。
Java Runtime Environment(JRE),Java 应用程序运行环境。 Java Plug-in,使Java Applet可在网页中运行的插件。
2. 建立JavaSE开发环境 。有关Java平台方面的软件都是免费开放的,可直接从Sun公司的Java技术支持网站(http://java.sun.com)上下载,网站上提供了众多不同标准和版本的Java语言平台。下载JavaSE开发包,直接输入地址http://java.sun.com/javase/downloads/index.jsp亦可。
安装过程是简捷的,双击下载文件,选择安装目录(如C:\java\jdk1.6.0_04),按步操作即可。
3. 设置环境变量
安装完毕需要设置path和classpath两个环境变量,这对于以后程序能否编译,编译成功的程序能否运行都是很关键的一个环节。
要设置环境变量,先要弹出如图1-3所示的属性窗口。这在Windows XP下可通过右击桌面中的“我的电脑”,在弹出的快捷菜单中选择“属性”命令即可;在Windows Vista下可通过“开始”菜单,右击“计算机”,在弹出的快捷菜单中选择“属性”命令,再单击左栏任务下的最后一项“高级系统设置”即弹出属性窗口。然后在属性窗口的“高级”选项卡下,单击“环境变量”按钮就会弹出“环境变量”设置窗口。path 用于设置Java编译、解释等工具的路径,这些工具文件在Java安装目录下的bin目录中。path的设置如图1-3所示。
classpath用于设置应用程序类路径和扩展的应用程序类路径,通常需要指定两项Java安装目录下的lib目录和当前运行的class所在目录(用“·”指定)。classpath的设置如图1-4所示。
4. bin目录下几个Java实用工具
在bin目录下有若干Java的工具软件,常用的几个工具软件见表1-3。 表1-3 几个Java实用工具
四、第一个Java程序的编译和运行 Java程序有两种常用的应用形式: 应用程序(Application)嵌入在HTML文件中的小程序(Applet)
本书大部分篇幅讨论的是Java应用程序的设计,Java应用程序是可以独立运行的程序,而Java Applet则不可独立运行,需要嵌入到网页文件中运行,Applet可使网页产生生动活泼的画面,使网页由静态转变为动态。
1. 第一个Java程序
下面就给出第一个Java程序,我们可以在记事本等编辑软件中编辑Java程序,编辑完毕保存成文本文件,但扩展名不能是记事本默认的“.txt”,而应是“.java”。如果给这个程序起名为Hello,那这个程序的全名则是Hello.java。
2. 编译和运行
一个Java程序编写完后先要编译才能解释运行。编译无错将生成结构中立的字节码文件。由表1-3得知javac.exe即为Java编译器,所以可以用命令javac Hello.java对以上的Hello.java文件进行编译,编译后生成字节码文件名为程序首行class后的类名,扩展名为“.class”,本例生成的字节码文件为HelloWorld.class。
之后用Java解释器java.exe对字节码文件解释即得结果。编译和运行过程如图1-6所示。
3. JCreator集成软件简介
JCreator是一款适合于Java语言编程初学者的集成软件,对JCreator进行基本设置后,即可直接完成程序编写、编译和运行Java程序的全过程。JCreator操作界面简洁,提供的基本功能也较丰富,有代码缩进、自动类库方法提示、关键字高亮显示、无限撤销等。J2SE+JCreator是Java初学者较好的选择,这可让学习者把更多精力放在Java语言本身,而非工具的使用上。JCreator操作界面如图1-7所示。
通常应先安装J2SE,后安装JCreator,这样JCreator会自动辨识J2SE文件夹,“JDK配置文件”选项下会自动配置classpath。否则需要通过“配置”→“选项”弹出“选项”对话框,后对“JDK配置文件”一栏进行设置。
如图1-8所示的“选项”对话框中,JDK配置文件已设定。假如尚未设定,或单击右侧的“删除”按钮将已有的设定内容删除,则需要重新设定。这可通过右上方的“新建”按钮完成。
当单击“新建”按钮后,会弹出如图1-9所示的“选择路径”对话框,选择已安装在机器里的JDK目录,后单击下方的“确定”按钮,即弹出如图1-10所示的“JDK配置文件”对话框,利用其上的“添加”按钮可继续加入扩展的类路径。JCreator中有些快捷键是十分好用的,它们可方便程序的编辑,成批语句行移动位置,可缩进程序中的语句,这样既符合了标准的书写格式,又让程序清晰可读;可以给语句行加上注释,在调试程序的时候十分好用。程序出错了,错在哪里需要排查才知,注释就可起到排查时的辅助作用。
4. Java程序的基本结构 下面给出第二个Java程序。
[HelloDate.java]
import java.util.*;
public class HelloDate{
public static void main(String[] args){
System.out.print("Hello, It is");
System.out.println(new Date());
}
}
程序的运行结果是:
1. Hello,It is Tue May 05 20:05:35 CST 2009
通过阅读以上两个程序,我们对Java程序建立了初步的认识。这个程序命名为HelloDate.java是随意的吗?否!那第一个程序怎么没命名成HelloWorld.java呢?原因就在“public”上。请记住“public class??”这样定义的类,必须将class后的那个类名作为文件名。
Java程序的基本组成有如下两部分: 导入包
1. import 包名;
类定义
1. class 类名{ 2. 类体 3. }
下面作进一步的说明:
1)可执行的Java程序,必有一个类中含main方法。
2)main方法有固定格式,如public static void main(String[] args),其中只有参数名args是可自定的。main前的每个修饰词都不可缺,顺序亦不可错。
3)方法和类都需用成对的花括号界定。
4)Java程序块中语法类同C++,每条语句用分号结束。
5)Java程序对大小写敏感,class后的类名由大写字母开头,变量名、对象名由小写字母开头,常用修饰词与方法名以小写字母开头。
本讲扼要介绍了Java语言的发展历程和Java程序工作原理。Java是20世纪90年代由Sun公司开发的面向对象的程序设计语言,Write once, Run anywhere的特点和JVM的工作模式是相契合的,它们给Java注入了强大的生命力。通过两个程序从编辑到运行的过程,揭示了Java程序编译和解释的运行机制,并对Java程序的基本结构建立了初步的认识。本讲只是一个Java语言基础的导引,要编写出自己的Java程序,必须学习Java中的数据类型和运算,学习程序的流程控制。
五、思考与练习
1. Write once, Run anywhere描述的是Java语言的什么特性? 2. 任何计算机都可以直接运行Java程序吗?
3. Java程序可以直接编译成适用于本地计算机的机器码吗? 4. 不同的操作系统上可下载同样的Java VM版本安装吗? 5. 从哪里下载J2SE?
6. 安装好J2SE后,安装目录为D:\JDK1.6,请问如何设置环境变量? 7. classpath的设置意义是什么?
8. Java程序文件和Java字节码文件的扩展名分别是什么? 9. JCreator与JavaSE是一种怎样的关系? 10. 在JCreator中如何编译和运行Java 程序? 11. Java程序中的类名通常由大写还是小写字母开头? 12. Java中的常用修饰词和变量通常由大写还是小写字母开头? 13. 编写一个Java程序,显示输出你的邮箱和电话号码。 第二讲 Java中的数据类型和运算
任何语言都需要操纵和处理数据,数据是有类型的,不同的数据类型有不同的运算方式。本讲主要讨论Java语言的数据和运算,主要内容有:
Java输入/输出的初步运用 Java的标识符和保留字 Java的基本数据类型 Java基本数据类型转换 Java中的运算
五、基础
一、Java输入/输出的初步运用大多数程序都要输出数据,有一部分程序需要在运行时输入数据。
如这样一个问题:读取一个整数,输出该数的平方。
这应该是一个很简单的问题吧?它需要用户先输入一个数,程序输出该数平方的结果。本节专门针对上面这个问题。一通百通,由此及彼。把它解决了,我们就了解了Java的输入/输出方法,然后能着手写出实用的程序。
1. 问题解析
问题复述:读取一个整数,输出该数的平方。 Java可将该问题分解成如下几个步骤: 创建一个来自标准输入设备的扫描器(①); 从扫描器上读取一个整数(②); 做求平方运算,自乘一次即可; 将运算结果向标准输出设备输出(③)。
其中,①②③皆是存疑的标识,通过下面的程序对照来步步解惑。 2. 程序解决
按如上步骤,对问题编程解决。
1. [Square.java]
2. import java.util.Scanner; 3. public class Square {
4. public static void main(String[] args){
5. Scanner sc = new Scanner(System.in); //① 6. int n = sc.nextInt(); //② 7. int s = n * n;
8. System.out.println(s); //③ 9. } 10. }
程序的输出结果是:
1. 12(用户交互输入) 2. 144(显示输出)
从main方法开始读起,语句后标示的数字符号注释即是对问题解析中存疑的解答。解①:扫描器的类型是Scanner,来自标准输入设备(默认键盘)的扫描器可由new Scanner(System.in)创建。
解②:可通过扫描器的规定方法获取数据,如用nextInt方法取得整数,用nextDouble方法取得双精度实数,类似的方法有很多,可取得各种不同类型的数据。
解③:输出数据的方法是System.out.println(……),其中括号中的省略号为输出内容,输出内容可由字符串与数值数据多项组成,项与项之间直接用“+”连接即成。
依照上一讲的说明,该程序的第二行中,class前加了public一词,所以程序文件必须命名为Square.java。
3. 程序解析扩展
在前面对程序作了基本的解析,但这些解析还不能令你透彻地理解程序。要透彻地理解,还需要掌握一些面向对象的基本知识,这里只作蜻蜓点水式的说明:
1)Scanner是一个类,它在java.util包中,所以本程序的首行为“import java.util. Scanner;”,意为载入此类;
2)要创建一个扫描器,需要给Scanner指出扫描源,如new Scanner(System.in); 3)Scanner扫描器拥有很多扫描输入数据的方法,如nextInt()即是其中之一; 4)System为系统类,它在java.lang包中,该包在程序编译时自动载入,故不用在程序开头加对应的载入语句;
5)System.out为数据标准输出流,该流拥有print和println方法,print方法的意义是不换行输出,println是换行输出。
二、Java的标识符和保留字
1. 标识符
Java程序中的类、属性、方法、对象、变量等元素都应有自己的名称,各元素的名称通称为标识符。
Java标识符定义规则:由字母、数字、_和$组成;开头字符必须是字母、下画线或$。标识符定义采用三原则:见名知义、规范大小写、不可用Java保留字。
例如,从以下字符串中挑出合法的Java标识符。
1. 2tow Mary5 else Hello world
2. bob do cheese_eat Hello!
合法的标识符:Mary5、bob、cheese_eat。非法标识符:2tow(开头字符不可为数字)、Hello world(标识符中不可有空格)、Hello!(!不可作为标识符的组成字符)、else和do(皆为Java保留字)。
合法的标识符是符合定义规则的字符串,不符合定义规则的字符串则是非法标识符。另外,标识符对字母大小写敏感,如myname与Myname是两个不同的标识符。
2. 保留字
保留字(reserved word),是指Java语言规定了特定含义的标识符。对于保留字,用户只能按照系统规定的方式使用,不能自行定义。
数据类型相关的保留字:boolean、int、long、short、byte、float、double、char、class和interface。
流程控制相关的保留字:if、else、do、while、for、switch、case、default、break、continue、return、try、catch和finally。
修饰符相关的保留字:public、protected、private、final、void、static、strictfp、abstract、transient、synchronized、volatile和native。
动作相关的保留字:package、import、throw、throws、extends、implements、this、super、instanceof和new。
其他保留字:true、false、null、goto和const。 需注意,保留字一律用小写字母表示。 三、Java的基本数据类型(1)
在Java中,每个存放数据的变量都是有类型的,如:
1. char ch; 2. float x; 3. int a,b,c;
ch是字符型的,就会分配到2个字节内存。不同类型的变量在内存中分配的字节数不同,同时存储方式也是不同的。所以给变量赋值前需要先确定变量的类型,确定了变量的类型,即确定了数据需分配内存空间的大小,数据在内存的存储方式。
1. Java中的数据类型
Java中的数据类型有基本数据类型和引用数据类型两大类,图2-1呈现了Java中数据类型的概貌。本讲就基本数据类型进行讨论,引用数据类型将在下一篇中论述。
以下讨论每一种数据类型时,对常量和变量作分别说明。所谓常量,就是直接的值;而变量则是放置常量的容器,除了放置常量之外,也可以给变量一个运算式,变量中的值是可变的。
2. 布尔型—boolean
布尔型又名逻辑型,它是最简单的数据类型,在流程控制时常会用到。有C++编程经验的学习者,要特别看清,Java中的布尔型数据不对应于任何整数值。
布尔型常量:true和false。需要注意的是,布尔常量的组成字母一律都是小写的。 布尔型变量:以boolean定义的变量,如:
1. boolean b = true; //定义变量b是boolean,且值为true
3. 字符类型—char (1)字符常量
字符常量指用单引号括起来的单个字符,如‘a’,‘A’。
请特别注意,字符的定界符是单引号,而非双引号。除了以上所述形式的字符常量值之外,Java还允许使用一种特殊形式的字符常量值,这通常用于表示难以用一般字符来表示的字符,这种特殊形式的字符是以一个“\”开头的字符序列,称为转义字符。Java中的常用转义字符见表2-1。
表2-1 Java中的转义字符(2)字符变量
以char定义的变量,如char c='a';
要特别加以说明的是,Java的文本编码采用Unicode集,Java字符16位无符号型数据,一个字符变量在内存中占2个字节。
例1:编程测试十六进制数41、51对应的字符,并相隔一个tab位输出。
分析:已知十六进制数,求字符。根据表2-1,可用'\uxxxx'的转义字符形式来表示所求字符,然后直接输出即可。 1.
[HDTest.java]
class HDTest{
public static void main(String[] args){
char a='\u0041';
char b='\u0051';
System.out.println(a+" "+b);
//字符之间以若干空格相间
}
}
程序运行结果:A Q
三、Java的基本数据类型(2)
4. 定点类型(整型)
定点类型包括了字节型、整型、短整型和长整型,它们在内存中虽然占据的字节数互不相同,但它们的存储方式是同样的,所以这里把这些类型归并在一起讨论。“定点”的意思是把小数点定在末尾,小数点后没有数字的数据,Java中通常把它们称为整数。
(1)定点常量
定点常量是整型常数,它可用十进制、八进制、十六种进制三种方式来表示。 十进制定点常量:如123、-456、0。
八进制定点常量:以0前导,形式为0dd……d。如0123表示十进制数83,-011表示十进制数-9。
十六进制定点常量:以0x或0X开头,如0x123表示十进制数291,-0X12表示十进制数-18。
(2)定点变量
定点变量即整型变量,可细分成字节型变量、整型变量、短整型变量和长整型变量四种。表2-2对各种定点变量的开销内存字节数和数值范围作简要说明。
表2-2 关于整型变量的说明
需要注意的是,如果要将一定点常量赋值给一个定点变量,需要查验常量是否在该变量的表达范围内,如超出范围程序会编译出错。
如:
1. byte b = 200; //JCreator编译时错误信息是“可能损失精度”
例2:阅读程序,分析其运行结果。
1. [OHTest.java]
2. class OHTest{
3. public static void main(String[] args){ 4. int x = 010;
5. System.out.println("x = "+ x); 6. int y = 0x10;
7. System.out.println("y = " + y);8. } 9. }
程序运行结果略,请思考并调试验证。 5. 浮点型(实型) (1)浮点常量
即带小数点的实型数值,可以由直接带小数点的数值和科学计数法两种形式来表示: 带小数点的数值形式:由数字和小数点组成,如0.123、.123、123.、123.0。 科学计数法表示形式:由一般实数和e±n(E±n)组成,如12.3e3、5E-3,它们分别表示12.3×103和5×10-3。需要注意的是,e或E之前必须有数字,且e或E后面的指数必须为整数。
(2)浮点变量
浮点变量有单精度变量和双精度变量之分,不同的精度开销的内存字节数和表达的数值范围均有区别。两种浮点变量占内存字节数和数值范围见表2-3。
表2-3 单精度变量和双精度变量简要说明
浮点常量也有单精度和双精度之分,前面列出的常量均是双精度常量,如果要特别说明为单精度常量,可以数据末尾加上f或F作为后缀,如12.34f。如果要特别指明一个浮点常量是双精度常量,数据末尾不需要添加后缀,或者在数据末尾加上d或D作为后缀,如12.34d。
例3:输入一个圆的半径,求圆面积。
分析:圆半径在编写程序时是不确定的,其值在程序运行时输入,这样程序就比较通用,可以处理不同半径的求面积和周长问题。这就涉及数据输入的问题,参照本讲第一节,输入数据通过Scanner扫描器解决。
1. [CircleArea.java]
2. import java.util.Scanner; 3. class CircleArea{
4. public static void main(String[] args){5. Scanner sc = new Scanner(System.in); 6. double r = sc.nextDouble(); 7. double area = Math.PI * r * r; 8. System.out.println(area); 9. } 10. }
四、Java基本数据类型转换
除了布尔型数据外,其他定点类型的数据和浮点类型的数据可以混合在一起进行运算,不同类型数据运算的结果是何种类型的呢?数据可由一种类型转换成另一种类型吗?在这一讲里这些疑问都将得到解决。
1. 自动类型转换
定点和浮点类型都可看作是数值型数据,当不同的数值型数据运算时,有一个内在的规则:先统一为同一类型,再进行运算。结果就为运算前统一的类型。这里的“统一”就是自动类型转换。
自动类型数据转换总是从低级到高级进行,byte、char、short的级别最低,double的级别最高,具体如图2-2所示。
byte、char、short型数据只要参与运算,就首先将类型转为int后进行运算,所以即使short型数据之间的运算,运算结果也是int型。如果int型数据与float型数据运算,int型数据会自动转换成float型,运算结果也为float型。
例4:如有以下变量定义,请回答算式:x + y、 x + y + z、 a + b、 a - x - y、x + c的结果分别是何种类型。
1. int x; 2. byte y; 3. long z; 4. float a; 5. double b; 6. char c;分析:x + y的结果应为int型,x + y + z的结果为long型,a + b的结果为double型,a - x - y的结果为float型,x + c的结果为int型。
2. 强制类型转换
不同类型数据之间的运算和赋值时,低级别类型的数据可以自动转换成高级别类型的数据。欲让高级数据转换成低级数据,需用到强制类型转换,其形式是:
(类型)数据
数据为需要类型转换的常量、变量或表达式。括号中的类型为数据要转换成的类型。 如:int x = 3.5; //编译出错,高级别的数据不能给低级别的变量赋值 int x = (int)3.5; //将3.5强制转换成int型数据,后给同级别的变量x赋值
例5:阅读程序Char_Int1.java、Char_Int2.java和Float_Int.java,分析它们的运行情况。
1. [Char_Int1.java]
2. class Char_Int1{
3. public static void main(String[] args){ 4. char y = 'a';
5. System.out.println( y ); 6. } 7. }
分析:程序输出结果为a,本程序无数据运算。
1. [Char_Int2.java]
2. class Char_Int2{
3. public static void main(String[] args){ 4. int x = 2; 5. char y = 'a';
6. System.out.println(y + x); 7. } 8. }
分析:程序中有x + y的运算,如图2-2所示,char型首先转换成int型,即字符对应的ASCII码值。而字符a对应的ASCII码为97,故程序结果为99。
思考:如何修改程序使之输出结果为字符c?
1. [Float_Int.java]2. class Float_Int{
3. public static void main(String[] args){ 4. int x=(int)5.2f; 5. float y = x;
6. System.out.println(x + " " + y); 7. } 8. }
程序运行结果:5 5.0 五、Java中的运算(1)
数据的运算通过运算符实现,Java语言提供了丰富的运算符,在本讲中将讨论算术运算、赋值运算、条件运算、关系运算和逻辑运算,以及各种运算符的优先级别。
1. 算术运算
算术运算由算术运算符完成,可以实现最基本的数值运算。算术运算符可分为双目和单目两类,双目运算符需要两个操作数参与,有加、减、乘、除、取余五种,见表2-4。
表2-4 双目运算符
表2-4中的op1与op2是两个操作数,它们可以是任意数值型的数据。特别要说明的是Java中的“%”运算与C++有别,它可对浮点数进行,符号同第一个操作数。如52.3%10的结果是2.3,-12%-5的结果是2。
此外,Java对符号“+”运算进行了扩展,它不仅可以对数值型数据进行加法运算,还可以进行字符串之间、数字与字符串之间的拼接。如System.out.println(“s= “ + 4*5);语句将输出s = 20的结果,括号中项与项之间的“+”就起了拼接的作用。
相对双目运算符,还有单目运算符,见表2-5,它们只对一个操作数进行运算。 表2-5 单目运算符表2-5中的op是一个操作数,对于“前缀+”和“前缀-”,op可以是任意数值型的常量、变量和表达式。如果是常量应是无符号的,如-3,如果带符号则该常量应带上小括号,如-(-3),结果负负得正,为3。
表2-5中的op对于前后缀的“++”和“--”,就不能是常量和表达式了,只能是变量。5++与++(a+b)均是错误用法。前后缀的“++”和“--”通常也称为自加、自减。在使用时另外还要注意如下几点:
++与--运算时结合方向自右向左,-i++相当于-(i++); 两个+号(或-号)之间不能有空格; 应避免使用复杂的表达式。
算术表达式即是用算术运算符与括号将运算对象连接起来的、符合Java规则的式子。就算术运算符之间,运算级别以“先*、/、%,后+、-,左结合”的原则进行,如n*-2、-n/-2。
例6:阅读程序,分析程序运行结果。
1. [TestMod.java]
2. class TestMod{
3. public static void main(String[] args){ 4. int x = 3, y = 10;
5. System.out.println(y-- % ++x); 6. System.out.println(-y % -x); 7. } 8. }
分析:x和y值的变化如程序右侧表2-6所示,第一行输出时,y取用10进行模运算,x先自加为4,结果为2;y接着自减到9,进行第二行输出,结果为-1。2. 赋值运算
将数据保存到变量中,这个过程就是“赋值”,所以赋值运算也是最基本的运算,赋值运算符号即为通常的等于符号“=”。Java中的赋值运算可分为基本赋值与复合赋值两种。
(1)基本赋值 基本赋值运算形式:
1. <变量> = <表达式>
其作用是将“=”右边表达式的值赋给左边的变量。表达式也可以是直接的常量或变量。该式加上分号即为赋值语句。
特别要说明的一点是,“=”右边的表达式的级别不能高于变量所定义类型的级别(数值型的级别如图2-2所示),否则要有强制转换的说明。而如果表达式类型与变量类型是不可互转的,这样的赋值运算在编译时将出错。
例7:阅读程序,理解赋值运算。
1. [AssignTest.java]
2. class AssignTest{
3. public static void main(String[] args){ 4. int a,b;
5. int c = (a = 5) + ( b = 13);
6. System.out.println(a + " " + b + " " + c); 7. } 8. }
程序运行结果为:5 13 18 五、Java中的运算(2) (2)复合赋值
复合赋值意味着的是运算且赋值,其形式:
1. <变量><简单运算符> = <表达式>
表2-7通过示例对复合赋值进行了简要说明。表2-7 复合赋值示例说明
复合赋值化解成基本赋值式为: <变量> = <变量><简单运算符>(<表达式>),表达式加上括号意指优先运算。
3. 关系运算
关系运算亦即比较运算。既然是比较就需要两个操作数参与。Java提供的关系运算符及其示例说明见表2-8。
表2-8 关系运算符及其说明
由表2-8知关系运算式的通常形式是:
1. op1 <关系运算符> op2
表中的op1与op2是两个可以比较的操作数,它们可以是基本数据类型的,也可以是复合类型的。目前暂只讨论前者。下面有几点说明在编程时需要注意:
“==”不要写成“=”;
任何类型的两个数据都可以通过“==”或“!=”来比较是否相等; 浮点数的比较小心慎用;
关系运算的结果是true和false,而非1或0;关系运算符优先级低于算术运算符优先级,高于赋值运算符优先级。 4. 逻辑运算
逻辑运算可以表达复合的条件,逻辑运算可以对结果是布尔型的数据进行复合或否定。逻辑运算符只有三个,关于它们的说明见表2-9。
表2-9 逻辑运算符及其说明
表2-9中的op1、op2、op皆是布尔型数据。与其他表达式不同的一点是,逻辑运算表达式中,不是每一个逻辑运算符都一定进行运算。在“&&”与运算时,只要前一个操作数的结果是false,结果就已确定为false,后面的运算就被忽略;在“||”或运算时,只要前一个操作数的结果是true,结果也就确定为true,后面的运算同样被忽略,这称为逻辑运算的截断性。
例8:阅读程序,理解逻辑运算的截断性。 [CutOffTest.java]
1. class CutOffTest {
2. public static void main(String[] args){ 3. int f = 0;
4. if (f!=0 && 3/f>5)
5. System.out.println("true"); 6. else
7. System.out.println("false"); 8. } 9. }
分析:if语句中的3/f>5的运算会发生除0溢出错误,因为f!=0的结果是false,后面所遇又是“&&”,根据逻辑运算的截断性,其后的表达式被忽略,所以程序可正常运行得到结果,结果为false。
加入逻辑运算符后,优先级别排序是:非运算(!)、算术运算、关系运算、逻辑与(&&)、逻辑或(||)、赋值(=)。
5. 条件运算条件运算可根据条件取值,运算符号由间隔的双字符组成“? :”。条件运算式形式是:
1. e1 ? e2 : e3
式中的e1为条件,应是布尔型数据,e2和e3可以是任意类型、但必须类型一致的数据。若条件e1为真,则条件运算结果为e2的值;若条件e1为假,则条件运算结果为e3的值。下面语句可以求a和b中的较大数,并赋值给变量max:
1. max = a > b ? a : b;
6. Java运算符的优先级
当运算趋于复杂时,表达式中可能会出现多种运算符,只有理解了Java运算符的优先级,才能运用得当,所得即所想。
Java运算符的优先级见表2-10。 表2-10 Java运算符的优先级
本讲属于Java程序设计基础的部分,介绍了Java语言标准设备的数据输入/输出方法、Java中的数据和运算。继续学习下一讲的程序流程控制,你就将初步具备程序设计的能力。
六、思考与练习
1. 如何在程序运行时交互输入数据? 2. 一个数字组成的字符串如何转化成整数? 3. Java程序如何向字符窗口输出数据? 4. 在Java中一个字符数据开销字节数是多少? 5. 在Java中怎样给出一个八进制整常数?6. 在Java中怎样给出一个十六进制整常数? 7. 在Java中如何输出数据?
8. Java表达整型的数据类型与C相比异同点是什么? 9. 如何给出一个单精度常数?
10. 字符常量和字符串的定界符分别是什么? 11. 在Java中一个整型数据占字节数是多少? 12. 在Java中用什么类型的变量存储一个逻辑值? 13. 阅读程序,分析程序运行情况。 (1)
1. class Test1{
2. public static void main(String[] args){ 3. char a = '\u0041'; 4. int b = a; 5. float c = b; 6. double d = c;
7. System.out.println(a + "\t" + b + "\t" + c + "\t" + d); 8. } 9. }
(2)
1. class Test2{
2. public static void main(String[] args){ 3. double e = 65.5; 4. float f = e; 5. int g = f; 6. char h = g;
7. System.out.println(e+ "\t"+ f+ "\t"+ g+ "\t"+ h); 8. } 9. }
(3)
1. class Test3{
2. public static void main(String[] args){ 3. System.out.println(10 % 3);4. System.out.println(10 % -3); 5. System.out.println(-10 % 3); 6. System.out.println(-10 % -3); 7. } 8. }
(4)
1. class Test4{
2. public static void main(String[] args){ 3. char x = '4'; 4. int y = 4;
5. if (x==y)System.out.println(y); 6. else System.out.println((char)y); 7. } 8. }
14. 输入两个整数,求平均数。
七、Java结构化编程
只有少数问题,诸如上一讲的“读取整数求平方”,可以通过若干语句的顺序执行得到结果。大多数任务需要运用到分支和循环才得以解决。本讲主要讨论Java程序的结构化编程和流程控制,主要内容有:
分支结构(if, switch)
循环结构(while, do-while, for) 循环嵌套和循环控制(break, continue) Java结构化编程综合 一、分支结构(1)
如果有这样一个问题“交互输入一个整数,若为正数求平方,若为负数求立方”,顺序执行的语句就无法解决了,此时要通过分支结构的程序来求解。
Java像C++一样,可以通过if语句和switch语句来建立分支结构的程序。相对来说,if语句更常用一些,因为if可表达所有的条件分支情况,而switch语句也有着特别的妙处,在某些情况下,可以令程序更加简练清晰。
1. if语句if语句是最基本的分支语句。它通过条件的直接判断,转向不同的程序流程,以此实现流程控制。
(1)if语句的最简形式
1. if ( 条件 ) 2. 语句块
条件为boolean数据,语句块指一组语句,若语句块中的语句多于一条,则在头尾需要用成对的大括号标识。图3-1解析了该语句的执行流程。当条件为true时,执行语句块,条件为false时,则跳过语句块。
这两条语句,保证max变量总是取得a和b中的较大数。 (2)if语句的基本形式
1. if ( 条件 ) 2. 语句块1 3. else 4. 语句块2
图3-2解析了if-else语句的执行流程。当条件为true时,执行语句块1,条件为false时,执行语句块2。只要执行到此if语句,总有一个语句块被执行。这是一个兼有输入和输出的问题,输入同样用Scanner扫描器解决,通过nextDouble()方法取得扫描到的double型数据。然后通过分支求y值。注意程序中所涉变量必须在使用之前定义类型。
例2:闰年计算规律为:“四年一闰,百年不闰,四百年再闰”。输入一个年份,判断该年份是否为闰年。分析:能被4整除的年份通常是闰年,而如果该年份还能被100整除,则存疑了。需要继续考察该年份能否被400整除,若能则是,否则非闰年。这样闰年就有两种情况:直接能被400整除的年份、被4整除而不能被100整除的年份。
[TestIf2.java]
1. import java.util.Scanner; 2. class TestIf2{
3. public static void main(String[] args){ 4. Scanner sc = new Scanner(System.in); 5. System.out.print("Year? "); 6. int year = sc.nextInt();
7. if( year % 400 == 0 || year % 4 == 0 && year % 100 != 0) 8. System.out.println(year+" is a leap year."); 9. else
10. System.out.println(year+" is't a leap year."); 11. } 12. }
一、分支结构(2)
如果遇到比以上示例复杂的问题,如y是一个三分支的函数,对x的取值范围需要考虑三种或更多的情况,此时if-else语句仍可以解决,那就是在语句块中继续嵌套if-else语句,形式如:
1. if ( 条件1 ) 2. if ( 条件2 ) 3. 语句块1 4. else 5. 语句块2 6. else 7. 语句块3
这样的嵌套理论可行,但程序的清晰性减弱,特别是嵌套层数在两层以上,就很容易出错。Java提供了if-else的扩展形式,可以自如应对多分支的问题。
(3)if语句的扩展形式
1. if ( 条件1 ) 2. 语句块1 3. else if ( 条件2 ) 4. 语句块25. ……
6. else if ( 条件n ) 7. 语句块n 8. [else
9. else语句块]
其中,末尾中括号的else部分是可选的。如果无可选的else部分,而前面的各条件又都为false时,程序流程流经这条if语句,什么也不执行;否则,不管前面的条件怎样苛刻,总有一个、当然也只有一个语句块执行。流程解读如图3-3所示。
例3:某企业工龄工资调整,工龄不到二年者不参与调整,其余员工中,工龄少于五年者按每年50元计算,工龄达五年少于十年者每年按65元计算,工龄达十年者每年按80元计算,工龄达十五年者每年按100元计算。此外,对达十二年以上者,额外增加200元。
分析:本问题是一个五分支的处理,五种情况分别是:不到二年、过二年不到五年、达五年不到十年、达十年不到十五年、达十五年。“此外”部分放在最后考虑。
[TestIf3.java]
1. import java.util.Scanner; 2. class TestIf3{
3. public static void main(String[] args){ 4. Scanner sc = new Scanner(System.in); 5. System.out.print("工龄? "); 6. int year = sc.nextInt();7. int num;
8. if( year<2 ) num = 0;
9. else if( year<5 ) num = year * 50; 10. else if( year<10 ) num = year * 65; 11. else if( year<15 ) num = year * 80; 12. else num = year * 80; 13. if( year>=12 ) num += 200;
14. System.out.println(year + "\t" + num); 15. } 16. }
2. switch语句
switch是用基于整型数据的多分支判断。 switch语句形式:
1. switch ( 表达式 ) { 2. case 值1: 语句组1; 3. case 值2: 语句组2; 4. ……
5. case 值n: 语句组n; 6. [default: default语句组;] 7. }
语句组指一组语句,与语句块不同的是头尾不需要用大括号括出。
switch中的表达式结果必须是一个整型值,该值用来与后续的值1、值2、??、值n比较,如果值是相同的,则执行case后的语句组。之后不再判断,连续地执行下去,直到遇到break语句或switch运行完毕。switch表达式值与值1匹配的极端情况,将使所有的语句组都顺序执行。具体流程如图3-4所示。所以break语句对于switch语句是相当重要的,switch如好脾气的、不知拒绝的主人,而break就是当机立断的管家,流程至break就终止switch语句的执行,转到switch后的语句。
例4 : 输入一百分制成绩, 输出对应的等级。90~100分的等级为‘A’,80~89分的等级为‘B’,70~79分的等级为‘C’,60~69分的等级为‘D’,60分以下的等级为‘E’。
1. [TestSwitch.java]
2. import java.util.Scanner; 3. class TestSwitch{
4. public static void main(String[] args){ 5. Scanner sc = new Scanner(System.in); 6. System.out.print("Score ? "); 7. int scscore = sc.nextInt(); 8. char grade; 9. switch(score/10){
10. case 9: case 10: grade = 'A'; break; 11. case 8: grade = 'B'; break; 12. case 7: grade = 'C'; break; 13. case 6: grade = 'D'; break; 14. default:grade = 'E'; 15. }
16. System.out.println(score+"\t"+grade);17. } 18. }
请思考一下,如果程序中无break语句,运行结果会是怎样? 二、循环结构
程序经常需要重复一些操作,如生成一个有规律可循的数列、自然数累加等问题。循环结构正好用来解决这样的问题。在Java中,有while、do-while、for三条语句可用来设计循环结构的程序。
1. while语句
while语句是条件循环语句,根据条件是否成立,确定循环是否进行。
1. while循环语句形式: 2.
3. while ( 条件 ) 4. 语句块
在循环体执行前先判断条件,只要条件成立,循环就继续。语句块即是重复的部分,称为循环体。语句块执行完毕再回到条件判断,直到条件不成立,流程转到下一语句。流程解析如图3-5所示。
例5:求前100个自然数的和。
分析:本题即求1+2+?+100,要设一个累加变量sum放置结果,在累加前需给sum设置初值,以后需不断在sum中加入递增的数,将此加数变量设为num,num的初值应为1,变化规则是每次递增1,当num超过100时,sum中的值即为所求结果。[TestWhile.java]
1. class TestWhile{
2. public static void main(String[] args){ 3. int sum = 0, num = 1; 4. while(num<=100){ 5. sum += num; 6. num ++; 7. }
8. System.out.println("sum: " + sum); 9. } 10. }
由此及彼,许多累加的问题都可仿照完成。
设计一个循环时,要考虑到循环的终点,而循环体中应该可以不断地修正循环条件,让循环条件由开始的成立向不成立转化,此时循环便结束。不设置终点的循环会永无穷尽,即陷入死循环。
2. do-while语句
do-while循环与while循环很相近,区别只在于先判断还是先执行。 do-while循环语句形式:
1. do
2. 语句块 3. while ( 条件 );
do-while语句流程解析如图3-6所示。大多数情况下while循环与do-while循环会得到一致的结果。惟一可能产生差异的是循环条件特别苛刻,第一次就不成立,此时while循环什么都不做,而do-while循环的循环体却会执行一次。例6:用do-while语句求前100个自然数的和。
1. [TestDoWhile.java]
2. class TestDoWhile{
3. public static void main(String[] args){ 4. int sum=0,num=1; 5. do{
6. sum += num; 7. num ++;
8. }while(num<=100); //while后的分号不可缺 9. System.out.println("sum: " + sum); 10. } 11. }
3. for语句
for循环是一种计数循环,可以设置循环变量,用来遍历某个范围的值。 for循环语句形式:
1. for ( 语句块1; 条件; 语句块2) 2. 语句块3
for语句流程解析如图3-7所示。for中有三项,它们分别用分号相间,其中语句块1只在循环起始执行了一次,而没参加具体的循环。语句块3是循环体,而语句块2是每次循环完毕执行的部分。for语句同while一样也是先判断后循环的语句,可用while来改写如下:
1. 语句块1;
2. while ( 条件 ) {当然这样的改写只是帮助对for语句的理解,相对来说,for语句还是更简明和更有效率的。
例7:用for语句求前100个自然数的和。
1. [TestFor.java]
2. class TestFor{
3. public static void main(String[] args){ 4. int sum,num;
5. for(sum=0, num=1; num<=100; num++) 6. sum += num;
7. System.out.println("sum: "+sum); 8. } 9. }
语句块1可用来初始化一个或多个变量,如果是多个变量取不同的初值,可在赋值表达式间以逗号相间。语句块1、条件、语句块2三项是可选的,最简的for语句形式是for(;;),这就相当于while(true),它们设定了一个永真循环,很有死循环的嫌疑。下一节的学习将澄清迷雾,我们会认识到除了循环条件外,在循环体内也可以设置中途退出的机制。
三、循环控制和循环嵌套循环控制是除了循环条件之外,控制循环是否进行的一个机制,这给处理循环问题带来了灵活性。循环体内的语句块可以是顺序执行的语句,可以是分支结构的语句,也可以是循环语句,循环中含循环,就是循环嵌套。
1. 循环控制
循环控制由break语句和continue语句实现。 (1)break语句
break语句很利落简单,语句形式: break;
break语句除了适用于switch外,也适用于for、while以及do-while三种循环语句,其作用是结束循环,程序流程至这些语句后的第一句。通常break语句配合条件语句一起使用。
例8:求1+2+3 +?+ N < 1000的N的最大数。
分析:这是一个自然数累加计算加判断的问题,加多少项是预先不知的,或者正是所求的,所以不管是用哪个循环语句来处理,条件的地方均无法明确给出。这就需要在循环体中边加边判断,如果结果有了,中途退出循环。
[TestBreak.java]
1. class TestBreak{
2. public static void main(String[] args){ 3. int sum = 0, num = 1; 4. while( true ){ 5. sum += num;
6. if( sum>1000 ) break; 7. num ++ ; 8. }
9. System.out.println("N: " + (num-1)); 10. } 11. }
(2)Continue语句 continue语句形式: continue;continue语句,也称短路语句。它适用于for、while以及do-while语句中,其作用是忽略其后的语句,中止当前循环,流程转至循环起始处,开始下一次循环。
例9:阅读程序,分析程序运行结果。
1. [TestContinue.java] 2.
3. class TestContinue{
4. public static void main(String[] args){ 5. for(int m = 1; m <= 10; m ++){ 6. if( m%2 == 0 ) continue; 7. System.out.print(m + "\t"); 8. } 9. } 10. }
2. 循环嵌套
在解决复杂一些的问题时,需要在循环里继续设置循环语句,这就是循环的嵌套。外层的循环称为外循环,内层的循环称为内循环。如输出二维矩阵的问题,用循环嵌套就很容易解决。
例10:阅读程序,分析程序运行流程。
1. [Matrix.java] 2.
3. class Matrix {
4. public static void main( String[] args ){ 5. for( int i=1; i<=3; i++ ) { 6. for ( int j=1; j<=5; j++)
7. System.out.print(i*j + "\t"); //内
循环体
8. System.out.println(); //换行 9. } 10. } 11. }
程序分析:i是外循环变量,当i取得一个值,循环体(斜体部分的代码)执行一次。其中有两条语句,一条是for循环语句(内循环),另一条是换行语句。当循环体执行时,即内循环变量j从初值到终值遍历一次,进行输出。i增值换下一个时,先换行。
本程序的输出结果是:1. 1 2 3 4 5 2. 2 4 6 8 10 3. 3 6 9 12 15
例11:输出一个三角形形式的九九乘法表。
1. 1×11=1 2. 2×1=2 2×2=4
3. 3×1=3 3×2=6 3×3=9
4. 4×1=4 4×2=8 4×3=12 4×4=16
5. 5×1=5 5×2=10 5×3=15 5×4=20 5×5=25
6. 6×1=6 6×2=12 6×3=18 6×4=24 6×5=30 6×6=36 7. 7×1=7 7×2=14 7×3=21 7×4=28 7×5=35 7×6=42
7×7=49
8. 8×1=8 8×2=16 8×3=24 8×4=32 8×5=40 8×6=48
8×7=56 8×8=64
9. 9×1=9 9×2=18 9×3=27 9×4=36 9×5=45 9×6=54
9×7=63 9×8=72 9×9=81
分析:这与前面矩阵输出的问题有些相像,同样也是输出多行的问题。乘法表有九行,可用循环变量i来记录行数(1~9行),第1行,有1个乘法算式;第2行,有2个乘法算式;第i行便有i个乘法算式。对于确定的第i行,如何来输出这i个算式呢?这又是一个重复处理的问题,可用内循环来解决。内循环变量设为j,j的变化从1到i。
[MutiTable.java]
1. class MutiTable{
2. public static void main( String[] args ){ 3. for( int i=1; i<=9; i++ ) { 4. for ( int j=1; j<=i; j++)
5. System.out.print( i + "*" +j+ "=" + i*j + "\t"
);
6. System.out.println(); 7. } 8. } 9. }
以上程序巧妙的是,循环变量i和j正巧是每个乘法算式的被乘数和乘数。 四、Java结构化编程综合
本节通过程序设计实例,来进一步掌握Java结构化编程方法。例12:输入一月份,求该月的天数。
分析:不同的月份有不同的天数,这需要判断后才能确定。把月份归为三类,30天的月份有4、6、9、11月份,28或29天(由年份再确定)的月份是2月份,其余皆为31天。这是一个多分支判断的问题,可用if或switch语句解决,根据月份判断天数部分用switch语句代码更简捷。
[Monthdays.java]
1. import java.util.Scanner; 2. class Monthdays{
3. public static void main(String[] args){ 4. int month,days;
5. Scanner sc = new Scanner(System.in); 6. System.out.print("Month? "); 7. month = sc.nextInt();
8. if(month<1 || month>12){ //若输入数据非月份数 9. System.out.println("error data");
10. System.exit(0); //退出运行 11. }
12. switch(month){
13. case 4: case 6: case 9: case 11: days = 30;break; 14. case 2: //2月份需要输入年份进一步判断 15. System.out.print("Year? "); 16. int year = sc.nextInt();
17. if(year%400==0 || year%4==0 && year%100!=0) 18. days = 29; 19. else days = 28; 20. break;
21. default: days = 31; 22. }
23. System.out.println("days: "+days); 24. } 25. }
程序运行中途要强制退出,可以用“System.exit(0);”语句。本例中若输入的年份数据是不合理的数据,程序输出error data后,便退出结束运行。
例13:输入一个自然数,判断该数是否为素数。分析:先确认什么样的数为素数。根据数学定义知,一个自然数除了1和本身之外没有其他的因子,该数便为素数。2专门规定为素数,且是最小的素数。对于自然数n,我们只要用2到n-1去测试,如果它们中没有一个是n的因子,n就为素数,否则n不是素数。
[PrimeTest.java]
1. import java.util.Scanner; 2. class PrimeTest{
3. public static void main(String[] args){ 4. Scanner sc = new Scanner(System.in); 5. System.out.print("number? ");
6. int n = sc.nextInt(); //n为被测数 7. int i; //i为1和本身外可能的因子 8. for(i=2;i<n;i++)
9. if(n%i == 0)break; //i是n的因子,n非素数 10. if(i == n) //所有可能的因子全非n的因子 11. System.out.println( n + " is a prime number." ); 12. else
13. System.out.println(n + " is not a prime number"); 14. } 15. }
例14:求所有三位素数。
分析:本题是上题的拓展。不是判断某一个数,而是判断所有三位数。这就需要用一个循环变量遍历三位数,因为所有的三位偶数都非素数,所以可直接遍历三位奇数。对于确定的三位数判断其是否为素数的方法同上。
[PrimeNumbers.java]
1. class PrimeNumbers{
2. public static void main(String[] args){ 3. int i,n;
4. for( n = 101; n<1000; n+=2 ){ //n遍历所有三位奇数 5. for( i=2; i<n; i++) 6. if( n%i == 0 )break;
7. if( i==n )System.out.print( n + "\t"); 8. } 9. } 10. }
程序运行结果:1. 101 103 107 109 113 127 131
137 139 149
2. 151 157 163 167 173 179 181
191 193 197 3. …
4. 983 991 997
例15:输出如下菲波那契数列的前20项,并求其和。
1. 1,1,2,3,5,8,13……
分析:菲波那契数列的前两项均为1,以后的每一项都是前两项之和。这是一个重复求和处理的问题,可用循环解决。循环次数是所要求的项数18。对所有项求和的问题可以设置一个累加器变量sum,初值为前两项之和,后在循环中每求得一新项,即将该项加进sum。
[Fei.java]
1. class Fei{
2. public static void main(String[] args){ 3. int a,b,c,sum;
4. a = b = 1; //a,b为前两项,均为1 5. sum = a + b; //累加器变量,设初值 6. System.out.print( a + "\t" + b ); 7. for( int i=1; i<=18; i++ ){
8. c=a+b; //生成的新项置放在变量c中 9. System.out.print("\t" + c); 10. sum += c;
11. a = b; //前两项后移一个数 12. b = c; 13. }
14. System.out.println("\tsum=" + sum); 15. } 16. }
程序运行结果:
1. 1 1 2 3 5 8
13 21 34 55
2. 89 144 233 377 610 987
1597 2584 4181 6765 3. sum=17710本讲介绍了Java语言的结构化编程,在main方法中的代码段总是由三种基本结构组成:顺序结构、分支结构和循环结构。分支结构通常用if或switch语句实现,循环结构常用while、do-while或for语句实现。在循环结构中可以继承包含循环,这即是循环嵌套,也称为多重循环,我们可用多重循环解决比较复杂的问题。break语句可以安排在switch和三种循环语句中,它总是会终止当前的语句。continue语句可以放在循环语句中,它只是结束当前循环,而并不会终止循环。
至此,我们已初入Java之门,可以解决一些基本的数据处理问题了。
思考与练习
1. 求150内的奇数之和。 2. 输入一个自然数,求N!。 3. 用循环嵌套的方法输出如下图案: