2-4  運算符號

運算式是由「運算子(Operator)」和「運算元(Operand)」組合而成的。「運算元」代表資料的表示方法,而「運算子」決定了資料的運算方式,它又可以被稱為「運算符號」。例如:以下的表示式中:

x = y + z;

表示式中的「x」、「y」、「z」代表運算元,而「+」、「=」則是運算子。

程式中可以使用的運算子主要可以區分為「算術運算子」、「指定運算子」、「關係運算子」、「邏輯運算子」、「字串運算子」等類型。

算術運算子

符號

說明

使用範例

+

加法運算

x+y;

-

減法運算,或是負號

x-y; -x

*

乘法運算

x*y

/

除法運算

x/y

%

餘數運算

x%y

++

遞增運算

x++; 等同x=x+1

--

遞減運算

x--; 等同 x=x-1

<< 

算數位元左平移

 

>> 

算數位元右平移,保留sign

 

>>> 

邏輯位元右平移,unsign

 

      算數運算子使用的方式如同一般的數學表示式,例如,以下的表示式:

4 + 7                      //結果為11

8 / 2                       //結果為4

%」運算式是取得兩數相除之後的餘數,例如,以下的表示式:

10 % 3                     //結果會是1

當運算式中含了算數運算符號時,執行的通則是由左向右執行。例如:以下運算式的執行後,「x」的值為「2」。

x = 3 + 2 – 3;

但以下的運算式的執行後,「x」的值卻為「9」。

x = 3 + 2 * 3;

不同的算術運算子會有執行的先後優先權,規則大致和數學式運算原則相同,參考下表

順序

符號

說明

1

( )

括號

2

-

負號

3

*/

乘法、除法

4

%

取餘數

5

+-

加法、減法

例如以下的運算式,執行後,「x」的值為「6」,而不是「4」:

x = 3 + 5 * 2 % 3 + 1 *2;

使用「()」可以改變運算子執行的先後順序,「()」中的運算式會被優先執行。如果「()」還有另外的「()」,則內部的「()」會先執行。例如以下的運算式,執行後,「x」的值為「25」:

x = 3 + ( 5 * ( 2 % 3 ) + 1 ) * 2;

++」和「--」運算子

++」和「--」這兩個運算子會使得變數本身的內容自動增加或是減少1。例如:運算式「x++」的結果等同於以下的運算式:

x = x + 1;

--」運算子的使用概念如同「++」運算子。但運算子置於變數之前或變數之後,對於變數本身的內容改變的時間並不相同。如果「++」或「--」放於變數之後,該變數會在整行運算式執行完成後,變數本身的值才會增加1。如果「++」或「--」放於變數之前,該變數本身的值會先增加1,再執行運算式。參考以下的範例:

程式2-14Chap2\Operator1.java

01 class Operator1

02 {

03    public static void main(String[] args)

04    {

05        int v1 = 10;

06        int v2 = 10;

07        int v3;

08

09        v3 = v1++;

10        System.out.println( “v1的值為” + v1 + “v3的值為” + v3);

11

12        v3 = ++v2;

13        System.out.println(“v2的值為” + v2 + “v3的值為” + v3 );

14    }

15 }

乍看之下,第10行和第13行程式的顯示結果會是相同的,但執行結果卻是:

v1的值為11 v3的值為10

v2的值為11 v3的值為11

最主要是:表示式「variable」會先執行完敘述後,再將「variable」本身的值遞增。所以,程式中第9行的敘述,「v1」先將值「10」指定給「v3」,然後本身在增加1。因此,執行的結果才會是:

v1的值為11 v3的值為10

而「++variable」表示式則是先將「variable」本身的值遞增,再執行敘述。所以,程式中第12行的敘述,「v12」本身先增加1,再將值「11」指定給「v3」。因此,執行的結果會是:

v2的值為11 v3的值為11

如果需要將「++」或是「--」運算子和其他的算數運算子合併使用,那麼,程式中需要利用「空格」或是「()」區隔「++」或是「--」運算子,否則,程式會發生編譯錯誤的情形,例如:以下的運算式就會產生編譯錯誤:

x = 10 +++y;

程式必須修改成:

x = 10 + ++y;

或是:

x = 10 + (++y);

