Java从入门到精通(第2版)
上QQ阅读APP看书,第一时间看更新

第6章 最重要的编程部件—运算符、表达式与语句

本章视频教学录像:2小时16分钟

运算符、表达式和语句是编程的主要部件,能够使系统程序直接对内存进行操作,从而大大提高程序的执行能力。本章介绍Java运算符的用法、表达式与运算符之间的关系,以及表达式里各种变量的数据类型的转换等。学完本章,读者能对Java语句的运作过程有一个更深一层的认识。

本章要点(已掌握的在方框中打钩)

□ 掌握各种运算符的用法

□ 掌握各种表达式的用法

□ 掌握表达式与运算符的关系

□ 掌握表达式中数据类型的转换技巧

6.1 运算符

本节视频教学录像:48分钟

程序是由许多语句(statement)组成的,而语句组成的基本单位就是表达式与运算符。Java的运算符可分为4类:算术运算符、关系运算符、逻辑运算符和位运算符。

Java中的语句有多种形式,表达式就是其中的一种形式。表达式是由操作数与运算符所组成的。操作数可以是常量、变量,也可以是方法,而运算符就是数学中的运算符号,如“+”、“-”、“*”、“/”、“%”等。例如下面的表达式(X+100),“X”与“100”都是操作数,而“+”就是运算符。

6.1.1 赋值运算符

若为各种不同类型的变量赋值,就需要用到赋值运算符(Assignment Operator)。简单的赋值运算符由等号(=)实现,只是把等号右边的值赋予等号左边的变量。例如:

        int num = 22 ;

需要初学者注意的是,在Java中的赋值运算符“=”,并不是数学意义上的“相等”。例如:

        num=num+1; //假设num是前面定义好的变量

这句话的含义是,把变量num的值加1,再赋(=)给num。而在数学意义上,通过约减处理(等式左右两边同时约去num),可以得到“0 = 1”,这显然是不对的。

下面通过一个范例来讲解赋值运算符的用法。

【范例6-1】 在程序中用“=”赋值(AssignmentOperator6 1.java)。

        01 //在程序中赋值采用“=”
        02 public class AssignmentOperator6_1
        03 {
        04   public static void main(String args[])
        05   {
        06    int num=22;  //声明整数变量num,并赋值为22
        07    System.out.println("第一次赋值后,num="+num);  //输出num的值
        08    num=num-3;  //将变量num的值减3之后再赋给num变量
        09    System.out.println("改变之后的值,num="+num);  //输出后num的值
        10   }
        11 }

【运行结果】

程序运行结果如下图所示。

【范例分析】

程序第06行和第10行均是使用赋值运算符“=”对左侧的变量进行赋值。被赋值的变量(如num)的最终值永远以最近的更新为准,如本例num的最终值为19。

当然,在程序中也可以将等号后面的值赋给其他的变量,例如:

        int sum=num1+num2;  //num1与num2相加之后的值再赋给变量sum存放

num1与num2的值经过运算后仍然保持不变,sum会因为“赋值”的操作而更改内容。

6.1.2 一元运算符

对于很多表达式而言,运算符前后都会有操作数。但有一类操作符比较特别,它只需要一个操作数。这类运算符称为一元运算符(或单目运算符,Unary Operator)。下表列出了一元运算符的成员。

举例说明这些符号的含义。

        +5;     //表示正数5
        ~x;     //表示取变量x的补码
        y=-x;    //表示负x的值赋给变量y
        !x;        //x的NOT运算,若x为true,则!x返回false。若x为false,则!x返回true
        x= ~x   //表示将x的值取反,并赋给自己

对于上表中“++”和“--”运算符在6.1.8小节专门介绍。下面的程序说明了一元运算符的应用。【范例6-2】的程序声明了byte类型的变量a及boolean类型的变量b,可以看到这两个变量分别进行了“~”与“!”运算。

【范例6-2】 一元运算符的使用(UnaryOperator6 2.java)。

        01 //下面这段程序说明了一元运算符的使用
        02 public class UnaryOperator6_2
        03 {
        04   public static void main(String args[])
        05   {
        06    byte a=java.lang.Byte.MAX_VALUE;      //声明并将其类型最大值赋给a
        07    boolean b=false;                     //声明布尔变量并赋值为false
        08    System.out.println("a="+a+",~a="+(~a));
        09    System.out.println("b="+b+",!b="+(!b));
        10   }
        11 }

【运行结果】

程序运行结果如下图所示。

【代码详解】

第6行声明了byte变量a,并赋值为该类型的最大值,即a的值为127。程序第7行声明了boolean变量b,赋值为false。

第8行输出a与~a的运算结果。

第9行输出b与!b的运算结果。b的值为flase,因此进行“!”运算后,b的值就变成了true。

6.1.3 算术运算符

