Java语言程序设计1583_第1页
Java语言程序设计1583_第2页
Java语言程序设计1583_第3页
Java语言程序设计1583_第4页
Java语言程序设计1583_第5页
已阅读5页,还剩1578页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1.1开篇例程

1.2Java程序结构 1.3Java程序运行 1.4小结 1.5习题

1.问题剖析与算法

1)问题剖析图1-1程序流程图

“裁纸奔月”问题如果能实现,则可以采取如下方法与步骤:

(1)准备工作:定义两个变量paperThick和n,分别记录纸的厚度与裁纸的次数,并把paperThick变量的初始值记为0.006cm,n的开始值记为0。1.1开篇例程

(2)如果paperThick<38500000000cm,则重复做以下工作:

①裁纸摞起来,即paperThick*2。

②次数加1,即n + 1。

(3)如果paperThick≥38500000000cm,则完成以下工作:

①输出纸的厚度,即打印paperThick。

②输出最后记录的次数,即打印n。

2)算法与程序流程图

上述解决问题的思路、方法与步骤称为算法,初学者常用程序流程图来描述算法,如“裁纸奔月”的算法可用图1-1所示的流程图来描述。图1-1程序流程图

2.程序与程序设计

1)程序

将算法用某种计算机编程语言表达出来即为程序。如“裁纸奔月”的算法用Java语言描述如下。

【例1-1】“裁纸奔月”程序代码。

publicclassEarthToMoon

{

staticfloatpaperThick=0.006f;

staticintn=0;

publicstaticvoidmain(String[]args)

{ while(paperThick<38500000000f)

{

paperThick=paperThick*2;

n++;

}

System.out.println(“纸厚=”+paperThick);

System.out.println(“裁纸次数=”+n);

}

}

将上述程序编辑、编译后在计算机CPU中运行称为进程,完成这个进程即得到我们所需要的结果。

2)程序设计

使用计算机解决问题时,从分析问题找到算法,再将算法转换为程序的过程,称为程序设计。要进行程序设计必须掌握程序设计语言,本书将以Java语言为例学习程序设计。1.2.1Java程序结构简析

分析开篇例程可知,Java程序最基本的单元为“类”,一个类由类头与类体两部分组成,类体又由数据成员与成员方法两部分组成,如图1-2所示。“裁纸奔月”程序的类如图1-3所示。1.2Java程序结构图1-2类图1-3“裁纸奔月”程序的类

1.类头

“裁纸奔月”程序中第一句publicclassEarthToMoon就是“类头部”,这一句在Java中表示定义一个类名为EarthToMoon的类。它由三部分组成:public控制修饰符表示该类是公有的(后续章节将详细讲解);class关键字表示其后紧接着的是类的名称,是Java语言中专门用来定义一个类的固定表示;EarthToMoon为程序员自己定义的名字,这就是所谓的“类名”。Java语言中由这三部分构成类头。注意:

(1)一个程序可由多个类构成,但在同一个 .java程序文件中只能有一个类用public修饰,其余类均不需public修饰符。

(2)类名是由字母或下划线开头的字母字符串,一般由有一定意义的英文单词组成,且约定每个英文单词的第一个字母都大写,如EarthToMoon可作为一个合法的类名。另外,类名应能“见名知意”,如EarthToMoon为类名,可想象为“奔月”。

2.类体

如“裁纸奔月”程序中第一个大括号与最后一个大括号之间所有内容构成“类体”,它又由paperThick和n两个数据成员和一个main()方法组成。

(1)数据成员:如“裁纸奔月”程序中staticfloatpaperThick=0.006f;表示定义一个浮点型的数据成员,且其初始值为0.006,其中static、float与f等将在第2章中详细讲解。

同理,staticintn=0;表示定义一个整数型的数据成员,初始值为0。

(2)成员方法:如“裁纸奔月”程序中下述语句序列构成main()方法。1.2.2初识编程规范

程序员在编写程序代码时有一个共同的约定,即编程规范。下面列举一些常用的编程规范,以便读者从第一次编程开始就养成良好的编程习惯。

(1)首先要用关键字class定义一个新类,类名紧跟其后。EarthToMoon是开篇例程定义的类名,public表明是一个公共类。

(2)一个 .java程序文件可以有多个类,每个类可以有多个方法,但最多只能有一个公

共类

(3)对于一个JavaApplication程序,必须有且只能有一个main()方法。该方法标志着执行应用程序的起点。

(4)任何方法中可以有多条语句。开篇例程main()方法中,就有多条语句。

(5) Java程序中的每一条功能操作语句都要以“;”结束,而结构定义语句不需要“;”结尾。

(6)为了增加程序的可读性,程序中可以加入注释行,如“//输出字符串”。

(7)类名中每个单词的第一个字母大写,其余小写。

(8)数据成员和成员方法名的命名规则与“类名”的命名相似,只是第一个字母要小写。1.3.1Java开发运行环境的建立

Java的开发运行环境是指Java的开发工具和相应的软硬件环境。目前有许多用于编写Java程序的开发工具,如Sun公司的Java2SDK和NetBeans,Borland公司的JBuider,IBM公司的VisualAgeforJava等。通过Internet可以下载这些Java开发工具。对于初学者建议使用Sun公司的Java2SDK(简称JDK)开发工具。本书大部分程序都是基于此环境运行通过的。在了解和掌握了Java语言的基本结构、基本思想和基本内容后,实际开发应用系统时,可选用某种开发环境,如NetBeans组织的集成开发环境NetBeans。1.3Java程序运行

1.建立JDK开发环境

Sun公司的JDK有SolarisSPARC/x86、Linuxx86、MicrosoftWindows等版本,它们的建立过程略有不同。这里我们以MicrosoftWindows版本的JDK为例说明其建立过程。

(1)下载JDK:从Sun公司的网站()上下载最新的JDK工具包。

