收入囊中篇---Java程式基礎(二)
前言:
本篇是接著上一篇更新的,如果沒有閱讀上一篇的話,可以查閱或回顧一下。
1、收入囊中篇——-Java基礎必備知識(一)
2、收入囊中篇——-Java程式基礎(二)
Java程式基礎目錄
1、Java的基本結構
2、變數和資料結構
變數
基本資料型別
整型
浮點型
布林型別
字元型別
常量
var關鍵字
變數的作用範圍
小結
3、整數運算(本篇內容)
溢位
簡潔的運算子
自增/自減
移位運算
位運算
運算優先順序
型別自動提升與強制轉型
小結
4、浮點數運算(本篇內容)
型別提升
溢位
強制轉型
練習:
小結
5、布林運算
```
短路運算
三元運算子
練習:
小結 ```
6、字元和字串
```
字元型別
字串型別
字串連線
多行字串
不可變特性
空值null
練習:
小結 ```
7、陣列型別
```
字串陣列
小結 ```
3、整數運算
Java的整數運算遵循四則運算規則,可以使用任意巢狀的小括號。四則運算規則和初等數學一致。例如:
//四則運算
public
class
Main
{
public
static
void
main
(
String
[]
args
)
{
int
i
=
(
100
+
200
)
*
(
99
-
88
);
// 3300
int
n
=
7
*
(
5
+
(
i
-
9
));
// 23072
System
。
out
。
println
(
i
);
System
。
out
。
println
(
n
);
}
}
//從左往右先乘除,後加減依次進行
整數的數值表示不但是精確的,而且整數運算永遠是精確的,即使是除法也是精確的,因為兩個整數相除只能得到結果的整數部分:
int
x
=
12345
/
67
;
// 184 ,整型只能夠得到整數
求餘運算使用
%
:
int
y
=
12345
%
67
;
// 12345÷67的餘數是17
特別注意:整數的除法對於除數為0時執行時將報錯,但編譯不會報錯。
溢位
要特別注意,整數由於存在範圍限制,如果計算結果超出了範圍,就會產生溢位,而溢位不會出錯,卻會得到一個奇怪的結果:
//運算溢位
public
class
Main
{
public
static
void
main
(
String
[]
args
)
{
int
x
=
2147483640
;
int
y
=
15
;
int
sum
=
x
+
y
;
System
。
out
。
println
(
sum
);
// -2147483641 ,正數相加竟然成為了負數
}
}
要解釋上述結果,我們把整數
2147483640
和
15
換成二進位制做加法:
由於最高位計算結果為
1
,因此,加法結果變成了一個負數。
要解決上面的問題,可以把
int
換成
long
型別,由於
long
可表示的整型範圍更大,所以結果就不會溢位:
//運算正常
public
class
Main
{
public
static
void
main
(
String
[]
args
)
{
long
x
=
2147483640
;
long
y
=
15
;
long
sum
=
x
+
y
;
System
。
out
。
println
(
sum
);
// 2147483655
}
}
簡潔的運算子
有一種簡寫的運算子,即
+=
,
-=
,
*=
,
/=
,它們的使用方法如下:
n
+=
100
;
// 3409, 相當於 n = n + 100;
n
-=
100
;
// 3309, 相當於 n = n - 100;
自增/自減
Java還提供了
++
運算和
——
運算,它們可以對一個整數進行加1和減1的操作:
// 自增/自減運算
public
class
Main
{
public
static
void
main
(
String
[]
args
)
{
int
n
=
3300
;
n
++
;
// 3301, 相當於 n = n + 1;
n
——
;
// 3300, 相當於 n = n - 1;
int
y
=
100
+
(
++
n
);
// 不要這麼寫
System
。
out
。
println
(
y
);
}
}
注意
++
寫在前面和後面計算結果是不同的,
++n
表示先加1再引用n,
n++
表示先引用n再加1。不建議把
++
運算混入到常規運算中,容易自己把自己搞懵了。
移位運算
在計算機中,整數總是以二進位制的形式表示。例如,
int
型別的整數
7
使用4位元組表示的二進位制如下:
00000000 0000000 0000000 00000111
可以對整數進行移位運算。對整數
7
左移1位將得到整數
14
,左移兩位將得到整數
28
:
int
n
=
7
;
// 00000000 00000000 00000000 00000111 = 7
int
a
=
n
<<
1
;
// 00000000 00000000 00000000 00001110 = 14
int
b
=
n
<<
2
;
// 00000000 00000000 00000000 00011100 = 28
int
c
=
n
<<
28
;
// 01110000 00000000 00000000 00000000 = 1879048192
int
d
=
n
<<
29
;
// 11100000 00000000 00000000 00000000 = -536870912
左移29位時,由於最高位變成
1
,因此結果變成了負數。
類似的,對整數28進行右移,結果如下:
int
n
=
7
;
// 00000000 00000000 00000000 00000111 = 7
int
a
=
n
>>
1
;
// 00000000 00000000 00000000 00000011 = 3
int
b
=
n
>>
2
;
// 00000000 00000000 00000000 00000001 = 1
int
c
=
n
>>
3
;
// 00000000 00000000 00000000 00000000 = 0
如果對一個負數進行右移,最高位的
1
不動,結果仍然是一個負數:
int
n
=
-
536870912
;
//11100000 00000000 00000000 00000000
int
a
=
n
>>
1
;
// 11110000 00000000 00000000 00000000 = -268435456
int
b
=
n
>>
2
;
// 11111000 00000000 00000000 00000000 = -134217728
int
c
=
n
>>
28
;
// 11111111 11111111 11111111 11111110 = -2
int
d
=
n
>>
29
;
// 11111111 11111111 11111111 11111111 = -1
對比了兩種的移位運算之後,我客觀總結一下>>(右移)使用1來填補,<<(左移)使用0來填補。
如果從數學原理來理解的話就是,左移是滿2進1,原位補0。右移是減1退位,原位補1。
還有一種不帶符號的右移運算,使用
>>>
,它的特點是符號位跟著動,因此,對一個負數進行
>>>
右移,它會變成正數,原因是最高位的
1
變成了
0
:
int
n
=
-
536870912
;
// 11100000 00000000 00000000 00000000
int
a
=
n
>>>
1
;
// 01110000 00000000 00000000 00000000 = 1879048192
int
b
=
n
>>>
2
;
// 00111000 00000000 00000000 00000000 = 939524096
int
c
=
n
>>>
29
;
// 00000000 00000000 00000000 00000111 = 7
int
d
=
n
>>>
31
;
// 00000000 00000000 00000000 00000001 = 1
對
byte
和
short
型別進行移位時,會首先轉換為
int
再進行位移。
仔細觀察可發現,左移實際上就是不斷地×2,右移實際上就是不斷地÷2。
位運算
位運算是按位進行與、或、非和異或的運算。
與運算的規則是,必須兩個數同時為
1
,結果才為
1
:
n
=
0
&
0
;
// 0
n
=
0
&
1
;
// 0
n
=
1
&
0
;
// 0
n
=
1
&
1
;
// 1
或運算的規則是,只要任意一個為
1
,結果就為
1
:
n
=
0
|
0
;
// 0
n
=
0
|
1
;
// 1
n
=
1
|
0
;
// 1
n
=
1
|
1
;
// 1
非運算的規則是,
0
和
1
互換:
n
=
~
0
;
// 1
n
=
~
1
;
// 0
異或運算的規則是,如果兩個數不同,結果為
1
,否則為
0
:
n
=
0
^
0
;
// 0
n
=
0
^
1
;
// 1
n
=
1
^
0
;
// 1
n
=
1
^
1
;
// 0
對兩個整數進行位運算,實際上就是按位對齊,然後依次對每一位進行運算。例如:
public
class
Main
{
public
static
void
main
(
String
[]
args
)
{
int
i
=
167776589
;
// 00001010 00000000 00010001 01001101
int
n
=
167776512
;
// 00001010 00000000 00010001 00000000
// i&n = 00001010 00000000 00010001 00000000 = 167776512
System
。
out
。
println
(
i
&
n
);
// 167776512
}
}
上述按位與運算實際上可以看作兩個整數表示的IP地址
10。0。17。77
和
10。0。17。0
,透過與運算,可以快速判斷一個IP是否在給定的網段內。
運算優先順序
在Java的計算表示式中,運算優先順序從高到低依次是:
()
!
~
++
——
*
/
%
+
-
<<
>>
>>>
&
|
+=
-=
*=
/=
記不住也沒關係,只需要加括號就可以保證運算的優先順序正確。
型別自動提升與強制轉型
在運算過程中,如果參與運算的兩個數型別不一致,那麼計算結果為較大型別的整型。例如,
short
和
int
計算,結果總是
int
,原因是
short
首先自動被轉型為
int
:
// 型別自動提升與強制轉型
public
class
Main
{
public
static
void
main
(
String
[]
args
)
{
short
s
=
1234
;
int
i
=
123456
;
int
x
=
s
+
i
;
// s自動轉型為int
short
y
=
s
+
i
;
// 編譯錯誤! 因為int型一般情況下是不可以轉化成為short型別的
}
}
但是也還是可以將結果強制轉型,即將大範圍的整數轉型為小範圍的整數。強制轉型使用
(型別)
,例如,將
int
強制轉型為
short
:
int
i
=
12345
;
//12345為int型
short
s
=
(
short
)
i
;
// 12345為short型
要注意,超出範圍的強制轉型會得到錯誤的結果,原因是轉型時,
int
的兩個高位位元組直接被扔掉,僅保留了低位的兩個位元組。
例如:7(int)=0111(int) <————> 7(short)= 11(short)
//強制轉換型別
public
class
Main
{
public
static
void
main
(
String
[]
args
)
{
int
i1
=
1234567
;
short
s1
=
(
short
)
i1
;
// -10617
System
。
out
。
println
(
s1
);
int
i2
=
12345678
;
short
s2
=
(
short
)
i2
;
// 24910
System
。
out
。
println
(
s2
);
}
}
因此,強制轉型的結果很可能是錯的。
小結
整數運算的結果永遠是精確的;
運算結果會自動提升;
可以強制轉型,但超出範圍的強制轉型會得到錯誤的結果;
應該選擇合適範圍的整型(
int
或
long
),沒有必要為了節省記憶體而使用
byte
和
short
進行整數運算。
4、浮點數運算
浮點數運算和整數運算相比,只能進行加減乘除這些數值計算,不能做位運算和移位運算。
在計算機中,浮點數雖然表示的範圍大,但是,浮點數有個非常重要的特點,就是浮點數常常無法精確表示。
舉個栗子:
浮點數
0。1
在計算機中就無法精確表示,因為十進位制的
0。1
換算成二進位制是一個無限迴圈小數,很顯然,無論使用
float
還是
double
,都只能儲存一個
0。1
的近似值。但是,
0。5
這個浮點數又可以精確地表示。
因為浮點數常常無法精確表示,因此,浮點數運算會產生誤差:
//浮點數運算誤差
public
class
Main
{
public
static
void
main
(
String
[]
args
)
{
double
x
=
1。0
/
10
;
double
y
=
1
-
9。0
/
10
;
// 觀察x和y是否相等:
System
。
out
。
println
(
x
);
System
。
out
。
println
(
y
);
}
}
由於浮點數存在運算誤差,所以比較兩個浮點數是否相等常常會出現錯誤的結果。正確的比較方法是判斷兩個浮點數之差的絕對值是否小於一個很小的數:
// 比較x和y是否相等,先計算其差的絕對值:
double
r
=
Math
。
abs
(
x
-
y
);
//Math。abs()是求絕對值的
// 再判斷絕對值是否足夠小:
if
(
r
<
0。00001
)
{
// 可以認為相等
}
else
{
// 不相等
}
浮點數在記憶體的表示方法和整數相比更加複雜。Java的浮點數完全遵循IEEE-754標準,這也是絕大多數計算機平臺都支援的浮點數標準表示方法。
型別提升
如果參與運算的兩個數其中一個是整型,那麼整型可以自動提升到浮點型:
public
class
Main
{
public
static
void
main
(
String
[]
args
)
{
int
n
=
5
;
double
d
=
1。2
+
24。0
/
n
;
// 6。0
System
。
out
。
println
(
d
);
}
}
需要特別注意,在一個複雜的四則運算中,兩個整數的運算不會出現自動提升的情況。
例如:
double
d
=
1。2
+
24
/
5
;
// d = 5。2 【 24 / 5= 4。8(int)=4 】
計算結果為
5。2
,原因是編譯器計算
24 / 5
這個子表示式時,按兩個整數進行運算,結果仍為整數
4
。
溢位
整數運算在除數為
0
時會報錯,而浮點數運算在除數為
0
時,不會報錯,但會返回幾個特殊值:
NaN
表示Not a Number
Infinity
表示無窮大
-Infinity
表示負無窮大
例如:
double
d1
=
0。0
/
0
;
// NaN
double
d2
=
1。0
/
0
;
// Infinity
double
d3
=
-
1。0
/
0
;
// -Infinity
這三種特殊值在實際運算中很少碰到,我們只需要瞭解即可。
強制轉型
可以將浮點數強制轉型為整數。在轉型時,浮點數的小數部分會被丟掉。如果轉型後超過了整型能表示的最大範圍,將返回整型的最大值。例如:
int
n1
=
(
int
)
12。3
;
// 12
int
n2
=
(
int
)
12。7
;
// 12
int
n2
=
(
int
)
-
12。7
;
// -12
int
n3
=
(
int
)
(
12。7
+
0。5
);
// 13
int
n4
=
(
int
)
1。2e20
;
// 2147483647 ,因為超過了整型能表示的最大範圍,所以放回整型的最大值
注意:1。2e20 = 1。2 x 1020
如果轉型後超過了整型能表示的最大範圍,將返回整型的最大值。
如果要進行四捨五入,可以對浮點數加上0。5再強制轉型:
// 四捨五入
public
class
Main
{
public
static
void
main
(
String
[]
args
)
{
double
d
=
2。6
;
int
n
=
(
int
)
(
d
+
0。5
);
//加上5,然後能進位的進位,不能進位的被丟棄
System
。
out
。
println
(
n
);
}
}
PS:想要關注更多最新文章可以關注我的微信【苦逼的學生仔】
上一篇:開了瓶起泡酒沒喝完怎麼辦?