算术运算符(Arithmetic Operator)用于量之间的运算。算术运算符在数学上经常会用到,下表列出了它的成员。

1.加法运算符“+”

将加法运算符“+”的前后两个操作数相加,如下面的语句。

        System.out.println("3+8="+(3+8));  //直接输出表达式的值

注意,上面语句的字符串("3 + 8 = ")外的第一个加号“+”为字符串连接符,第二个加号才是加法运算符“+”。

2.减法运算符“-”

将减法运算符“-”前面的操作数减去后面的操作数,如下面的语句。

        num=num-3;    //将num-3运算之后赋值给num存放
        a=b-c;         //将b-c运算之后赋值给a存放
        120-10;         //运算120-10的值

3. 乘法运算符“*”

将乘法运算符“*”的前后两个操作数相乘,如下面的语句。

        b=b*5;   //将b*5运算之后赋值给b存放
        a=a*a;    //将a*a运算之后赋值给a存放
        19*2;     //运算19*2的值

4. 除法运算符“/”

将除法运算符“/”前面的操作数除以后面的操作数,如下面的语句。

        a=b/5;  //将b/5运算之后的值赋给a存放
        c=c/d;  //将c/d运算之后的值赋给c存放
        15/5;    //运算15/5的值

使用除法运算符时,要特别注意数据类型的问题。若被除数和除数都是整型,且被除数不能被除数整除时,这时输出的结果为整数(即整型数/整型数=整型数)。这是因为整型变量无法保存小数点后面的数据所致,因此在声明数据类型及输出时要特别小心。以下面的程序为例,在程序里给两个整型变量a、b赋值,并将a / b的运算结果输出。

【范例6-3】 除法运算符的使用(DivisionOperator6 3.java)。

        01 //下面这段程序说明了除法运算符的使用方法
        02  public class DivisionOperator6_3
        03 {
        04   public static void main(String[]args)
        05   {
        06     int a=13;
        07     int b=4;
        08     System.out.println("a="+a+",b="+b);
        09     System.out.println("a/b="+(a/b));
        10     System.out.println("a/b="+((float)a/b));    //进行强制类型转换
        11   }
        12 }

【运行结果】

程序运行结果如下图所示。

【代码详解】

第9行与10行,程序分别做出了不同的输出。在第9行,因为a、b皆为整数类型,输出结果也会是整数类型,程序运行结果与实际的值不同。

在第10行,为了保证程序运行结果与实际的值相同,使用了强制性的类型转换,即将整数类型(int)转换成浮点数类型(float),程序运行的结果和实际的值相同。

⑸ 取余运算符“%”

将取余运算符“%”前面的操作数除以后面的操作数,取其得到的余数。下面的语句是取余运算符的使用范例。

        num=num % 3;    //将num%3运算之后赋值给num存放
        a=b % c;        //将b%c运算之后赋值给a存放
        100 % 7;         //运算100%7的值为2

以下面的程序为例,声明两个整型变量a、b,并分别赋值为5和 3,再输出a%b的运算结果。

【范例6-4】 取余数(也称取模)操作(ModuloOperation6 4.java)。

        01 //在Java中用%进行取模操作
        02 public class ModuloOperation6_4
        03 {
        04    public static void main(String[]args)
        05    {
        06     int a=5;
        07     int b=3;
        08     System.out.println(a+"%"+b+"="+(a % b));
        09     System.out.println(b+"%"+a+"="+(b % a));
        10    }
        11 }

【运行结果】

程序运行结果如下图所示。

【代码详解】

设a和b为两个变量,取余运算的规则是。

其中 是a除以b的取整。

根据上述的公式,对于程序第08行,a%b = 5-1*3 = 2,而对于第09行,b%a= 3 - 0 * 5 =3。这里之所以把这个公式专门说明一下,是因为这里需要初学者注意的是,Java中的取余操作数也可以是负数和浮点数,而在C/C++中,取余运算的操作数只能是整数。例如,在Java中,下面的语句的是合法的。

        5%-3=2          //5对负3取余等于2
        5.2%3.1=2.1     //根据上述公式,余数为5.2-1*3.1=2.1

6.1.4 逻辑运算符

逻辑运算符只对布尔型操作数进行运算并返回一个布尔型数据。也就是说,逻辑运算符的操作数和运行结果只能是真(true)或者假(false)。常见的逻辑运算符有3个,即与(&&)、或(||)、非(!),如下表所示。

下面是使用逻辑运算符的例子。

        1>0&&3>0           //结果为true
        1>0||3>8             //结果为true
        !(1>0)            //结果为false

在第1个例子中, 只有1>0(true)和3>0(true)两个都为真,表达式的返回值为true,即表示这两个条件必须同时成立才行;在第2个例子中,只要1>0(true)和3>8(false)有一个为真,表达式的返回值即为true。第三个例子中,1>0(true),该结果的否定就是false。