(2)安装JDK:找到下载的安装文件(如jdk-6u2-windows-i586-p.exe)并运行它,按提示完成JDK的安装。

(3)配置系统文件的默认查找路径。在WindowsNT/2000/XP系统中,若将JDK安装在G:\Java\jdk1.6.0_02目录中,则应在【我的电脑】上单击右键,选择【属性】,在【高级】选项卡的【环境变量】中的【系统变量】里进行设置。方法是:在现有的path变量内添加“G:\Java\jdk1.6.0_02\bin”,让操作系统自动查找编译器(javac.exe)、解释器(java.exe)等命令所在的路径;另外新建一个classpath变量,其值为“.;g:\Java\jdk1.6.0_02\lib”,设置程序被编译和执行时需要的类路径,如图1-4和图1-5所示。图1-4path变量的设置图1-5classpath变量的设置

2.Java工具集

Java工具集为开发人员提供了创建和运行Java代码的工具,这些工具见表1-1。表1-1Java2SDK开发工具1.3.2Java程序的编辑

Java源程序是无格式的纯文本文件,可以用任何文本编辑器(如Notepad、Word)来编写Java程序,推荐使用UltraEdit等文本编辑器。如开篇例程在UltraEdit中编辑的结果如图1-6所示。

编辑完成后将其以EarthToMoon.java为磁盘文件名保存,如图1-6左边所示。图1-6例1-1源程序的编辑编程约定:

(1)对于类声明的整个主体,在界定类主体的左花括号“{”和右花括号“}”之间缩进一级。一般建议三个空格形成一级,这种缩进格式强调了类定义的结构,有助于阅读程序。同样道理,方法体也缩进一级。

(2) Java是区分大小写的。不正确地使用标识符的大写和小写字母通常会导致编译错误。

(3)磁盘文件主名必须与public修饰的类名相同,文件名必须以 .java结尾,如果漏掉了 .java扩展名,则Java编译器不能编译这个类。1.3.3Java程序的编译

想要编译Java源程序,必须安装Java2SDK工具,其中Java编译器名为javac.exe。安装配置完成后,就可以用javac编译例1-1。如图1-7所示,进入EarthToMoon.java所在目录,输入javacEarthToMoon.java后回车。图1-7Java源程序的编译1.3.4Java程序的运行

在Sun提供的JDK开发工具包中有个名为java.exe的命令,有人把它叫Java虚拟机,是专用来执行被编译后的Java字节码文件的。当Java程序被编译通过,生成字节码文件后,就可以运行它得到运行结果。在字节码文件所在目录输入命令javaEarthToMoon不需要 .class,就可得到开篇例程的运行结果,如图1-8所示。图1-8Java字节码文件的运行解决问题的方法和步骤称为算法;把算法用计算机语言描述出来就是计算机程序;从发现问题、分析问题找到算法,接着选用计算机语言表达算法的过程叫程序设计。

Java语言是一种面向对象的编程语言,它由类构成,类又由类名与类体两部分组成,数据成员与成员方法构成类体。利用Java语言进行程序设计就是:找寻数据成员,构造成员方法实现操作数据成员的过程。在此过程中,参与程序设计的人员都应遵守一个共同的约定,这个约定就是所谓的编程规范。1.4小结要运行Java程序,还需下载、安装和配置Sun公司提供的Java开发运行工具包。Java源程序文件是文本文件,故可用任一文本编辑器对其完成编辑,然后用Sun公司提供的JDK中的编译器(javac.exe)对Java源文件进行编译,生成字节码文件,最后用Java解释器(java.exe)对字节码文件进行解释执行,达到问题的最终解决。1.什么是算法?

2.什么是程序?

3.什么是程序设计?

4. Java程序的基本编程单元是什么?简要说明其组成。

5. Java编译器的英文名称是什么?它的作用是什么?

6. Java解释器的英文名称是什么?它的作用是什么?

7.试编写一个显示“ILoveInternet”的JavaApplicatioin程序。1.5习题2.1标识符与关键字

2.2简单计算

2.3基本数据类型

2.4运算符 2.5程序流程控制 2.6方法2.7小结2.8习题

1.标识符

Java用标识符(identifier)表示变量名、类名和方法名,它们的命名要符合正确的命名规则,即合法的Java标识符。Java中对正确标识符的规定如下:

(1)标识符必须由字母、下划线“_”或美元符号“$”开头。

2.1标识符与关键字

(2)标识符中的其他字符一定是字母、数字、下划线或美元符号。

(3)标识符的字符中不能有空格。

(4)一个标识符的长度不能超过65535个字符。

Java语言区分字母大小写,所以VALUE、Value、value表示不同的标识符。

2.关键字

关键字(keyword)是Java语言为特殊目的而保留的标识符,程序员不能改变它们,也不能作为一般标识符来使用而赋予别的含义。表2-1列出了Java中的关键字。表2-1Java的关键字关键字const和goto当前并未使用。单词false、null和true都是为特定的目的而保留的,也不可以作变量名。关键字区分大小写,并只由小写字母构成。

再来看一下第1章中的程序EarthToMoon.java。在类头的定义中,有关关键字和标识符的说明如图2-1所示。图2-1定义说明在这个程序中,其他标识符的名字有main、String、args、System、out、println、n和paperThick。标识符main是开始执行程序的方法名;String和System是Java类名;args、out、n和paperThick是可以调用一些程序资源的变量;println是把信息送到控制窗口的方法名。【例2-1】程序设计的目的是要解决一些实际问题。例如,根据给定的半径计算出圆的面积。这里将π的值始终取为3.14,而半径的值可以不断变化,要求计算并显示结果。

程序Area.java示例。2.2简单计算程序运行结果如图2-2所示。图2-2Area.java的运行结果2.2.1常量

在计算中有一个不发生变化的数据,即π的值被规定为3.14。在程序中用VALUE_PAI这个标识符来表示。