位元平移運算子

<<」、「>>」、「>>>」也可以分類為位元算術運算子,這些運算子可以用來操控整數型別資料中個別位元的運算,使用的技巧在於二進位位元的移動。

<<」稱為「算數左平移」,使用範例如同:

6 << 1

該行敘述計算的結果為「12」,請參考下表:

6的二進位

0

0

0

0

0

1

1

0

左平移1bit

0

0

0

0

1

1

0

X

結果

0*27

0*26

0*25

0*24

1*23

1*22

0*21

0*20

算數左平移是直接將原數的二進位內容往左移動,「6<<1」是指將6的二進位值往左移動1bit,左平移時,不用考慮正負數,所以左平移後,右邊留下的位置直接補「0」,再將每個bit的值相加就是平移後的結果了。

1 * 23 + 1 * 22 + 0 *21 + 0 * 20 = 12

        算數左平移的結果會得到一個倍數值,例如:「6<<1」的結果為12,「6<<2」的結果為24。如果是利用int來計算左平移,當結果超過int能夠容納的範圍時,數值會產生轉折的現象,程式的執行是不會發生問題的。但是,如果利用byte來計算左平移時,程式就有可能會產生問題了。以下的程式執行後,left的值會是-2

int left = 2147483647;        //int的最大值

left = left << 1;

但如果您是用byte來計算結果,例如以下的範例:

               byte left = 100;

               left = left << 1;

程式編譯時產生的錯誤畫面如下:

命令提示字元

Operator.java:31possible loss of precision

found  int

requiredbyte

             left = left << 1;

1 error

這是因為byte型別在運算時會自動轉型為int型別,而int型別並無法自動轉型為byte型別,所以程式才會在編譯時產生錯誤。您可以自行指定運算後需要轉換的型別即可修正這項錯誤了。例如:以下的範例的執行結果為「-56」:

               byte left = 100;

               left = ( byte ) ( left << 1);          //使用明確轉型

>>」稱為「算數右平移」,使用範例如同:

6 >> 1

該行敘述計算的結果為「3」,請參考下表:

6的二進位

0

0

0

0

0

1

1

0

 

右平移1bit

 

0

0

0

0

0

1

1

0

結果

0*27

0*26

0*25

0*24

0*23

0*22

1*21

1*20

 

算數右平移是直接將原數的二進位內容往右移動,「6>>1」是指將6的二進位值往右移動1bit,超出的部分,如上表右邊的灰色的bit數需要捨去。右平移時,如果是正數,平移後,左邊留下的位置的值,直接補上「0」,再將每個bit的值相加就是平移後的結果了。

1 * 21 + 1 * 20 = 3

負數右平移時,左邊留下的位置的值,需要補上「1」,例如:「-10>>2」的執行結果為:「-3」。請參考下表:

-10的二進位

1

1

1

1

0

1

1

0

 

右平移2bit

 

 

1

1

1

1

0

1

1

1的結果

1

1

1

1

1

1

0

1

 

>>>」稱為「邏輯右平移」,使用範例如同:

6 >> >1

該行敘述計算的結果為「3」,請參考下表:

6的二進位

0

0

0

0

0

1

1

0

 

右平移1bit

 

0

0

0

0

0

1

1

0

結果

0*27

0*26

0*25

0*24

0*23

0*22

1*21

1*20

 

邏輯右平移是直接將原數的二進位內容往右移動,「6>>>1」是指將6的二進位值往右移動1bit,超出的部分,如上表右邊的灰色的bit數需要捨去。邏輯右平移時,不需要考慮正負數,所以平移後,左邊留下的位置的值,直接補上「0」即可。最後,再將每個bit的值相加就是平移後的結果了。

1 * 21 + 1 * 20 = 3

了解了算數運算子的使用方式之後,那麼,請您自行判斷,那兩個運算式的結果會相同:

01    16%4

02    16/2

03    16*4

04    16>>2=4

05    16>>>2

指定運算子

符號

說明

使用範例

=

將右邊的運算結果,指定給左邊的變數

x = 10;

+=

將左邊變數的值加上右邊的值,再指定給左邊的變數

x += 1; 等同x = x + 1

-=

將左邊變數的值減去右邊的值,再指定給左邊的變數

x -= 1; 等同x = x - 1

*=

將左邊變數的值乘以右邊的值,再指定給左邊的變數

x *= 2; 等同x = x * 2

/=

將左邊變數的值除以右邊的值,再指定給左邊的變數

x /= 2; 等同x = x * 2

%=

將左邊變數的值除以右邊的值,再指定餘數給左邊的變數

x %= 2; 等同x = x % 2

&=

將左邊變數的值和右邊的值相連,再指定給左邊的變數

x &= y; 等同x = x & y

^=

位元互斥指定運算

x ^= y; 等同x = x ^ y

<<=

算數位元左平移

x <<= 2; 等同x = x << 2

>>=

算數位元右平移

x >>= 2; 等同x = x >> 2

指定運算子大概是最常用的運算子了,最簡單的使用方式不外乎是:

x = 2;

x = x + 2;

執行時,JVM會將「=」右邊的運算式執行完成後,再將結果指定給左邊的變數。所以,「=」運算子的左邊不可能出現數值常數,如同以下的敘述會產生編譯時的錯誤:

x + 3 = 2;

除了基本的「=」運算之外,Java為了讓程式更精簡,就將一些常用的運算式的寫法加以簡化,例如:運算式「x = x + 2;」就可以簡化成:

x += 2;

請注意,這事通則,如果使用「*=」、「/=」、「%=時,這種情形並不保證永遠如此了,

例如:假定「x」的值為2,以下的運算式一:

x *= 2 + 5;

執行後,「x」的值為「14」。嗯,這沒問題,但如果您改寫成運算式二:

x = x * 2 + 5;

執行後,「x」的值卻變成了「9」?當程式使用「*=」時,執行時會將右邊的運算式執行完成,再將執行結果和左邊變數的值相乘,最後,再將結果重新指定給左邊的變數。但在「運算式二」中卻是先執行「x * 2」運算,再將結果加「5」,最後才將結果指定給左邊的「x」變數,答案自然會不同。您在使用這類型的運算子時,請多加留意。
如果有多個指定運算子

一行Java的程式敘述中可以出現一個以上的指定運算子,但是,每個指定運算子的左邊只能有一個變數,不可以同時包含其他的變數或是數值常數。所以,下列的敘述是不合法的:

x *= 1 + y -= 3;

但,以下的範例程式則可以正常的執行:

程式2-15Chap2\Operator2.java

01 class Operator2

02 {

03    public static void main(String[] args)

04    {

05        int x = 2, y=1;

06        x *= y -= 3;

07        System.out.println( “x的值為” + x + “y的值為” + y);

08    }

09 }

程式的執行結果為:

x的值為-4 y的值為-2

編譯器對指定運算子的處理順序是由右到左,所以,範例程式中的第6行的執行流程是:

y -= 3;                 //先執行這段敘述,原先y值為「1」,執行後,y值為「-2

x *= -2;                //再執行這段敘述,原先x值為「2」,執行後,x值為「-4

關係運算子

符號

說明

使用範例

==

等於

x == 10;

!=

不等於

x != 10;

小於

x < 10;

<=

小於或等於

x <= 10;

大於

x > 10;

>=

大於或等於

x >= 10;

>>=

算數位元右平移

x >>= 10;  等同x = x >> 2

關係運算子用來比較運算子左右邊的值是否相等。如果相等,則比較的結果代表「成立」,並傳回「true」值;如果不相等,則比較的結果代表「不成立」,並傳回「false」值。例如:

10 >= 10;                      //運算的結果傳回「true

邏輯運算子

符號

說明

使用範例

&

且(AND)運算

x & y;

|

或(OR)運算

x | y;

!

否(NOT)運算

!x;

^

XOR運算

x ^ y;

&&

短路(Short-circuit)且運算

x && y;

||

短路(Short-circuit)或運算

x || y;

?

if-then-else三元運算

a>10 ? x : y;

&」、「|」、「!」、「^」邏輯運算子的執行結果會傳回「true」或是「false」值,執行的結果可以用下表來表示:

A敘述

B敘述

A & B

A | B

!A

A ^ B

true

true

true

true

false

false

false

true

false

true

true

true

true

false

false

true

false

true

false

false

false

false

true

false

