《Java從入門到失業》第三章:基礎語法及基本程序結構(3.7):運算符(自增自減、關係運算、邏輯運算、條件運算、位運算、賦值運算、類型轉換)

3.7運算符

3.7.2自增自減運算符

       在程序中,變量的加1、減1操作是經常會碰到的。Java和其他語言一樣,給我們提供了自增、自減運算符來方便的完成這些操作。「++」表示自增,「–」表示自減。我們看一個例子:

int a = 3;  
a++;  
System.out.println(a);// 結果是4  
int b = 8;  
b--;  
System.out.println(b);// 結果是7  

自增自減運算符會改變變量的值,因此它們的操作數不能是數值。例如5++就是一條非法的語句。另外這2個運算符還有另外一種形式,就是放在操作數的前面,我們看一個例子:

int a = 3;  
++a;  
System.out.println(a);// 結果是4  
int b = 8;  
--b;  
System.out.println(b);// 結果是7 

另外,我們還可以把變量和自增自減運算符當做一個整體,參與到賦值語句或者是運算表達式中。例如:

int a1 = 3;  
int b = a1++;  
int a2 = 3;  
int c = ++a2;  
int a3 = 3;  
int d = a3++ * 5;  
int a4 = 3;  
int e = ++a4 * 5;  
System.out.println(b);// 結果是3  
System.out.println(c);// 結果是4  
System.out.println(d);// 結果是15  
System.out.println(e);// 結果是20  

我們可以看到,無論是賦值語句,還是運算表達式,當++在操作數後面的時候,都是先賦值或先參與運算,然後再自己增加1。當++在操作數前面的時候,都是先自己增加1,再賦值或參與運算。對於自減也是一樣。可以總結一個規律:運算符在前面時先起作用,運算符在後面時後起作用。

  這裡有一個小笑話,我們都知道C++語言,這門語言把++運算符用到語言命名上了,本意是C語言的擴展。但是根據運算法則,運算符在後面後起作用,因此反對C++的程序員說我們使用的它的時候還沒起作用呢,應該命名為++C才對。

3.7.3關係運算符

  前面的運算符對應數學中的加減乘除取余等運算,關係運算符對應的是比較2個數的關係,關係有等於、不等於、大於、大於等於、小於和小於等於。列表如下:

運算符

表達式

結果(假設a=15,b=10)

==

a==b

false

!=

a!=b

true

a>b

true

>=

a>=b

true

a<b

false

<=

a<=b

false

使用和結果都很簡單,沒什麼可講的。

3.7.4邏輯運算符

       邏輯運算包括3個:邏輯與、邏輯或、邏輯非,對應的運算符和說明如下:

運算符

表達式

說明

&&

expression1 && expression2

邏輯與。當且僅當兩個操作數都為真,條件才為真

||

expression1 || expression2

邏輯或。如果任何一個為真,條件為真。

!

!expression1

邏輯非。用來反轉操作數的邏輯狀態。

例如:

boolean a = true;  
boolean b = false;  
boolean c = a && b;// 結果是false  
boolean d = a || b;// 結果是true  
boolean e = !a;// 結果是false

需要注意的是,邏輯與和邏輯或都是採用「短路」的方式進行運算的。就是某一個表示的結果已經能夠確定整個運算表達式的結果的時候,剩下的表達式就不用再進行計算了。

例如:

int a = 5;  
boolean b = (a < 4) && (a++ < 10);// a<4結果是false,整個表達式結果就是false,因此a++不會運算,a的值依然是5  
  
int c = 5;  
boolean d = (c < 6) || (c++ < 10);// c<6結果是true,整個表達式結果就是true,因此a++不會運算,c的值依然是5

3.7.5條件運算符

  條件運算符也被稱為三元運算符。該運算符有3個操作數,並且需要判斷布爾表達式的值。該運算符的主要是決定哪個值應該賦值給變量,表達式為:

condition ? expression1 : expression2

當條件condition為真時,計算expression1並返回,否則計算expression2並返回。

例如:

int a = 5;  
int b = 10;  
int c = 20;  
int d = a < b ? c + a : c + b;// a<b結果是true,因此d=c+a=25

3.7.6位運算符

       在Java中,處理整型數值時,可以直接對數值的二進制的各個位進行操作,我們先列一個表,然後再進行例子演示:

操作符

說明

&

按位與操作符。如果相對應位都是1,則結果為1,否則為0

|

按位或操作符。如果相對應位都是0,則結果為0,否則為1

^

按位異或操作符。如果相對應位值相同,則結果為0,否則為1

~

按位取反操作符。翻轉操作數的每一位,即0變成1,1變成0

<< 

按位左移運算符。左操作數按位左移右操作數指定的位數

>> 

按位右移運算符。左操作數按位右移右操作數指定的位數

>>> 

按位右移補零操作符。左操作數的值按右操作數指定的位數右移,移動得到的空位以零填充。

我們看一個例子:

int a = 55; // 二進制為 0011 0111  
int b = 18; // 二進制為 0001 0010  
int c = a & b;// 結果二進制為 0001 0010,18  
int d = a | b;// 結果二進制為 0011 0111,55  
int e = a ^ b;// 結果二進制為0010 0101,37  
int f = ~a;// 結果二進制為 1100 1000,-56  
int g = a << 2;// 結果二進制為 1101 1100,220  
int h = a >> 2;// 結果二進制為 0000 1101,13  
int i = a >>> 2;// 結果二進制為 0000 1101,13 

對於按位與&運算,有個小技巧,就是可以快速判斷一個整數m的二進制從右往左數第n位是否為1,判斷方法為看m&2n-1的值,值為0,則第n位為0,值為2n-1,則第n為為1。

對於<<運算,要注意幾點:

  1. 對byte、short、char型進行左移運算,移位之前,它們會自動轉換為int
  2. 右側的參數,需要進行模32運算,其實就是保證右側的參數小於32(當左側是long,則模64,保證右側的參數小於64),因為左移超過32沒有意義。
  3. 因為右側參數不可能超過32(64),所以其實符號位是不變的。
  4. 左移n位,其實相當於乘以2n(由十進制轉二進制公式可以得出)

例如:

20的二進制補碼:0001 0100,左移兩位後:0101 0000,結果是80  

-20的二進制補碼:1110 1100,左移兩位後:1011 0000,結果是-80

對於>>運算,需要注意幾點:

  1. 右移是帶符號移動的,即如果是正數,高位補0,如果負數,高位補1
  2. 右移n位,相當於除以2n取整

例如:

20的二進制補碼:0000 0000 0000 0000 0000 0000 0001 0100

右移兩位後:0000 0000 0000 0000 0000 0000 0000 0101,結果是5

-20的二進制補碼:1111 1111 1111 1111 1111 1111 1110 1100

右移兩位後:1111 1111 1111 1111 1111 1111 1111 1011,結果是-5

對於>>>運算,需要注意幾點:

  1. 右移是不帶符號的,即不管正負,高位都補0

例如:

20的二進制補碼:0000 0000 0000 0000 0000 0000 0001 0100

右移兩位後:0000 0000 0000 0000 0000 0000 0000 0101,結果是5

-20的二進制補碼:1111 1111 1111 1111 1111 1111 1110 1100

右移兩位後:0011 1111 1111 1111 1111 1111 1111 1011,結果是1073741819

 3.7.7賦值運算符

       Java還支持把一些二元運算符和賦值符號聯合起來使用,我們把它們稱為賦值運算符,歸結如下:

操作符

說明

舉例

+=

左操作數加右操作數,結果賦值給左操作數

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

^=

左操作數和右操作數按位異或,結果賦值給左操作數

C ^= 2即C = C ^ 2

|=

左操作數和右操作數按位或,結果賦值給左操作數

C |= 2即C = C | 2

3.7.8運算優先級

       Java可以在一個表達式中進行多個運算,這就涉及到運算符優先級問題了。下表按優先級從高到底給出運算符的排序(排在一行的優先級相同):

操作符

結合性

[] 、()、 .(點操作符)

從左向右

++、–、 +(一元運算)、-(一元運算)、!、~

從右向左

*、/、%

從左向右

+、-

從左向右

<<、>>、.>>>

從左向右

<、<=、>、>=

從左向右

==、!=

從左向右

&

從左向右

^

從左向右

|

從左向右

&&

從左向右

||

從左向右

?:

從右向左

=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=、>>>=

從右向左

是不是看着頭都大了?筆者也頭大,筆者強烈不推薦在一個表達式中使用多個運算符,可讀性太差了。

3.7.9數值類型轉換

       在程序的運行過程中,經常會碰到一種數值轉換為另一種數值類型。有時候是程序自動轉換的,有時候是我們用代碼顯性轉換的。下圖列出了數值類型轉換的過程:

 

需要注意的是,對於int轉float、long轉float,long轉double,是可能會丟失精度的。例如:

int n = 123456789;  
float f = n;// n包含的位數比float多,結果f為1.23456792E8

3.7.9.1自動類型轉換

       自動類型轉換經常發生在2個不同類型操作數進行二元操作時。例如:

int n = 123;  
float f = 456.3f;  
float ff = n + f;// 自動將n轉換為float,然後相加,結果是579.3  

對於這種自動轉換,遵循如下規則:

  • 如果兩個操作數中有一個double,則另一個會轉換為double。
  • 否則如果有一個操作數是float,另一個會轉換為float。
  • 否則如果有一個操作數是long,另一個會轉換為long。
  • 否則兩個操作數都被轉換為int。

3.7.9.2強制類型轉換

       上面我們知道了自動類型轉換,有時候我們想把double轉換為int,可以嗎?Java中是允許這種數值轉換的,方法就是用強制類型轉換,但是會丟失精度。強制類型轉換的格式為:

(type)value

type是最終想要的類型,value是被強制轉換的原數值,例如:

float f = 456.3f;  
int nf = (int) f;// 截斷小數部分,結果是456

需要注意的是,如果將一個數值從一種類型轉換為另一種類型,但是又超出目標類型的範圍,結果就會無法預料。例如把300轉換為byte類型:

byte b = (byte) 300;// 結果是44