常量是程序初始化内存地址后,其值不能再改变的内存地址的符号名称,它必须是正确的Java标识符。

计算中使用常量之前,必须先定义和初始化常量。常量的定义常常采用图2-3所示的形式。图2-3常量定义的形式在如下程序中,将VALUE_PAI定义为double类型的常量。(关于double类型的具体细节将在2.3节中讨论。)

finaldoubleVALUE_PAI=3.14;

上述语句把VALUE_PAI与存放double类型数据3.14的内存地址关联起来。由于使用了修饰关键词final,在程序执行过程中,地址中的内容是固定的;关键词double表明定义的常量是double类型的值。从这时起,在方法main()中,VALUE_PAI与double值3.14是完全对应的。图2-4描绘了定义的结果(用锁来表示不可变更的值)。图2-4常量的定义结果

Java允许在一条语句中定义多个常量,之间用逗号分开。例如,下面的语句定义了两个常量。

finaldoubleHEIGHT=500,WIDTH=150;

这种方式没有单一语句那样突出,且妨碍了注释。考虑到程序的可读性,一般不鼓励这种方式定义常量。

另外,Java编程中有一个习惯,常量标识符的名字采用大写字母,单词之间用下划线隔开,且名称对其目标要具有描述性。例如,用VALUE_PAI来表示π。2.2.2变量

程序Area.java中除了使用常量,还使用了double类型的变量。变量是程序执行过程中,内容可以改变的内存地址的符号名字。

使用变量的原则是“先说明后使用”,即变量在使用前必须先声明,以便计算机留出相应大小的存储空间,用来存放在程序执行过程中发生改变的数据。基本的变量定义通常采用图2-5所示的形式。图2-5变量定义的形式程序中,radius、area都是double类型的局部简单变量。因为在定义中没有修饰符final,所以它们是变量;因为对它们的使用仅限于定义它们的方法,所以它们是局部变量。

doubleradius=2.5;

即变量radius初始化为double类型,值为2.5。radius定义的结果如图2-6所示。因为radius的值可以改变,因此在它的描述中没有锁。图2-6变量的定义结果如果没有初始值,局部变量就没有被初始化。对于下面这条语句:

doubleradius;

其定义结果如图2-7所示。图2-7无初始值变量的定义结果其中“-”表示未被初始化的值。尽管定义变量的时候可以不做初始化,但Java编译器要求所有变量在使用之前必须初始化。否则在使用未初始化变量时,编译出错。

一条语句可以定义多个变量,其间用逗号分开。下面的语句就采用这种风格定义并初始化变量weightInPounds(体重:用英镑度量)和heightInFeet(身高:用英尺度量)。

doubleweightInPounds=70.5,heightInFeet=4.2;如果考虑程序的可读性,比较好的定义风格是一条语句定义一个变量。

另外,Java编程对变量的命名也遵循一定的习惯。变量标识符的名称应该能描述它所代表的值,而且由小写字母开始。如果变量名字仅由一个单词构成,那么整个名字都小写,如radius、area;如果变量名字由多个单词构成,那么这些单词连在一起,除了相连单词(不包括第一个单词)的第一个字母,其余字母都是小写,如weightInPounds、heightInFeet等具有描述性的变量。2.2.3操作

先看程序Area.java中的一条语句:

doublearea=VALUE_PAI*radius*radius;

这条语句有以下几个含义:

(1)定义了一个变量area,并进行了初始化。

(2) VALUE_PAI*radius*radius是一个表达式。

(3)将表达式VALUE_PAI*radius*radius的值赋给变量area。

(4) area=VALUE_PAI*radius*radius;是一条赋值语句。在这条赋值语句中出现了符号“*”,它表示算术运算中的乘法,被称为算术运算符。可以推知,诸如“+”、“-”、“/”等能实现算术运算的符号都称作算术运算符。

那么什么是表达式呢?

表达式是算法语言的基本组成部分,它表示一种求值规则,通常由操作数、运算符和圆括号组成。操作数是参加运算的数据,可以是常数、常量、变量或方法引用,如VALUE_PAI和radius,且出现的变量名必须已经被初始化(值为2.5)。表达式按照运算符的优先级进行计算,求得一个表达式的值。运算符中圆括号的优先级最高,运算次序是“先内层后外层”,因此先计算由圆括号括起来的子表达式。圆括号还可以多级嵌套。

Java规定了表达式的运算规则,对操作数类型、运算符性质、运算结果类型及运算次序都做了严格的规定,程序员使用时必须严格遵循系统的规定,不得自定义。

在变量area的定义中用表达式VALUE_PAI*radius*radius的值来初始化。表达式用了double类型的乘法操作符 *,将常量VALUE_PAI与两个变量radius相乘,结果表示圆的面积。程序的下一部分向用户显示关于圆的信息,这部分由两个println()调用组成。

//显示结果

System.out.println(“Theradiusis:”+radius);

System.out.println(“Theareaofthiscircleis:”+“”+area);

第一个println()方法调用的功能是:把表达式 "Theradiusis:"+radius的值提供给println()。对于操作符“+”而言,当它的两个操作数都是数值型时,操作符执行加法;当其中至少一个操作数是字符串(String)类型时,操作符执行String连接。“Theradiusis:”和radius求值,因为radius的值是2.5,连接生成了一个值为 “Theradiusis:2.5” 的字符串,这个值成为println()调用的真正参数。这条语句执行之后,程序输出:

Theradiusis:2.5

与此类似,在执行下一个println()方法调用时:首先"Thearea

ofthiscircleis:" 和 ""求值,连接生成了值为 "Theareaofthiscircleis:"的字符串;然后该值与变量area连接,由于area的值在初始化时通过计算、赋值已经确定为19.625,因此生成字符串的值为 "Theareaofthiscircleis:19.625"。在这之后,程序输出如下:

Theradiusis:2.5

Theareaofthiscircleis:19.625

至此,程序Area.java显示了常量、变量的作用和一些基本操作。

Java的数据类型分为两种,分别是基本类型和引用类型。

(1)基本类型(primitive):包括整数、字符、浮点和布尔等类型。

(2)引用类型(reference):包括数组、类和接口等类型。如按钮、窗口等控件属于引用类型变量。

2.3基本数据类型

Java在处理这两种类型的数据时,在存储机制上采用了不同策略。Java将引用类型的对象通过new关键字存储在“堆”里,但这种方式对于特别小的、简单的变量,不是很有效。因此,对于这些类型需要特殊对待:通过创建一个并非“引用”的“自动”变量,使得变量拥有“值”,并置于堆栈中,使之更加高效。这些需要特殊对待的类型就是Java中的基本类型。

Java的基本类型包括整型、字符型、浮点型和布尔型四类,每一类称为一种数据类型(datatype)。数据类型定义了数据的性质、取值范围、存储方式以及对数据所能进行的运算和操作。程序中的每一个数据都属于一种类型,确定了数据的类型也就相应地确定了数据的性质以及对数据进行的操作,同时数据也受到类型的保护,确保了数据不被非法操作。

Java定义的基本数据类型,它们的分类及关键字如下:

整型:byte、short、int、long。

字符型:char。

浮点型:float、double。

布尔型:boolean。

可以看出,Java设置了多种数据类型,如byte、short、int和long,float和double。这样做的目的是让程序员能充分地利用特定的计算机硬件(如PCs、移动电话、PDAs)所支持的各种类型。Java需确定每种基本类型所占存储空间的大小。它们的大小并不像其他大多数语言那样随机器硬件架构的变化而变化。这种所占存储空间大小的不变性是Java程序具有可移植性的原因之一。2.3.1整数类型

Java定义了四种表示整数的整型:字节型(byte)、短整型(short)、整型(int)、长整型(long)。它们的特性如表2-2所示。表2-2整数类型一个整数隐含为整型(int)。当要将一个整数强制表示为长整数时,需在后面加L或l。

Java的整数有三种进制的表示形式:

十进制:用多个0~9之间的数字表示,如123和 -100,其首位不能为0。

八进制:以字母0打头,后跟多个0~7之间的数字,如0123。

十六进制:以字母0x或0X打头,后跟多个0~9之间的数字或a~f之间的小写字母以及A~F之间的大写字母,a~f或A~F分别表示值10~15,如0X123E。

关于int类型的操作在程序CelToFah.java中得到了演示。【例2-2】整数摄氏温度到整数华氏温度的转换。转换公式为:Fahrenheit=32+(9/5)

Celsius。

publicclassCelToFah

{

publicstaticvoidmain(String[]args)

{//设置将被转换的摄氏温度

intcelsius=32;//转换成相应的华氏温度

intfahrenheit=32+((9*celsius)/5);

//显示结果

System.out.println("Celsiustemperatureis:"+celsius);

System.out.println("equalsFahrenheittemperatureis:"+fahrenheit);

}

}

程序运行结果如图2-8所示。图2-8CelToFah.java的运行结果2.3.2字符类型

字符型(char)用来存储字符。一个字符用一个16位的Unicode码表示。所有可见的ASCII字符都可以用单引号括起来成为字符,如 'a'、'A'、'#' 等。关于字符类型的特性如表2-3所示。表2-3字

型通常,程序员并不是用数字来表示字符,而是用单引号表示字符,编译器会自动将字符表达翻译成Unicode编码。比如,当程序员需要使用字母a时,用'a'而不是它的Unicode值97。由于char的表达本质上是整数,所以对整数定义的操作符也可以用于char变量和值。

Unicode字符集编码按照值的大小有以下规则:

(一)顺序规则

‘0’<‘1’<‘2’<…<‘9’

‘a’<‘b’<‘c’<…<‘z’

‘A’<‘B’<‘C’<…<‘Z’

(二)计算规则

对除9以外的任何数字d,表达式'd'+1都生成下一个数字的字符。例如,'7'+1生成'8'的Unicode值56。

对除z以外的任何小写拉丁字母d,表达式‘d’+1都生成下一个小写字母的字符。例如,‘b’+1生成‘c’的Unicode值99。

对除Z以外的任何大写拉丁字母D,表达式‘D’+1都生成下一个大写字母的字符。例如,‘O’+1生成‘P’的Unicode值80。

这些关系可以使字符按照它所代表的是小写字母、大写字母或数字来有效地分类。

关于上述char类型的性质,程序LowerToUpper.java给出了演示。

【例2-3】把一个小写字母转换成与它对应的大写字母。publicclassLowerToUpper

