JAVA程序员必读:基础篇(3)语言基础
语言基础 我们先看看一个具体例子,给你们有个先入为主的感觉。以下是一个BasicsDemo程序,它的作用是从1加到10,并显示结果: public class BasicsDemo { public static void main(String[] args) { int sum = 0; for (int current = 1; current <= 10; current++) { sum += current; } System.out.println("Sum = " + sum); } } 这个程序的输出为: Sum = 55 从上面的例子中,我们可以看出即使是一个小的程序都要使用JAVA编程语言的许多传统特性,其中包括变量、操作符和流程控制语句。以上的代码可能看起来有点复杂,但是一旦你学习完本系列教程,你就会发觉它实际上很简单。本节教程将教给你一些JAVA编程语言的基础。 3.1 变量 你可以在程序中使用变量来容纳数据。这一小节将数据类型、怎样初始化变量以及怎样在代码块中引用变量。 实际上,对象是存储它的状态在变量中的。它的具体定义为:变量是用标识符命名的数据项。 你必须清除地为你想在程序中使用地每一个变量提供一个名字和类型。这个变量的名字必须是一个合法的标识符:以字母开头的一串Unicode字符。你可以使用变量名来引用变量包含的数据。这个变量的类型决定了什么类型的数值可以容纳以及什么的操作可以对它进行操作。为了得到一个变量、类型和名字,你必须编写变量声明,如下: type name 除了名字和类型你还要给变量一个作用域。变量的作用域是由变量声明位置决定的。 以下MaxVariablesDemo程序,声明了八个不同类型的变量,如下: 这个程序的输出为: The largest byte value is 127 The largest short value is 32767 The largest integer value is 2147483647 The largest long value is 9223372036854775807 The largest float value is 3.40282e+38 The largest double value is 1.79769e+308 The character S is upper case. The value of aBoolean is true 下面的部分详细介绍了变量的各个方法,包括数据类型、名字、作用域、初始化以及final变量。这个MaxVariablesDemo程序使用了你可能不熟悉并且不在本节提到的两项:几个常数MAX_VALUE以及一个if-else语句。每个MAX_VALUE常数是定义在由JAVA平台提供的其中一个数字类中,它是最大的数值。 3.1.1 数据类型 每一个变量必须有一个数据类型。一个变量的数据类型决定了变量能容纳的数值和操作符。比如 ,在MaxVariablesDemo程序中,声明int largestInteger决定了largestInteger是一个整型数据类型(int)。整型只能容纳整型数(可以是正数也可以是负数)。你可以完成算术操作,比如,整型变量的加法等等。 JAVA程序语言有两类的数据类型:原始和引用。一个原始类型的变量为它的类型包含了适当大小和格式的单一数值:一个数字、字符或者一个布尔型数值。比如,一个整型数值是一个32位数据。 下面表格所有的JAVA支持的所有原始数据类型,还给出了它们的大小和格式以及简短的描述。MaxVariablesDemo程序为每一个原始类型声明了一个变量: 关键字 | 描述 | 大小/格式 | 整型 | byte | 字节长度整型 | 8位两个补码 | Short | 短整型 | 16位两个补码 | int | 整型 | 32位两个补码 | long | 长整型 | 64位两个补码 | 实数 | Float | 单精度浮点型 | 32位IEEE 754 | Double | 双精度浮点型 | 64位IEEE 754 | 其它类型 | Char | 单个字符 | 16位Unicode字符 | boolean | 布尔型数值(true或者false) | 真或假 | 在其它语言中,原始类型数据的格式和大小可能依靠于程序运行的平台。相比之下,Java程序语言可以指定原始数据类型的大小和格式。因此,你不必担心系统从属问题。 你可以在你的代码中直接为原始变量设置数值。比如,如果你需要为一个整型变量设置为4,你可以编写以下代码: int anInt = 4; 数字4就是一个整型数值。下面是各种原始数值举例: 数值 | 类型 | 178 | Int | 8864L | Long | 37.266 | Double | 37.266D | Double | 87.363F | float | 26.77e3 | double | ' c ' | char | true | boolean | false | boolean | 总得说来,没有小数点的数就是整型。你可以通过在数字后面加一个'L' 或者'l'指定为一个长整型。一般使用'L'而不使用'l',因为'l'很容易与数字'1'混起来。有小数点的数为双精度类型。你可以在数字后面放置'f' 或者 'F'来指定为实数。而字符型数值可以是处在单引号中间的任何单一的Unicode字符;两个布尔型数是true和false。 数组、类以及接口是引用的类型。引用类型变量的数值跟原始类型的数值比较起来,它是数值的一个引用或者是由变量代表的数值。 一个引用称为一个指针或者在其它语言中称为内存地址。JAVA编程语言象其它语言一样不支持地址的详细使用,你可以使用变量的名字来取代。 你可以在你的代码中直接为原始变量设置数值。比如,如果你需要为一个整型变量设置为4,你可以编写以下代码: int anInt = 4; 数字4就是一个整型数值。下面是各种原始数值举例: 数值 | 类型 | 178 | Int | 8864L | Long | 37.266 | Double | 37.266D | Double | 87.363F | float | 26.77e3 | double | ' c ' | char | true | boolean | false | boolean | 总得说来,没有小数点的数就是整型。你可以通过在数字后面加一个'L' 或者'l'指定为一个长整型。一般使用'L'而不使用'l',因为'l'很容易与数字'1'混起来。有小数点的数为双精度类型。你可以在数字后面放置'f' 或者 'F'来指定为实数。而字符型数值可以是处在单引号中间的任何单一的Unicode字符;两个布尔型数是true和false。 数组、类以及接口是引用的类型。引用类型变量的数值跟原始类型的数值比较起来,它是数值的一个引用或者是由变量代表的数值。 一个引用称为一个指针或者在其它语言中称为内存地址。JAVA编程语言象其它语言一样不支持地址的详细使用,你可以使用变量的名字来取代。 3.1.2 变量名 程序是用变量名来引用变量数值的。比如,当显示largestByte变量的数值的时候,MaxVariablesDemo程序就使用名字largestByte。一个名字,比如largesByte包含了一个单一的标识符,被称为简单的名字(即变量名)。在JAVA编程语言中,对于变量名有下面的必须满足: - 它必须是一个合法的标识符。一个标识符是以字母开头的一串Unicode字符。
- 它必须不是一个关键字、布尔型字符(true或者false)或者保留字NULL。
- 它必须在作用域中是唯一的。在不同的作用域才允许存在相同名字的变量。在一些条件下,如果变量被定义在一个嵌套的代码块中,它可能和其它变量共享相同的名字。这点在以后的教程中会提到。
这里有个约定:变量名是以小写字母开头,而类名是以一个大写字母开头的。如果变量名包含了多个单词,而每个单词要组合在一起,则在每个单词的第一个字母大写,比如IsVisible。而下划线(_)可以处在变量的任何地方,但是一般地它只用在常数中分离单词,因为常数名都是用大写字母的,利用下划线可以看得更清除。 3.1.3 作用域 变量的作用域是一个程序的区域,在上面变量可以通过它的名字来引用。其次,作用域也决定什么时候系统为变量创建和清除内存。作用域只应用成员变量并决定是否变量可以从所在类的外部使用。在程序中变量声明的位置建立它的作用域并且将它放置到以下四类之一: 成员函数作用域 当地变量作用域 方法参数作用域 异常处理参数作用域 (图14) 如图14所示。成员变量示类或者对象的成员。它是在类中定义而不在任何方法或者构造函数中定义。成员函数的作用域是类的完全定义。但是,当成员是使用在成员初始化表达式中的时候,成员的定义需要在它使用之前出现。在后面的教程中我们要再深入学习成员变量,这里就不讲了。 你可以在一个代码块中定义当地变量。总的说来,当地变量的作用域从它的声明扩展到了它被定义的代码块结束。在MaxVariablesDemo中,定义在主方法中的所有变量都是当地变量。程序中的每一个变量的作用域从变量的定义扩展到了主方法的结束,它在程序代码中是用右括号}来指示的。 参数是方法或者构造函数的正式参数,它们用于传递数值给方法和构造函数。参数的作用域是整个方法或者构造函数。 异常处理参数跟参数很相似,差别在是前者是传递参数给异常处理而后者是传递给方法或者构造函数。异常处理参数的作用域处在{和}之间的代码,它紧跟着catch语句。利用异常来处理错误向你说明了怎样编写一个带有参数的异常处理。以下是一个代码例子: if (...) { int i = 17; ... } System.out.println("The value of i = " + i); // 错误 最后的行不汇编因为当地变量I已经出了作用域。i的作用域是处在{和}之间的代码块。变量I在右括号}之后就不存在了。改正的方法可以是讲变量的声明移到if语句块的外面,或者是将println方法调用移动到if语句块中。 3.1.4 变量初始化 当地变量和成员变量可以利用一个赋值语句来初始化。变量的数据类型必须与赋给它的数值的数据类型相匹配。下面程序中的当地变量声明,其初始化如下: // 整型 byte largestByte = Byte.MAX_VALUE; short largestShort = Short.MAX_VALUE; int largestInteger = Integer.MAX_VALUE; long largestLong = Long.MAX_VALUE; // 实数型 float largestFloat = Float.MAX_VALUE; double largestDouble = Double.MAX_VALUE; // 其它类型 char aChar = 'S'; boolean aBoolean = true; 参数和异常处理参数不能利用这种方法来初始化。它的参数的数值只能通过来设置。 3.1.5 final变量 你可以在任何作用域声明一个变量。Final变量的数值不能在初始化之后进行改变。这样的变量跟其它语言中的常量很相似。 为了声明一个final变量,你可以在类型之前的变量声明使用final关键字,比如: final int aFinalVar = 0; 前面的语句声明了一个final变量并一起对它进行了初始化。如果你在后面还想给aFinalVar赋其它的值的话,就会导致一个编译错误。在必要的时候,你可以推迟对一个final本地变量进行初始化。你可以先定义,然后在之后再初始化,如下: final int blankfinal; . . . blankfinal = 0; 已经声明了但是还没有初始化的final本地变量称为空白final。同时,一旦final本地变量被初始化,它就不能再设置了。并且之后的任何对blankfinal赋值的操作都将导致一个编译错误。 当你声明了一个变量,你就显性地设置了变量的名字和数据类型。JAVA编程语言右两类的数据类型:原始和引用。原始数据的变量包含一个数值。以下这张表显示了所有的原始数据类型以及它们的大小和格式。 关键字 | 描述 | 大小/格式 | 整型 | byte | 字节长度整型 | 8位两个补码 | Short | 短整型 | 16位两个补码 | int | 整型 | 32位两个补码 | long | 长整型 | 64位两个补码 | 实数 | Float | 单精度浮点型 | 32位IEEE 754 | Double | 双精度浮点型 | 64位IEEE 754 | 其它类型 | Char | 单个字符 | 16位Unicode字符 | boolean | 布尔型数值(true或者false) | 真或假 | 变量声明的位置隐含地设置了变量的作用域,它决定了代码的哪一部分可以通过变量名来调用这个变量。具体有以下四种类型的作用域:成员变量作用域、本地变量作用域、参数作用域以及异常处理参数作用域。 你可以使用赋值操作符(=)来在声明的地方对变量进行初始化。 你也可以将变量声明为final。Final变量的数值在初始化之后不能再被改变。 3.2 操作符 本节教程描述了怎执行各种操作,比如算术和赋值操作。 一个操作符利用一个、两个或者三个运算对象来执行了一个函数。只需要一个运算对象的操作符称为单元运算符。例如++是一个单元操作符,它是对运算对象自增1。需要两个运算对象的操作符号称为双元操纵符。比如等于号(=)就是一个双元操作符,它指定右边的运算对象给左边的运算对象。最后,三元操作符需要三个运算对象。JAVA编程语言有一个三元运算符?:,它是一个简要的if-else语句。 单元操作符支持前缀或者后缀记号。前缀记号是指操作符出现在它的运算对象之前,例如: operator op //前缀记号 后缀记号是指运算对象出现在操作符之前,例如: op operator //后缀记号 所有的双元操作符使用中缀记号,即操作符出现在两个运算对象的中间: op1 operator op2 //中缀记号 三元操作符也是使用中缀记号,例如: op1 ? op2 : op3 //中缀记号 操作除了执行一个操作,还返回一个数值。返回数值和它的类型依靠于操作符号和运算对象的类型。比如,算术操作符它完成基本的算术操作(加、减)并且返回数值作为算术操作的结果。由算术操作符返回的数据类型依靠于它的运算对象的类型:如果你对两个整型数相加,你就会得到一个整型数。 我们可以将操作符分成以下几类: 算术运算符 关系和条件运算符 移位和逻辑运算符 赋值运算符 其它的运算符 下面我们逐个介绍。
3.2.1 算术操作符 JAVA编程语言为所有的浮点型和整型数支持多种算术运算符。这些运算符为+(加)、-(减)、*(乘)、/(除)以及%(模)。下面的表总结了双元算术运算符。 运算符 | 使用 | 描述 | + | op1 + op2 | op1 加上op2 | - | op1 - op2 | op1 减去op2 | * | op1 * op2 | op1乘以op2 | / | op1 / op2 | op1 除以op2 | % | op1 % op2 | op1 除以op2的余数 | 以下有一个例程,ArithmeticDemo,它定义了两个整型数和两个双精度的浮点数并且使用五种算术运算符来完成不同的运算操作。这个程序同时使用了+符号来连接字符串。程序如下: 这个程序得输出为: 变量数值... i = 37 j = 42 x = 27.475 y = 7.22 加... i + j = 79 x + y = 34.695 减... i - j = -5 x - y = 20.255 乘... i * j = 1554 x * y = 198.37 除... i / j = 0 x / y = 3.8054 计算余数... i % j = 37 x % y = 5.815 混合类型... j + y = 49.22 i * x = 1016.58 这里注意,当一个整数和一个浮点数用为运算符来执行单一算术操作的时候,结果为浮点型。整型数是在操作之前转换为一个浮点型数的。下面的表总结了根据运算对象的数据类型由算术操作符返回的数据类型。它们是在操作执行之前进行数据转换的。 结果的数据类型 | 运算数据类型 | long | 任何一个运算对象都不是float或者doule型,而且最少有一个运算对象为long | int | 任何一个运算对象都不是float或者doule型,而且还不能为long型 | double | 最少有一个运算对象为double | float | 最少有一个运算对象为float,但不能是double型 | 除了双元的运算符+和-,还有以下单元运算符: 运算符 | 用法 | 描述 | + | +op | 如果op是一个byte、short或者char型的,op变成int型 | - | -op | 取op的相反数 | 另外两个简练的算术操作符为++和--。++是完成自加1的作用;而—是完成自减的作用。不管是++还是—都可能出现在运算对象的前面(前缀)或者后面(后缀),但是它们的作用是不一样的。前缀的格式为:++op或--op,它实现了在加/减之后才计算运算对象的数值;而后缀的格式为:op++或op--,它实现了在加/减之前就计算运算对象的数值。 下面的程序SortDemo中使用两次++和一个--,如下: 这个程序将10整型数值放置在一个数组arrayOfInts中。这个程序使用了arrayOfInts.length来获得数组中的元素个数。单个的元素可以由arrayOfInts[index]来访问,这里index是一个整型数,它指示数组中元素的位置。这里一定要注意index的下标是从0开始的。 这个程序的作用是输出从小到大的10个整数,结果为: 3 8 12 32 87 127 589 622 1076 2000 还是让我们好好看看这个SortDemo程序是怎样工作的。下面是控制外部循环的语句: for (int i = arrayOfInts.length; --i >= 0; ) { ... } 这个for语句是一个循环结构。其中--i >= 0给出了循环的条件,即--i大于等于0就继续循环,一旦小于0就结束循环。使用前缀--意味着最后的一次循环迭代是发生在当i等于0的时候。如果我们改变改变一下使用后缀的,那么循环迭代的最后一次发生在当i等于-1的时候,这样就会发生错误,因为数组的下标是从0开始的,-1是一个无效的数组下标。 程序中其它两个循环使用了后缀的++。在两个例子中,使用前缀或者后缀是没有问题的。当其中一个运算符号返回的数值没有用到,则约定使用后缀的。 下面的表格总结自增/自减运算符: 运算符 | 用法 | 描述 | ++ | op++ | 自增1;它是在自增之前计算op的数值的。 | ++ | ++op | 自增1;它是在自增之后计算op的数值的。 | -- | op-- | 自减1;它是在自减之前计算op的数值的。 | -- | --op | 自减1;它是在自减之后计算op的数值的。 | 3.2.2 关系与条件运算符 关系运算符是比较两个数值并决定它们的关系。比如!=在如果两个运算对象不相等的情况下返回true。以下这个表格总结了关系运算符: 运算符 | 用法 | 在什么情况下返回true | > | op1 > op2 | op1大于op2的时候 | >= | op1 >= op2 | op1大于等于op2的时候 | < | op1 < op2 | op1小于op2的时候 | <= | op1 <= op2 | op1小于等于op2的时候 | == | op1 == op2 | op1等于op2的时候 | != | op1 != op2 | op1不等于op2的时候 | 接下来介绍一个例子RelationalDemo。 下面是一个例子RelationalDemo,它定义了三个整型数并且使用关系运算符来比较它们,如下: 程序的输出为: 变量数值... i = 37 j = 42 k = 42 大于... i > j = false j > i = true k > j = false 大于等于... i >= j = false j >= i = true k >= j = true 小于... i < j = true j < i = false k < j = false 小于等于... i <= j = true j <= i = false k <= j = true 等于... i == j = false k == j = true Not equal to... i != j = true k != j = false
关系运算符经常用在条件运算符中来构造更复杂的判断表达式。JAVA变成语言支持六种条件运算符:五个双元运算符和一个单元运算符,如下表所示; 运算符 | 用法 | 什么情况返回true | && | op1 && op2 | op1 和 op2都是true,有条件地计算op2 | || | op1 || op2 | op1 或者 op2是true,有条件地计算op2 | ! | ! op | op为false | & | op1 & op2 | op1 和 op2都是true,总是计算op1和op2 | | | op1 | op2 | op1 或者 op2是true,总是计算op1和op2 | ^ | op1 ^ op2 | 如果op1和op2是不同的,也就是说,有其中一个运算对象是真的而不是两个都为真的时候 | &&运算符可以完成条件AND的操作。你可以使用两个不同的关系运算符和&&来决定是否两个关系都为true。下面的一行代码使用了这个技术来决定是否数组的索引处在两个边界之间。它决定了是否索引都大于等于0并且小于等于NUM_ENTRIES(它是在之前被定义为常数)。 0 <= index && index < NUM_ENTRIES 这里注意在一些实例中,第二个运算对象可能不用运算,因为如果第一个运算对象是false,则结果就一个是false,因此不用在计算第二个运算对象了。你看看以下的代码: (numChars < LIMIT) && (...) &&运算符只有在两个运算对象都为true的时候,才返回true。因此,如果numChars大于等于LIMIT的时候,&&左边的运算对象就为false,这时就不用在计算右边的运算对象就返回了数值false。在这个例子中,编译器将不会计算右边的运算对象。如果右边运算对象有副效应的的话(比如读一个流、更新一个数值或者进行一个计算)这个有重要的含义。 当两边的运算对象都是boolean(布尔型),运算符号&跟&&执行相同运算。但是,&总是要计算两边的运算对象然后再在两个运算对象同为true的时候才返回true。同样地,当运算对象都为boolean(布尔型)的时候,|执行与操作符号||一样的功能。这个|运算符总是要计算两边的运算对象然后在最少有一边为true的时候才返回true。当它们的运算对象为数字的时候,& 和|是按位操作的。 3.2.3 移位和逻辑运算符 移位运算符通过对第一个运算对象左移或者右移位来对数据执行位操作。下面的这个表总结了JAVA编程语言中有效的移位运算符。 运算符 | 用法 | 操作 | >> | op1 >> op2 | 将op1右移op2个位 | << | op1 << op2 | 将op1左移op2个位 | >>> | op1 >>> op2 | 将op1右移op2个位(无符号的) | 每一个运算符移动左边的运算对象的位数都是由右边的运算符给出的。这个移位的方向取决于运算符本身。比如,下面的语句是实现将整数13右移1位的目的: 13 >> 1; 13的二进制为1101.右移一位的结果为110,即为十进制的6.左边的位用零来填充。下面的表给出了JAVA编程语言提供的四种运算符来对它们的运算对象执行按位操作: 运算符 | 用法 | 操作 | & | op1 & op2 | 按位与 | | | op1 | op2 | 按位或 | ^ | op1 ^ op2 | 按位异或 | ~ | ~op2 | 按位求补 | 当它的运算对象为数字的时候,&运算符为每一个运算对象的每位执行按位AND功能。AND在运算对象的相应位为1的时候结果才为1,其余情况结果都为0,如下表所示: op1 | op2 | 结果 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 假如你要对数13和12作AND操作,比如13&12。运算的结果为12,因为12的二进制为1100,遥13的二进制为1101,具体运算过程如下所示: 1101 //13 & 1100 //12 ------ 1100 //12 如果两个运算对象都为1,AND的结果就为1,或者结果就为0。因此,当你对两个运算对象执行AND操作的时候,你可以看到左边的两位结果位1,而右边两位的结果为0。当两个操作对象都位数字的时候,|操作符执行或操作,而^执行异或操作。或操作是说只要有一个运算对象为1结果就为1。下面的表格给出了或操作的结果: op1 | op2 | 结果 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 异或是指当运算对象不同时结果才为1,或者结果为0。下表给出了异或运算符的计算结果: op1 | op2 | 结果 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 最后,补运算符号是将运算对象的每一位倒置,即如果原来的位位1结果就为0,如果原来的位为0则结果为1。 通常,按位操作对于管理boolean标识是很有用的。假如,程序中有几个boolean标识来指示各种组件的状态:它是否可见、它是否可以拖拉等等。与其定义一个单独的boolean变量,不如你定义单一的变量flags,在flags的每一位都代表了这些标识的当前状态。然后你可以使用位操作来设置或者得到每一个标识。首先,为程序设置常数来指示各种标识。定义一个变量flags,它的位要根据每个flag的当前状态来设置。下面的代码将flags初始化为0,意味着所有的标识的初始状态都位0。 如下: static final int VISIBLE = 1; static final int DRAGGABLE = 2; static final int SELECTABLE = 4; static final int EDITABLE = 8; int flags = 0; 为了当有事物变得可见要设置"visible"标识,你可以使用以下的语句: flags = flags | VISIBLE; 为了测试可见性,你可以编写如下的语句: if ((flags & VISIBLE) == VISIBLE) { ... } 下一页是完整的程序BitwiseDemo。 下面是完整的程序BitwiseDemo: 这个程序的输出为: Flags are Visible and Draggable. Flags are now also Editable. 3.2.4 赋值运算符 你可以基本的赋值运算符=来为其它的赋值。MaxVariablesDemo程序使用=来初始化所有的本地变量: // 整型 byte largestByte = Byte.MAX_VALUE; short largestShort = Short.MAX_VALUE; int largestInteger = Integer.MAX_VALUE; long largestLong = Long.MAX_VALUE; //实数 float largestFloat = Float.MAX_VALUE; double largestDouble = Double.MAX_VALUE; //其它类型 char aChar = 'S'; boolean aBoolean = true; JAVA编程语言同样提供了几个简洁的赋值运算符以进行算术、移位或者按位操作或者赋值操作。假如你想为一个变量增加一个数并将结果指定给该变量,你可以这样做: i = i + 2; 当然你还可以这样来进行简化,主要是用了+=运算符: i += 2; 上面的两行是等价的。 下面的表列出了所有的赋值运算符和它们的等价形式: 运算符 | 用法 | 等价形式 | += | op1 += op2 | op1 = op1 + op2 | -= | op1 -= op2 | op1 = op1 - op2 | *= | op1 *= op2 | op1 = op1 * op2 | /= | op1 /= op2 | op1 = op1 / op2 | %= | op1 %= op2 | op1 = op1 % op2 | &= | op1 &= op2 | op1 = op1 & op2 | |= | op1 |= op2 | op1 = op1 | op2 | ^= | op1 ^= op2 | op1 = op1 ^ op2 | <<= | op1 <<= op2 | op1 = op1 << op2 | >>= | op1 >>= op2 | op1 = op1 >> op2 | >>>= | op1 >>>= op2 | Op1 = op1 >>> op2 | 3.2.5 其它的运算符 下面的表格给出了JAVA编程语言支持的其它运算符。 运算符 | 描述 | ?: | 作用相当于if-else语句 | [] | 用于声明数组,创建数组以及访问数组元素 | . | 用于访问对象实例或者类的类成员函数 | ( params ) | 意义一个以逗号分开的参数系列 | ( type ) | 将数值转换为一个的类型 | new | 创建一个新的对象或者新的数组 | instanceof | 决定是否第一个运算对象是第二个运算对象的一个实例 | 下面的表格给出了JAVA编程语言支持的其它运算符。 下面逐个介绍: (1)简化的if-else语句: 这个?:运算符是一个条件运算符号,它是一个简化的if-else语句: op1 ? op2 : op3 这个?:运算符号在op1为true的时候返回op2,或者返回op3。 (2) [ ] 运算符 你可以使用方括号来声明数组、创建数组并且访问数组中一个特殊的元素。以下是数组声明的一个例子: float[] arrayOfFloats = new float[10]; 前面的代码声明了一个数组来容纳10个浮点型的数字。以下给出访问第7个元素的方法: arrayOfFloats[6]; 这里注意第7个元素的下标是6,因为第一个元素的下标为0. (3).运算符 点运算符是访问对象实例或者类的类成员函数。 (4)()运算符 当声明或者调用一个方法的时候,你可以在(和)之间列出方法的参数。你可以利用()来指定一个空的参数列表。 这个运算符可以将数值转换为一个指定的类型。 你可以使用new运算符号来创建一个新对象或者一个新数组。以下是一个从java.lang包中的Interger类创建新的整型对象: Integer anInteger = new Integer(10); (6)instanceof运算符 instanceof操作符测试第一个运算对象是否是第二个运算对象的实例,比如: op1 instanceof op2 这里,op1必须是对象的名字而且op2必须是类的名字。如果对象直接或者间接地来自类,那么对象就被认为是类的一个实例。 3.3 表达式、语句和块 这一节教程讨论怎样将运算符和变量集中到表达式中去。表达式是代码的构建块。你同样还可以学到怎样构造语句和语句块。 变量和运算符是程序的基本构造块。你可以集中变量和运算符来组成表达式(表达式是执行计算并返回数值的代码段)。通过使用大括号{和}你可以创建代码块。 3.3.1表达式 表达式执行程序的工作。跟其它事情一样,表达式用于计算和为变量赋值并且帮助控制程序的执行流程。表达式任务有两重:利用表达式的元素来执行计算;返回计算结果的数值。 就象前面的章节讨论的,运算符要返回一个数值,因此运算符的使用就是一个表达式。如下的程序: … char aChar = 'S'; boolean aBoolean = true; // 显示 System.out.println("The largest byte value is " + largestByte); ... if (Character.isUpperCase(aChar)) { ... } 每一个条语句执行一个操作并且返回一个数值。如下表所示: 表达式 | 方式 | 返回数值 | aChar = 'S' | 将字符'S'赋给字符变量aChar。 | 返回赋值以后的aChar值 | "The largest byte value is " + largestByte | 连接字符串"The largest byte value is "和largestByte转换为字符串的数值 | 返回结果的字符串:The largest byte value is 127 | Character.isUpperCase(aChar) | 调用isUpperCase方法 | 返回方法的数值:true | 表达式返回数值的数据类型依靠于使用在表达式中的元素。这个表达式aChar = 'S'返回一个字符型,因为赋值操作符返回相同数据类型(操作符和aChar以及'S'都是字符型)的数值。从其它表达式可以看出,表达式可以返回一个boolean数值、字符串等等。 只要表达式子需要的数据类型匹配其它的数据类型,JAVA编程语言允许你将多个小的表达式构造复合的表达式。下面是一个复合的表达式例子: x * y * z 在这个消息的例子中,各个运算对象的先后顺序不是很重要因为乘法的结果跟顺序无关。但是这对于其它表达式有时并不适合。例如,下面的表达式就跟顺序有关了,它关系到是哪个先乘还是先加的问题: x + y / 100 你可以利用括号(和)来正确指定表达式。比如为了避免上面表达式子的含糊性,你可以这样编写: (x + y)/ 100 //推荐使用这种方法,以养成良好的编程习惯 如果你没有在复合的表达式中指定各个运算的顺序,那么运算的顺序将取决于表达式中的运算符的优先顺序,即优先顺序高的先进行计算。比如除法运算符的优先顺序高于加法运算符,所以以下的两条语句是等价的。 x + y / 100 x + (y / 100) 虽然它们是等价的,还是推荐是推荐使用第二种。在编写复合表达式的时候,推荐你利用圆括号(和)来清楚指定计算的顺序,这便于程序的阅读以及维护。 下面的表格给出了各种运算符的优先顺序。前面的运算符优先顺序高于后面的运算符,同一行的运算符,其优先顺序相同: 后缀运算符 | [] . (params) expr++ expr-- | 单元运算符 | ++expr --expr +expr -expr ~ ! | 创建运算符 | new (type)expr | 乘法运算符 | * / % | 加法运算符 | + - | 移位运算符 | << >> >>> | 关系运算符 | < > <= >= instanceof | 相等与不等 | == != | 按位运算符 | AND & | 按位运算符 | OR ^ | 按位运算符 | OR | | 逻辑运算符 | AND && | 逻辑运算符 | OR || | 条件运算符 | ? : | 赋值运算符 | = += -= *= /= %= &= ^= |= <<= >>= >>>= | 当相同优先级的运算符出现在相同的表达式中,所有的双元运算符的运算顺序是从左到右,而赋值运算符是从右到左计算的 3.3.2 语句 语句粗略地等价于自然语言的句子。一个语句组成了一个执行的完整单元。以下表达式的类型可以通过用逗号(;)终止表达式来组成一条语句: - 赋值语句
- 任何使用++或者--的语句
- 方法调用
- 对象创建表达式
这些语句的种类称为表达式语句。以下是表达式语句: aValue = 8933.234; //赋值语句 aValue++; //增量语句 System.out.println(aValue); //方法调用语句 Integer integerObject = new Integer(4); //对象创建语句 除了上面的这些表达式语句,还有另外两种语句: (1)定义变量的声明语句。以下给出声明变量的语句: double aValue = 8933.234; // 声明语句 (2)控制流程语句,它控制了语句执行的先后顺序。For循环语句和if语句都是控制流程语句的例子。 3.3.3 块 块是一组处在{和}之间的零条或者多条语句,它可以使用在程序的任何地方。下面的例子给出了两个块,每一个块中包含了一条语句: if (Character.isUpperCase(aChar)) { System.out.println("The character " + aChar + " is upper case."); } else { System.out.println("The character " + aChar + " is lower case."); } 3.4 控制流程语句 程序使用控制流程语句来有条件地执行语句、循环语句或者跳转到程序中的其它部分执行语句。这节教程介绍怎样利用比如if-else和while来控制你的程序流程。 当你编写程序的时候,你在文件中键如语句。如果你没有使用控制流程语句,编译器将以从左到右、从上到下的顺序执行这些语句。你可以在你的程序中使用控制流程语句来有条件地执行语句、来重复执行语句块地代码、改变程序执行的顺序等等。比如,在下面的代码段中,if语句就有条件执行了括号中的System.out.println,这里的条件是Character.isUpperCase(aChar)的返回值: char c; ... if (Character.isUpperCase(aChar)) { System.out.println("The character " + aChar + " is upper case."); } JAVA编程语言提供了几个控制流程语句,在下表给出了: 语句 | 关键字 | 循环语句 | while, do-while , for | 判断语句 | if-else, switch-case | 异常处理 | try-catch-finally, throw | 分支语句 | break, continue, label:, return | 控制流程语句的基本格式为: 控制流程语句描述 { 语句(参数) } 如果块中只有一条语句,大括号{和}并不是必要的。但是这里推荐使用{和},因为代码会更容易阅读,避免在修改代码的时候发生错误。 控制流程语句有: - while和do-while语句
- for语句
- if-else语句
- switch语句
- 异常处理语句
- 分支语句
这里注意,虽然goto是一个保留字,它在C语言中也是一个控制流程语句,但是当前JAVA编程语言还不支持这个goto语句。 好吧,我们开始对各个控制流程语句逐个介绍。 3.4.1 while和do-while语句 你可以使用while语句当条件保持为true的时候,持续执行语句块。While语句的通常语法为: while (expression) { statement } 首先,while语句执行表达式,它将返回一个boolean数(true或者false)。如果表达式返回true,while语句执行相应的语句。While语句继续测试表达式并执行块代码直到表达式返回false。 下面给出一个例程WhileDemo,它使用while语句来浏览字符串的各个字符并复制字符串直到程序找到字符g为止: public class WhileDemo { public static void main(String[] args) { String copyFromMe = "Copy this string until you " + "encounter the letter 'g'."; StringBuffer copyToMe = new StringBuffer(); int i = 0; char c = copyFromMe.charAt(i); while (c != 'g') { copyToMe.append(c); c = copyFromMe.charAt(++i); } System.out.println(copyToMe); } } 最后一行打印出来的数值为:Copy this strin. JAVA编程语言提供了另外一个语句,它跟while语句和相似,即do-while语句。Do-while的语法为: do { statement(s) } while (expression); 不象while语句,do-while语句是先执行循环中的语句后再计算表达式的,所以do-while语句就至少执行一次语句。 下面是对上面的程序利用do-while来修改而得的: public class DoWhileDemo { public static void main(String[] args) { String copyFromMe = "Copy this string until you " + "encounter the letter 'g'."; StringBuffer copyToMe = new StringBuffer(); int i = 0; char c = copyFromMe.charAt(i); do { copyToMe.append(c); c = copyFromMe.charAt(++i); } while (c != 'g'); System.out.println(copyToMe); } } 最后一行打印出来的数值为:Copy this strin 3.4.2 for语句 for语句提供了一个简便的方法来进行循环。For语句的语法如下: for (初始条件;终止条件;增量) { 语句 } 初始条件是初始化循环的表达式,它在循环开始的时候就被执行一次。而终止条件决定什么时候终止循环。这个表达式在每次循环的过程都被计算。当表达式计算结果为false的时候,这个循环结束。最后,增量是循环一次增加多少(即步长)的表达式。所有的这些都是可选的。实际上,为了实现无限制的循环,这三个表达式都可以省略。 for ( ; ; ) { // 无限制的循环 ... } 下面是一个例程ForDemo,它使用了一个for语句来打印一个数组的所有元素: public class ForDemo { public static void main(String[] args) { int[] arrayOfInts = { 32, 87, 3, 589, 12, 1076, 2000, 8, 622, 127 }; for (int i = 0; i < arrayOfInts.length; i++) { System.out.print(arrayOfInts[i] + " "); } System.out.println(); } } 这个程序的输出为: 32 87 3 589 12 1076 2000 8 622 127. 这里注意你可以在for循环的初始化语句中声明一个本地变量。这个变量的作用域只是在for语句的块中,它可以用在终止条件语句和增量表达式中。如果控制for循环的变量没有用在循环的外部,最好的方法是在初始化表达式中声明这个变量。例如i、j、k等经常用来在for控制语句中,在for循环的初始化表达式中声明它们来限制它们的生命周期以减少错误 3.4.3 if-else语句 if语句可以使程序根据一些条件有选择地执行语句。举个例子,假如你的程序根据boolean变量DEBUG的值来打印了调试信息。如果DEBUG是true,你的程序就打印出调试信息,比如变量x的数值;或者就不打印。具体的程序如下: if (DEBUG) { System.out.println("DEBUG: x = " + x); } 上面是一个简单的if语句的例子。总得说来,if语句的语法格式为: if (表达式) { 语句(参数) } 如果你想在if判断表达式为false的时候执行不同的语句,你可以使用else语句。举个另外的例子吧,假如你的程序需要执行不同的工作,主要是根据用户是点击OK键还是击警告窗口中的其它按钮。你可以使用if和else语句来实现这个目的: . . . // response is either OK or CANCEL depending // on the button that the user pressed . . . if (response == OK) { // 在这里添加执行OK动作的代码 } else { // 在这里添加执行Cancel动作的代码 如果if部分为false,则执行else块。另外一种else语句的格式是else if,它根据执行另外的表达式执行语句。一个if语句可以跟着任意个else if语句,但只能有一个else语句。下面是一个例程IfElseDemo,它根据测试分数的数值来指定一个等级:90%或者更高为A,80%或者更高为B等等。 public class IfElseDemo { public static void main(String[] args) { int testscore = 76; char grade; if (testscore >= 90) { grade = 'A'; } else if (testscore >= 80) { grade = 'B'; } else if (testscore >= 70) { grade = 'C'; } else if (testscore >= 60) { grade = 'D'; } else { grade = 'F'; } System.out.println("Grade = " + grade); } } 程序的输出为: Grade = C 这时你可以注意到testscore的数值可以满足组合if条件76 >= 70 and 76 >= 60,但是当系统处理组合if语句的时候,一旦条件满足,适当的语句就将被执行(grade = 'C';),并且不需要计算剩余的条件控制就跳出if语句。JAVA编程语言支持运算符?:,它是一个简化的if语句。 下面再看看上面教程中的MaxVariablesDemo程序: if (Character.isUpperCase(aChar)) { System.out.println("The character " + aChar + " is upper case."); } else { System.out.println("The character " + aChar + " is lower case."); } 下面你可以利用?:运算符来重写这段代码: System.out.println("The character " + aChar + " is " + (Character.isUpperCase(aChar) ? "upper" : "lower") + "case."); 如果这个isUpperCase方法返回true,这个?:运算符返回字符串"upper" 。或则,它就返回字符串"lower" 3.4.4 switch语句 使用switch语句可以根据一个整型表达式有条件地执行语句。下面的例程SwitchDemo,声明了一个整型变量month,它的数值代表了月份。这个程序显示了月份的名字,主要是根据month的数值并且使用了switch语句。 public class SwitchDemo { public static void main(String[] args) { int month = 8; switch (month) { case 1: System.out.println("January"); break; case 2: System.out.println("February"); break; case 3: System.out.println("March"); break; case 4: System.out.println("April"); break; case 5: System.out.println("May"); break; case 6: System.out.println("June"); break; case 7: System.out.println("July"); break; case 8: System.out.println("August"); break; case 9: System.out.println("September"); break; case 10: System.out.println("October"); break; case 11: System.out.println("November"); break; case 12: System.out.println("December"); break; } } } 这个switch语句计算它的表达式,在这个例子中是计算month的数值,然后计算适当的case 语句。这样,程序的输出为:August。 当然,你可以使用if语句来实现它: int month = 8; if (month == 1) { System.out.println("January"); } else if (month == 2) { System.out.println("February"); } . . . 决定使用if语句还是switch语句的关键主要是根据可读型以及其它因素。if语句可以在更大范围或者条件来决定,而switch只有根据单个整型变量来做决定。同时,提供给case语句的数值必须是单一的。另外一点是,switch语句在每个case之后有一个break语句。每个break语句终止了swtich语句,并且控制流程继续switch块之后的第一个语句。break语句是必须的,因为没有它,case语句就会失效,也就是说,没有break语句,控制流程按顺序执行case语句。 下面的程序SwichDemo2将介绍为什么它是有效的; 这个程序的输出为: Number of Days = 29 最后的break语句是不需要的,因为流程将跳出switch语句了。但是,我们推荐使用为最后一个case语句使用一个break以防你需要还增加更多的case语句。这就使得更容易修改代码以及减少错误。 一般地,你可以使用break来在分支语句中终止循环。最后你可以在switch最后使用缺省地语句来处理所有的上面没有处理的情况。 int month = 8; . . . switch (month) { case 1: System.out.println("January"); break; case 2: System.out.println("February"); break; case 3: System.out.println("March"); break; case 4: System.out.println("April"); break; case 5: System.out.println("May"); break; case 6: System.out.println("June"); break; case 7: System.out.println("July"); break; case 8: System.out.println("August"); break; case 9: System.out.println("September"); break; case 10: System.out.println("October"); break; case 11: System.out.println("November"); break; case 12: System.out.println("December"); break; default: System.out.println("Hey, that's not a valid month!"); break; } 3.4.5 异常处理语句 JAVA编程语言提供一个异常机制用来帮助程序报告和处理错误。当一个错误发生的时候,程序就产生一个异常。即正常的程序路程被中断并且系统缓建想找到一个异常处理:一个能处理特殊类型错误的代码块。这个异常福利可以视图恢复错误或者如果它决定了错误是不可修复的,它提供一个较好退出程序的方法。 有三条语句可以处理异常: - try语句,它可以在异常产生的地方识别语句块。
- catch语句,它必须和try语句一起用,它可是识别可以处理特定异常错误的语句块。这个语句是在如果有一个特定的异常发生在try块中就被执行。
(3)finally语句,它必须跟try语句一起用,它可以识别语句块,这些语句块是在不管在try块中是否有错误发生被执行的。 下面是这三个语句的语法格式: try { 语句(参数) } catch (异常错误类型 名字) { 语句(参数) } finally { 语句(参数) } 上面简述了由JAVA编程语言提供的用来报告和处理错误的语句。但是,另外的因素和原因,比如运行和检查异常之间的差异以及异常类的分级结构(代表异常的不同种类)在使用异常机制的时候起着重要的作用。 3.4.6 分支语句 JAVA编程语言支持下面的三种分支结构: break语句 continue语句 return语句 下面逐个介绍: (1)break语句 break语句有两种形式:未标志的和标志的。你在前面就已经看过了未标志的break语句。一个未标志的break语句终止swtich语句,控制流程马上转到switch语句下方的语句。下面的例程BreakDemo,它包含了一个for循环查找数组中特定的数值: public class BreakDemo { public static void main(String[] args) { int[] arrayOfInts = { 32, 87, 3, 589, 12, 1076, 2000, 8, 622, 127 }; int searchfor = 12; int i = 0; boolean foundIt = false; for ( ; i < arrayOfInts.length; i++) { if (arrayOfInts[i] == searchfor) { foundIt = true; break; } } if (foundIt) { System.out.println("Found " + searchfor + " at index " + i); } else { System.out.println(searchfor + "not in the array"); } } } 当数值被找到的时候,这个break语句终止了for循环。控制流程就转到for语句的下面的语句继续执行。这个程序的输出为: Found 12 at index 4 未标志形式的break语句被用来终止内部的switch、for、while或者do-while。而标志形式的break语句终止一个外部的语句,它是通过在break语句中使用一个标志来实现的。下面的例程BreakWithLabelDemo跟前面的例子有点相似,只不过它是在一个两维数组中搜索一个数值。利用两个嵌套的for循环遍历了整个数组。当数值被找到了,标志形式的break语句就终止标志的search语句,这个search语句是在for循环外部的: public class BreakWithLabelDemo { public static void main(String[] args) { int[][] arrayOfInts = { { 32, 87, 3, 589 }, { 12, 1076, 2000, 8 }, { 622, 127, 77, 955 } }; int searchfor = 12; int i = 0; int j = 0; boolean foundIt = false; search: for ( ; i < arrayOfInts.length; i++) { for (j = 0; j < arrayOfInts[i].length; j++) { if (arrayOfInts[i][j] == searchfor) { foundIt = true; break search; } } } if (foundIt) { System.out.println("Found " + searchfor + " at " + i + ", " + j); } else { System.out.println(searchfor + "not in the array"); } } } 这个程序的输出为: Found 12 at 1, 0 这个语法看起来可能有点费解。break语句终止了标志语句,它不能将控制流程转到这个标志处。控制流程迅速将标志(终止的)后面的语句。 (2)continue语句 你可以使用continue语句来跳过当前的for、while或者do-while循环。未标志形式跳到内部循环体的末尾处并且计算控制循环的boolean表达式,跳过循环的其它部分。下面的例程ContinueDemo遍历字符串中的所有字母。如果当前字母不是一个p,contiue语句就忽略循环的其它部分并且处理下一个字符。如果它是一个p字母,程序就对计数器增1,再将p转换维大写字母: public class ContinueDemo { public static void main(String[] args) { StringBuffer searchMe = new StringBuffer( "peter piper picked a peck of pickled peppers"); int max = searchMe.length(); int numPs = 0; for (int i = 0; i < max; i++) { //interested only in p's if (searchMe.charAt(i) != 'p') continue; //process p's numPs++; searchMe.setCharAt(i, 'P'); } System.out.println("Found " + numPs + " p's in the string."); System.out.println(searchMe); } } 这个程序的输出为: Found 9 p's in the string. Peter PiPer Picked a Peck of Pickled PePPers |