第3章 基础语法
3.1 类型、变量与运算符
Java是支持面向对象的语言,但我们还是要先了解一些基本语法元素。由于每种语言都有其诞生的宗旨与演化过程,对这些基本语法元素,也就会有其独特的特性。
3.1.1 类型
在Java中,并非所有的东西都当对象来处理(默然说话:对,Java的确并非是象C#那样完全面向对象的语言,它包含了一些非面向对象的概念,比如这里要谈到的数据类型与所占用内存大小的问题,这主要是为了提高Java程序的执行效率而做出的牺牲。)。基本上,Java可区分为基本数据类型(Primitive Type)和引用数据类型(Reference Type)。
这里介绍基本类型,第4章介绍引用类型。
所谓基本数据类型,就是在使用时,得考虑一下数据用多少内存长度来存比较经济,利用程序语法告诉JVM,然后由JVM为你在内存中进行配置与管理。Java中的基本数据类型主要有整数、字节、浮点数、字符与布尔。
整数:就是整数喽,小学重点学的是自然数,整数就是自然数,零,负整数的集合。它是一系列非连续的数字(默然说话:数学用语:非线性),在坐标轴上是一个一个独立的小点。在Java中为了更好的使用内存,又把整型细分为short(占2个字节)、int(占4个字节)、long(占8个字节)三种类型。(默然说话:字节就是内存的一个长度单位,1字节表示能装下一个字符的内存空间,字节也可以表示数字——其实本来就是数字——字节越长,能表示的数字也就越大)。
字节:byte,长度是1字节。在需要逐字节处理数据时(图像,视频,音乐等各种二进制编码的数据)就会使用byte。同样,也可用于表示整数,范围是-128-127。
浮点数:主要用来储存小数数值,可分为float(占4字节)和double(占8字节)。double比float可以装下的数字大得多,可表示的精确度也比较大。
字符:char用来储存’A’、’B’、’林’等字符符号。在JDK8中,Java的字符采用Unicode6.2.0编码,JVM结果采用UTF-16 Big Endian,所以每个字符类型占2字符,汉字字符与英文字符在Java中同样都是用双字节储存。
(默然说话:编码?Unicode?UTF?建议先看看以下文件(《哪来的纯文本文档?》和《Unicode与UTF》)
http://openhome.cc/Gossip/Encoding/TextFile.html
http://openhome.cc/Gossip/Encoding/UnicodeUTF.html
布尔:boolean类型可以表示true与false,分别代表逻辑的“真”与“假”。在Java中不用在意boolean类型的长度,因为你也无法将boolean类型与其他类型做运算。
每种类型的内存长度不同,可储存的数值范围也就不同。例int能储存-2147483648~2147483647,如果存储值超出了范围,就称为溢出(Overflow),会造成程序出现错误。API使用了常量来记录各种类型的边界——也就是最大值和最小值,下面的程序可以实现。
package cn.com.speakermore;
/**
*各基本数据类型的范围
* @author 默然说话
*/
public class Range {
public static void main(String[] args){
System.out.println("类型 \t最小值 \t最大值");
//整数范围
System.out.println("Byte \t"+Byte.MIN_VALUE+" \t"+Byte.MAX_VALUE);
System.out.println("Short \t"+Short.MIN_VALUE+" \t"+Short.MAX_VALUE);
System.out.println("Integer \t"+Integer.MIN_VALUE+" \t"+Integer.MAX_VALUE);
System.out.println("Long \t"+Long.MIN_VALUE+"\t"+Long.MAX_VALUE);
//小数范围
System.out.println("Float \t"+Float.MIN_VALUE+" \t"+Float.MAX_VALUE);
System.out.println("Double \t"+Double.MIN_VALUE+" \t"+Double.MAX_VALUE);
//字符范围
System.out.println("Character \t"+(int)Character.MIN_VALUE+" \t"+(int)Character.MAX_VALUE);
//布尔的两个值
System.out.println("Boolean \t"+Boolean.TRUE+" \t"+Boolean.FALSE);
}
}
结果如下:
图3.1各基本数据类型的范围
对于初学Java的人,会看到一些新的语法与API在里面。
“//”符号的意思就是注释的一种,在Java中一共有三种注释,双斜杠“//”是单行注释,也就是一次只能注释掉一行。(默然说话:注释的意思,就是用来说明或记录程序中的一些注意事项的语句,这些都是给人看的,机器是不管它们的,所以在行的开头加上“//”,机器就会忽略这一行的内容。)
“/”与“/”是另一种常见的注释,它叫多行注释,也就是它能注释掉从“/”与“/”之间的所有内容,机器会完全忽略斜杠星与星斜杠之间的所有内容。
不过要注意多行注释是不能嵌套的,如下:
/* 注释1:OOXX
/*注释2:XXOO
*/
*/
机器会认为倒数第二个“/”是注释的结束,而最后一个“/”是错误的语法。从而报错。
System.out.println()是标准API(默然说话:API就是“应用程序接口”的英文缩写。用人话说,应用程序接口就是可以在我们所写的代码中可以调用的、现成的代码。再简单点:API就是写好的代码!!!现在明白了吧?再举个例子:你写了个代码,它可以在别的代码里被调用,OK!恭喜你!你写了一个API!!),它的作用就是进行一个文字的输出,其中print就是输出的意思,后面的ln是line的缩写,意思是输出一行,也就是带换行符进行输出,这句代码执行完毕之后会自动换一行,这样能让我们看清楚输出的内容。
你们还应该看到我在程序里打了许多的空格和\t,这些是用来调整格式的。以便让输出的内容显得整齐好看些。\t的意思就是添加一个表格列,也就是四个空格的距离。
最后就是好多的加号“+”,这里是进行字符串的连接,把所有的内容连接成一个字符串,以进行输出。
3.1.2 变量
如果我们要编写程序,就会需要保存数据,如果要保存数据,就需要使用内存。内存就是用来存储数据的嘛。可是,你怎么告诉计算机,我要使用一块内存呢?答案就是:使用变量。变量的使用要遵守一定的语法规则:先看例子:
int number=10;
double PI=3.14;
System.out.println(number);
System.out.println(PI);
System.out.println(number);
这个片段的第1行在告诉机器,我要有一块内存,我把它叫number,可以放int类型的数据,使用“=”赋值运算符指定number变量的值为10。(默然说话:注意“=”不读作“等于”而读用“赋值”,非常重要,非常重要,非常重要!)
1. 基本规则
对于基本类型来说,想要声明何种类型的变量,就使用byte、short、int、long、float、double、char、boolean等关键字来声明。变量命名时要注意:它不可以使用数字作为开头,也不可以使用除了下划线(_)和$以外的任何特殊字符开头(如!@#%……&()——+)。第二个开始的字符可以是字母,数字,下划线和$。另外,变量名不能使用Java已定义的关键字。如,public、static、int、double等就不能作为变量的名字使用。
变量命名的规范主要以清楚易懂为主。初学者最易犯的问题就是随便用一些简单字母来作为变量名称,这其实就是让制造一个让自己在这一行呆不下去的陷阱。因为这样写出来的代码,别人看不懂,时间一长,自己也看不懂了。连代码都看不懂了,你还呆在这一行干嘛?呆着发呆么?另外也会造成自己写不出大规模的代码,基本上超过300行代码,用简单变量名就会出现大量重名的情况,也会出现变量用途不清的问题(默然说话:无数学员先烈已经用他们的事迹告诉我,如果你用简单字母,你就是找(er)屎(bi)!!这也让我反思自己的教学代码。现在即使是写个一两行的代码,也尽量保证自己的变量名是有意义的,以做好模范带头作用,感谢那些学员先烈们,致以最高的敬意!)。
在Java业内,命名现在已经有了基本统一的规范(默然说话:尽快按此养成习惯,你就打好了年薪百万的基础!),这个命名还有个很有意思的名字,叫驼峰命名法(Camel Case),它规定了变量命名以小写字母开头,第二个单词开始,首字母大写。(默然说话:最好使用英文单词,不要放弃学习英文的机会呀,如果实在不愿意,那也请使用全拼,这样减少同音字带来的意思理解上的偏差*)例如:
int numberOfStudent;
int length
int niDeXingMing
我们到目前为止的代码都是写在main()方法中的,在方法中声明的变量称为局部变量。在Java中声明一个局部变量,就会为变量配置一块内存,但Java不会为局部变量的内存空间进行处理,所以当你声明了一个局部变量之后,未对它进行赋值就使用的话,会得到一个错误信息。
图3.2 变量使用错误:尚未初始化
(默然说话:顺便解释一下初始化的意思:初始化就是在声明变量的时候同时对它进行赋值。还有个提法是:初始化就是第一次给变量赋值。不过我个人比较倾向于前者,因为在Java里,变量的第一次赋值是可以在声明的同时进行的,也就是可以把变量的声明和赋值写成一条语句。例如:int i=10;而绝大多数情况下,我们也是这样在Java里初始化变量的。)
如果在指定变量值之后,就不想再改变变量值,可以在声明变量时加上final限定,如果后续自己或别人不小心修改了final变量,就会出现编译错误。
图3.3变量使用错误:常量不能被第二次赋值
被final修饰的变量是不能被第二次赋值的,它被要求在声明的同时必须给它赋值(也就是声明和赋值必须写在一个分号——分号代表语句结束——之内。)并且不能进行第二次的赋值。因为它的值不能被修改,所以我们又把用final修饰的这种特殊的变量称为常量。“常”就是“一直这样,无法被修改”的意思。
2. 字面常量
在Java中写下一个值,如我们前面给变量赋值的那个“值”,被称为字面常量。在整数字面常量表示上,除了十进制表示之外,还可以使用八进制或十六进制表示。特点就是八进制在最前面加0,十六进制在最前面加0x。例如下面的代码:
int number1=12;//十进制:12
int number2=0xC;//十六进制:还是12
int number3=014;//八进制:还是12
System.out.println("number1="+number1);//十进制输出number1
System.out.println("number2="+number2);//十进制输出number2
System.out.println("number3="+number3);//十进制输出number3
它的输出结果是:
图3.4 关于字面常量的使用
从上面可以看到,字面常量可以有很多种表达方式,但它们的结果是一样的。
如果你要表示字符,你可以使用单引号(’)括住字符。
char ch=’S’;
char name=’林’;
转义符 | 说明 |
---|---|
\ | 字符斜杠 |
\’ | 字符单引号 |
\” | 字符双引号 |
\uxxxx | 十六进制数指定的Unicode字符,x是十六进制数字 |
\xxx | 八进制指定的Unicode字符,x是八进制数字 |
\b | 退格符(目前基本没用) |
\f | 换页符(目前基本没用) |
\n | 换行符 |
\r | 新行符(光标移到行首) |
这里要注意,单引号内只能是一个字符,不能写多个。如果你需要写多个字符,则使用双引号括住内容,这叫做字符串。
String uname=”默然说话”;
需要特别注明的是,String不是一个基本数据类型,它是属于对象型的数据类型。
在Java代码我们还会用到叫做“转义字符”的字面常量。比如单引号,它在Java就是字符类型的标志,那如何输出一个单引号呢?如果你直接打单引号,计算机会认为你有语法错误,因为计算机已经接受了单引号是字符类型的标志,所以我们只能使用另一个符号来代表字符单引号,这组符号通常都用斜杠(\)来开头,见下表:
表3.1 常用转义符号
转义符 | 说明 |
---|---|
\ | 字符斜杠 |
\’ | 字符单引号 |
\” | 字符双引号 |
\uxxxx | 十六进制数指定的Unicode字符,x是十六进制数字 |
\xxx | 八进制指定的Unicode字符,x是八进制数字 |
\b | 退格符(目前基本没用) |
\f | 换页符(目前基本没用) |
\n | 换行符 |
\r | 新行符(光标移到行首) |
还有两个较为特殊的字面量,就是逻辑运算里会用到的真和假,记作true和false。在计算机里,它们被称为布尔类型(boolean)。
3.1.3 运算符
程序为运算而生,程序开始,运算开始,程序结束,运算结束。而运算是要通过运算符来完成的。
Java的运算符可以分为算术运算、关系运算、逻辑运算和位运算四大类型。
1. 算术运算
运算符 | 说明 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 模 |
++ | 自增1 |
-- | 自减1 |
+= | 指定自增 |
-= | 指定自减 |
*= | 指定自乘 |
/= | 指定自除 |
%= | 指定取模 |
算术运算就是生活中的数学基本运算,也就是加、减、乘、除这类运算符。具体见下表:
表3.2 算术运算符
运算符 | 说明 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 模 |
++ | 自增1 |
-- | 自减1 |
+= | 指定自增 |
-= | 指定自减 |
*= | 指定自乘 |
/= | 指定自除 |
%= | 指定取模 |
算术运算的符号要注意,乘法没有使用乘号,而是使用了星号。算术运算的规则与我们数学课所学一致,先乘除,后加减,如果你要改变运算优先级,需要加上小括号,例如:
System.out.println(2+2+8/4);//结果是6
System.out.println((2+2+8)/4);//结果是3
上面两句代码的输出是不同的,因为运算的步骤不一样了。
除了我们在数学课所学到的一些计算,Java还有许多新增加的算术运算,例如取模(%),这个符号只能在两个整数之间进行,它的结果是整除之后的余数。例如:
System.out.println(5%2);//结果是1
此外,还有就是著名的++和--了。这是从C语言一直传下来的,它只能和变量配合使用,作用就是把变量的值取出,加1,然后在再保存给变量(我们称之为赋值)。
int i=1;
i++;//相当于i=i+1
System.out.println(i);//结果是2
--也是一样的,把变量的值取出,减1,然后再保存给变量。
int i=1;
i--;//相当于i=i-1
System.out.println(i);//结果0
+=非常类似于++运算,只是++运算只能加1,而+=运算可以指定加几,例如:
int i=1;
i+=3;//相当于i=i+3;
System.out.println(i);//结果是4
其他指定运算符相似,不再一一说明。
2. 关系运算
运算符 | 说明 |
---|---|
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
== | 等于(两个等号) |
!= | 不等于(惊叹号等号) |
数学上有大于、等于、小于、大于等于、小于等于、等于和不等于的比较运算,这些运算符被称为关系运算符。
表3.3 关系运算符
运算符 | 说明 |
---|---|
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
== | 等于(两个等号) |
!= | 不等于(惊叹号等号) |
这些运算符的规则也和数学上的规则是一至的,运算符可以作用于整数,小数,产生的结果只会有两个:真(true)和假(false)。代码如下:
public class Comparison {
public static void main(String[] args){
System.out.println("10>5的结果:"+(10>5));//10>5要加小括号,因为比较运算符的优先级没有算术运算符高
System.out.println("10>=5的结果:"+(10>=5));
System.out.println("10<5的结果:"+(10<5));
System.out.println("10<=5的结果:"+(10<=5));
System.out.println("10==5的结果:"+(10==5));
System.out.println("10!=5的结果:"+(10!=5));
}
}
运行结果如下:
run:
10>5的结果:true
10>=5的结果:true
10<5的结果:false
10<=5的结果:false
10==5的结果:false
10!=5的结果:true
成功构建 (总时间: 1 秒)
注意,等于比较是使用两个等号(==),一个等号(=)是表示赋值,它的意思就是把等号右边的值保存到等号的左边变量中。所以,如果你写1=3,Java就会报错的,因为赋值号(=)的左边只能是变量。
(默然说话:其实上面的代码并没有什么实际的应用意义,在实际的应用中,真和假只是用来决定程序的执行路径的,并不需要显示出来。比较运算通常也是和if、for、while等关键字连用的,这里的代码属于纯教学代码,仅供观赏,基本理解即可,不用深入研究。)
3. 逻辑运算
运算符 | 说明 |
---|---|
&& | 逻辑与 |
| | 逻辑或 |
! | 逻辑非(只有一个惊叹号) |
如果我们要综合多个条件判断,得出最终的结果,我们就必须使用到逻辑运算符,逻辑运算符有三个,一般我们称为“与”、“或”、“非”。
表3.4 Java的逻辑运算符
运算符 | 说明 |
---|---|
&& | 逻辑与 |
| | 逻辑或 |
! | 逻辑非(只有一个惊叹号) |
逻辑运算要注意的问题是,参与逻辑运算的值只有布尔值,也就是说,逻辑运算符只能对真和假进行运算。逻辑运算最后的结果也是真和假,所以我们说,逻辑运算符就是把多个真假的关系运算为一个真假。
上面的话说得太理论,举几个具体的例子。
生活经常会有许多连续的判断,比如成绩上80且数学有90分为优秀学生,成绩90分以上或有20人推荐可以成为三好学生。从前面的两个例子可以看出,第一个例子要2个条件均成立,才能得到优秀学生的称号,而第二个例子只要有一个条件成立,就能得到三好学生的称号。第一个例子就是我们逻辑运算“与”,第二个例子就是我们的“或”运算。
成绩>=80&&数学>=90
成绩>90||推荐人>20
“非”运算就是取反,类似于我们平时说的“不”,高取反,就是“不高”,用Java语言来描述就是:
!高
逻辑数1 | 逻辑数2 | &&结果 | ||结果 |
---|---|---|---|
True | True | True | True |
True | False | False | True |
False | True | False | True |
False | False | False | False |
关于与或的逻辑运算结果通常会有一张表,简单说,就是与(&&)运算要求参与运算的所有值都为真,结果才会是真,或(||)运算只要有一个条件为真,结果就会是真。
表3.5 Java逻辑运算的规律
逻辑数1 | 逻辑数2 | &&结果 | ||结果 |
---|---|---|---|
True | True | True | True |
True | False | False | True |
False | True | False | True |
False | False | False | False |
另外,说点Java语言中特有的,叫做“短路运算(Short-Circuir Evaluation)”。也就是当进行多次的逻辑运算时,由于前面提到过的规律,与运算如果第一个表达式得到一个False,它就不会再计算另一个表达式了,而或运算同样,如果第一个表达式得到一个True,它也不会再计算另一个表达式了。我们可以利用这个特点,来解决程序中的许多bug,例如:
if(user!=null&&user.getName().eqauls(“Jack”)){
//做某事
}
上面是一个判断用户的名称叫Jack的语句,它会有一个问题,就是如果user对象为空,则user.getName()就会报NullPointerException的异常,这是很头疼的一件事情,因为在实际程序运行过程,很难保证user对象不会为空。而在前面加上了user!=null这个判断之后,如果user!=null为False(也就是说user为空),则Java就不会去执行user.getName()这句代码,不执行,就没有异常了。这就巧妙地利用了“短路运算”,避免一些烦人的异常问题。
逻辑或是类似的道理,这里就不再举例了。
4. 位运算
运算符 | 说明 |
---|---|
& | 按位与 |
按位或 | |
^ | 按位异或 |
~ | 按位补 |
位运算是如今在应用开发的代码中很少用到的部分,这里作简单介绍。不感兴趣的同学可以跳过。
位运算有与、或、异或和补四种运算。
这种运算是按位进行的运算。也就是说,它不关心你的数字是多少,它只按你这个数字的二进制形式按位的顺序进行逻辑运算。由于生活中极少用到二进制的运算,所以位运算也就很少接触。不过,这也是Java里的一个特点,也有它的用处(比如在加解密算法中,就是很常用的)。
表3.6 Java位运算符
运算符 | 说明 |
---|---|
& | 按位与 |
按位或 | |
^ | 按位异或 |
~ | 按位补 |
位运算是逐位运算,所以如果你使用十进制方式来表示,你会完全不明白位运算做了什么。所以位运算第一步总是把十进制数转化为二进制数,再对位进行逻辑运算。因为二进制数只有2个数,一个是1,一个是0,所以我们可以把1看成真,把0看成假,然后根据前面我们所讲过的逻辑运算规则进行运算。算完之后再转为十进制,这样我们就了解到最终的结果用十进制表示是什么样
5. 条件运算
Java有个条件运算符,使用方式如下:
条件?条件为真时的返回值:条件为假时的返回值
这个表达式在一些情况下很有用处,可以缩短我们的代码,使表达简单化,但缺点就是会让人看不太懂。比如判断一个数是否为偶数,如果用if…else可以写成这样:
if(number%2==0){
System.out.println(“该数为偶数”);
}else{
System.out.println(“该数为奇数”);
}
如果使用条件运算符,则可以写成这样:
String result=number%2==0?”偶数”:”奇数”
System.out.println(“该数为”+result);
代码大大缩短,但是,的确没有if…else那么容易明白。
3.1.4 类型转换
数据类型 | 大小 |
---|---|
byte | 1字节 |
char | 2字节 |
short | 2字节 |
int | 4字节 |
long | 8字节 |
float | 8字节 |
double | 16字节 |
前面已经讲过,变量标识了一块内存,我们可以使用变量对计算机的内存进行值的保存。声明变量,就是声明了一块内存用来装值。那接下来要说的,就是这块内存应该多大?
无论你的内存有多大,总有被用完的时候,所以,如何高效率的使用内存就摆上了桌面。要高效率的使用内存,就得给每个变量分配一块大小合适的内存,如何完成这一工作?这就是由数据类型来决定的。所以当我们谈到数据类型的时候,我们总会说这个类型有多大的内存。如下表:
表3.7 数据类型所占内存大小
数据类型 | 大小 |
---|---|
byte | 1字节 |
char | 2字节 |
short | 2字节 |
int | 4字节 |
long | 8字节 |
float | 8字节 |
double | 16字节 |
当我们进行赋值或计算时,由于计算机要求类型一致才能进行赋值和运算,所以就存在类型转换的情况。类型转换的基本原则有两条:第一是类型之间要兼容,第二是把小数放到大类型会自动完成(自动类型转换),把大数放到小类型里就必须强制进行(强制类型转换)。
比如:
float PI=3.14
因为Java语言默认把小数看作是double类型,而double(16字节)类型比float(8字节)类型要大。所以计算机会报错。
图3.5 需要强制类型转换的报错信息
正如错误消息所说,因为大数放到小类型中,有可能会不够装,造成丢失精度或出现错误,编译器并不想为此后果负责,所以它告诉你,并要你亲自确认这个转换是你自己愿意的。这就是所谓的强制类型转换。我们可以用两种方式进行,较通用的方式就是在大数之前打小括号,括号中写要转换的类型,如下:
float PI=(float)3.14;
另一种写法是在大数的末尾加个F(大小写均可),如下:
float PI=3.14f;
3.2 流程控制
现实生活中需要解决的事情很多,在计算机发明之后,想要使用计算机解决的需求也是各式各样:这就要求使用各种条件判断语句来定义程序执行的流程。
3.2.1 if…else
语法:
if(条件式){
语句1;
}else{
语句2;
}
条件式的运算结果为true时,会执行语句1,否则执行语句2。
package cn.com.speakermore;
/**
* 判断一个数的奇偶
* @author mouyo
*/
public class Odd {
public static void main(String[] args){
int input=10;
int remain=input % 2;//取余数
if(remain==1){
System.out.println(input+"是一个奇数");
}else{
System.out.println(input + "是一个偶数");
}
}
}
说明:范例中的Input变量,实际上可以由用户输入取得值,具体在第4章说明
还有一种多重判断语句,语法如下:
if(条件式1){
语句1;
}else if(条件式2){
语句2;
}else{
语句3;
}
如果条件式1不满足,就执行条件式2的判断,如果满足就执行语句2,然后跳过整个多重判断,执行后面的语句。换个说法,多重判断语句就是找到第一个条件式为真之后的语句去执行,执行完毕之后,直接执行后面的语句。不会去执行条件式为真之后的任何判断语句。
例如,应用在处理学生的成绩等级问题:
package cn.com.speakermore;
/**
* 等级多重判断
*文件名:Level.java
* @author mouyo
*/
public class Level {
public static void main(String[] args){
int score=88;
char level;
if(score>=90){
level='A';
}else if(score>=80){
level='B';
}else if(score>=70){
level='C';
}else if(score>=60){
level='D';
}else{
level='E';
}
System.out.println("得分等级:"+level);
}
}
3.2.2 switch
在JDK 7前,switch只可用于比较整数、字符、Enum(默然说话:Enum就是枚举类型),从JDK 7开始,增加了对字符串的比较。Enum后面会再详细说明。
switch的语法:
switch(变量或表达式){
case 整数、字符、字符串或Enum:
语句;
break;
case 整数、字符、字符串或Enum:
语句;
break;
……
default:
语句;
break;
}
首先就是switch关键字,全小写,后面跟小括号和大括号。小括号中写需要和case进行比较值的变量或表达式,值只能是整数、字符、字符串或Enum,变量和表达式取到值之后就会开始与case后设定的整数、字符、字符串或Enum常量值进行比较(默然说话:注意!case后的常量值类型必须与小括号里的变量类型或表达式执行结果的数据类型一致,否则报语法错误),如果相等就执行之后的语句,直到遇到了break才跳出switch语句。如果没有符合的整数、字符、字符串或Enum,则执行default后面的语句,直到遇到了break才跳出switch语句。default语句是可以省略的。
来看看前面范例的Level类,如何改用switch运行。
package cn.com.speakermore;
/**
* switch方式的得分等级
* 文件名:Level2.java
* @author mouyo
*/
public class Level2 {
public static void main(String[] args){
int score=86;
//很聪明的做法,因为我们是按分数段处理,所以除以10就可去掉个位数,保留10位数,
//这样就可以把大于的情况转换为等于的情况了
int quotient=score/10;
char level;
switch(quotient){
case 10:
case 9:
level='A';
break;
case 8:
level='B';
break;
case 7:
level='C';
break;
case 6:
level='D';
break;
default:
level='E';
break;
}
System.out.println("得分等级:"+level);
}
}
在这个程序中,使用除法并取得运算后的商,如果大于90,除以10就只会得到9或10(100分除以10,商就是10),在case 10中没有任何代码,也没有break,所以继续往下执行到case 9的语句,直到遇到break才会离开switch。(默然说话:对了,顺便说一下,在Java中,一个整型数除以另一个整型数,最后的结果一定是整型数,所以86/10的商不是8.6,而是8,后面的小数点会被舍弃)
这个程序与多重if的区别,就在于变量quotient的值只会被取一次,而前面多重if语句每一次比较都要从变量score中取值,所以switch在这一点上效率是比较高一点的。
3.2.3 for
在Java中如果要进行重复性指令执行,可以使用for循环。基本语法之一如下:
for(初始值;循环条件;自增量){
语句(循环体);
}
for循环语法的小括号中,初始值只执行一次,如果是声明变量,结束for循环时该变量会被销毁。第一个分号之后的循环条件是一个最后结果是boolean类型的表达式,每次执行循环体语句之前都会被执行一次,如果得到true,才会继续执行循环体语句,如果是false,则循环结束。第二个分号后的自增量则是每次循环体语句执行完后都会执行一次。自增量最重要的作用就是让循环条件不会永久为真,以免出现死循环。
比如下面的代码,它能在控制台上从1显示到10:
package cn.com.speakermore.ch03;
/**
* 从1读到10
* 文件名:OneToTen.java
* @author mouyo
*/
public class OneToTen {
public static void main(String[] args) {
for(int i=1;i<=10;i++){
System.out.println(i);
}
}
}
这个程序的意思就是i开始为1,只要i是小于等于10就执行循环体语句(System.out.println(i)
,意思就是显示变量i的值),然后i自增1。这是for循环最常见的应用形式。如果for循环体语句只有一条,可以省略大括号,但为了可读性与维护性,强烈不建议省略。
九九乘法表常被用于介绍较为复杂的for循环写法。如下:
/**
* 九九乘法表
* 文件名:NineNineTable.java
* @author mouyo
*/
public class NineNineTable {
public static void main(String[] args) {
for(int i=1;i<10;i++){
for(int j=1;j<=i;j++){
System.out.print(i+"*"+j+"="+i*j+"\t");
}
System.out.println("");
}
}
}
执行结果如下:
图3.6 九九乘法表
其实for循环只是将3个复合语句块写在一个小括号中而已,第一个语句块只会执行一次,第二个语句块专门判断是否继续下一次循环,第三个语句块只是一般的语句块。
for循环小括号中的语句块以分号作分隔,而在一个语句块中如果想写两个以上的语句,则使用逗号分隔。有兴趣的话,可以研究下面这个九九表的写法,只使用了一个for循环就完成九九表打印,很逼格哦!不过,基本是看不懂,非常不建议这样写:
package cn.com.speakermore.ch03;
/**
* 高逼格九九乘法表
* 文件名:NineNineTable2.java
* @author mouyo
*/
public class NineNineTable2 {
public static void main(String[] args) {
for(int i=1,j=1;i<10;j=((j==i)?(++i/i):(j+1))){
System.out.print(i+"*"+j+"="+i*j+(j==i?'\n':' '));
}
}
}
经验:for循环小括号中第二个语句块如果没有写,默认就是true,所以如果见到如下的循环,表示死循环:
for(;;){
……
}
3.2.4 while
while循环语法如下:
while(循环条件){
循环语句;
}
while循环的执行顺序很容易看出来。首先判断条件是否为真,若为真则执行大括号里的循环语句,然后再次判断循环条件是否为真,如此循环,直到循环条件为假,跳出循环。
while循环比较合适用于循环次数未知的情况,例如,让用户输入学生姓名,直到用户输入quit才停止。
下面是一个好无聊的游戏,不过的确可以说明咱们while循环的特点:
import java.util.Random;
/**
* 好无聊,不断生产随机数,直到出现5为止
* @author mouyong
*/
public class RandomStop {
public static void main(String[] args) {
while(true){//条件永真,死循环
int number=new Random().nextInt(10);//产生0-9的随机数
System.out.println(number);
if(number==5){
System.out.println("总算猜到了5,累死了!");
break;//break关键字,跳出循环
}
}
}
}
某一次执行结果如下:
图3.7 无聊的游戏1
while的条件判断设置为true,会造成一直执行循环体,也就是我们通常所说的死循环。new Random().nextInt(10)会随机产生0-9之间的整数。在while循环中如果执行到break,则会跳出循环。
while循环总是先判断循环条件,之后才开始执行循环,所以有可能会出现一开始循环条件为假,则直接跳过循环语句部分的情况。如果需要先执行一次循环语句,然后再判断循环条件,则可以使用do…while,它是先执行一次循环语句,然后才进行循环条件的判断,语法如下:
do{
循环语句
}while(循环条件);
要注意的一个问题是,while后面是以分号作为结束的,这个分号是不能写漏的。上面那个无聊的游戏也可以使用do…while循环来完成,而且还可以省掉一个if判断:
import java.util.Random;
/**
*
* @author mouyong
*/
public class RandomStop2 {
public static void main(String[] args) {
int number;
do{
number=new Random().nextInt(10);
}while(number!=5);
}
}
某一次执行的结果如下:
图3.7 无聊的游戏2
第一个程序之所以会有两次判断,原因就是因为一开始number的值并没有产生,所以要先想办法进入到循环中,而do…while一来就先执行循环语句,产生了number,再判断要不要执行循环,刚好可以解决这个尴尬的问题。
3.2.5 break、continue
break可以离开当前switch、for、while、do…while的语句块,并执行后面的语句,在switch中主要用来中断下一个case的比较,在for、while与do…while中,主要用于中断当前循环。
continue只作用于循环,switch中不能使用。它不会跳出循环,只会省略之后的循环语句,并回到循环的开头进行下一次循环,它并不能跳出循环。
下面做个对比,让大家更加理解break和continue的区别:
/**
*
* @author mouyo
*/
public class ContinueAndBreak {
public static void main(String[] args) {
for(int i=0;i<10;i++){
if(i==5){
break;
}
System.out.println("i="+i);
}
}
}
这段程序会显示i=1到i=4,因为在i等于5时就会执行break而离开循环。
图3.8 循环中使用break
再看这段程序:
/**
*
* @author mouyo
*/
public class ContinueAndBreak {
public static void main(String[] args) {
for(int i=0;i<10;i++){
if(i==5){
continue;
}
System.out.println("i="+i);
}
}
}
这段程序会显示除5以外的所有情况,因为当i=5时,会执行continue直接略过之后的语句,也就是该次的System.out.println()并没有被执行,直接跳回开头的i++,并继续下一次循环,所以i=5没有被显示。
图3.9 循环中使用continue
3.3 重点复习
在Java中的基本类型主要可区分为整数、字节、浮点数、字符与布尔。整数可细分为short(占2字节)、int(占4字节)与long(占8字节)。byte类型顾名思义,长度就是1字节。浮点数可分为float(占4字节)与double(占8字节)。char类型用来存储’A’、’B’、’林’等字符符号。在JDK8中,Java的字符采用Unicode6.2.0编码,JVM采用UTF-16Big Endian,所以每个字符数据类型占2字节。boolean类型可表示true和false。如果储存值超出类型范围,称为溢出,会造成程序不可预期的结果。
Java在程序中注释分为//单行注释与/多行注释/。
数据保存于内存中的位置在程序语言中称为变量。对基本类型来说,想要声明何种类型的变量,就使用相应的类型关键字来声明。变量在命名时有一些规则,它不可以使用数字作为开头,也不可以使用一些特殊字符,但变量名不可以与Java关键词同名,也不可以与java保留字同名。
在Java中的命名规范,通常会以小写字母开始,并在每个单字开始时第一个字母使用大写,称为驼峰命名法。
在方法中声明的变量称为局部变量,不可以声明局部变量后未初始化之前就使用变量,编译程序遇到这种情况也会编译错误。
==是由两个连续的=组成,而不是一个=,=是赋值运算,这一点要注意。
&&与||有所谓的短路运算。因为与只要其中一个为假,就可以判定结果为假,所以对&&来说,只要左操作数为假,就会直接返回false,不会再去运算右操作数。因为或只要有一个为真,结果就是真,所以只要左操作数为真,就会直接返回true,不会再去运算右操作数。
++和—运算写在变量前与写在变量后是有区别的,所以不是特别需要,最好单独书写,不要与其他的四则运算混合书写,以免出现莫名其妙的逻辑错误。
在JDK7之后,switch可用于比较整数、字符、字符串与Enum。
for()循环语法的圆括号中,初始化只执行一次,如果变量是在初始化时声明,则循环结束时就会被销毁。
3.4 课后练习
3.4.1 选择题
- 如果有以下的程序代码:
int number;
System.out.println(number);
以下描述正确的是()
A.执行时显示0
B.执行时显示随机数字
C.执行时出现错误
D.编译失败
- 如果有以下程序代码
System.out.println(10/3);
以下描述正确的是()
A.执行时显示3
B.执行时显示3.33333333333333
C.执行时出现错误
D.编译失败
- 如果有如下代码:
float radius=88.2;
double area=2*3.14*radius*radius;
System.out.println(area);
以下描述正确的是()
A.执行时显示48853.6272
B.执行时显示48853
C.执行时出现错误
D.编译失败
- 如果有如下代码:
byte a=100;
byte b=200;
byte c=(byte)(a+b);
System.out.println(c);
以下描述正确的是()
A.执行时显示300
B.执行时显示127
C.执行时出现错误
D.编译失败
- 如果有如下代码:
System.out.println(Integer.MAX_VALUE+1==Integer.MIN_VALUE);
以下描述正确的是()
A.执行时显示true
B.执行时显示false
C.执行时出现错误
D.编译失败
- 如果有如下代码:
System.out.println(-Integer.MAX_VALUE==Integer.MIN_VALUE);
以下描述正确的是()
A.执行时显示true
B.执行时显示false
C.执行时出现错误
D.编译失败
- 如果有如下代码:
int i=10;
int number=i++;
number=--I;
以下描述正确的是()
A.执行时显示true
B.执行时显示false
C.执行时出现错误
D.编译失败
- 如果有如下代码:
int i=10;
int number=++i;
number=++i;
以下描述正确的是()
A.执行后number为11,i为11
B.执行后number为11,i为12
C.执行后number为12,i为11
D.执行后number为12,i为12
- 如果有如下代码:
for(int i=1;i<10;i++){
if(i==5){
continue;
}
System.out.println(“i=”+i);
}
以下描述正确的是()
A.显示i=1到4,以及6到9
B.显示i=1到9
C.显示i=1到4
D.显示i=6到9
- 如果有如下代码:
for(int number=0;number!=5;number=new Random().nextInt(10)){
System.out.println(number);
}
以下描述正确的是()
A.执行时显示数字永不停止
B.执行时显示数字0后停止
C.执行时显示数字5后停止
D.执行时显示数字,直到number为5后停止
3.4.2 操作题
- 如果有m与n两个int变量,分别储存1000与495两个值,请使用程序算出最大公因子。
- 在三位的整数中,例如153可以满足13+53+33=153,这样的数称为水仙花数,试以程序找出所有三位数的水仙花数(据说有4个)