上表中雙線框線的儲存格表示執行的條件。如果A敘述的執行結果為「true」,而且B敘述的執行結果也是「true」,則敘述「A & B」的執行結果會是「true」。參考以下的範例程式:

程式2-16Chap2\Operator3.java

01 class Operator3

02 {

03    public static void main(String[] args)

04    {

05        int x = 2, y = 1, z = 3;

06       

07        //x > 2falsey > 0true,「&」運算後變成false

08        System.out.println( “( x > 2 ) & ( y > 0 )的執行結果為” + (( x > 2 ) & ( y > 0 )));

09       

10        //x > ytruey > zfalse,「|」運算後變成true

11        System.out.println( “( x > y ) | ( y > z )的執行結果為” + (( x > y ) | ( y > z )));

12       

13        //x > ytrue,「!」運算後變成false

14        System.out.println( “!( x > y )的執行結果為” + ( !( x > y )));

15       

16        //x > ytruey > zfalse,「^」運算後變成true

17        System.out.println( “( x > y ) ^ ( y > z )的執行結果為” + (( x > y ) ^ ( y > z )));

18    }

19 }

程式的執行結果為:

( x > 2 ) & ( y > 0 )的執行結果為false

( x > y ) | ( y > z )的執行結果為true

!( x > y )的執行結果為false

( x > y ) ^ ( y > z )的執行結果為true

短路運算子

Java提供了兩個很有趣的布林邏輯運算子,「&&」和「||」。您可以把它當作是「&」和「|」的加強版。先判斷以下的程式:

int a = 10, b = 20;

boolean result;

result = ( a < 5 ) & ( b < 10 );       //result的值為false

您應該可以正確的判斷出執行後「result」的值為「false」。但問題是,目前使用的是「&」運算子,如果「a < 5」的結果已經是「false」,我們不用再判斷「b < 10」的執行結果就可以得知「result」的值會是「false」吧。但實際上「b < 10」這段敘述仍然被執行。

使用「&&」運算子可以解決這項問題,如果上述的程式改為:

result = ( a < 5 ) && ( b < 10 );       //result的值為false

