1. 程式人生 > >《Java 解惑》 第二章 表示式之謎

《Java 解惑》 第二章 表示式之謎

簡述:

《Java 解惑》  第二章 表示式之謎



謎題1:奇數性

package com.anialy.test.java_puzzlers.chapter_1;

public class 奇數性 {
	public static void main(String[] args) {
		System.out.println("-1: " + isOdd(-1));
	}
	
	private static boolean isOdd(int a){
		return a % 2 == 1;
	}
}

輸出:


原因:

Java中對於所有int數值a和所有的非零int數值b,都滿足下面的恆等式

(a / b) * b + (a % b) == a


當取餘操作返回一個非零的結果時,它與左運算元,也就是a具有相同的正負符號


解決方式:

1)右數使用零

	private static boolean isOdd(int a){
		return a % 2 != 0;
	}

2)用“&”與操作

	private static boolean isOdd(int a){
		return (a & 1) != 0;
	}


謎題2: 找零時刻

package com.anialy.test.java_puzzlers.chapter_1;

public class 找零時刻 {
	public static void main(String[] args) {
		System.out.println(2.0 - 1.1);
	}
}

輸出:



原因:

不是所有的小數都可以用二進位制浮點數精確表示, 對於貨幣計算儘量使用int, long, BigDecimal


解決方式:

1)通過printf設定輸出精度

package com.anialy.test.java_puzzlers.chapter_1;

public class 找零時刻 {
	public static void main(String[] args) {
		System.out.printf("%.1f", 2.0 - 1.1);
	}
}

2)使用BigDecimal

package com.anialy.test.java_puzzlers.chapter_1;

import java.math.BigDecimal;

public class 找零時刻 {
	public static void main(String[] args) {
		System.out.println(new BigDecimal("2.0")
		    .subtract(new BigDecimal("1.1")));
	}
}


謎題3: 長整除

package com.anialy.test.java_puzzlers.chapter_2;

public class 長整除 {
	public static void main(String[] args) {
		final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
		final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
		System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
	}
}


輸出:



原因:

右側的操作均為int型,從int提升為long是一種拓寬基本型別轉換


解決方式:

1) 增加L標示符




謎題4:初級問題

在使用過程中,如果標示為long的數字,不要用小寫,以防看不清






謎題5:十六進位制的趣事

package com.anialy.test.java_puzzlers.chapter_2;

public class 十六進位制的趣事 {
	public static void main(String[] args) {
		System.out.println(Long.toHexString(
				0x100000000L + 0xcafebabe));
	}
}

輸出:



原因:

左運算元, 0x100000000L

右運算元,0xcafebabe

其中,兩個數字相加時, 由於十六進位制的數字並不是通過負號來標示正負值的,所以對於最高位為c的右運算元, 在運算時,從int拓寬基本型別至long後,高位

都用 f 填充(編譯器判斷為負數的預設操作),即0xffffffffcafebabeL 之後這個數字加上左運算元0x0000000100000000L 得到的就是0x00000000cafebabeL



謎題6:多重轉型

package com.anialy.test.java_puzzlers.chapter_2;

public class 多重轉型 {
	public static void main(String[] args) {
		System.out.println((int)(char)(byte) -1);
	}
}

輸出:



原因:

byte是有符號型別,在byte數值-1轉換為char時,會發生符號擴充套件,char(為2個byte)數值的16位都被置位了,等於2^16 - 1

此外,如果最初的數值型別是有符號的,就執行符號擴充套件;如果是char, 那麼不管要被轉換為什麼型別,一律是零擴充套件(高位用0補足)


解決方式:

1)char 不希望進行符號擴充套件,則需要使用一個位掩碼來明確表示

(int)((char) 'A' & 0xffff)

2)

如果一個byte數值轉換為char時, 無符號擴充套件,那就必須使用一個位掩碼做限制

(byte) -1 & 0xff


謎題7: 互換內容

package com.anialy.test.java_puzzlers.chapter_2;

public class 互換內容 {
	public static void main(String[] args) {
		int x = 1984;
		int y = 2001;
		x^= y^= x^= y;
		System.out.printf("x: %d , y: %d", x, y);
	}
}

輸出:

 

原因:

Java運算元是從左向右求值的


解決方式:

1)改變結算順序呢,從左向右

package com.anialy.test.java_puzzlers.chapter_2;

public class 互換內容 {
	public static void main(String[] args) {
		int x = 1984;
		int y = 2001;
		x = (y ^= (x ^= y)) ^ x;
		System.out.printf("x: %d , y: %d", x, y);
	}
}


謎題8: Dos Equis

package com.anialy.test.java_puzzlers.chapter_2;

public class DosEquis {
	public static void main(String[] args) {
		char x = 'X';
		int i = 0;
		System.out.println(true ? x : 0);
		System.out.println(false ? i : x);
	}
}

輸出:


原因:

1)如果第二個和第三個運算元具有相同的型別,那麼它就是條件表示式的型別。

2)如果一個操作輸的型別是T, T表示byte、short或char, 而另一個運算元是一個int型別的常量表達式,它的值可以用型別T表示,那麼條件表示式的型別就是T

3)否則,將對運算元型別進行二進位制數字提升,而條件表示式的型別就是第二個和第三個運算元被提升之後的型別



謎題9:半斤

x +=  i 合法,x = x + i 不合法



原因:

對於複合賦值,簡單的賦值是非法的,這需要顯示的轉型。



謎題10: 八兩

x = x + i 合法,x += i 不合法



原因:

右側是String型別的情況下,“+=” 操作符不允許左側是Object型別