在逻辑运算中,“&&”和“||”属于所谓的短路逻辑运算符 (Short-Circuit Logical Operators)。对于逻辑运算符“&&”,要求左右两个表达式都为true时才返回true,如果左边第一个表达式为false时,它立刻就返回false,就好像短路了一样立刻返回,省去了一些不必要的计算开销。

类似的,对于逻辑运算符“||”,要求左右两个表达式有一个为true时就返回true,如果左边第一个表达式为true时,它立刻就返回true。

下面的这个程序说明了逻辑运算符的运用。

【范例6-5】 短路逻辑运算符的使用(ShortCircuitLogical6 5.java)。

        01  public class ShortCircuitLogical6_5
        02  {
        03    public static void main(String[]args)
        04    {
        05      int i=5;
        06      boolean flag=(i<3)&&(i<4);  //&&短路,(i<4)系统不做运算
        07      System.out.println(flag);
        08      flag=(i>4)||(i>3);            //||短路,(i>3)系统不做运算
        09      System.out.println(flag);
        10    }
        11  }

【运行结果】

程序运行结果如下图所示。

【代码详解】

在第06行,由于i =5,所以(i < 3)为false,对于&&逻辑运算符,其操作数之一为false,其返回值必然为false,故在确定其左边的操作数为false,对于后一个运算操作数:(i < 4)无需计算,也就是“&&”短路。

类似的,对于“||”运算符,在第08行中,由于i =5,所以(i >4)为true,对于||逻辑运算符,其操作数之一为true,整体返回值必然为true,故在确定其左边的操作数为true,对于后一个运算操作数:(i > 3)无需计算,也就是“||”短路。

有的时候,我们需要逻辑“与”操作和“或”操作的两个操作数均需要运算,这时我们就需要使用避免短路的逻辑运算符——“&”和“|”,它们分别是可短路的逻辑运算符(&&和||)一半的字符。

下面的程序说明了短路逻辑运算符“&&”和非短路逻辑运算符的区别。

【范例6-6】 短路逻辑运算符(”&”和 “|”)和非短路逻辑运算符(“&&”和 “||”)对比。

        01  public class ShortCircuitLogical6_5_2
        02  {
        03    public static void main(String args[])
        04    {
        05      if(1==2&&1/0==0) {  //false&&错误=false
        06        System.out.println("1:条件满足!");
        07      }
        08      /*
        09      if(1==2&1/0==0) {  //false&错误=错误
        10        System.out.println("2:条件满足!");
        11      }
        12      */
        13      if(1==1||1/0==0){  //true||错误=true
        14        System.out.println("3:条件满足!");
        15      }
        16     /*
        17      if(1==1|1/0==0){  //true|错误=错误
        18        System.out.println("4:条件满足!");
        19      }
        20      */
        21    }
        22  }

【运行结果】

程序运行结果如下。

【代码详解】

我们知道,0是不能作为除数的。代码的第05行和第13行却可以编译通过。这是因为第06行逻辑运算符“&&”的第一个操作数是(1 == 2),它的值为false,这样第二个操作数直接被短路了,也就是不被系统“理睬”,故此第二个操作数中的(1/0)也就“侥幸蒙混过关”。

而第09行的代码编译无法通过,这是因为非短路逻辑运算符“&”左右两边的操作数均需要运算,任何一个操作数含有不符合Java语法规定的地方,均不被编译器认可。

类似的,13行可以编译通过,但含有非法操作符(1/0),但是短路操作符“||”的第一个操作数为(1==1)为true,所以第二个操作数直接被编译器“忽略”,最终if语句内逻辑判断值为true,所以14行打印输出“条件满足”。

但是同样的事情并没有发生在第17行语句上,这是因为非短路逻辑或运算符“|”的左右两个运算数都被强制运算,因此任何一个操作数不符合Java语法都不行,所以需要注释掉。

6.1.5 位运算符

位操作是指对操作数以二进制位(bit)为单位进行的运算。其运算的结果为整数。位操作的第一步是把操作数转换为二进制的形式,然后按位进行布尔运算,运算的结果也为二进制数。位操作运算符(bitwise operators )有7个,如下表所示。

我们用图例来说明上述运算符的含义,如下图所示。

下面的程序演示了“按位与”和“按位或”的操作。

【范例6-7】 “按位与”和“按位或”操作(BitwiseOperator6 7.java)。

        01  public class BitwiseOperator6_7
        02  {
        03    public static void main(String args[]){
        04      int x=13;
        05      int y=7;
        06      System.out.println(x&y);    //按位与,结果为5
        07      System.out.println(x|y);    //按位或,结果为15
        08    }
        09  }

【运行结果】

程序运行结果如下。

【代码详解】

第06行,实现与操作,相“与”的两位,如果全部为1结果才是1,有一个为0结果就是0。

