java 代码性能优化_Java代码性能优化(四)
1.奇偶判斷
不要使用 i % 2 == 1 來判斷是否是奇數,因為i為負奇數時不成立,請使用 i % 2 != 0 來判斷是否是奇數,或使用高效式 (i & 1) != 0來判斷。
2.小數精確計算System.out.println(2.00?-1.10);//?0.8999999999999999
上面的計算出的結果不是 0.9,而是一連串的小數。問題在于1.1這個數字不能被精確表示為一個double,因此它被表示為最接近它的double值,該程序從2中減去的就是這個值,但這個計算的結果并不是最接近0.9的double值。
一般地說,問題在于并不是所有的小數都可以用二進制浮點數精確表示。二進制浮點對于貨幣計算是非常不適合的,因為它不可能將1.0表示成10的其他任何負次冪。解決問題的第一種方式是使用貨幣的最小單位(分)來表示:System.out.println(200-110);?//?90
第二種方式是使用BigDecimal,但一定要用BigDecimal(String)構造器,而千萬不要用 BigDecimal(double)來構造(也不能將float或double型轉換成String再來使用BigDecimal(String)來構造,因為在將float或double轉換成String時精度已丟失)。
例如new BigDecimal(0.1),它將返回一個BigDecimal,也即0.1000000000000000055511151231257827021181583404541015625,正確使用BigDecimal,程序就可以打印出我們所期望的結果0.9:System.out.println(new?BigDecimal("2.0").subtract(new?BigDecimal("1.10")));//?0.9
另外,如果要比較兩個浮點數的大小,要使用BigDecimal的compareTo方法。
3.int整數相乘溢出
我們計算一天中的微秒數:long?microsPerDay?=?24?*?60?*?60?*?1000?*?1000;//?正確結果應為:86400000000
System.out.println(microsPerDay);//?實際上為:500654080
問題在于計算過程中溢出了。這個計算式完全是以int運算來執行的,并且只有在運算完成之后,其結果才被提升為long,而此時已經太遲:計算已經溢出。
解決方法使計算表達式的第一個因子明確為long型,這樣可以強制表達式中所有的后續計算都用long運算來完成,這樣結果就不會溢出:long?microsPerDay?=?24L?*?60?*?60?*?1000?*?1000;
4.負的十六進制與八進制字面常量
“數字字面常量”的類型都是int型,而不管他們是幾進制,所以“2147483648”、“0x180000000(十六進制,共33位,所以超過了整數的取值范圍)”字面常量是錯誤的,編譯時會報超過int的取值范圍了,所以要確定以long來表示“2147483648L”“0x180000000L”。
十進制字面常量只有一個特性,即所有的十進制字面常量都是正數,如果想寫一個負的十進制,則需要在正的十進制字面常量前加上“-”即可。
十六進制或八進制字面常量可就不一定是正數或負數,是正還是負,則要根據當前情況看:如果十六進制和八進制字
面常量的最高位被設置成了1,那么它們就是負數:System.out.println(0x80);//128
//0x81看作是int型,最高位(第32位)為0,所以是正數
System.out.println(0x81);//129
System.out.println(0x8001);//32769
System.out.println(0x70000001);//1879048193
//字面量0x80000001為int型,最高位(第32位)為1,所以是負數
System.out.println(0x80000001);//-2147483647
//字面量0x80000001L強制轉為long型,最高位(第64位)為0,所以是正數
System.out.println(0x80000001L);//2147483649
//最小int型
System.out.println(0x80000000);//-2147483648
//只要超過32位,就需要在字面常量后加L強轉long,否則編譯時出錯
System.out.println(0x8000000000000000L);//-9223372036854775808
從上面可以看出,十六進制的字面常量表示的是int型,如果超過32位,則需要在后面加“L”,否則編譯過不過。如果為32,則為負int正數,超過32位,則為long型,但需明確指定為long。System.out.println(Long.toHexString(0x100000000L?+?0xcafebabe));//?cafebabe
結果為什么不是0x1cafebabe?該程序執行的加法是一個混合類型的計算:左操作數是long型,而右操作數是int類型。為了執行該計算,Java將int類型的數值用拓寬原生類型轉換提升為long類型,然后對兩個long類型數值相加。因為int是有符號的整數類型,所以這個轉換執行的是符號擴展。
這個加法的右操作數0xcafebabe為32位,將被提升為long類型的數值0xffffffffcafebabeL,之后這個數值加上了左操作0x100000000L。當視為int類型時,經過符號擴展之后的右操作數的高32位是-1,而左操作數的第32位是1,兩個數值相加得到了0:
0x 0xffffffffcafebabeL
+ 0x 0000000100000000L
-----------------------------
0x 00000000cafebabeL
如果要得到正確的結果0x1cafebabe,則需在第二個操作數組后加上“L”明確看作是正的long型即可,此時相加時拓展符號位就為0:System.out.println(Long.toHexString(0x100000000L?+?0xcafebabeL));//?1cafebabe
5.窄數字類型提升至寬類型時使用符號位擴展還是零擴展System.out.println((int)(char)(byte)-1);//?65535
結果為什么是65535而不是-1?
窄的整型轉換成較寬的整型時符號擴展規則:如果最初的數值類型是有符號的,那么就執行符號擴展(即如果符號位為1,則擴展為1,如果為零,則擴展為0);如果它是char,那么不管它將要被提升成什么類型,都執行零擴展。了解上面的規則后,我們再來看看迷題:因為byte是有符號的類型,所以在將byte數值-1(二進制為:11111111)提升到char時,會發生符號位擴展,又符號位為1,所以就補8個1,最后為16個1;然后從char到int的提升時,由于是char型提升到其他類型,所以采用零擴展而不是符號擴展,結果int數值就成了65535。如果將一個char數值c轉型為一個寬度更寬的類型時,只是以零來擴展,但如果清晰表達以零擴展的意圖,則可以考慮
使用一個位掩碼:int?i?=?c?&?0xffff;//實質上等同于:int?i?=?c?;
如果將一個char數值c轉型為一個寬度更寬的整型,并且希望有符號擴展,那么就先將char轉型為一個short,它與char上個具有同樣的寬度,但是它是有符號的:int?i?=?(short)c;
如果將一個byte數值b轉型為一個char,并且不希望有符號擴展,那么必須使用一個位掩碼來限制它:char?c?=?(char)(b?&?0xff);//?char?c?=?(char)?b;為有符號擴展
6. ((byte)0x90 == 0x90)?
答案是不等的,盡管外表看起來是成立的,但是它卻等于false。為了比較byte數值(byte)0x90和int數值0x90,Java通過拓寬原生類型將byte提升為int,然后比較這兩個int數值。因為byte是一個有符號類型,所以這個轉換執行的是符號擴展,將負的byte數值提升為了在數字上相等的int值(10010000?111111111111111111111111 10010000)。在本例中,該轉換將(byte)0x90提升為int數值-112,它不等于int數值的0x90,即+144。
解決辦法:使用一個屏蔽碼來消除符號擴展的影響,從而將byte轉型為int。((byte)0x90?&?0xff)==?0x90
7. 三元表達式(?:)char?x?=?'X';
int?i?=?0;
System.out.println(true???x?:?0);//?X
System.out.println(false???i?:?x);//?88
條件表達式結果類型的規則:
(1) 如果第二個和第三個操作數具有相同的類型,那么它就是條件表達式的類型。
(2) 如果一個操作的類型是T,T表示byte、short或char,而另一個操作數是一個int類型的“字面常量”,并且它的值可以用類型T表示,那條件表達式的類型就是T。
(3) 否則,將對操作數類型進行提升,而條件表達式的類型就是第二個和第三個操作被提升之后的類型。
現來使用以上規則解上面的迷題,第一個表達式符合第二條規則:一個操作數的類型是char,另一個的類型是字面常量為0的int型,但0可以表示成char,所以最終返回類型以char類型為準;第二個表達式符合第三條規則:因為i為int型變量,而x又為char型變量,所以會先將x提升至int型,所以最后的結果類型為int型,但如果將i定義成final時,則返回結果類型為char,則此時符合第二條規則,因為final類型的變量在編譯時就使用“字面常量0”來替換三元表達式了:final?int?i?=?0;
System.out.println(false???i?:?x);//?X
在JDK1.4版本或之前,條件操作符 ?: 中,當第二個和延續三個操作數是引用類型時,條件操作符要求它們其中一個必須是另一個的子類型,那怕它們有同一個父類也不行:public?class?T?{
public?static?void?main(String[]?args)?{
System.out.println(f());
}
public?static?T?f()?{
//?!!1.4不能編譯,但1.5可以
//?!!return?true?new?T1():new?T2();
return?true???(T)?new?T1()?:?new?T2();//?T1
}
}
class?T1?extends?T?{
public?String?toString()?{
return?"T1";
}
}
class?T2?extends?T?{
public?String?toString()?{
return?"T2";
}
}
在5.0或以上版本中,條件操作符在延續二個和第三個操作數是引用類型時總是合法的。其結果類型是這兩種類型的最小公共超類。公共超類總是存在的,因為Object是每一個對象類型的超類型,上面的最小公共超類是T,所以能編譯。在JAVA程序中,性能問題的大部分原因并不在于JAVA語言,而是程序本身。養成良好的編碼習慣非常重要,能夠顯著地提升程序性能。
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的java 代码性能优化_Java代码性能优化(四)的全部內容,希望文章能夠幫你解決所遇到的問題。

- 上一篇: java word表格_java操作wo
- 下一篇: java 添加jbutton_java