《Java從入門到失業》第三章:基礎語法及基本程序結構(3.7):運算符(自增自減、關係運算、邏輯運算、條件運算、位運算、賦值運算、類型轉換)
- 2020 年 8 月 31 日
- 筆記
- 《Java從入門到失業》, Java從入門到失業
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。
對於<<運算,要注意幾點:
- 對byte、short、char型進行左移運算,移位之前,它們會自動轉換為int
- 右側的參數,需要進行模32運算,其實就是保證右側的參數小於32(當左側是long,則模64,保證右側的參數小於64),因為左移超過32沒有意義。
- 因為右側參數不可能超過32(64),所以其實符號位是不變的。
- 左移n位,其實相當於乘以2n(由十進制轉二進制公式可以得出)
例如:
20的二進制補碼:0001 0100,左移兩位後:0101 0000,結果是80
-20的二進制補碼:1110 1100,左移兩位後:1011 0000,結果是-80
對於>>運算,需要注意幾點:
- 右移是帶符號移動的,即如果是正數,高位補0,如果負數,高位補1
- 右移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
對於>>>運算,需要注意幾點:
- 右移是不帶符號的,即不管正負,高位都補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