8.?+=復合賦值問題
x+=i與x=x+i等效嗎,許多程序員都會認為第一個表達式x+=i只是第二個表達式x=x+i的簡寫方式,但這并不準確。
?
Java語言規范中提到:復合賦值 E1 op= E2等價于簡單賦值 E1 = (T)((E1) op (E2)),其中T是E1的類型。
復合賦值表達式自動地將所執行計算的結果轉型為其左側變量的類型。如果結果的類型與該變量的類型相同,那么這
個轉型不會造成任何影響,然而,如果結果的類型比該變量的類型要寬,那么復合賦值操作符將悄悄地執行一個窄化
原生類型轉換,這樣就會導致結果不正確:
- short ?x= 0 ;??
- int ?i?=? 123456 ;??
- x?+=i;??
- System.out.println(x); //-7616 ??
?
使用簡單的賦值方式就不會有這樣的問題了,因為寬類型不能自動轉換成窄的類型,編譯器會報錯,這時我們就會注
意到錯誤: x = x + i;//編譯通不過
?
請不要將復合賦值操作符作用于byte、short或char類型的變量;在將復合賦值操作符作用于int類型的變量時,要確
保表達式右側不是long、float或double類型;在將復合賦值操作符作用于float類型的變量時,要確保表達式右側不
是double類型。其實一句:不要將讓左側的類型窄于右側的數字類型。
?
總之,不要在short、byte或char類型的變量之上使用復合賦值操作符,因為這一過程會伴隨著計算前類型的提升與計
算后結果的截斷,導致最后的計算結果不正確。
9.?i =++i;與i=i++;的區別
- int ?i?=? 0 ;???
- i?=?i++;??
- System.out.println(i);??
上面的程序會輸出什么?大部分會說是 1,是也,非也。運行時正確結果為0。
?
i=++i;相當于以下二個語句(編譯時出現警告,與i=i;警告相同):
i=i+1;
i=i;
?
i = i++;相當于以下三個語句:
int tmp = i;
i = i + 1;
i = tmp;
?
下面看看下面程序片段:
- int ?i?=? 0 ,?j?=? 0 ,?y?=? 0 ;??
- i++; //相當于:i=i+1; ??
- System.out.println( "i=" ?+?i); //?i=1 ??
- ++i; //相當于:i=i+1; ??
- System.out.println( "i=" ?+?i); //?i=2 ??
- i?=?i++; //相當于:int?tmp=i;i=i+1;i=tmp; ??
- System.out.println( "i=" ?+?i); //?i=2 ??
- i?=?++i; //編譯時出現警告,與i=i;警告相同。相當于:i=i+1;i=i; ??
- System.out.println( "i=" ?+?i); //?i=3 ??
- j?=?i++; //相當于:int?tmp=i;i=i+1;j=tmp; ??
- System.out.println( "j=" ?+?j); //?j=3 ??
- System.out.println( "i=" ?+?i); //?i=4 ??
- y?=?++i; //相當于:i=i+1;y=i; ??
- System.out.println( "y=" ?+?y); //?y=5 ??
- System.out.println( "i=" ?+?i); //?i=5 ??
?
10.?Integer.MAX_VALUE + 1=?
- System.out.println(Integer.MAX_VALUE?+? 1 );??
上面的程序輸出多少?2147483647+1=2147483648?答案為-2147483648。
?
查看源碼Integer.MAX_VALUE 為MAX_VALUE = 0x7fffffff;所以加1后為0x80000000,又0x80000000為整型字面常量,滿了32位,且最位為1,所以字面上等于 -0,但又由于 -0就是等于0,所以-0這個編碼就規定為最小的負數,32位的
最小負數就是-2147483648。
11.?-1<<32=?、-1<<65=?
如果左操作數是int(如果是byte、short、char型時會提升至int型再進行位操作)型,移位操作符只使用其右操作數
的低5位作為移位長度(也就是將右操作數除以32取余);如果左操作數是long型,移位操作符只使用其右操作數的低
6位作為移位長度(也就是將右操作數除以64取余);
?
再看看下面程序片段就會知道結果:
- System.out.println(- 1 ?<<? 31 ); //?-2147483648?向左移31%32=31位 ??
- System.out.println(- 1 ?<<? 32 ); //?-1?向左移32%32=0位 ??
- System.out.println(- 1 ?<<? 33 ); //?-2?向左移33%32=1位 ??
- System.out.println(- 1 ?<<? 1 ); //?-2?向左移1%32=1位 ??
- ??
- System.out.println(-1L?<<? 63 ); //?-9223372036854775808?向左移63%64=63位 ??
- System.out.println(-1L?<<? 64 ); //?-1?向左移64%64=0位 ??
- System.out.println(-1L?<<? 65 ); //?-2?向左移65%64=1位 ??
- System.out.println(-1L?<<? 1 ); //?-2?向左移1%64=1位 ??
- ??
- byte ?b?=?- 1 ; //?byte型在位操作前類型提升至int ??
- System.out.println(b?<<? 31 ); //?-2147483648?向左移31%32=31位 ??
- System.out.println(b?<<? 63 ); //?-2147483648?向左移63%32=31位 ??
- ??
- short ?s?=?- 1 ; //?short型在位操作前類型提升至int ??
- System.out.println(s?<<? 31 ); //?-2147483648?向左移31%32=31位 ??
- System.out.println(s?<<? 63 ); //?-2147483648?向左移63%32=31位 ??
- ??
- char ?c?=? 1 ; //?char型在位操作前類型提升至int ??
- System.out.println(c?<<? 31 ); //?-2147483648?向左移31%32=31位 ??
- System.out.println(c?<<? 63 ); //?-2147483648?向左移63%32=31位 ??
?
12.?一個數永遠不會等于它自己加1嗎?i==i+1
一個數永遠不會等于它自己加1,對嗎?如果數字是整型,則對;如果這個數字是無窮大或都是浮點型足夠大(如
1.0e40),等式就可能成立了。
?
Java強制要求使用IEEE 754浮點數算術運算,它可以讓你用一個double或float來表示無窮大。
?
浮點型分為double型、float型。
?
無窮分為正無窮與負無窮。
?
無窮大加1還是無窮大。
?
一個浮點數值越大,它和其后繼數值之間的間隔就越大。
?
對一個足夠大的浮點數加1不會改變它的值,因為1不足以“填補它與其后者之間的空隙”。
?
浮點數操作返回的是最接近其精確數學結果的浮點數值。
?
一旦毗鄰的浮點數值之間的距離大于2,那么對其中的一個浮點數值加1將不會產生任何效果,因為其結果沒有達到兩
個數值之間的一半。對于float類型,加1不會產生任何效果的最小數是2^25,即33554432;而對于double類型,最小
數是2^54,大約是1.8*10^16。
?
33554432F轉二進制過程:
33554432的二進制為:10000000000000000000000000,將該二進制化成規范的小數二進制,即小數從右向左移25位
1.0000000000000000000000000,化成浮點數二進制0,25+127, 00000000000000000000000 00(丟棄最后兩位),即0, 10011000, 00000000000000000000000,最后的結果為1.00000000000000000000000*2^25
毗鄰的浮點數值之間的距離被稱為一個ulp,它是最小單位(unit in the last place)的首字母縮寫。在5.0版本中,引入了Math.ulp方法來計算float或double數值的ulp。
?
二進制浮點算術只是對實際算術的一種近似。
- //?注,整型數不能被?0?除,即(int)XX/0運行時拋異常 ??
- double ?i?=? 1.0 ?/? 0.0 ; //?正無窮大 ??
- double ?j?=?- 1.0 ?/? 0.0 ; //?負無窮大 ??
- //?Double.POSITIVE_INFINITY定義為:POSITIVE_INFINITY?=?1.0?/?0.0; ??
- System.out.println(i?+? "?" ?+?(i?==?Double.POSITIVE_INFINITY)); //Infinity?true ??
- //?Double.NEGATIVE_INFINITY定義為:NEGATIVE_INFINITY?=?-1.0?/?0.0; ??
- System.out.println(j?+? "?" ?+?(j?==?Double.NEGATIVE_INFINITY)); //-Infinity?true ??
- System.out.println(i?==?(i?+? 1 )); //?true ??
- System.out.println( 0 .1f?==? 0.1 ); //?false ??
- float ?f?=? 33554432 ;??
- System.out.println(f?+? "?" ?+?(f==(f+ 1 ))); //3.3554432E7?true ??
?
13.?自己不等于自己嗎?i!=i
NaN(Not a Number)不等于任何數,包括它自身在內。
?
double i = 0.0/0.0;可表示NaN。
?
float和double類型都有一個特殊的NaN值,Double.NaN、Float.NaN表示NaN。
?
如果一個表達式中產生了NaN,則結果為NaN。
- System.out.println( 0.0 ?/? 0.0 ); //?NaN ??
- System.out.println(Double.NaN?+? "?" ?+?(Double.NaN?==?( 0.0 ?/? 0.0 ))); //NaN?false ??
?
14.?自動拆箱
- //?為了兼容以前版本,1.5不會自動拆箱 ??
- System.out.println( new ?Integer( 0 )?==? new ?Integer( 0 )); //?false ??
- //?1.4編譯非法,1.5會自動拆箱 ??
- System.out.println( new ?Integer( 0 )?==? 0 ); //?true ??
?
15.?為什么-0x00000000==0x00000000、-0x80000000== 0x80000000
為了取一個整數類型的負值,要對其每一位取反(如果是對某個十六進制形式整數求負,如:-0x00000000則直接對這
個十六進制數進行各位取反操作——但不包括前面的負號;如果是對某個十進制求負,如-0,則需先求其絕對值的十
六進制的原碼后,再各位取反),然后再加1。
注:如果是對某個十進制數求負,如-1(0xffffffff),實質上按照平時求一個負數補碼的方式來處理也是一樣的,求某個負數的補碼規則為:先求這個數絕對值的原碼,然后從該二進制的右邊開始向左找第一個為1的位置,最后將這個1前的各位取反(包括最高位符號位,即最高位0取反后為1),其他位不變,最終所得的二進制就為這個負數的補碼,也就是最終在內存中負數所表示的形式。不過在找這個第一個為1時可能找不到或在最高位,比如-0,其絕對值為0(0x00000000);也有可能最高位為1,比如-2147483648,其絕對值為2147483648(0x80000000),如果遇到絕對值的原碼為0x00000000或0x80000000的情況下則不變,即為絕對值的原碼本身。
?
-0x00000000的運算過程:對0x00000000先取反得到0xffffffff,再加1,-0x00000000的最后結果就為 0xffffffff+1
,其最后的結果還是0x00000000,所以-0x00000000 == 0x00000000。前面是對0x00000000求負的過程,如果是對0求負呢?先求0的十六進制形式0x00000000,再按前面的過程來即可。或者根據前面規則對0x00000000求負不變,即最后
結果還是0x00000000。
?
-0x80000000的運算過程:對0x80000000先取反得到0x7fffffff,再加1,-0x80000000的最后結果就為 0x7fffffff+1
,其最后的結果還是0x80000000,即-0x80000000 == 0x80000000。前面是對0x80000000求負的過程,如果是對
2147483648求負呢?先求2147483648的十六進制形式0x80000000,再按前面的過程來即可。或者根據前面規則對0x80000000求負不變,即最后結果還是0x80000000。
?
-0x00000001的運算過程,實質上就是求-1的補碼過程,即對其絕對值的十六進制0x00000001求補碼,即為0xffffffff
,即-1的補碼為0xffffffff。
?
- System.out.println(Integer.MIN_VALUE?==?-Integer.MIN_VALUE); //?true? ??
- /* ?
- ?*??0x80000000取反得0x7fffffff,再加1得0x80000000,因為負數是 ?
- ?*??以補碼形式存儲于內存中的,所以推導出結果原碼為:0x80000000, ?
- ?*??即為-0,又因為-0是等于0的,所以不需要-0這個編碼位,那就多了 ?
- ?*??一個0x80000000編碼位了,所以最后就規定0x80000000為最小負數? ?
- ?*/ ??
- System.out.println(- 0x80000000 ); //?-2147483648 ??
- /* ?
- ?*??0x7fffffff取反得0x80000000,再加1得0x80000001,因為負數是 ?
- ?*??以補碼形式存儲于內存中的,所以推導出結果原碼為:0xffffffff, ?
- *??第一位為符號位,所以最后的結果就為?-0x7fffffff?=?-2147483647 ?
- ?*/ ??
- System.out.println(- 0x7fffffff ); //?-2147483647 ??
?
另外,還發現有趣現象:最大整數加1后會等于最小整數:
- //?MAX_VALUE?=?0x7fffffff;?MIN_VALUE?=?0x80000000; ??
- System.out.println((Integer.MAX_VALUE?+? 1 )?==?Integer.MIN_VALUE); //?true ??
- //?MIN_VALUE?=?0x8000000000000000L;?MIN_VALUE?=?0x8000000000000000L; ??
- System.out.println((Long.MAX_VALUE?+? 1 )?==?Long.MIN_VALUE); //?true ??
當然,-Byte. MIN_VALUE==Byte.MIN_VALUE、-Short.MIN_VALUE== Short.MIN_VALUE、-Long.MIN_VALUE== Long.MIN_VALUE,也是成立的。
16.?Math.abs結果一定為非負數嗎?
- System.out.println(Math.abs(Integer.MIN_VALUE)); //?-2147483648 ??
上面的程序不會輸出2147483648,而是-2147483648,為什么?
?
其實我們看一下Math.abs源碼就知道為什么了,源碼:(a < 0) ? -a : a;,結合上面那個迷題,我們就發現-Integer.MIN_VALUE= Integer.MIN_VALUE,所以上面的答案就是最小整數自己。
?
另外我們也可以從API文檔看到對Math.abs()方法的解釋:如果參數等于 Integer.MIN_VALUE 的值(即能夠表示的最
小負 int 值),則結果與該值相同且為負。
?
所以Math.abs不能保證一定會返回非負結果。
?
當然,Long.MIN_VALUE也是這樣的。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