13的二进制: 00000000 00000000 00000000 00001101

7的二进制: 00000000 00000000 00000000 00000111

“与”&的结果: 00000000 00000000 00000000 00000101

所以输出的结果为5。

第07行,实现或操作,位“或”的两位,如果全为0才是0,有一个为1结果就是1。

13的二进制: 00000000 00000000 00000000 00001101

7的二进制: 00000000 00000000 00000000 00000111

“或”|的结果: 00000000 00000000 00000000 00001111

所以输出的结果为15。

6.1.6 三元运算符

三元(Ternary)运算符也称三目运算符,它的运算符是“?:”,有三个操作数。操作流程如下:首先判断条件,如果条件满足,就会赋予一个变量一个指定的内容(冒号:之前的),不满足会赋予变量另外的一个内容(冒号之后的),其操作语法如下。

        数据类型 变量 = 布尔表达式 ? 条件满足设置内容 : 条件不满足设置内容;

下面的程序说明了三元运算符的使用。

【范例6-8】 三元运算符的使用(TernaryOperator6 8.java)。

        01  public class TernaryOperator6_8
        02  {
        03    public static void main(String args[])
        04    {
        05      int x=10;
        06      int y=20;
        07      int result=x>y?x:y;
        08      System.out.println("1st result="+result);
        09      x=50;
        10      result=x>y?x:y;
        11      System.out.println("2nd result="+result);
        12    }
        13  }

【运行结果】

程序运行结果如下图所示。

【代码详解】

result = x > y ? x : y表示的含义是:如果x的内容大于y,则将x的内容赋值给result,否则将y的值赋值给result。对于第07行,x = 10 和y = 20,result的值为y的值,即20。而对第10行,x = 50和y = 20,result的值为x的值,即50。

本质上来讲,三元运算符是简写的if…else语句。以上的这种操作完全可以利用if…else代替(在第7章我们会详细描述有关if…else的流程控制知识)。

6.1.7 关系运算符与if语句

if语句通常用于某个条件进行真(true)、假(false)识别。if语句的格式如下。

        if (判断条件)
        语句 ;

如果括号中的判断条件成立,就会执行后面的语句;若是判断条件不成立,则后面的语句就不会被执行。如下面的程序片段。

        if (x > 0)
        System.out.println("I like Java !");

当x的值大于0时,判断条件成立,就会执行输出字符串“I like Java!”的操作;相反,当x的值为0或是小于0时,if语句的判断条件不成立,就不会进行上述输出操作。下表列出了关系运算符的成员,这些运算符在数学上也是经常使用的。

在Java中,关系运算符的表示方式和在数学中类似,但是由于赋值运算符为“=”,为了避免混淆,当使用关系运算符“等于”时,就必须用2个等号(==)表示;而关系运算符“不等于”的形式有些特别,用“!=”代表,这是因为想要从键盘上取得数学上的不等于符号“≠”较为困难,同时“! ”有"非"的意思,所以就用“!=”表示不等于。

当使用关系运算符(Relational Operator)去判断一个表达式的成立与否时,若是判断式成立则会产生一个响应值true,若是判断式不成立,则会产生响应值false。以下面的程序为例,判断if语句括号中的条件是否成立,若是成立则执行if后面的语句。

【范例6-9】 关系运算符的使用(RelationOperator6 9.java)。

        01 //下面这段程序说明了关系运算符的使用方法,关系运算符返回值为布尔值
        02 public class RelationOperator6_9
        03 {
        04    public static void main(String[]args)
        05    {
        06     if(5>2)
        07       System.out.println("返回值:"+(5>2));
        08
        09    if(true)
        10       System.out.println("Hello Java!");
        11
        12     if((3+6)==(3-6))
        13       System.out.println("I like Java!");
        14    }
        15 }

【运行结果】

程序运行结果如下图所示。

【代码详解】

在第6行中,由于5>2的条件成立,所以执行第7行的语句:输出返回值true。

在第9行中,由于if语句的参数为true,判断亦成立,所以接着执行第10行的语句:输出字符串“Hello Java!”。

在第12行中,3+6并不等于3-6,if的判断条件不成立,所以第13行语句不被执行。

6.1.8 递增与递减运算符

递增与递减运算符在C / C++中就已经存在了,Java中将它们保留了下来,这是因为它们具有相当大的便利性。下表列出了递增与递减运算符的成员。

【范例6-10】 “++”运算符的两种使用方法(IncrementOperator6 10. java)。

        01 //下面这段程序说明了“++”的两种用法
        02 public class IncrementOperator6_10
        03 {
        04   public static void main(String args[])
        05   {
        06    int a=3,b=3;
        07     System.out.print("a="+a);  //输出a
        08     System.out.println(",a++="+(a++)+",a="+a);  //输出a++和a
        09     System.out.print("b="+b);  //输出b
        10     System.out.println(",++b="+(++b)+",b="+b);  //输出++b和b
        11   }
        12 }

