java技巧(二)

1、

任何浮点操作,只要它的一个或多个操作数为NaN,那么其结果为NaN。

2、

操作符重载是很容易令人误解的。在本谜题中的加号看起来是表示一个加法,但是通过为变量i 选择合适的类型,即String,我们让它执行了字符串连接操作

3、

不要在short、byte 或char 类型的变量之上使用复合赋值操作符。因为这样的表达式执行的是混合类型算术运算,它容易造成混乱。例如。i 的初始值(short)0xffff)。在执行移位操作时,第一步是将i 提升为int 类型。所有算数操作都会对short、byte和char 类型的操作数执行这样的提升。这种提升是一个拓宽原始类型转换,因此没有任何信息会丢失。这种提升执行的是符号扩展,因此所产生的int 数值是0xffffffff。然后,这个数值右移1 位,但不使用符号扩展,因此产生了int数值0x7fffffff。最后,这个数值被存回到i 中。为了将int 数值存入short变量,Java 执行的是可怕的窄化原始类型转换,它直接将高16 位截掉。这样就只剩下(short)oxffff。

如果你声明i 为一个char,因为char 是无符号的,所以发生在移位之前的拓宽原始类型转换不会执行符号扩展。

4、

<=操作符在这些类型的操作数上不是反对称的,因为Java 的判等操作符(==和!=)在作用于对象引用时,执行的是引用ID 的比较,而不是值的比较。

new Integer(0) == new Integer(0)//false

new Integer(0) == 0

5、

Integer.MIN_VALUE,即-2^31,他的十六进制表示是0x80000000。其符号位为1,其余所有的位都是0。如果我们对这个值取负值,那么我们将得到0x7fffffff+1,也就是0x80000000,即Integer.MIN_VALUE!换句话说,Integer.MIN_VALUE 是它自己的负值,Long.MIN_VALUE 也是一样。对这两个值取负值将会产生溢出,但是Java 在整数计算中忽略了溢出。

6、

当try 语句块和finally 语句块都意外结束时,在try 语句块中引发意外结束(return,continue,break)的原因将被丢弃,而整个try-finally 语句意外结束的原因将于finally 语句块意外结束的原因相同。

千万不要用一个return、break、continue 或throw 来退出一个finally 语句块,并且千万不要允许将一个受检查的异常传播到一个finally 语句块之外去。

除非try语句块中调用System.exit 。当System.exit 被调用时,虚拟机在关闭前要执行两项清理工作。首先,它执行所有的关闭挂钩操作,这些挂钩已经注册到了Runtime.addShutdownHook 上。这对于释放VM 之外的资源将很有帮助。如:

public class HelloGoodbye1 {
	public static void main(String[] args) {
		System.out.println("Hello world");
		Runtime.getRuntime().addShutdownHook(
			new Thread() {
				public void run() {
					System.out.println("Goodbye world");
				}
		});
		System.exit(0);
	}
}
总之,System.exit 将立即停止所有的程序线程,它并不会使finally 语句块得到调用,但是它在停止VM 之前会执行关闭挂钩操作。当VM 被关闭时,请使用关闭挂钩来终止外部资源。

7、

如果一个catch 子句要捕获一个类型为E 的被检查异常,而其相对应的try 子句不能抛出E 的某种子类型的异常,那么这就是一个编译期错误。

多个继承而来的throws 子句的交集,将减少而不是增加方法允许抛出的异常数量。

8、

public class UnwelcomeGuest {
	public static final long GUEST_USER_ID = -1;
	private static final long USER_ID;
	static {
		try {
			USER_ID = getUserIdFromEnvironment();
		} catch (IdUnavailableException e) {
			USER_ID = GUEST_USER_ID;
			System.out.println("Logging in as guest");
		}
	}		
	private static long getUserIdFromEnvironment() throws IdUnavailableException {
		throw new IdUnavailableException();
	}
	public static void main(String[] args) {
		System.out.println("User ID: " + USER_ID);
	}
}
class IdUnavailableException extends Exception {
}
该程序无法通过编译,USER_ID 域是一个空final(blank final),它是一个在声明中没有进行初始化操作的final 域。一个空final 域只有在它是明确未赋过值的地方才可以被赋值。

重构静态语句块中的代码为一个助手方法:

public class UnwelcomeGuest {
	public static final long GUEST_USER_ID = -1;
	private static final long USER_ID = getUserIdOrGuest;
	private static long getUserIdOrGuest {
		try {
			return getUserIdFromEnvironment();
		} catch (IdUnavailableException e) {
			System.out.println("Logging in as guest");
			return GUEST_USER_ID;
		}
	}
...// The rest of the program is unchanged
}
9、

当调用一个构造器时,实例变量的初始化操作将先于构造器的程序体而运行。

对于一个对象包含与它自己类型相同的实例的情况,并不少见。例如,链接列表节点、树节点和图节点都属于这种情况。你必须非常小心地初始化这样的包含实例,以避免StackOverflowError 异常。
构造器必须声明其实例初始化操作会抛出的所有被检查异常。下面这个展示了常见的“服务提供商”模式的程序,将不能编译,因为它违反了这条规则:

public class Car {
	private static Class engineClass = ...;
	private Engine engine = (Engine)enginClass.newInstance();
	public Car(){ }
}
尽管其构造器没有任何程序体,但是它将抛出两个被检查异常,InstantiationException 和IllegalAccessException。它们是Class.Instance抛出的,该方法是在初始化engine 域的时候被调用的。订正该程序的最好方式是创建一个私有的、静态的助手方法,它负责计算域的初始值,并恰当地处理异常。在本案中,我们假设选择engineClass 所引用的Class 对象,保证它是可访问的并且是可实例化的。
public class Car {
	private static Class engineClass = ...;
	private Engine engine = newEngine;
	private static Engine newEngine() {
		try {
			return (Engine)engineClass.newInstance();
		} catch (IllegalAccessException e) {
		throw new AssertionError(e);
} catch (InstantiationException e) {
throw new AssertionError(e);
}
}
public Car(){ }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值