JAVA更多的类谜题75:头还是尾
这个程序的行为在1.4版和5.0版的Java平台上会有所改变。这个程序在这些版本中会做什么?(如果只能访问5.0版本的平台,那么可以在编译时使用-source 1.4标志来模拟1.4版本的行为。)
导入Java . util . random;
public类coin side {
private static Random rnd = new Random();
public static CoinSide flip(){
return rnd . next boolean()?
人头。实例:反面。实例;
}
公共静态void main(String[]args){
system . out . println(flip());
}
}
class Heads扩展coin side {
private Heads(){ }
public static final Heads INSTANCE = new Heads();
public String toString(){
return " heads ";
}
}
类Tails扩展coin side {
private Tails(){ }
public static final Tails INSTANCE = new Tails();
public String toString(){
return“tails”;
}
}
该程序似乎根本没有使用5.0版本的任何新功能,因此很难看出为什么它们在行为上应该有所不同。其实这个程序是无法在1.4版或者更早版本的平台上编译的:
coinside.java: 7:
不兼容类型for?:都不是对方的子类型
第二个操作数:Heads
第三个操作数:Tails
return rnd . nextboolean()?
^
条件运算符(?)的行为在5.0版[JLS2 15.25]之前非常有限。当第二个和第三个操作数是引用类型时,条件运算符要求其中一个必须是另一个的子类型。头和尾不是彼此的子类型,所以这里有一个错误。为了编译这段代码,可以将其中一个操作数转换为两者的公共超类:
return rnd.nextBooleam()?
(硬币侧)头。实例:反面。实例;
在5.0或更高版本中,Java语言更加宽松,当第二个和第三个操作数是引用类型时,条件运算符总是合法的。结果类型是这两种类型的最小公共超类。公共超类总是存在的,因为Object是每个对象类型的超类。在实际使用中,这种变化的主要结果是条件操作符更经常地做正确的事情,但较少出现编译时错误。对于我们当中的语言新手来说,条件运算符作用于引用类型的结果与对第二个和第三个操作数调用以下方法的结果具有相同的编译时类型:
T choose(T a,T b) {}
这个谜题中显示的问题在1.4版和更早的版本中经常出现,迫使您插入转换来掩盖代码的真正目的。也就是说,拼图本身就是人为的。在5.0版本之前,程序员使用类型安全枚举模式编写coin side[e kitem 21]会更自然一些:
以下程序都是由同步的静态方法组成的。那么它会打印出什么呢?每次运行这个程序,能保证打印出同样的内容吗?
public class ping pong {
public static synchronized void main(String[]a){
Thread t = new Thread(){
public void run(){ pong();}
};
t . run();
system . out . print(" Ping ");
}
静态同步void Pong(){
system . out . print(" Pong ");
}
}
在多线程程序中,通常正确的观点是程序每次运行的结果都可能发生变化,但上面的程序总是打印相同的内容。在一个同步的静态方法被执行之前,它将获得一个与其类对象相关联的监控锁[8.4.3.6·JLS]。所以在上面的程序中,主线程将在创建第二个线程之前获得与PingPong.class关联的锁。只要主线程持有这个锁,第二个线程就不可能执行同步的静态方法。具体来说,第二个线程只能在打印出main方法的Ping并完成执行后才能执行pong方法。只有当主线程放弃锁时,才允许第二个线程获取锁并打印Pong。根据上面的分析,我们似乎确信这个程序应该总是打印PingPong。但是这里有一个小问题:当你尝试运行这个程序时,你会发现它总是打印PongPing。到底发生了什么?
位律师回复
0条评论