{

publicstaticvoidmain(String[]args)

{//设置相关的小写字母

charlowerLetter='h';

//转换成大写字母

intupperLetter=‘A’+(lowerLetter-‘a’);

//显示结果

System.out.println(“Lowercaseis:”+lowerLetter);

System.out.println(“Uppercaseis:"+(char)upperLetter);

}

}

程序运行结果如图2-9所示。

另外,还有一些控制字符不能直接显示,可以利用转义序列来表示,如表2-4所示。图2-9LowerToUpper.java的运行结果表2-4转义序列【例2-4】关于转义序列的用法,参考程序CharOfChange.java。

publicclassCharOfChange

{

publicstaticvoidmain(String[]args)

{

//单引号前面可以不加\,直接输出

System.out.println("Hesaid,\"Let’sstudycharof'\\'\"");

}

}

程序运行结果如图2-10所示。图2-10CharOfChange.java的运行结果2.3.3浮点类型

Java用浮点型表示数学中的实数,包括整数部分和小数部分。浮点数用于需要小数位精确度的计算。例如,计算平方根或三角函数等,都会产生浮点型的值。浮点型有float(32位)和double(64位)两种类型,分别称作单精度型和双精度型,其格式完全遵循IEEE754标准。关于浮点类型的特性如表2-5所示。表2-5浮点类型浮点数有两种表示方式。

标准计数法:由整数部分和小数部分构成,中间有小数点分开。整数部分或小数部分其中之一可以为空,如123.456、1.、.9、0.0等。

科学计数法:由十进制整数、小数点、小数和指数部分构成,指数部分由字母E或e加幂表示,如1.2345E+2、0.23E-4、-45.e+23、23.68E12都是用科学记数法表示的浮点数,它们依次等于1.2345×102、0.23×10-4、-45×1023、23.68×1012。

一个浮点数隐含为double型。在一个浮点数后加字母F或f,可将其强制转换为float型。【例2-5】程序RealDemo.java演示浮点数的使用。

publicclassRealDemo

{

publicstaticvoidmain(String[]args)

{//对于float变量进行赋值,小数后面必须加上f

floatx=123.456f;//对于double变量进行赋值,小数后面不一定需要加上d

doubley1=23.789;

doubley2=78.654d;

//显示结果

System.out.println("x="+x);

System.out.println("y1="+y1);

System.out.println("y2="+y2);

}

}

程序运行结果如图2-11所示。图2-11RealDemo.java的运行结果2.3.4布尔类型

布尔型(boolean)用来表示逻辑值,也称为逻辑型。boolean是以19世纪英国数学家GeorgeBoole的名字命名的,正是他使逻辑值的研究正式化。boolean类型有真(true)和假(false)两个值,并且这种类型所占存储空间的大小没有明确定义,仅定义为能够取字面值true或false。true和false不能转换成数字表示形式。

所有关系运算(如a<b)的返回值都是布尔型的值,可以利用布尔型的值控制语句中的条件表达式,如if、while、for等语句。

2.2节通过分析一个简单计算的操作过程,初步明确了运算符与表达式的概念。在程序中,涉及了算术运算符“*”和赋值运算符“=”,并且分析了表达式的求值过程。

在实际操作中,Java定义了各种运算操作,并提供了丰富的运算操作符。当一个表达式中存在多个操作符时,就需要考虑各部分的计算顺序。而这个顺序就由操作符的优先级和结合规则来确定。因此在学习Java的运算符操作之前,必须先对优先级有一个初步的掌握。2.4运算符2.4.1优先级

先看一条语句:

intx=3*5/2;

按照整型的计算规则,结果是7还是6呢?这取决于表达式的计算顺序。如果按照(3*5)/2的顺序计算,结果是7;如果按照3*(5/2)的顺序计算,结果是6。由于在算术运算中乘法与除法的运算级别相同,在没有设定括号的前提下,表达式按照自左向右(被称为“左结合”)的顺序计算,因此语句的执行结果是7。大多运算符按照从左向右的次序进行计算,少数运算符的运算次序是从右向左的(被称为“右结合”),如赋值运算符、三元条件运算符等,后面将进行讲解。再看下面的表达式:

4/2+8 //除法优先级高于加法

-4*8 //取反优先级高于乘法

12+15%/2 //取余数优先级高于加法

Java对操作符的优先级做了严格的规定,程序员使用时必须严格遵循系统的规定,不得自定义。括号可以用来改变计算的顺序。例如:

inta=x+y-2/2+z;

为上述表达式加上括号后,就有了不同的含义:

inta=x+(y-2)/(2+z);

可以想象,利用括号的嵌套,可以完成更为复杂的运算关系,如:

inta=(2+(3+5)*10)/(6-4);先计算子表达式(3+5),然后再计算外层括号中的表达式,结果为41。

另外,需要注意的用法是,括号经常在不必需的地方使用,以明确运算顺序。这个技巧更易于读者理解程序代码。例如,表达式x*y+x/y-z和带括号的表达式(x*y)+(x/y)-z进行同样的计算,但是后者具有更加明确的意思。2.4.2赋值运算符

1.赋值运算

赋值运算的作用是使变量获得值。赋值的格式如下:

<变量名>=<表达式>

其中,“=”是赋值运算符,它有两个操作数(“=”被称为二元操作符):左边的操作数是目标变量;右边的操作数是表达式。运算时,赋值操作符根据表达式的结果更新与目标变量对应的内存地址中的值,即先计算<表达式>的值,再将表达式的结果值赋给<变量名>。因此,赋值的运算次序是从右向左的,即“右结合”。

表2-6执行了一系列赋值语句,每条语句执行后,变量对应的内存发生了变化,直观地体现了赋值语句的执行过程。表2-6赋值语句的执行过程

2.赋值运算的语法错误

赋值中的变量名必须已声明,而且表达式必须能计算出确定的值,否则将产生编译错误。例如,程序FuZhi1.java和程序FuZhi2.java分别验证了这些要求。

【例2-6】FuZhi1.java程序。

publicclassFuZhi1

{

publicstaticvoidmain(String[]args)

{

k=10;

System.out.println(“k=”+k);

}

}

当k声明时,系统将产生“不能解析符号”的编译错误,程序运行结果如图2-12所示。图2-12FuZhi1.java的运行结果【例2-7】FuZhi2.java程序。

publicclassFuZhi2

{

publicstaticvoidmain(String[]args)

{

inti,k;

k=i+10;

}

}

由于变量i没有具体的值,不能参加运算,系统将产生“变量i可能还未被初始化”的编译错误,程序运行结果如图2-13所示。图2-13FuZhi2.java的运行结果

3.赋值的优先级和结合律

先看下面这条赋值语句:

x=9*10;

赋值操作符会产生一个值,这个值被看做是赋值操作的值,即左操作数(x)的新值。假设x和y是已经定义好的int变量,此赋值语句之前的语句计算出15,又把15存储在变量x中。由于赋值生成了一个值,进而可以写出下面的表达式:

y=x=9*10;这个赋值表达式意味着什么呢?关键在于对赋值操作符优先级与结合律的理解。正确的编译是9与10先作乘法,于是“=”的优先级低于“*”,因此,上面的表达式编译为:

y=x=(9*10);

再观察x的两边都有赋值操作符“=”,这种情况下,“=”的结合律决定了x是与右边的“=”先结合,还是与左边的“=”先结合。由于赋值操作符是右结合的,因此,表达式的正确编译为:

y=(x=(9*10));

它表明将赋值操作x=(9*10)的值90赋给变量y,于是赋值操作y=(x=(9*10))的值是90。

4.复合赋值

语句“i=i+5;”完成的功能很简单,即取出变量i中的值,再加上5,将结果再存储到变量i中。

Java有一个完成同样功能的复合加法运算,写作:

i+=5;

“+=”是一种特殊的操作符,用它进行常见的操作,可以认为是编程语言的简化。其他一些复合赋值操作符是-=、*=、/=、%=,它们依次对目标变量进行减法、乘法、除法和求余运算,再把值赋给目标变量。

将复合运算的概念进行延伸,可以涉及到其他的运算操作,如表2-7所示。表中涉及到的运算将在接下来的内容中进行讲解。表2-7赋值运算符与其他运算符的简捷使用方式

5.赋值相容

如果目标变量与表达式的类型是相同的,就可以赋值,称为类型相同。如果两者类型不相同,并且目标变量类型比表达式类型长时,系统会自动将表达式转化为较长的类型,如int型转化为long型,这时也可以赋值,称为赋值相容(assignmentcompatible)。

例如:

longbigValue=99L;//类型相同

longbigval=6;

//6是int型,自动转化为long型,赋值相容

doublez=12.414F //12.414F是float型,赋值相容如果变量类型比表达式类型短,则赋值不相容,编译时产生“可能存在的精度损失”的错误。例如,

intsmallval=99L //99L是long型,赋值不相容

floatz1=12.414; //12.414是double型,赋值不相容

赋值不相容时,需要使用强制类型转换,其格式如下:

(<目标类型>)<表达式>

例如,

i=(int)99L;

//数值的转换

intj=(int)(bigValue); //变量的转换

将long型转换为int型后,可以赋值。2.4.3算术运算符

Java的算术运算符分为一元运算符和二元运算符。一元运算符只有一个操作数,而二元运算符有两个操作数参加运算。

1.一元运算符:一元正(+),一元负(-)

inti=10,j,k;

j=+i; //取i的原值,则j=10

k=-i; //取与i相反符号的值,则k=-10一元运算符与操作数之间不允许有空格。

编译器能正确识别下面的这条语句:

k=5*-i;

执行结果是-50。但读者在阅读程序的时候会感到费解,所以最好明确地写成:

k=(5*-i);

2.二元运算符:加(+),减(-),乘(*),除(/)和取余(%)

其中,+、-、*、/完成加、减、乘、除四则运算,%则是求两个操作数相除的余数。这五种运算符均适用于整型和浮点型。当在不同数据类型的操作数之间进行算术运算时,所得结果的类型与精度最高的那种类型一致。程序SuanShu.java列举了算术运算中的典型操作,下面请仔细分析其运行结果。

【例2-8】SuanShu.java程序。publicclassSuanShu

{

publicstaticvoidmain(String[]args)

{

System.out.println("3*4="+3*4);

System.out.println("3*4.0="+3*4.0);

System.out.println("15/2="+15/2);

System.out.println("15.2/2="+15.2/2);

System.out.println("7%2="+7%2);

System.out.println("-7%2="+-7%2);

System.out.println("7%-2="+7%-2);

System.out.println("7.0%2="+7.0%2);

}

}

程序运行结果如图2-14所示。图2-14SuanShu.java的编译结果从结果中可以得到一些运算规则:

(1)整数除法会直接去掉结果的小数位,而不是四舍五入地求整数位。

(2)整数求余,其结果的符号与被除数相同。

(3)求余操作数中只要有浮点数,结果即为浮点数。

3.一个小应用

用变量w表示今天是星期几,对应关系如表2-8所示。假设w=1表示今天是星期一,如何利用w表示明天(星期二)和昨天(星期天)。表2-8w与星期几的对应关系利用整数求余就可以很容易地解决这个问题,解决办法如下:

tomorrow=(w+1)%7; //值为2:明天

yesterday=(w-1+7)%7; //值为0:昨天

根据这道题的解决思路,可以考虑:怎样表示月份的上一个月和下一个月?2.4.4类型转换

在前面的讲解中,已经涉及到了数值表达式中的操作数为不同类型的情况,并且我们对类型的转换作了简单介绍。现在对类型转换再进行一些系统的讨论。

如果加法的一个操作数是int型,另一个操作数是char型,如何计算?或者,乘法的一个操作数是int型,另一个操作数是double型,又如何计算?这就需要进行类型转换。Java有两种转换基本类型的方法:自动提升和强制转换。

1.自动提升

Java在进行任何运算之前,编译器在必要的时候会自动进行类型的提升。

程序TypeAutoUpdate.java对七种基本类型进行了两两运算,从结果就可以看出系统在运算的时候,进行了类型转换。

【例2-9】TypeAutoUpdate.java程序。publicclassTypeAutoUpdate

{

publicstaticvoidmain(String[]args)

{byteb=100;

charc='a';

shorts=1000;

inti=40000;

longl=20000L;

floatf=25.4f;

doubled=5.5;

doubleresult=(f*b)+(l*f)+(i/c)+(d*s);System.out.println("f*b(25.4f*100)="+f*b);

System.out.println("l*f(20000L*25.4f)="+l*f);

System.out.println("i/c(40000/'a')="+i/c);

System.out.println("d*s(5.5*1000)="+d*s);

System.out.println("result="+result);

}

}

程序运行结果如图2-15所示。图2-15TypeAutoUpdate.java的执行结果对于结果,有以下几点说明:

(1)计算(f*b)时,b自动提升为float型。

(2)计算(l*f)时,l自动提升为float型。

(3)计算(i/c)时,c自动提升为int型。

(4)计算(d*s)时,s自动提升为double型。

(5)再相加时,中间结果都变为double型,所以result只能声明为double型,声明为其他类型就会出错。通常,表达式中出现的最大的数据类型决定了表达式最终结果的数据类型。

(6) Java允许把任何基本类型转换成别的基本数据类型,但布尔型除外,因为它根本不允许进行任何类型的转换处理。

自动提升的规则如下:

(1)所有的byte型和short型的值被提升到int型。

(2)整数运算时,如果有一个运算数是long型,则其他运算数的值都被提升到long型。

(3)浮点运算时,如果有一个运算数是float型,则其他整数值都被提升到float型。

(4)浮点运算时,如果有一个运算数是double型,则其他运算数的值都被提升到double型,计算结果也是double型。类型的自动提升发生的条件是:两种类型是兼容的,或者目标类型的范围比原类型的范围大。

当目标类型的范围能容纳原类型的信息时,不会造成任何信息的丢失,这种转换被称为“扩展转换”(wideningconversion)。然而如果要执行一种名为“窄化转换”(narrowingconversion)的操作,即目标类型的范围不能容纳原来类型的信息时,就有可能面临信息丢失的危险。此时,编译器会强制我们进行类型转换。

2.强制转换

除自动提升情况之外,要想转换数据类型,需要进行强制类型转换。强制类型转换的方法是:在数据前放一对包括新的类型名的括号,其格式如下:

(目标类型)<表达式>

如语句“inti=(int)66666L;”表示将long型的66666L转换成int型,然后赋给变量i。阅读程序ForceChange.java,分析结果。

【例2-10】ForceChange.java程序。publicclassForceChange

{

publicstaticvoidmain(String[]args)

{

doubled=390.456;

floatf=(float)d;

longl=(long)d;

inti=(int)d;

shorts=(short)d;

byteb=(byte)d;

System.out.println("d=390.456");

System.out.println("(float)d="+f);

System.out.println("(long)d="+l);

System.out.println("(int)d="+i);

System.out.println("(short)d="+s);

System.out.println("(byte)d="+b);

System.out.println("(char)65="+(char)65);

}

}

程序运行结果如图2-16所示。图2-16ForceChange.java的执行结果2.4.5自增与自减运算符

递增和递减运算是两种相当不错的快捷运算(常称作“自动递增”和“自动递减”运算)。其中,递减操作符是“--”,意思是“减少一个单位”;递增操作符是“++”,意思是“增加一个单位”。比如,若定义“inti=10;”,则++i就等价于“i=i+1”。递增和递减操作符不仅改变了变量,并且以变量的值作为生成的结果。这两种操作符各有两种使用方式,通常称为“前缀式”和“后缀式”。“前缀式”表示运算符位于变量或表达式的前面,如++j、--j;“后缀式”表示运算符位于变量或表达式的后面,如j++、j--。那么前缀形式与后缀形式有什么区别呢?看下面两段代码的执行结果。

代码一:

inti=10;

System.out.println(i++);//结果:10

代码二:

inti=10;

System.out.println(++i);//结果:11为什么结果是这样呢?原因是:对于前缀式使用方式而言,先执行变量的递增或递减运算,再返回生成的值参与其他运算;对于后缀式使用方式而言,先返回变量的原值参与其他运算,再执行变量的递增或递减运算。

表2-9执行了一系列递增递减语句,观察语句执行后变量所对应内存发生的变化,并分析运行结果。表2-9递增递减语句的执行过程从执行结果可以看出:++i(++j)返回变量增加后的值,j--返回变量减少之前的值。

但是在编程时,切忌不恰当地使用递增和递减运算符,使得程序变得很费解。像下面的语句:

intk=5+i++*++j;

下面的代码段与它是等同的,但是比它容易理解多了。

++j;

intk=5+(i*j);

++i;2.4.6关系运算符

关系运算符用于比较两个值之间的大小,结果返回布尔值,它们计算的是操作数的值之间的关系。如果关系是真的,关系表达式会生成true(真);如果关系不真实,则生成false(假)。关系运算符有6种:等于(==)、不等于(!=)、大于(>)、大于等于(>=)、小于(<)和小于等于(<=),例如:

3<=2; //结果:false

‘A’<‘a’; //结果:true

阅读程序RelationOperate.java,分析结果。【例2-11】RelationOperate.java程序。

publicclassRelationOperate

{

publicstaticvoidmain(String[]args)

{

intx=9,y=11;

booleanresult;

System.out.println("x="+x+"y="+y);

System.out.println("x==y"+(x==y));

System.out.println("x!=y"+(x!=y));

System.out.println("x<y"+(x<y));

System.out.println("x<=y"+(x<=y));

System.out.println("x>y"+(x>y));

System.out.println("x>=y"+(x>=y));

result=((x!=y)==true);

System.out.println("x!=y"+result);

}

}

程序运行结果如图2-17所示。图2-17RelationOperate.java的执行结果在使用“==”运算符时,程序员会犯一个常见的错误,如下面的程序:

while(x=y){

}

尽管还没有学习程序结构设计,但从语意上可以推断:程序员很明显是想测试是否相等(==),而不是进行赋值操作(=)。在Java中,赋值操作的结果并不是布尔值,而编译器期望的是一个布尔值。由于Java不会自动地将int数值转换成布尔值,所以在编译时会抛出一个错误,阻止程序进一步运行。2.4.7逻辑运算符

逻辑运算符只能处理布尔值,所得结果也都是布尔值。Java的逻辑操作符有:“与”(&&)、“或”(||)、“非”(!)。它们能根据参数的逻辑关系,生成一个布尔值。逻辑运算符的真值表如表2-10所示。表2-10逻辑运算符的真值表例如:

out.println((i>0)&&(i<9)); //判断i值是否在0~9之间

System.out.println((ch=='A')||ch=='a'); //判断ch是否为字母A或a

另外,&&和||运算符还有一个有趣的属性——“短路”现象(short-circuit)。对于&&运算符而言,从左向右依次逐个计算条件是否成立,一旦发现有一个条件不成立,就立即终止计算并且得到复合条件的结果值为false,不再计算余下的条件是否成立,因此表达式靠后的部分有可能不会被运算。同样,对于||运算符而言,从左向右依次逐个计算条件是否成立,一旦发现有一个条件成立,就立即终止计算并且得到复合条件的结果值为true,不再计算其余的条件。例如有3个用&&连接的表达式,当计算第一个为false时,那么整个逻辑表达式为false,其余的两个表达式将不再计算。程序ShortCircuit.java表明了这一点。【例2-12】ShortCircuit.java程序。

publicclassShortCircuit

{

publicstaticvoidmain(String[]args)

{

intn=5;

intm=8;

System.out.println("compareresultis:"+((n>m)&&(++n)>m));

System.out.println("nis"+n);

System.out.println("compareresultis:"+((n<m)&&(++n)>m));

System.out.println("nis"+n);

System.out.println("compareresultis:"+((n<m)||(++n)>m));

System.out.println("nis"+n);

}

}

程序运行结果如图2-18所示。图2-18ShortCircuit.java的执行结果对结果进行分析如下:

(1)程序首先计算下面的表达式:

(n>m)&&(++n)>m

因为n>m为false,因此整个表达式为false,程序不再计算(++n)>m,所以n仍然为5,没有机会增加1了。

(2)程序接着计算下面的表达式:

(n<m)&&(++n)>m因为n<m为true,无法确定整个逻辑表达式的值,所以要接着计算(++n)>m的值,无论结果怎样,n的值终于加1成为6。

(3)程序最后计算下面的表达式:

(n<m)||(++n)>m)

