第2章 Java程序设计基础    本章主要介绍基本数据类型、运算符、控制语句、字符串及数组等Java程序设计所要掌握的基础知识。基本数据类型部分主要介绍Java中的数据类型、标识符与关键字、常量与变量等内容;运算符与表达式部分主要介绍算术运算符、位运算符、布尔运算符、赋值运算符、关系运算符、条件运算符及它们的优先级;控制语句部分主要介绍选择结构、循环结构及跳转结构所使用的各种语句及其特性;字符串部分主要介绍String类与StringBuffer类的使用方法与注意事项;最后介绍数组概念及一维数组、多维数组及对象数组的定义与使用。 2.1 Java的基本数据类型   数据类型是程序设计中最基本的概念。Java语言中的数据类型非常丰富,所有变量都必须属于某一个数据类型。不同的数据类型所存储的值的种类,以及可能进行的操作方式都是不同的。本节主要讲解Java的基本数据类型,常量、变量的基本概念,以及变量的定义方法。 2.1.1 数据类型   Java的数据类型可以分为两大类:基本数据类型与复合数据类型。其中,基本数据类型包括整型、浮点型、布尔型和字符型;复合数据类型包括数组、接口和类,如图2.1所示。 图2.1 Java的数据类型 2.1.2 标识符与关键字   在程序设计语言中,用来标识成分(如类、对象、变量、常量、方法和参数名称等)存在与唯一性的名字,称为标识符。标识符是由字母、下划线或美元符号$开头,后接若干任意字符的字符串。在程序设计中,程序设计者可以根据需要,自由地确定简单且易于理解的标识符,但同时也应该遵循一定的语法规则。   Java对标识符定义的规定如下。 * 标识符可以由字母、数字、下划线(_)或美元符号($)组成。 * 标识符必须以字母、下划线(_)或美元符号($)开头。 * 标识符的长度不限,但在实际应用中不宜过长。 * Java语言中的标识符区分大小写,例如Myworld与myworld是不同的。 * 标识符不能与关键字(保留字)同名。   下面举例说明。   合法标识符:xyz、_xyz、$xyz。   非法标识符:5xyz(首字符不能为数字)、Xyz@(含有非法字符)、if(关键字)。   在Java语言中,有一部分标识符被确定为关键字(保留字),因为在程序设计中,该部分标识符已经被使用或者被赋予特定意义。表2.1为Java语言中的关键字。 表2.1 Java中的关键字 abstract boolean break byte case cast catch char class const continue default do double else extends final finally float for future generic goto if implements import inner instanceof int long native new null operator outer package private protected public rest return short static super switch synchronized this throw transient try var void volatile while    2.1.3 常量   常量是指在程序运行过程中固定不变的量。常量共包含以下6类。 1.整型常量   整型常量包括4种类型:int、long、short、byte。4种类型的取值范围如表2.2所示。 表2.2 Java整型数据的取值范围 数据类型 所占字节 表示范围 int(整型) 4 -2147483648~2147483647 long(长整型) 8 -9223372036854775808~9223372036854775807 short(短整型) 2 -32768~32767 byte(位) 1 -128~127      整数类型的文字可使用十进制、八进制和十六进制表示。首位为"0"表示八进制的数值;首位为"0x"表示十六进制的数值。请看下面的例子:    8 表示十进制值8 075 表示八进制数值75(也就是十进制数61) 0x9ABC 表示十六进制的数值9ABC(也就是十进制数39612)      整数默认为int类型,如在其后有一个字母"L"则表示一个long值(也可以用小写字母"l")。由于小写字母"l"与数字"1"容易混淆,因而,建议大家采用大写字母"L"。   上面所说的整数long的形式如下:    8L 表示十进制值8,是一个long值 075L 表示八进制数值75,是一个long值 0x9ABCL 表示十六进制的数值9ABC,是一个long值    2.浮点型常量   为了提高计算的准确性,由此引入了浮点型数据。浮点型数据包括两种:单精度和双精度,取值范围如表2.3所示。 表2.3 浮点型数据的取值范围 数据类型 所占字节 表示范围 float(单精度) 4 -3.4E38~3.4E38 double(双精度) 8 -1.7E308~1.7E308      如果不明确指明浮点数的类型,浮点数默认为double。下面是几个浮点数:    3.14159 (double型浮点数) 2.08E25 (double型浮点数) 6.56f (float型浮点数)      在两种类型的浮点数中,double类型的浮点数具有更高的精度。 3.字符型常量   字符型常量是由一对单引号包含起来的单个字符,例如'a','2'。Java中的字符型数据是16位无符号型数据,它表示Unicode集,而不仅仅是ASCII集。   与C语言类似,Java也提供转义字符,以反斜杠(\)开头,将其后的字符转变为另外的含义。表2.4列出了Java中的转义字符。 表2.4 Java中的转义字符 转义字符 含 义 \n 换行 \t 垂直制表符 \b 退格 \r 回车 \f 走纸换页 \\ 反斜杠 \' 单引号    续表 转义字符 含 义 \" 双引号 \ddd 八进制数 \xdd 十六进制数 \udddd 泛代码字符    4.字符串型常量   字符串型常量是由一对双引号包含的,由0个或多个字符组成的一个字符序列。 5.布尔型常量   布尔型常量只能取值为true和false,其中true表示逻辑真,false表示逻辑假。 6.null常量   null常量只有一个值,用null表示为空。 2.1.4 变量   变量就是在程序运行过程中值可以变化的量。变量名是用户为变量定义的标识符,变量的值存储在系统为变量分配的存储单元中。Java变量的类型包括:整型变量、浮点型变量、布尔型变量、字符型变量、字符串型变量、数组变量、对象(引用)变量等。 1.变量的定义   在Java程序设计过程中,变量需要先定义后使用。定义变量的时候需要确定变量的类型和变量的标识符。   声明Java变量的语法格式如下:    type var1[,ver2][,...] type var1[=init1][,var2[=init2],...]    其中,type为变量类型名,var为变量名,init为初始值,方括号中的内容为可选项。例如:    int a = 1, b, c; //定义整型变量a、b、c,并为a赋初值1 float f = 1.12f; //定义单精度浮点型变量f,并赋初值 double d = 32.34; //定义双精度浮点型变量d,并赋初值 char char1 = 'c'; //定义字符型变量char1,并赋初值 String s = "abc"; //定义字符串型变量s,并赋初值 boolean b = true; //定义布尔型变量b,并赋初值    2.变量基本数据类型的转换   在Java程序中,将一种数据类型的常数或变量转换为另外一种数据类型,称为类型转换。当多种数据类型的数据混合运算时,不同类型的数据必须先转化为同一种数据类型。类型转换有两种:自动类型转换(或称隐含类型转换)和强制类型转换。   1) 自动类型转换   自动类型转换遵循的规则是把低精度类型转换为高精度类型。当把占用位数较短的数据转化成占用位数较长的数据时,Java执行自动类型转换,不需要在程序中做出特别的 说明。   例如,下面的语句把int型数据赋值给long型数据,在编译时不会发生任何错误:    int i = 10; long j = i;      假若对主数据类型执行任何算术运算或按位运算,在正式执行运算之前,"比int小"的数据(char、byte、short)会自动转换成int,这样一来,最终生成的值就是int类型。   整型、实型、字符型数据同样可以混合运算。运算中,不同类型的数据先转化为同一种类型,然后进行运算,转换遵循从低级到高级的原则。通常,表达式中最大的数据类型是决定了表达式最终结果大小的那个类型。例如:若将一个float值与一个double值相乘,结果就是double值;如将一个int值和一个long值相加,则结果为long值。   2) 强制类型转换   当两种类型彼此不兼容,或者目标类型的取值范围小于源类型时,就必须进行强制类型转换。强制类型转换的语法如下:    目标类型 变量 = (目标类型)值      经过强制类型转换,将得到一个在"()"中声明的数据类型的数据,该数据是从指定变量所包含的数据转换而来的。例如:    int x,y;//定义int型变量x,y byte z;//定义byte型变量z //强制类型转换 x = (int)22.11 + (int)11.22; y = (int)'a' + (int)'b'; z = (byte)(x+y);    3.变量的作用域   变量的作用域是指可访问该变量的代码域。声明一个变量的同时也就指明了变量的作用域。按作用域来分,变量可以有下面几种:局部变量、类变量、方法参数、例外处理参数。局部变量在方法或方法的部分代码中声明,其作用域为它所在的代码块(整个方法或方法中的某块代码)。   Java语言用大括号将若干语句组成语句块,变量的有效范围是声明它的语句所在的语句块,一旦程序的执行离开了这个语句块,将无法继续调用其中的变量。例如:    if (...) { int i = 25; ... } System.out.println("The value of i = " + i); // 错误      最后一行无法进行汇编是因为变量i已经超出了作用域。i的作用域是"{"和"}"之间的代码块。在该代码块外,变量i就不存在了,所以无法调用。改正的方法是可以将变量的声明移到if语句块的外面,或者是将println方法调用移动到if语句块中。 2.2 Java运算符与表达式 2.2.1 算术运算符   算术运算符分为单目运算符和双目运算符,具体运算符如表2.5所示。 表2.5 算术运算符 类 别 运 算 符 用 法 描 述 单目运算符 + +op1 正值 - -op1 负值 ++ op1++,++op2 自加1 -- op1--,--op2 自减1 双目运算符 + op1 + op2 加 - op1-op2 减 * op1 * op2 乘 / op1 / op2 除 % op1 % op2 取模      在算术运算中,有以下值得注意的地方。 * 算术运算符的总体原则是先乘除、再加减,括号优先。 * 整数除法会直接砍掉小数,而不是进位。 * 取模运算符的操作数可以为浮点数。如:37.2%10=7.2。这点与C语言不同。 * Java也可用一种简写形式进行运算,并同时进行赋值操作。例如,为了将10与变量x相加,并将结果赋给x,可用x+=10。 * Java对加运算符进行了扩展,使它能够进行字符串的连接,如"abc"+"def",得到串"abcdef"。 * 前缀++、--与后缀++、--的区别如下。 * ++i(前缀++)在使用i之前,使i的值加1,因此执行完++i后,整个表达式和i的值均为i+1;i++(后缀++)在使用i之后,使i的值加1,因此执行完i++后,整个表达式的值为i,而i的值变为i+1。 * --i(前缀--)在使用i之前,使i的值减1,因此执行完--i后,整个表达式和i的值均为i-1;i--(后缀--)在使用i之后,使i的值减1,因此执行完i--后,整个表达式的值为i,而i的值变为i-1。 2.2.2 关系运算符   关系运算符用来比较两个数据,确定一个操作数与另外一个操作数之间的关系,返回布尔类型的数据true或false。关系运算符都是二元运算符,如表2.6所示。 表2.6 关系运算符 运 算 符 用 法 返回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    2.2.3 布尔运算符   用布尔运算符进行布尔逻辑运算时,布尔型变量或表达式的组合运算可以产生新的布尔值。布尔运算符如表2.7所示。 表2.7 布尔运算符 运 算 符 用 法 描 述 && op1 && op2 求与运算。如果op1为false,不计算op2 || op1 || op2 求或运算。如果op1为true,不计算op2 ! ! op1 求反运算。取与op1相反的值    2.2.4 位运算符   位运算符用来对二进制位进行操作,在Java语言中,位运算符的操作数只能为整型和字符型数据。位运算符如表2.8所示。 表2.8 位运算符 运 算 符 用 法 描 述 ~ ~op1 按位取反 & op1 & op2 按位与 | op1 | op2 按位或 ^ op1 ^ op2 按位异或 >> op1 >> op2 op1右移op2位 << op1 << op2 op1左移op2位 >>> op1 >>> op2 op1无符号右移op2位    2.2.5 赋值运算符   赋值运算符(=)可以把一个数据赋给另外一个变量,相当于将左边的数据放到右边的变量里面去。例如:var = data;。 * Java语言支持简单算术运算符和赋值运算符的联合作用。 * 如果左侧变量的数据类型的级别高,则把右侧的数据转换为相同的高级数据类型,然后赋给左侧的变量,如表2.9所示。 * 如果右边高,则需要使用强制类型转换运算符。 表2.9 类型转换表 原 类 型 允许自动转换的目标类型 byte short、int、long、float、double short int、long、float、double char int、long、float、double int long、float、double long float、double float double double 无 boolean 无    提示: 赋值运算符右端的表达式也可以是赋值表达式,形成连续赋值的情况,例如:a=b=c=10;。由于赋值运算符具有右结合性,因此先执行右边的运算符,但一般情况下建议不用。   在赋值符(=)前加上其他运算符,即构成扩展赋值运算符,如表2.10所示。例如:a = a + 6用扩展赋值运算符可表示为a += 6。这样一来既可加快输入速度,又可加快计算机的运算速度。扩展赋值运算符的通用格式如下:    var op = expression      即相当于:    var = var op expression    表2.10 扩展赋值运算符及其等价表达方式 运 算 符 用 法 等价表达式 += 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<>>= op1>>>op2 op1=op1>>>op2    2.2.6 条件运算符   条件运算符(? :)是一个特殊的操作符,它支持条件表达式,即一个简单的双重选择语句if-else的简缩。条件运算符为三元运算符,它的一般形式如下:    exp1?exp2:exp3      表达式exp1的结果为布尔型的值;表达式exp2与表达式exp3的数据类型相同;如果exp1的值为真,则表达式的值为exp2,否则为exp3。例如:    answer = type==1?right:wrong;      上面表达式表示,如果变量type的值等于1,则answer = right,否则answer = wrong。这样利用条件运算符可以简单地实现if-else(2.3.1节会详细讲解)的功能。 2.2.7 表达式和运算符的优先级   表达式可以是由常量、变量、运算符、方法调用的序列,它执行这些元素指定的计算并返回某个值。在对一个表达式进行计算时,要按运算符的优先顺序从高向低进行。同一级别里的运算符具有相同的优先级,算术运算符具有左结合性,赋值运算符具有右结合性。表2.11给出了运算符的优先级。 表2.11 运算符的优先级 优 先 级 运 算 符 从 高 到 低 . [] () ++ -- ! ~ instanceof new * / % + - >> >>> << < > <= >= & ^ | && || ?: = += -= *= /= %= ^= &= |= <<= >>= >>>=      表2.11中,"."为分量运算符,"[]"为下标运算符,"new"为内存分配运算符,"instanceof"为实例运算符。另外,在表达式中使用括号"()",可以使表达式的结构更清晰,使程序更具可读性。 2.3 Java控制语句   为了完成程序状态的改变,编程语言通常使用控制语句来产生执行流,如决定程序顺序执行还是分支执行。Java的程序控制语句分为选择、循环和跳转几类。选择语句是根据表达式结果或变量状态来选择执行不同的代码。循环语句使程序能够重复执行一个或一个以上的语句。跳转语句允许程序以非线性的方式执行。下面将分析Java 的所有控制语句。如果你熟悉C/C++语言,那么掌握Java语言的控制语句将很容易。事实上,Java的控制语句与C/C++语言中的语句几乎完全相同。当然它们还是有一些差别的,尤其是break语句与continue语句。 2.3.1 选择结构   分支语句提供了一种控制机制,使得程序可以不执行某些语句,而转去执行特定的语句。 1.条件语句if-else   if-else语句根据判定条件的真假来执行两种操作中的一种,它的格式如下:    if(boolean-expression) statement1; [else statement2;]    * 布尔表达式boolean-expression是返回布尔型数据的表达式。 * 每条单一的语句后都必须有分号。 * 语句statement1、statement2 可以为复合语句,这时要用大括号{}括起来。建议对单一的语句也用大括号括起来,这样程序的可读性强,而且有利于程序的扩充。{}外面不加分号。 * else子句是任选的。 * 若布尔表达式的值为true,则程序执行statement1,否则执行statement2。 * if-else语句的一种特殊形式如下:       if(expression1){     statement1;    }else if (expression2){     statement2;    }...    }else if (expressionM){     statementM;    }else {     statementN;    }      else子句不能单独作为语句使用,它必须和if配对使用。else总是与离它最近的if配对。可以通过使用大括号{}来改变配对关系。 2.多分支语句switch   switch语句是Java的多路分支语句。如果程序有多个分支则可以使用switch,它提供了一个比一系列if-else-if语句更好的选择。switch语句的通用形式如下:    switch (expression) { case value1: statement1; break; case value2: statement2; break; ... case valueN: statementN; break; default: default statement; }      switch语句的执行过程如下:表达式的值与每个case语句中的常量作比较。如果发现一个与之相匹配的,则执行该case语句后的代码。如果没有任何case常量与表达式的值相匹配,则执行default语句。   表达式expression必须为byte、short、int或char类型。每个case语句后的值value必须是与表达式类型兼容的特定常量(它必须为常量,而不是变量)。重复的case值是不允许的。在case语句序列中的break语句将引起程序流从整个switch语句退出。当遇到一个break语句时,程序将从整个switch语句后的第一行代码开始继续执行。这有一种"跳出"switch语句的效果。default语句是可选的。如果没有相匹配的case语句,也没有default语句,则什么也不执行。   可以将一个switch语句作为一个外部switch语句的语句序列的一部分,这称为嵌套switch语句。在使用嵌套语句时,每个switch语句均定义了自己的块,因此外部switch语句和内部switch语句的case常量不会产生冲突。   概括起来说,switch语句有以下3个重要的特性需注意。 * switch语句不同于if语句的是,switch语句仅能测试相等的情况,而if语句可以计算任何类型的布尔表达式。也就是说,switch语句只能寻找case常量间某个值与表达式的值相匹配的情况。 * 在同一个switch语句中没有两个相同的case常量。当然,外部switch语句中的case常量可以和内部switch语句中的case常量相同。 * switch语句通常比一系列嵌套if语句更有效。 2.3.2 循环结构   循环语句的作用是反复执行某一段代码,直到满足终止循环的条件为止,一个循环一般应包括四部分内容。 * 初始化部分:用来设置循环的一些初始条件,如计数器清零等。 * 循环体部分:这是反复循环的某一段代码,可以是单一的一条语句,也可以是复合语句。 * 迭代部分:这是在当前循环结束、下一次循环开始前执行的语句,常常用来使计数器加1或减1。 * 终止部分:通常是一个布尔表达式,每一次循环都要对该表达式求值,以验证是否满足循环终止条件。   Java中提供的循环语句有while语句、do-while语句和for语句,下面分别进行介绍。 1.while循环语句   while语句是Java最基本的循环语句。当它的控制表达式为真时,while语句重复执行一个语句或语句块。它的通用格式如下:    while(condition) { body }      条件表达式condition为真时,循环体就会被执行;为假时,程序控制就传递到循环后面紧跟的语句行,循环体(body)一次也不会被执行。条件表达式可以是任何布尔表达式。如果只有单个语句需要重复,大括号可以不写。 2.do-while循环语句   do-while与while语句最大的区别是:在循环开始时,即使条件表达式为假,循环也至少要执行一次,因为它的条件表达式在循环的结尾。换句话说,do-while在一次循环结束后再测试终止表达式,而不是在循环开始的时候。do-while的通用格式如下:    do { body } while (condition);      与while语句一样,条件表达式condition必须是一个布尔表达式。do-while循环总是先执行一次循环体,然后再判断条件表达式的真假。如果表达式为真,则循环继续;否则循环结束。 3.for循环语句   在前面的内容中曾使用过for循环,在这里将详细介绍for循环的使用。for循环是一种功能强大、结构形式灵活的循环,其通用格式如下:    for(initialization; condition; iteration) { body }      for循环的执行过程如下:   第一步,当循环启动时,先执行其初始化initialization部分。通常,这是设置循环控制变量值的一个表达式,作为控制循环的计数器,初始化表达式仅被执行一次。   第二步,判断条件表达式condition的值,如果这个表达式为真,则执行循环体;如果为假,则循环终止。条件condition必须是布尔表达式,它通常将循环控制变量与目标值相比较。   第三步执行循环体的迭代部分。这部分通常是使循环控制变量增加或减少的一个表达式。   接下来重复循环,首先计算条件表达式的值,然后执行循环体,接着执行迭代表达式。这个过程不断重复直到控制表达式变为假。   Java语言中的for语句与C语言、C++语言中的for语句有所不同,Java允许在for循环内声明变量,如for(int n=10; n>0; n--)。在for循环内声明变量时必须注意,该变量的作用域仅局限于for循环内,在for循环外,变量就不存在了。在Java中,还允许两个或两个以上的变量控制循环,即允许在for循环的初始化部分和迭代部分声明多个变量,每个变量之间用逗号分开。   值得注意的是,在JDK 1.5中,新提供了一种for循环的使用方法,即for-each循环。 2.3.3 跳转结构   跳转语句的作用是把控制转移到程序的其他部分。Java 支持 3 种跳转语句:break、continue和return。 1.break语句   在Java中,break语句主要有3种作用。第一,在switch语句中,用来终止一个语句序列,这点前面已经讲过。第二,用来退出一个循环。第三,作为goto 语句来使用。   break语句能用于任何 Java循环中,包括人们有意设置的无限循环。例如:    int i = 1; while(i > 0) { if(i == 10) break; i++; }    提示: 在Java 语言中,循环的终止是由条件语句来控制的。只有在某些特殊的情况下,才用break语句来终止循环。   Java中没有goto语句,因为goto语句会破坏程序的可读性,而且影响编译的优化。但是,在有些地方goto语句对于构造流程控制是有用的并且是合法的。因此,Java定义了break语句的一种扩展形式来处理这种情况。通过使用这种形式的break,可以终止一个或者几个代码块。这些代码块不必是一个循环或一个switch语句的一部分,它们可以是任何块。而且,由于这种形式的break语句带有标签,所以可以明确指定执行从何处重新开始执行。不过,在程序设计中应尽量避免使用这种方式。 2.continue语句   有时你想要继续运行循环,但是要忽略这次循环体的语句,continue语句提供了一个结构化的方法来实现。continue语句是break语句的补充,但只能用在循环体结构中。在while和do-while循环中,continue语句使控制直接转移给控制循环的条件表达式,然后继续循环过程。在for循环中,循环的迭代表达式被求值,然后执行条件表达式,循环继续执行。continue也可以指定一个标签来说明继续执行的循环。例如:    outer: for (int i=0; i<10; i++){ for(int j=0; j<10; j++){ if(j > i) { continue outer; } } }      当满足j > i条件时,相应的语句被执行后,程序跳转到外层循环,执行外层循环迭代语句i++,然后开始下一次循环。 3.return语句   return语句用来明确地从一个方法中返回,即退出该方法。将它作为跳转语句是因为return语句可以使程序控制返回到调用它的方法。在Java中,单独的return语句如果用在一个函数体中间时会产生编译错误,因为这时会有一些语句执行不到。可以通过把return语句嵌入某些语句(如if)中来使程序在未执行完函数的所有语句时退出。例如:    method () { boolean t = true; System.out.println("Before the return"); if(t) return; System.out.println("This won't execute"); }      该方法只会打印"Before the return"。if(t)语句是必要的,没有它,Java编译器将报告unreachable code错误,因为编译器知道最后的语句将永远不会被执行。在这里使用的if语句"蒙骗"了编译器。 2.4 字 符 串   前面已经讲过字符型数据是指用单引号括起来的单个字符,还需要指出的是因字符是占两个字节的Unicode字符,故一个汉字也是被当作一个字符来处理。而字符串是指字符的序列,它是组织字符的基本数据结构。Java语言中的Java.lang包中提供了两种类型的字符串类来处理字符串,它们是String类和StringBuffer类。对于一个字符串来说,如果创建之后不需要改变,我们称为字符串常量,String类就是用于存储和处理字符串常量的;如果一个字符串创建之后需要对其进行改变,则称之为字符串变量,StringBuffer类就是用于存储和处理字符串变量的。   与其他编程语言不同的是,Java中的字符串和下一节要讲的数组都是Java中的类,而不是基本的数据类型。接下来的两节你会初步接触到类及其使用,刚开始读者可能会有些东西不太清楚,不过没关系,带着疑问去学习第3章的内容会使学习效率更高。读者也可以在学完第3章之后重新阅读字符串与数组这两节的内容。 2.4.1 String类 1.创建String类对象   在Java语言中,创建String字符串的方法有很多,这些方法称为String的构造方法,即用什么方法来生成String类的对象。利用String类提供的构造方法不需要任何参数就可以生成一个空字符串,或者通过传递参数生成一个字符串,如下所示:    String str = new String(); String str = new String("MyString");      也可以由字符数组生成一个字符串对象,如下所示:    String str=new String(char tmp[]);      同样可以由字符数组的一部分来生成一个字符串对象,如下所示:    String str=new String(char tmp[],int startIndex,int charNumber);    其中,tmp[]代表生成的字符数组,startIndex代表字符串在数组中的起始位置,而charNumber代表包含的字符个数。   由于Java编译器会自动为每一个字符串常量生成一个String类的实例,因此字符串常量String有一个非常好用的构造方法,只需用双引号括起一串字符即可直接初始化一个String对象,例如:    String str4="MyString";    提示: 使用双引号得到的其实已经是一个String类的对象,而new String(String)构造方法是对传入的参数String创建一个副本,这样的形式实际上是创建了两个String对象,性能上是不划算的,应避免使用。 2.操作String类对象   操作字符串String类的主要成员方法汇总如下。 * int length():返回字符串的长度,汉字也算一个字符。 * char charAt(int index):返回index+1位置的字符。 * int compareTo(String str):按字母顺序进行字符串比较。 * boolean equals(Object obj)和boolean equalsIgnoreCase(String str):判断字符串是否相等,前者区分大小写,后者不区分大小写。 * int indexOf(String str)和int lastIndexOf(String str):返回字符串第一次出现str的位置。前者返回从前往后第一次出现str的位置,后者返回从后往前第一次出现str的位置。 * boolean startsWith(String prefix)和boolean endsWith(String sufix):前者判断该字符串是否以prefix为前缀,后者判断该字符串是否以sufix为后缀。 * char[ ] toCharArray():将字符串转为字符数组。 3.修改String类对象   对给定字符串的各种修改操作并不会改变原有字符串的值。修改String的操作方法汇总如下。 * String toLowerCase()和String toUpperCase():前者将字符串中的所有大写字母转为小写字母,后者将字符串中的所有小写字母转为大写字母。 * String substring(int beganIndex) 和String substring(int beganIndex,int endIndex):这两个方法用来得到给定的字符串中指定的字符子串。前者取beganIndex之后的字符串,后者获得beganIndex和endIndex之间的子串。 * replace(char oChar, char nChar):用于将字符串中的所有字符oChar替换成字符nChar。 * concat(String otherString):用于将当前字符串与给定的字符串连接起来。   对于String类还需要说明的一点是,它对字符串的处理是否是线程访问安全的,因为任何一门编程语言都会涉及对字符串的处理,因此线程访问安全是很重要的。String类的重要特性之一就是线程访问安全。因此,String对象是不可改变的,任何涉及对String类所表示字符串操作的方法,都是返回一个新创建的String对象。因此,在对一个String对象的使用中,往往会同时创建大量并不需要的String实例,消耗了不必要的系统资源,下一节介绍的StringBuffer类会弥补这方面的不足。 2.4.2 StringBuffer类   StringBuffer类的使用与String类有很多相似之处,但是其内部的实现却有很大的差别。StringBuffer类实际上是封装一个字符数组,同时提供了对这个字符数组的相关操作。其中大部分使用方法与String类是一样的,本节不再举例说明,以下分别列出其构造方法与常用的成员方法。 1.创建StringBuffer类对象   StringBuffer类的构造方法如下。 * StringBuffer():创建一个空的StringBuffer对象。 * StringBuffer(int length):设置初始容量。 * StringBuffer(String str):利用已有字符串String对象来初始化StringBuffer对象。 2.StringBuffer类的成员方法   上一节已经说过了,StringBuffer具有大部分String的成员方法,这里就不一一列举了。下面只介绍StringBuffer与String不同的成员方法。 * append(StringBuffer sb):在一个StringBuffer字符串最后追加一个字符串,即两个字符串连接。 * insert(int index, substring):在index的位置插入substring子串。 * delete(int start, int end):删除start到end之间的字符子串。 * void setCharAt(int index, char c):将index处的字符换成字符c。 * String toString():将字符串变量StringBuffer转化为字符串常量String。 提示: 因为System.out.println()方法是不能接受可变串的,因此在打印StringBuffer之前要使用toString()方法将其转化为String。   最后需要说明的是,StringBuffer本质上是一个字符数组的操作封装,与String相比,任何修改性的操作都是在同一个字符数组上进行,而不像String那样为了线程访问安全创建大量副本对象。因此,如果是一段需要在一个字符串上进行操作的代码,推荐使用StringBuffer来提高性能。当然,如果不考虑性能的话,可以全部选择String进行操作。 2.5 数 组   数组是一组具有相同类型和名称的变量的集合,是一种常用的数据结构。这些变量称为数组的元素,每个数组元素都有一个编号,这个编号叫作下标,我们可以通过下标来区别这些元素。数组元素的个数有时也称为数组的长度。   在Java语言中,数组也是类,这与其他的语言(例如C语言)有所不同。在本小节中,我们将学习一维数组、多维数组以及对象数组的定义和使用方法。 2.5.1 一维数组   数组的使用过程通常是先定义一个数组,然后进行初始化和元素的引用,以下将分别讲解每个过程。 1.一维数组的定义   定义一维数组的一般格式如下:    type arrayName[];      在上面的定义中,类型type可以为Java中任意的数据类型,包括简单类型和复合类型。arrayName表示数组的名称,必须为一个合法的标识符。"[]"指明该变量是一个数组类型变量。   另外,Java中还有一种定义数组的方法如下:    type[] arrayName;      上述这两种定义的效果是相同的,比如我们想定义一个整型数组,可以用这两种方式定义:    int intArray[]; int[] intArray;      上面的例子声明了一个整型数组,组中的每个元素为整型数据。值得注意的是,Java语言在定义数组时并不为数组元素分配内存,因此"[]"中不用指出数组中元素的个数,即不用指明数组长度,在没有给数组赋值以前,这样定义的数组是不能被访问的,这点与C语言或C++语言是不同的。 2.一维数组的初始化   要想让数组能够访问它的元素,需要对数组进行初始化,因为一维数组必须经过初始化之后才可以引用。数组的初始化分为静态初始化和动态初始化两种,接下来我们分别介绍这两种初始化方式。   静态初始化是在定义数组的同时对数组元素进行初始化,这种方式通常用于数组元素个数比较少的情况。格式如下:    int intArray[] = {1,4,4,8,2,9}; int[] intArray = {1,4,4,8,2,9};      在上面的初始化中,虽然没有指定数组的长度,但已经给出了初值的个数,这时系统会自动按照所给的初值个数计算出数组的长度并分配相应的空间。上面的两行代码定义了1个含有6个元素的整型数组,6个元素都是整型的,并且每个元素均有自己的初始值。   如果要进行动态的初始化,我们要用到运算符new,其格式如下:    arrayName = new type[arraySize];    其中,arraySize指的是数组的长度,可以是整型的常量和变量,该语句的作用是给arrayName数组分配arraySize个type类型大小的空间。如果参数arraySize为常量,就为数组分配一个固定的空间;如果参数arraySize是变量,则意味着根据参数动态地为数组分配空间。例如:    int[] intArray; intArray = new int[6];    表示新建一个名为intArray的整型数组,然后为这个数组分配6个整数所占据的内存空间。通常,这两部分可以合在一起写,格式如下:    type arrayName = new type[arraySize];      上面的语句可以写成:    int[] intArray = new int[6];    提示: Java中数组的这种写法实际是类实例化的过程,刚开始你可能会不大习惯,因为与其他编程语言差别比较大,不过等你熟悉了Java中对象的实例化过程后,自然就会习惯这种写法。 3.一维数组的引用   所谓数组元素的引用,指的是如何在程序中引用初始化后的数组元素,引用的方 式如下:    arrayName[index];    其中,index为数组的下标,可以是整型常数、变量和表达式,例如:    a[6], b[i], c[6*i] (i为整型)      下标的范围是从0开始,一直到数组的长度减1。对于前面的例子intArray来说,它的元素只有intArray[0]到intArray[5],而没有intArray[6]。在Java语言中,出于对安全性的考虑,是要对数组元素进行越界检查的,这与C/C++语言不同。此外,在Java语言中,可以通过数组的属性length获得数组的长度,也就是元素的个数。 2.5.2 多维数组   多维数组可以看作是数组的数组,如果将多维数组看作是比较特殊的一维数组,则数组的元素本身就是数组。例如,对于一个二维整型数组,可以将其看作是一个特殊的一维数组,只不过该数组的每个元素也是一个一维整型数组。由于多维数组的情况与二维数组类似,因此在本小节中,我们将以二维数组为例介绍多维数组的定义、初始化和元素的引用。 1.多维数组的定义   与一维数组的定义类似,二维数组有如下定义方式:    type arrayName[][]; type[][] arrayName; type[] arrayName[];    其中,type代表数组元素的类型,可以为简单类型和复合类型。与一维数组一样,二维数组定义时同样没有分配内存空间,如果要引用数组元素,也必须首先对二维数组进行初始化。 2.多维数组的初始化   二维数组的初始化同样分为静态初始化和动态初始化两种。静态初始化是在定义数组的同时就为数组分配了内存空间,比如定义一个3*3的数组并赋初值:    int[][] intArrary = {{3,5,3},{4,7,9},{3,6,4}};      对于静态初始化,不必给出二维数组每一维的大小,系统会根据给出的初始值的个数自动计算出数组每一维的大小。同时,二维数组中每一维的大小不一定相同,例如:    int[][] intArrary = {{3},{4,7},{3,6,4}};    提示: 在Java语言中能为二维数组的每一维指定不同的大小,是由于Java中将二维数组看作是数组的数组,数组空间不是连续分配的。同样多维数组的每一维大小也可不同。   对于二维数组的动态初始化,也可以有两种方法,-种是直接为数组的每一维分配空间,其定义方式如下:    arrayName = new type[arrayLength1][arrayLength2];      上面的定义中,arrayLength1和arrayLength2代表二维数组的两个维的大小,例如:    int intArray = new int[5][8];      另一种方法是从最高维开始,分别为每一维分配空间,例如:    arrayName = new type[arrayLength1][]; arrayName [0] = new type[arrayLength20]; arrayName [1] = new type[arrayLength21]; ... arrayName [arrayLength1-1] = new type[arrayLength2m];      在上面的定义中,arrayName代表数组的名称,arrayLength1代表最高维长度,也就是将二维数组看作特殊的一维数组时,对应的一维数组的长度,arrayLength20、arrayLength21、...、arrayLength2m分别代表这个特殊的一维数组中每个元素所代表的一维数组的长度,如下面的例子所示:    int[][] intArray = new int[2][]; intArray[0] = new int[5]; intArray[1] = new int[8];      值得注意的是,Java语言中对数组的初始化与C、C++语言是不同的,在C、C++语言中必须一次指明数组每一维的长度。 3.多维数组的引用   对二维数组中的每个元素,其引用格式如下:    arrayName[index1][index2]    其中,arrayName代表二维数组的名称,index1与index2为下标,这个下标与一维数组一样,可以是常量、变量或表达式。如intArray[2][3],而且每个下标也是从0开始到该维度的长度减1。 2.5.3 对象数组   在前面已经讲过,数组元素可以是简单类型,也可以是复合类型,因此可以用数组来存储一系列的对象。对象数组的定义和其静态初始化过程,与一般数组的定义及其静态初始化的过程是一样的,而对对象数组进行动态初始化的时候与一般数组的动态初始化有所差别,对象数组的动态初始化处理起来会稍微复杂一些,一般是按照如下步骤进行初 始化:    type[] arrayName = new type[arraySize]; arrayName[0] = new type(paramList); arrayName[1] = new type(paramList); ... arrayName[arraySize-1] = new type(paramList);      例如:    String[] arrayName = new String[3] arrayName[0] = new String("hello"); arrayName[1] = new String("String"); arrayName[2] = new String("array");      有的时候(通常在生成的对象不需要参数时)初始化可以用循环来实现:    for(int i=0;i