java-语句、表达式、运算符及其优先级、结合性

语句

  • 语句是组成程序的基本单位,java语句是java标识符的集合,由关键字、常量、变量和表达式组成,是成员方法的主要组成部分,必须包含在类的方法体中。语句以“;”作为结束标志。
  • 执行语句是为了完成某个操作或修改某个数据。
  • java的语句类型有:
    • 变量声明语句,比如:int x, y = 10;
    • 执行语句:
      • 流程控制语句:if-else,switch-case,while,do-while,for,break,continue
      • 表达式语句:在表达式后面加上“;”就是一个表达式语句,比如:x =0; x = x + 1;
      • 方法调用语句
    • 空语句:只有一个分号的语句,空语句什么也不做,只表示语句的存在。
    • 复合语句:
      • 复合语句也称为块语句,是包含在一对大括号中的任意语句序列。与其他语句用“;”作为结束符不同,复合语句右大括号后面不需要“;”,尽管复合语句含有任意多个语句,但是从语法上讲,一个复合语句被看作一个简单语句。
      • 复合语句示例: 复合语句为局部变量创建了一个作用域,该作用域为程序的一部分。如果在复合语句之外访问在复合语句内创建的变量,则会发生错误。
public class test{  
    public static void main(String args[]){
        int i = 3;
        float f = 3.14f;

        {
            i++;
            float f2 = f + 1f;
            System.out.println(f + " + 1 = " + f2 + ".");
        }

        System.out.println("i = " + i + ".");
        //System.out.println("f2 = " + f2 + ".");
    }
}


编译,运行,会在标准输出看到:

3.14 + 1 = 4.1400003.  
i = 4.  

如果去掉//System.out.println("f2 = " + f2 + ".");的注释,也就是尝试在复合语句之外访问在复合语句内创建的变量,那么编译时会报错:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:  
    f2 cannot be resolved to a variable

    at test.main(test.java:13)


关于java的流程控制语句,本文不做扩展,本文着重深入了解表达式。


表达式

  • 由操作符、操作数和标点符号组成的序列,用来说明一个计算过程。比如:z = x + y;
    • 操作数也称为运算量或数据,可以是常量、变量或函数,“任何有值的东西”都是表达式
    • 操作符描述的是对数据进行的操作。根据操作符需要的操作数的个数的不同,操作符分为:单目运算符、双目运算符、三目运算符。
  • 表达式可以嵌套,比如:2 + 3 + 4 * (5 + 6);
  • 表达式根据某些约定、求值顺序、结合性和优先级规则来进行计算:
    • 约定:类型转换的约定,由低->高。
    • 求值顺序:Java规定从左到右(求值顺序与结合性及优先级不同,详情可查看参考文档)。

表达式有两种功能:每个表达式都产生一个值( value ),同时可能包含副作用( side effect ),比如可能修改某些值。


算数运算符

算术运算符用在数学表达式中,它们的作用和在数学中的作用一样。下表列出了所有的算术运算符。
表格中的实例假设整型变量A的值为10,变量B的值为20:

操作符 描述 例子
+ 加法 - 相加运算符两侧的值 A + B等于30
- 减法 - 左操作数减去右操作数 A – B等于-10
* 乘法 - 相乘操作符两侧的值 A * B等于200
/ 除法 - 左操作数除以右操作数 B / A等于2
% 取模 - 右操作数除左操作数的余数 B % A等于0

需要说明的:

  • “+”和“-”运算符还可以作为数据的正负符号,比如:+5、-7。
  • 除法运算时,除数不可以为0,例如:int a = 500 / 0;,系统会报出ArithmeticException异常。
  • 算数运算符都是二元运算符。

自增和自减运算符

自增、自减运算符都是单目运算符,可以放在操作数之前,也可以放在操作数之后。操作数必须是整型或浮点型变量

  • 前自增(前自减)会先将变量的值加1(减1),然后再使该变量参与表达式的运算。
  • 后自增(后自减)会先使变量参与表达式的运算,然后再将该变量的值加1(减1)。

下面是一个例子test.java:

public class test{  
    public static void main(String args[]){
        int a = 4, b;
        b = ++a;
        System.out.println(b);
        b = a++;
        System.out.println(b);
    }
}


编译、运行,会在标准输出打印:

5  
5  

关系运算符

关系运算符属于二元运算符,用于程序中的变量和变量之间、变量和常量之间、其他类型的信息之间的比较。关系运算符的返回类型是boolean类型,当运算符对应的关系成立时,运算结果是true,否则结果是false。关系运算符通常用在条件语句中,来作为判断的依据。
关系运算符的种类和用法如下表:

运算符 作用 举例 操作数据 结果
> 比较左方是否大于右方 'a' > 'b' 整型、浮点型、字符型 false
< 比较左方是否小于右方 156<456 整型、浮点型、字符型 true
比较左方是否等于右方 'c' = = 'c' 基本数据类型、引用型 true
>= 比较左方是否大于等于右方 479 >= 426 整型、浮点型、字符型 true
<= 比较左方是否小于等于右方 12.45 <= 45.5 整型、浮点型、字符型 true
!= 比较左方是否不等于右方 "y" != "t" 基本数据类型、引用型 false