因为n<m为true,因此整个表达式为true,程序不再计算(++n)>m,所以n仍然为6,没有机会增加1了。2.4.8位运算符

位运算是对整数基本数据类型中的单个“比特”(bit)进行操作,是对数据进行按位操作的手段。位运算符对两个参数中对应的位执行布尔代数运算,并最终生成一个结果。Java的位运算符有:非(~)、与(&)、或( | )、异或( ^ )。位运算符的真值表如表2-11所示。表2-11位运算符的真值表例如:

6|2=6 等价于二进制 0110|0010=0110

4&2=0 等价于二进制 0100&0010=0000

6^2=4 等价于二进制 0110^0010=0100

~4=-5 等价于二进制 ~00000100=11111011

现在来做一个小测试:一个值的第3位是否被设置成了1?

解决思路:用1和第3位作与运算,用0和其余位作与运算,观察结果。如果结果是0,第3位是0,否则,第3位被置成1。阅读程序BitOperate.java,运行结果如图2-19所示。【例2-13】BitOperate.java程序。

publicclassBitOperate

{

publicstaticvoidmain(String[]args)

{intx=12;

//8的二进制表示为00001000(满足第3位是1,其余位是0的条件)inttest=8;

intresult=x&test;

if(result==0)

System.out.println("Bit3isnotset.");

if(result!=0)

System.out.println("Bit3isset.");

}

}图2-19BitOperate.java的执行结果当&、^和|操作符处理布尔值true/false时,它们被看做是位逻辑运算符。位逻辑运算符和位运算符的工作方式相同(用false代替0,true代替1)。真值表如表2-12所示。表2-12位逻辑运算符的真值表与表2-10做一个比较会发现:位逻辑运算符和逻辑运算符在“与”、“或”的运算规则上完全相同。那么它们在使用上有什么差别呢?前面讲过,&& 和 || 运算符具有“短路”现象,而&和|运算符没有这种现象。在计算表达式的值的时候,它们会做完所有的运算,再返回整个逻辑表达式的值。将程序ShortCircuit.java进行改写,比较一下结果。

【例2-14】改写后的ShortCircuit.java程序。publicclassShortCircuit

{

publicstaticvoidmain(String[]args)

{

intn=5;

intm=8;

System.out.println("compareresultis:"+((n>m)&(++n)>m));

System.out.println("nis"+n);

System.out.println("compareresultis:"+((n<m)|(++n)>m));

System.out.println("nis"+n);

}

}

程序运行结果如图2-20所示。图2-20改写后的ShortCircuit.java的执行结果2.4.9移位运算符

使用移位操作符可以将一个数字的所有位向左或者向右移动指定数目的二进制位。Java提供的移位运算符有:右移位(>>)、无符号右移位(>>>)、左移位(<<)。

右移位运算符考虑操作数的符号,因为负值意味着最左边一位是1,向右移位负数将在左边产生一个新的1。如果希望操作数字实际的位,并在移位负数时不在左端附加1,使用无符号右移位运算符(>>>,也称为零填充右移位运算符)。不管被移位的数字是正还是负,它在左端产生一个0,例如:

9>>2=11001右移2位为0010

1<<2=40001左移2位为0100

程序MoveBit.java演示了移位操作,运行结果清楚地表明了操作规则。

【例2-15】MoveBit.java程序。

publicclassMoveBit

{

publicstaticvoidmain(String[]args)

{

inta=12;

intb=-12;

inttemp;

System.out.println("12的二进制表示:"+Integer.toBinaryString(a));

temp=12>>1;//结果:6

System.out.println("12>>1:"+Integer.toBinaryString(temp));

temp=

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论