【运行结果】

程序运行结果如下图所示。

【代码详解】

在第8行中,输出a++及运算后的a的值,后缀加1的意思是先执行对该数的操作,再执行加1操作,因此先执行a输出操作,再将其值自加1,最后输出,所以第一次输出3,第二次输出4。

在第10行中,输出++b运算后b的值,先执行自加操作,再执行输出操作,所以两次都输出4。

同样,递减运算符“--”的使用方式和递增运算符“++”是相同的,递增运算符“++”用来将变量值加1,而递减运算符“--”则是用来将变量值减1。此外,递增与递减运算符只能将变量加1或减1,若是想要将变量加减非1的数时,还是得用原来的“a = a+2”的方法。

6.1.9 括号运算符

除了前面所讲的内容外,括号()也是Java的运算符,如下表所示。

括号运算符()是用来处理表达式的优先级的。下面是一个简单的加减乘除式子。

        3+5+4*6-7  //未加括号的表达式

相信根据读者现在所学过的数学知识,这道题应该很容易解开。按加减乘除的优先级(*、/的优先级大于+、-)来计算,这个式子的答案为25。但是如果想先计算3+5+4及6-7之后再将两数相乘,就必须将3+5+4及6-7分别加上括号,而成为下面的式子。

        (3+5+4)*(6-7)  //加上括号的表达式

经过括号运算符()的运作后,计算结果为-12,所以括号运算符()可以对括号内表达式的处理顺序优先。

6.1.10 运算符的优先级

下表列出了各个运算符的优先级的排列,数字越小的表示优先级越高(优先级排名靠前)。

上表的最后一栏是运算符的结合性(associativity)。

什么是结合性?

结合性就是可以让程序设计者了解到运算符与操作数之间的关系及其相对位置。举例来说,当使用同一优先级的运算符时,结合性就非常重要了,它决定谁会先被处理。读者可以看看下面的例子。

        a = b + d / 5 * 4 ;

这个表达式中含有不同优先级的运算符,其中 “/”与“*”的优先级高于“+”,而“+”又高于“=”。但是读者会发现,“/”与“*”的优先级是相同的,到底d该先除以5再乘以4呢?还是5乘以4后d再除以这个结果呢?结合性的定义就解决了这方面的困扰。算术运算符的结合性为“由左至右”,就是在相同优先级的运算符中,先由运算符左边的操作数开始处理,再处理右边的操作数。在这个式子中,由于“/”与“*”的优先级相同,因此d会先除以5再乘以4,得到的结果加上b后,将整个值赋给a存放。

6.2 表达式

本节视频教学录像:35分钟

表达式是由常量、变量或是其他操作数与运算符所组合而成的语句。如下面的例子,均是表达式正确的使用方法。

        -49  //表达式由一元运算符“-”与常量49组成
        sum+2  //表达式由变量sum、算术运算符与常量2组成
        a+b-c/(d*3-9)  //表达式由变量、常量与运算符所组成

此外,Java还有一些相当简洁的写法,是将算术运算符和赋值运算符结合成为新的运算符。下表列出了这些运算符。

下面的几个表达式,皆是简洁的写法。

        a++    //相当于a=a+1
        a-=5   //相当于a=a-5
        b%=c  //相当于b=b%c
        a/=b--  //相当于计算a=a/b之后,再计算b--

这种独特的写法虽然看起来有些怪异,但是它却可减少程序的行数,提高运行的速度!请看下面这个范例。

【范例6-11】 程序的简洁写法(代码SimpleWriting6 11.java)。

        01 //下面是关于简洁写法的一段程序
        02 public class SimpleWriting6_11
        03 {
        04    public static void main(String[]args)
        05    {
        06     int a=5,b=8;
        07
        08     System.out.println("改变之前的数是:a="+a+",b="+b);
        09     a+=b; //等价于a=a+b;
        10     System.out.println("改变之后的数是:a="+a+",b="+b);
        11    }
        12 }

【运行结果】

程序运行结果如下图所示。

【代码详解】

第6行分别把变量a、b赋值为5和8。

第8行在运算之前先输出变量a、b的值,a为5,b为8。

第9行计算a+=b,这个语句也就相当于a = a +b,将a+b的值存放到a中。计算5+8的结果后赋值给a存放。

在第10行,再输出运算之后变量a、b的值,所以a的值变成13,而b仍为8。

下表列出了一些简洁写法的运算符及其范例说明。

下面举一个实例来说明这些简洁的表达式在程序中该如何应用。以SimpleWriting6_11_2为例,输入两个数,经过运算之后,来看看这两个变量所存放的值有什么变化。

