Java解惑学习有感(九)---高级之谜

谜题86:有毒的括号垃圾

1、符号-2147483648 构成了一个合法的Java表达式,它由一元负操作符加上一个int型字面常量2147483648组成。通过添加一对括号来注解(很不重要的)赋值顺序,即写成-(2147483648),就会破坏这条规则。上述情况也适用于 long 型字面常量。

2、Java 不支持负的十进制字面常量;int 和 long 类型的负数常量都是由正数十进制字面常量前加一元负操作符(-)构成。这种构成方式是由一条特殊的语言规则所决定的:在 int 类型的十进制字面常量中,最大的是 2147483648。而从 0到 2147483647 的所有十进制字面常量都可以在任何能够使用 int 类型字面常量的地方出现,但是字面常量 2147483648 只能作为一元负操作符的操作数来使用

谜题87:紧张的关系

以下代码:

public class Transitive {
public static void main(String[] args) throws Exception {
long x = Long.MAX_VALUE;
double y = (double) Long.MAX_VALUE;
long z = Long.MAX_VALUE - 1;
System.out.print((x == y) + “ “); // Imprecise!
System.out.print((y == z) + “ “); // Imprecise!
System.out.println(x == z); // Precise!
}
}

打印的结果是 true true false,这显然证明了操作符 == 作用于原始类型时具有不可传递性。

总结:要警惕到 float 和 double 类型的拓宽原始类型转换所造成的损失。它们是悄无声息的,但却是致命的。

谜题88:原生类型的处理

1、原生类型 List 和参数化类型 List<Object>是不一样的。如果使用了原生类型,编译器不会知道在 list 允许接受的元素类型上是否有任何限制,它会允许你添加任何类型的元素到 list 中。这不是类型安全的:如果你添加了一个错误类型的对象,那么在程序接下来的执行中的某个时刻,你会得到一个 ClassCastException 异常。如果使用了参数化类型 List<Object>,编译器便会明白这个 list 可以包含任何类型的元素,所以你添加任何对象都是安全的。

2、原生类型的成员被擦掉,是为了模拟泛型被添加到语言中之前的那些类型的行为。如果你将原生类型和参数化类型混合使用,那么便无法获得使用泛型的所有好处,而且有可能产生让你困惑的编译错误。另外,原生类型和以 Object为类型参数的参数化类型也不相同。最后,如果你想重构现有的代码以利用泛型的优点,那么最好的方法是一次只重构一个 API,并且保证新的代码中绝不使用原生类型。

谜题89:泛型迷药

1、一个泛型类的内部类可以访问到它的外围类的类型参数

2、当你在一个泛型类中嵌套另一个泛型类时,最好为它们的类型参数设置不同的名字,即使那个嵌套类是静态的也应如此。对于语言设计者来说,或许应该考虑禁止类型参数的遮蔽机制,同样的,局部变量的遮蔽机制也应该被禁止。

谜题90:荒谬痛苦的超类

研究以下代码:

public class Outer {

class Inner1 extends Outer{}
class Inner2 extends Inner1{}
}

编译是会报错的,

显示地包含构造器的代码:

public class Outer {
public Outer() {}
class Inner1 extends Outer{
public Inner1() {
super(); // 调用 Object()构造器
}
}
class Inner2 extends Inner1{
public Inner2() {
super(); // 调用 Inner1()构造器
}
}
}

结果会发现是调用Inner1()构造器出错了

要想实例化一个内部类,如类 Inner1,需要提供一个外部类的实例给构造器。一般情况下,它是隐式地传递给构造器的,但是它也可以以expression.super(args)的方式通过超类构造器调用(superclass constructor invovation)显式地传递。如果外部类实例是隐式传递的,编译器会自动产生表达式:它使用 this 来指代最内部的其超类是一个成员变量的外部类。这确实有点绕口,但是这就是编译器所作的事情。在本例中,那个超类就是Inner1。因为当前类 Inner2 间接扩展了 Outer 类,Inner1 便是它的一个继承而来的成员。因此,超类构造器的限定表达式直接就是 this。编译器提供外部类实例,将 super 重写成 this.super。