則「b < 10」敘述不會被執行。「&&」和「||」被稱為「短路(Short-circuit」運算子,以「&&」為例,如果運算子前的敘述的執行結果為「false」,則不需要再執行「&&」後的敘述即可將「false」的結果回傳。「||」運算子的執行概念也是相同,如果運算子前的敘述的執行結果為「true」,則不需再執行「||」後的敘述即可將「true」的結果回傳。參考以下的範例:

程式2-17Chap2\Operator4.java

01 class Operator4

02 {

03    public static void main(String[] args)

04    {

05        int x = 2, y = 1;

06        int a = 2, b = 1;

07

08        //判斷「&」和「&&」的不同

09        System.out.println( “(x>3) & (y++>0)的執行結果為” + ((x>3) & (y++>0)));

10        System.out.println( “執行後,x = ” + x + “ y = ” + y );

11        System.out.println( “(a>3) && (b++>0)的執行結果為” + ((a>3) && (b++>0)));

12        System.out.println( “執行後,a = ” + a + “ b = ” + b);

13       

14        //判斷「|」和「||」的不同

15        System.out.println( “---------------------------------” );

16        int x1 = 2, y1 = 1;

17        int a1 = 2, b1 = 1;

18        System.out.println( “(x1<3) | (y1++>0)的執行結果為” + ((x1<3) | (y1++>0)));

19        System.out.println( “執行後,x1 = ” + x1 + “ y1 = ” + y1 );

20        System.out.println( “(a1<3) || (b1++>0)的執行結果為” + ((a1<3) || (b1++>0)));

21        System.out.println( “執行後,a1 = ” + a1 + “ b1 = ” + b1);

22    }

23 }

執行結果為:

(x>3) & (y++>0)的執行結果為false

執行後,x = 2 y = 2

(a>3) && (b++>0)的執行結果為false

執行後,a = 2 b = 1

---------------------------------

(x1<3) | (y1++>0)的執行結果為true

執行後,x1 = 2 y1 = 2

(a1<3) || (b1++>0)的執行結果為true

執行後,a1 = 2 b1 = 1

範例程式中的第56行中安排了兩組相同值的變數,第9行程式採用了第一組的變數,由判斷式中可以明顯的得知「x > 3」的結果會是「false」,但由於使用了「&」運算子,所以「y++ > 0」的敘述仍然被執行,所以第9行執行後,y的值增加1而變成了2

程式中的第11行採用了第二組的變數,同樣的,由判斷式中也可以得知「a > 3」的結果也會是「false」,但採用「&&」運算子後,結果直接被傳回,所以「b++ > 0」的敘述並不會被執行。因此第11行執行後,b的值仍然是1

邏輯運算子也會有執行的先後優先權,參考下表:

順序

符號

說明

1

!

否運算

2

~

位元否運算

3

&

且運算

4

^

互次或運算

5

|

或運算

6

&&

短路且運算

7

||

短路或運算

「?」三元運算子

Java程式包含了一個特殊的三元運算子「?」,某種程度上,它可以取代「if-then-else」敘述,是一種較有效率的寫作方式。「?」一般的格式為:

expressionresult1result2;

敘述代表:「如果expression的敘述成立,則回傳「result1」的執行結果:如果expression的敘述不成立,則回傳「result2」的執行結果」。觀察以下範例程式的執行結果:

程式2-18Chap2\Operator5.java

01 class Operator5

02 {

03    public static void main(String[] args)

04    {

05        int x = 2, y = -2;

06        int result;

07

08        result = x > 0 ? x : -x;

09        System.out.println( “x的值為:” + x + “,它的絕對值為:” + result);

10

11        result = y > 0 ? y : -y;

12        System.out.println(“y的值為:” + y + “,它的絕對值為:” + result);

13    }

14 }

程式的執行結果為:

x的值為:2,它的絕對值為:2

y的值為:-2,它的絕對值為:2

程式的第8行中,判斷x的值大於0,敘述成立,所以回傳「x」給「result」變數;但第11行程式中,y初始值為「-2」,判斷結果y並不會大於0,敘述不成立,所以回傳「-y」給「result」變數。

位元邏輯運算子

當「&」、「|」、「^」、「~」單獨使用於數值時,它們可以是位元邏輯運算子。位元邏輯運算子會對運算元的每一個位元單獨作運算。參考下表:

A

B

0A | B

A & B

A ^ B

~A

0

0

0

0

0

1

1

0

1

0

1

0

0

1

1

0

1

1

1

1

1

1

0

0

以較簡單的NOT~)運算子為例,該運算子會將數值的位元反向,例如:數字「30」的二進位可以表示成:

00011110

在套用NOT運算後,得到的值為:

11100001

AND&)運算子只有在輸入的兩個位元都是「1」時,才會產生「1」的輸出位元,其餘的情形都會輸出「0」。例如:運算式「30 & 15」的執行結果為「14」。

       00011110        30

     00001111        15

------------------------------------------------

       00001110        14

OR|)運算子只有在輸入的兩個位元都是「0」時,才會產生「0」的輸出位元,其餘的情形都會輸出「1」。例如:運算式「30 | 15」的執行結果為「31」。

       00011110        30

     00001111        15

------------------------------------------------

       00011111        31

XOR^)運算子只有在輸入的兩個位元只有一個是「1」時,才會產生「1」的輸出位元,其餘的情形都會輸出「0」。例如:運算式「30 ^ 15」的執行結果為「17」。

       00011110        30

︿     00001111        15

------------------------------------------------

       00010001        17

再觀察以下的範例程式,程式中示範各種位元運算的執行結果:

程式2-19Chap2\Operator6.java

01 class Operator6

02 {

03    public static void main(String[] args)

04    {

05        int x = 30, y = 15;

06        int result;

07

08        System.out.println( “x的值為:” + x + “y值為:” + y);

09        result = ~x;

10        System.out.println( “NOT x值為:” + result);

11        result = x & y;

12        System.out.println( “x AND y值為:” + result);

13        result = x | y;

14        System.out.println( “x OR y值為:” + result);

15        result = x ^ y;

16        System.out.println( “x XOR y值為:” + result);

17    }

18 }

程式的執行結果為:

x的值為:30y值為:15

NOT x值為:-31

x AND y值為:14

x OR y值為:31

x XOR y值為:17