【范例6-12】 程序的简洁写法2(SimpleWriting6 11 2.java)。

        01 //下面的程序说明了简洁表达式的使用方法,但这种方式现在已不提倡使用了
        02 public class SimpleWriting6_11_2
        03 {
        04   public static void main(String[]args)
        05    {
        06     int a=10,b=6;
        07
        08     System.out.println("改变之前的数:a="+a+",b="+b);
        09     a-=b++;  //先计算a-b的值,将结果设给a之后,再将b值加1
        10     System.out.println("改变之后的数:a="+a+",b="+b);
        11    }
        12 }

【运行结果】

程序运行结果如下图所示。

【代码详解】

第8行输出运算前变量a、b的值。在程序中a、b的赋值为10和6,因此输出的结果a为10,b为6。

第9行计算a -= b++,也就是执行下面这两条语句。

        a=a-b;  //(a=10-6=4,所以a=4)
        b++;  //(b=b+1=6+1=7,所以b=7)

第10行,将经过运算之后的结果输出,即可得到a为4、b为7的答案。

优秀程序设计里有个著名的KISS(Keep It Simple and Stupid )原则,即让代码保持简单,这里Stupid不是愚蠢的意思,而是表示一目了然。一目了然的代码易于理解和维护。由于上述简洁表达式的使用方法不易理解,所以目前已不提倡使用了。

6.2.1 算术表达式

算术表达式用于数值计算。它是由算术运算符和变量或常量组成,其结果是一个数值。

【范例6-13】 简单的算术表达式的使用(ArithmeticExpression6 13.java)。

        01 public class ArithmeticExpression6_13
        02 {
        03   public static void main(String[]args)
        04    {
        05     int a=12345679,b=9;
        06
        07     System.out.println("a*b="+a*b);
        08    }
        09 }

【运行结果】

程序运行结果如下图所示。

6.2.2 关系表达式

关系表达式常用于程序判断语句中,由关系运算符组成,其运算结果为逻辑型。

【范例6-14】 简单的关系表达式的使用(RelationExpression6 14.java)。

        01  public class RelationExpression6_14
        02  {
        03
        04     public static void main(String[]args)
        05     {
        06      int a=5,b=4;
        07      boolean t1=a>b;
        08      boolean t2=a==b;
        09      System.out.println("a>b:"+t1);
        10      System.out.println("a==b:"+t2);
        11     }
        12  }

【运行结果】

程序运行结果如下图所示。

【代码详解】

在第07行,先进行a > b的逻辑判断,由于a=5,b=4,所以返回true,并赋值给布尔变量t1。

在第08行,先进行a == b的逻辑判断,由于a=5,b=4,二者不相等,所以返回false,并赋值给布尔变量t2。第09和10行分别把对应的布尔值输出。

6.2.3 逻辑表达式

用逻辑运算符将关系表达式或逻辑量连接起来的有意义的式子称为逻辑表达式。逻辑表达式的值也是一个逻辑值,即“true”或“false”。

【范例6-15】 简单的逻辑表达式的使用(LogicExpression6 15.java)。

        01  public class LogicExpression6_15
        02  {
        03
        04    public static void main(String args[])
        05    {
        06      boolean t=(1+1==2)&&(1+2==3);
        07      System.out.println("(1+1==2)&&(1+2==3):"+t);
        08    }
        09  }

【运行结果】

程序运行的结果如下图所示。

【代码详解】

在第06行,由于加号(+)运算符的优先级高于逻辑等号(==),所以先进行的是加法操作,(1 +1 == 2) && (1 + 2 ==3) →(2 == 2) && (3 ==3),然后再实施逻辑判断(2 == 2)返回true,与逻辑判断(3 ==3)返回true ,显然,true&& true = true。所以最终的输出结果为true。

6.2.4 赋值表达式

赋值表达式由赋值运算符和操作数组成,赋值运算符用于给变量赋值。

【范例6-16】 简单的赋值表达式的使用(AssignExpress6 16.java)。

        01  public class AssignExpress6_16
        02  {
        03    public static void main(String args[])
        04    {
        05      int x=123;
        06      int y=6;
        07      int z;
        08      z=x*y-700;
        09      System.out.println("Assignment Expression:x*y-700="+z);
        10    }
        11  }

【运行结果】

程序运行的结果如下图所示。

【代码详解】

赋值表达式的功能是先计算右侧表达式的值,再赋予左边的变量。赋值运算符具有右结合性。在08行,初学者不能将赋值表达式z = x * y - 700理解为将x的值,或x *y 赋值给z,然后再减去700。正确的流程是先计算表达式x * y - 700值,再将计算的结果赋值给z。

6.2.5 表达式的类型转换

在前面5.6节我们曾提到过数据类型的转换。除了强制类型转换外,当int类型遇上了float类型,到底谁是“赢家”,运算的结果是什么数据类型呢,在这里,要再一次详细讨论表达式的类型转换。