逻辑运算符

逻辑运算符包括:逻辑与(&&、&)、逻辑或(||、|)和逻辑非(!),返回值为boolean类型,操作数也必须是boolean类型的数据。在逻辑运算符中,除了逻辑非(!)是一元运算符之外,其余两个都是二元运算符,其用法和含义如下表:

运算符含义用法结合方向
&&、&逻辑与op1&&op2左到右
||、|逻辑或op1||op2左到右
!逻辑非!op右到左

使用逻辑运算符进行逻辑运算,运行结果如下表:

表达式1表达式2表达式1&&表达式2表达式1||表达式2!表达式1
truetruetruetruefalse
truefalsefalsetruefalse
falsefalsefalsefalsetrue
falsetruefalsetruetrue

从上表可以看出:

  • 逻辑与:只有表达式1和表达式2都为true的时候才返回true,否则返回false
  • 逻辑或:只有表达式1和表达式2都为false的时候才返回false,否则返回true
  • 逻辑非:如果表达式为true,则返回值为false;如果表达式为false,则返回值为true
TIPS:  
逻辑运算符“&&”和“&”都表示逻辑与,
逻辑运算符“||”和“|”都表示逻辑或,

“&&”和“||”是短路的:
“&&”在第一个表达式为false的时候,不会计算第二个表达式;
“||”在第一个表达式为true的时候,不会计算第二个表达式;

“&”和“|”是非短路的:
在任何情况下,都会计算第二个表达式。


下面看一个例子test.java:

public class test{  
    public static boolean func1(){
        System.out.println("func1 is called.");
        return true;
    }

    public static boolean func2(){
        System.out.println("func2 is called.");
        return false;
    }

    public static void main(String args[]){
        boolean r;
        r = func2() && func1();
        r = func2() &  func1();
        r = func1() || func2();
        r = func1() |  func2();
    }
}


编译、运行,会在标准输出打印:

func2 is called.  
func2 is called.  
func1 is called.  
func1 is called.  
func1 is called.  
func2 is called.  

位运算符

位运算符用于处理整型和字符型的操作数,对其内存进行操作,数值在内存中一律以补码的方式存储。例如int型常量7的二进制表示是00000000 00000000 00000000 00000111(正数的原码,反码,补码相同),-8的二进制表示是11111111 11111111 11111111 11111000(负数的补码是在原码的基础上,符号位不变,其余各个未取反,再加1);最高位为符号位,0表示正数,1表示负数。java语言提供的位运算符如下表:

运算符含义用法
~按位取反~op1
&按位与op1 & op2
|按位或op1 | op2
^按位异或op1 ^ op2
  • 按位与&
    • 双目运算符
    • 如果两个操作数对应位都是1,结果位才是1,否则为0。如果两个操作数的精度不同,则结果的精度与精度高的操作数相同。
  • 按位或|
    • 双目运算符
    • 如果两个操作数对应位都是0,结果位才是0,否则为1。如果两个操作数的精度不同,则结果的精度与精度高的操作数相同。
  • 按位非~
    • 单目运算符
    • 将操作数二进制表示形式中的1全部修改为0,0全部修改为1。
  • 按位异或^
    • 双目运算符
    • 如果两个操作数对应位相同(同为0或同为1),则结果位是0,否则是1。如果两个操作数的精度不同,则结果的精度与精度高的操作数相同。

下面看几个例子:

  • 5&-4的运算过程 p1
  • 3|6的运算过程 p2
  • ~7的运算过程 p3
  • 10 ^ 3的运算过程 p4

移位运算符

java提供的移位运算符如下表:

运算符含义用法
<<左移op1 << op2
>>右移op1 >> op2
>>>无符号右移op1 >>> op2

java中移位运算符有三种,操作数的类型只有byte、short、int、long和char五种。

  • 左移运算符<<
    • 将左边的操作数在内存中的二进制数据,左移右边的操作数指定的位数,右边移空的部分补0。
    • 48 << 1,是将48的二进制数向左移一位,移位后的结果是96。
  • 右移运算符>>
    • 将左边的操作数在内存中的二进制数据,右移右边的操作数指定的位数,如果最高位(也就是符号位)为0,左边移空的部分补0,如果最高位是1,左边移空的部分补1,简而言之,就是左边移空的部分补上符号位。
    • 48 >> 1,是将48的二进制数向右移一位,移位后的结果是24。移位的过程如下图:
      youyi
  • 无符号右移运算符>>>
    • 不管最高位是0还是1,左边移空的部分都补上0。
TIPS:  
移位能让用户实现整数乘以或除以2的n次方的效果。例如y<<2与y*4的结果相同;y>>1与y/2的结果相同。总之,一个数左移n位,就是将这个数乘以2的n次方;一个数右移n位,就是将这个数除以2的n次方。

三元运算符