修改代码如下即可:

public class Outer {
class Inner1 extends Outer {}
class Inner2 extends Inner1{
public Inner2() {
Outer.this.super();
}
}
}

谜题91:序列杀手

1、在 readObject 或readResolve 方法中,请避免直接或间接地在正在进行反序列化的对象上调用任何方法。如果你必须在某个类型 C 的 readObject 或 readResolve 方法中违背这条建议,请确定没有 C 的实例会出现在正在被反序列化的对象图的某个循环内。不幸的是,这不是一个本地的属性:一般说来,你需要考虑到整个系统来验证这一点。

2、Java 的序列化系统是很脆弱的。为了正确而且高效地序列化大量的类,你必须编写 readObject 或 readResolve 方法。这个谜题说明了,为了避免破坏反序列化的实例,你必须小心翼翼地编写这些方法。HashSet、HashMap 和 Hashtable 的 readObject 方法很容易产生这种错误。对于平台设计者来说,如果你决定提供序列化系统,请不要提供如此脆弱的东西。健壮的序列化系统是很难设计的。

谜题92:双绞线

研究以下代码:

public class Twisted {
private final String name;
Twisted(String name) {
this.name = name;
}
private String name() {
return name;
}
private void reproduce() {
new Twisted("reproduce") {
void printName() {
System.out.println(name());
}
}.printName();
}
public static void main(String[] args) {
new Twisted("main").reproduce();
}
}

结果会打印main

1、本例中的 Twisted 类,所有的本地的、内部的、嵌套的和匿名的类都可以毫无限制地访问彼此的成员

2、在这个程序中,name 方法并没有被继承到 reproduce 方法中的匿名类中。所以,匿名类中对于 printName方法的调用必须关联到外围(“main”)实例而不是当前(“reproduce”)实例。

谜题93:类的战争

常量变量将会被编译进那些引用它们的类中。一个常量变量就是任何被常量表达式初始化的原始类型或字符串变量。令人惊讶的是,null 不是一个常量表达式

谜题94:迷失在混乱中

1、这个随机数发生器,java.util.Random,使用的是一个 64 位的种子,而它产生的随机数完全是由这个种子决定的。52 张牌有 52!种排列,而种子却只有 264个。它能够覆盖的排列占所有排列的多少呢?你相信是百分之 2.3×10-47 吗?这只是委婉地表示了“实际上就没怎么覆盖”。

2、如果你使用java.security.SecureRandom 代替 java.util.Random,你会得到一个 160 位的种子,但是它给你带来的东西少得惊人:对于元素个数大于 40 的数组,这个shuffle 方法仍然不能返回它的某些排列(因为 40!>2160) 。对于一个 52 个元素的数组,你只能获得所有可能的排列的百分之 1.8×10-18 。

谜题95:只是些甜点

1、for(int i = 0; i < 100; i++); {
count++;
}

注意不要多打出个分号

2、研究以下代码:

import java.util.*;
public class BananaBread {
public static void main(String[] args) {
Integer[] array = { 3, 1, 4, 1, 5, 9 };
Arrays.sort(array, new Comparator<Integer>() {
public int compare(Integer i1, Integer i2) {
return i1 < i2 ? -1 : (i2 > i1 ? 1 : 0);
}
});
System.out.println(Arrays.toString(array));
}
}

所有平台实现上都会打印出[3, 1, 4, 1, 5, 9],它的比较器(comparator)承受
着“是头我赢,是尾你输”的综合症

3、System.out.println(true?false:true == true?false:true);

结果为false,它书写的布局和它的操作符的优先级并不匹配。加一些括号可以解决问题



如果有疑问或者对该博文有何看法或建议或有问题的,欢迎评论,恳请指正!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值