Java是一个很有弹性的程序设计语言,当上述情况发生时,只要坚持“以不流失数据为前提”的大原则,即可进行不同的类型转换,使不同类型的数据、表达式都能继续存储。依照大原则,当Java发现程序的表达式中有类型不相符的情况时,就会依据下列规则来处理类型的转换。

⑴ 占用字节较少的数据类型转换成占用字节较多的数据类型。

⑵ 字符类型会转换成int类型。

⑶ int类型会转换成float类型。

⑷ 表达式中若某个操作数的类型为double,则另一个操作数也会转换成double类型。

⑸ 布尔类型不能转换成其他类型。

⑴和⑵体现“大鱼(占字节多的)吃小鱼(占字节少的)”思想。⑶和⑷体现“精度高者优先”思想,占据相同字节的类型向浮点数(float、double)靠拢。⑸中体现了Java对逻辑类型坚决“另起炉灶”的原则,布尔类型变量的值只能是true或false,它们和整型数据无关。而在C/C++中,逻辑类型和整型变量的之间关系是“剪不断,理还乱”,即所有的非零整数都可看作为逻辑“真”,只有0才看做为“假”。

下面的范例说明了表达式类型的自动转换。

【范例6-17】 表达式类型的自动转换(TypeConvert6 17.java)。

        01 //下面的程序说明了表达式类型的自动转换问题
        02 public class TypeConvert6_17
        03 {
        04    public static void main(String[]args)
        05    {
        06     char ch='a';
        07     short a=-2;
        08     int b=3;
        09     float f=5.3f;
        10     double d=6.28;
        11
        12     System.out.print("(ch/a)-(d/f)-(a+b)=");
        13     System.out.println((ch/a)-(d/f)-(a+b));
        14    }
        15 }

【运行结果】

程序运行的结果如下图所示。

【代码详解】

先别急着看结果,在程序运行之前可先思考一下,这个复杂的表达式(ch / a)- (d / f) - (a + b)最后的输出类型是什么?它又是如何将不同的数据类型转换成相同的呢?读者可以参考下图的分析过程。

第12行和第13行分别用了System.out.print和System.out.println方法。二者的区别在于,前者输出内容后不换行,而后者输出内容后换行。println中最后两个字符“ln”实际上是英文单词“line”的简写,表明一个新行。

由于我们知道,转义字符“\n”也有换行的作用,所以,

System.out.print("我要换行!\n");和语句System.out.println("我要换行!");

是等效的,都能达到输出内容后换行的效果。

6.3 语句

本节视频教学录像:14分钟

在学会使用运算符和表达式后,就可以写出最基本的Java程序语句了。表达式由运算符和操作数组成,语句(statement)则由表达式组成。例如 a + b是一个表达式, 加上分号后就成为了下面的形式。

        a + b ;

这就是一个语句。计算机执行程序就是由若干条语句进行的。每个语句后用分号(;)隔开。多个语句只要用分号隔开,可以处于同一行,如下面的语句是合法的。

        char ch = 'a' ; short a = -2 ; int b = 3 ;

但为了让程序有良好的可读性,并且方便添加注释,我们推荐读者遵循一条语句占据一行的模式,例如:

        char ch='a';   //定义字符型变量ch,并赋值为’a’
        short a=-2;    //定义短整型变量a,并赋值为-2
        int b=3;       //定义整型变量b,并赋值为3

6.3.1 语句中的空格

在Java程序语句中,空格是必不可少的。一方面,所有的语言指令都需要借助空格来分割标注。例如下面的语句。

        int a;   //变量类型(int)和变量(a)之间需要空格隔开

另一方面,虽然Java编译器在编译的过程中,会把所有非必需的空格过滤掉,但空格可以使程序更具有可读性,更加美观。

例如下面的程序使用了空格,程序的作用很容易看懂。

【范例6-18】 语句中的空格使程序易懂(SpaceDemo6 18.java)。

        01  public class SpaceDemo6_18
        02  {
        03    public static void main(String args[])
        04    {
        05      int a;
        06      a=7;   //等号两边使用了空格
        07      a=a*a;  //等号和乘号两边使用了空格
        08      System.out.println("a*a="+a);
        09    }
        10  }

【运行结果】

程序运行结果如下图所示。

【代码详解】

在有了一定的编程经验之后,读者可以体会到,源程序除了要实现基本的功能,还要体现出“编程之美”。笔者推荐,在所有可分离的操作符中间都加上一个空格,这样让代码更加舒展,例如对于07行,一般的编程风格为

        a=a+a;   //风格1

而笔者推荐的风格,每个操作符(如这个语句的加号“=”和“+”)左右两边都手工多敲一个空格,

        a=a+a;  //风格2

上述两个语句在功能上(对于编译器来说)是完全相同的,但后者更具有美感。当然,对于编程风格,“仁者见仁,智者见智”,这里仅仅是一种推荐,而不是一种必须。

但是对于逻辑判断的等号“==”、“&&”、“||”、简写操作符“+=”、“-=”等、左右移符号“<<”、“>>”及“>>>”等,是不可以用空格隔开的。如果这些符号中间添加空格的话,编译器就会不“认识”它们,从而无法编译通过。

6.3.2 空语句

前面所讲的语句都要进行一定的操作,但是Java中有一种语句什么也不执行,这就是空语句。空语句是由一个分号(;)组成的语句。

空语句是什么也不执行的语句。在程序中空语句常常用来作空循环体。例如

        while((char) System.in.read()!='\n')
        {    //为了突出空语句,特地加了一个大括号
        ;
        }

本语句的功能是,只要从键盘输入的字符(System.in.read()方法)不是回车则重新输入。一般来说,while的条件判断体后不用添加分号的,后面紧跟一个循环体,而这里的循环体为空语句。上述语句可以用下面更加简洁语句来描述:while(getchar()!='\n') ;

关于while循环的知识点我们会在随后的第七章详细描述,这里读者仅需知道这个概念即可。

空语句还可以用于在调试时留空以待以后添加新的功能。如果不是出于这种目的,一般不建议使用空语句,因为空语句不完成任何功能,但同样会额外占用计算机资源。

提示

一条Java语句后面可以跟多个分号吗?如 int x ;; 是合法的吗?(面试题)如果认为每条语句只能跟一个分号表明本语句结束,那么就会回答“不合法”。事实上,由于多个Java语句可以处于同一行,那么int x ;;就可以解读为“int x;”这条语句和另外一个空语句“;”共处于一行之上。int x语句后面即使跟10个分号也合法。

6.3.3 声明语句

在前面已经多次用到了声明语句。其格式一般如下。

        <声明数据类型> <变量1> …<变量n>;

使用声明语句可以在每一条语句中声明一个变量,也可以在一条语句中声明多个变量。还可以在声明变量的同时,直接与赋值语句连用为变量赋值。例如

        int a;    //一条语句中声明一个变量
        int x,y;   //一条语句中声明多个变量
        int t=1;  //声明变量的同时,直接为变量赋值

如果对声明的成员变量没有赋值,那么将赋为默认的值。默认初始值:整型的为0;布尔类型变量默认值为false;引用数据类型和字符串类型默认都为null。更加详细的声明读者可以参阅5.7节。

6.3.4 赋值语句

除了可以在声明语句中为变量赋初值外,还可以在程序中为变量重新赋值,这就用到了赋值语句。

例如:

        pi = 3.1415;
        r = 25;
        s = pi*r*r;

在程序代码中,使用赋值语句给变量赋值,赋值符号右边可以是一个常量或变量,也可以是一个表达式。程序在运行时先计算表达式的值,然后将结果赋给等号左边的变量。

6.4 高手点拨

本节视频教学录像:9分钟

1. &和&&、|和||的关系是怎么样?(Java面试题)

答:对于“与操作”:有一个条件不满足,结果就是false。普通与(&):所有的判断条件都要执行;短路与(&&):如果前面有条件已经返回了false,不再向后判断,那么最终结果就是false。

对于“或操作”:有一个条件满足,结果就是true。对于普通或(|):所有的判断条件都要执行;短路或(||):如果前面有条件返回了true,不再向后判断,那么最终结果就是true。

2. 递增与递减运算符

递增与递减运算符通常单独使用,不与其他操作符一起组成语句。

3. 位运算的技巧

任何数与 0000 0001(二进制) 进行或(|)运算后,第一位将变为1,与 1111 1110(二进制)进行与(&)运算后,第一位将变为0。

位运算通常用于设置或获取标志位,及判断相应的操作是否成功。

6.5 实战练习

本节视频教学录像:25分钟

1. 编写程序,计算表达式“((12345679*9)>(97654321*3))? true : false”的值。

2. 编写程序,实现生成一随机字母(a-z,A-Z),并输出,运行结果如下图所示。

拓展知识。

⑴ Math.random()返回随机 double 值,该值大于等于 0.0 且小于 1.0。

例如: double rand = Math.random(); // rand 储存着[0,1) 之间的一个小数

⑵ 大写字母A~Z对应整数65 ~ 90、小写字母a~z对应整数97 ~ 122。

3. 编写程序,实现产生(或输入)一随机字母(a-z,A-Z),转为大写形式,并输出。请分别使用三元运算和位运算实现,运行结果如下图所示。

拓展知识点。

⑴ 大写字母A~Z对应整数65 ~ 90、小写字母a~z对应整数97 ~ 122。

⑵ 可以使用 0x 表示16进制数,如0x10 表示16进制的 10。