三元运算符是java中唯一一个三目运算符,其操作数有三个,第一个是条件表达式,其余两个是值。条件表达式成立时,取第一个值;条件表达式不成立时,取第二个值。示例代码如下:
boolean b = 20 < 45 ? true : false;
因为条件表达式20 < 45成立,所以取第一个值,此例的结果是true。


赋值运算符

赋值运算符=是一个二元运算符,其功能是将右边的操作数所含的值赋给左边的操作数。语法格式如下:
[变量类型] 变量名 = 所赋的值;
左边的操作数必须是一个变量,右边的操作数可以是变量、常量或表达式。

赋值运算符是有返回值的。比如int x, y, z; x = y = z = 5;因为赋值运算符是右结合的,所以先计算z = 5,此时z的值为5,并且z = 5这个表达式的值为5,所以x = y = z = 5转换成了x = y = 5,然后计算y = 5,此时y的值为5,并且y = 5这个表达式的值是5,所以x = y = 5转换成了x = 5,此时x的值是5。所以最终x、y、z的值都是5。
混合赋值运算符包括:

运算符描述例子
+=加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数C + = A等价于C = C + A
-=减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数 C - = A等价于C = C - A
*=乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数 C * = A等价于C = C * A
/=除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数 C / = A等价于C = C / A
%=取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数 C%= A等价于C = C%A
<<=左移位赋值运算符C << = 2等价于C = C << 2
>>=右移位赋值运算符C >> = 2等价于C = C >> 2
&=按位与赋值运算符C&= 2等价于C = C&2
^ =按位异或赋值操作符C ^ = 2等价于C = C ^ 2
|=按位或赋值操作符 C | = 2等价于C = C | 2

instanceof运算符

instanceof运算符是一个二元运算符,只用于对象引用变量,它的作用是测试一个对象是否为特定类型(类类型或接口类型)的对象。用法:
( Object reference variable ) instanceof (class/interface type)
如果左侧的对象引用变量 是class的实例 或者是class或interface的子类的实例,那么返回true,否则返回false。

下面是一个例子test.java:

interface i{  
}

class c implements i{  
}

class c2 extends c{  
}

public class test{  
    public static void main(String args[]){
        c2 obj = new c2();
        System.out.println(obj instanceof c);
        System.out.println(obj instanceof c2);
        System.out.println(obj instanceof i);
    }
}


编译、运行,会在标准输出打印:

true  
true  
true  

运算符的优先级和结合性

在同一个表达式中,先计算优先级高的运算符,再计算优先级低的运算符,对于优先级相同的运算符,按照结合性来确定计算顺序,如果运算符是左结合的,就先计算左边的运算符,如果运算符是右结合的,就先计算右边的运算符。

序列号

符号

名称

结合性(与操作数)

目数

说明

1

.

从左到右

双目

 

( )

圆括号

从左到右

 

 

[ ]

方括号

从左到右

 

 

2

+

正号

从右到左

单目

 

-

负号

从右到左

单目

 

++

自增

从右到左

单目

前缀增,后缀增

- -

自减

从右到左

前缀减,后缀减

~

按位非/取补运算

从右到左

单目

 

逻辑非

从右到左

单目

!”不可以与“=”联用

3

*

从左到右

双目

 

/

从左到右

双目

整数除法:取商的整数部分,小数部分去掉,不四舍五入

%

取余

从左到右

双目

 

4

+

从左到右

双目

 

-

从左到右

双目

 

5

<< 

左移位运算符

从左到右

双目

 

>> 

带符号右移位运算符

从左到右

双目

 

>>> 

无符号右移

从左到右

双目

 

6

小于

从左到右

双目

<=

小于或等于

从左到右

双目

 

大于

从左到右

双目

 

>=

大于或等于

从左到右

双目

 

instanceof

确定某对象是否属于指定的类

从左到右

双目

 

7

等于

从左到右

双目

!=

不等于

从左到右

双目

 

8

&

按位与

从左到右

双目

 

9

|

按位或

从左到右

双目

 

10

^

按位异或

从左到右

双目

 

11

&&

短路与

从左到右

双目

 

12

||

短路或

从左到右

双目

 

13

? :

条件运算符

从右到左

三目

 

14

=

赋值运算符

从右到左

双目

 

+=

混合赋值运算符

 

-=

 

*=

 

/=

 

%=

 

&=

 

|=

 

^=

 

<<=

 

>>=

 

>>>=

 

概括而言,优先级从高到低的顺序是:后缀运算符(点、圆括号、方括号)、单目运算符、算数运算符、移位运算符、关系运算符和instanceof、位运算符、逻辑运算符、条件运算符、赋值运算符、混合赋值运算符。
右结合的运算符有:所有的单目运算符、条件运算符、所有的赋值运算符。其余的运算符都是左结合的。


参考文档

感谢浏览tim chow的作品!

如果您喜欢,可以分享到: 更多

如果您有任何疑问或想要与tim chow进行交流

可点此给tim chow发信

如有问题,也可在下面留言: