Java谜题7——更多的类谜题

来源:互联网 发布:mac升级系统好吗 编辑:程序博客网 时间:2024/05/21 10:22

Java谜题7__更多的类谜题

谜题66:一件私事 | 谜题67:对字符串上瘾 | 谜题68:灰色的阴影 | 谜题69:黑色的渐隐 | 谜题70:一揽子交易 | 谜题71:进口税 | 谜题72:终极危难 | 谜题73:你的隐私正在公开 | 谜题74:同一性的危机 | 谜题75:头还是尾 | 名字重用的术语表

通过上面的链接可以学习这个章节的各个谜题,下面是学习之后,做的一些笔记。
 
谜题66:一件私事

首先,复写一个父类的方法的时候,一定要提供给这个域更广泛的访问权限,否则的话就不能实现完美的隐藏。在复写的类里就存在两个可以调用,但是方法名一样的函数,这就是error所在。

其次,强制类型转化也可以完成对父类的方法的调用,即使是在子类中被隐藏的方法!(这说明了覆写与隐藏之间的一个非常大的区别。一旦一个方法在子类中被覆写,你就不能在子类的实例上调用它了(除了在子类内部,通过使用super关键字来方法)。然而,你可以通过将子类实例转型为某个超类类型来访问到被隐藏的域,在这个超类中该域未被隐藏。

本谜题的教训是隐藏通常都不是一个好主意。Java语言允许你去隐藏变量、嵌套类型,甚至是静态方法(就像在谜题48所展示的那样),但是你不能认为你就应该去隐藏。隐藏的问题在于它将导致读者头脑的混乱。你正在使用一个被隐藏实体,或者是正在使用一个执行了隐藏的实体吗?要避免这类混乱,只需避免隐藏。

总之,当你在声明一个域、一个静态方法或一个嵌套类型时,如果其名字与基类中相对应的某个可访问的域、方法或类型相同,就会发生隐藏。隐藏是容易产生混乱的:违反包容性的隐藏域在某种意义上是特别有害的。更一般地讲,除了覆写之外,要避免名字重用。

谜题67:对字符串上瘾

VM不能找到main方法是因为它并不在那里。尽管StrungOut有一个被命名为main的方法,但是它却具有错误的签名。一个main方法必须接受一个单一的字符串数组参数[JVMS 5.2]。VM努力要告诉我们的是StrungOut.main接受的是由我们的String类所构成的数组,它无论如何都与java.lang.String没有任何关系。

如果你确实需要编写自己的字符串类,看在老天爷的份上,千万不要称其为String。要避免重用平台类的名字,并且千万不要重用java.lang中的类名,因为这些名字会被各处的程序自动加载。程序员习惯于看到这些名字以无限定的形式出现,并且会很自然地认为这些名字引用的是我们所熟知的java.lang中的类。如果你重用了这些名字的某一个,那么当这个名字在其自己的包内被使用时,该名字的无限定形式将会引用到新的定义上。

可以证明,在这样的上下文环境中,有一条规则决定着程序的行为,即当一个变量和一个类型具有相同的名字,并且它们位于相同的作用域时,变量名具有优先权[JLS 6.5.2]。变量名将遮掩(obscure)类型名[JLS 6.3.2]。相似地,变量名和类型名可以遮掩包名。这条规则真的是相当地晦涩,任何依赖于它的程序都极有可能使它的读者晕头转向。

幸运的是,遵守标准的Java命名习惯的程序继续从来都不会遇上这个问题。类应该以一个大写字母开头,以MixedCase的形式书写;变量应该以一个小写字母开头,以mixedCase的形式书写;而常量应该以一个大写字母开头,以ALL_CAPS的方式书写。单个的大写字母只能用于类型参数,就像在泛型接口Map<K,V>中那样。包名应该以lower.case的方式命名[JLS 6.8]。

可以引用到一个被遮掩的类型名的,其技巧就是在某一种特殊的语法上下文环境中使用该名字,在该语法上下文环境中允许出现一个类型但是不允许出现一个变量。在转型表达式的括号中间的部分就是这样一种上下文环境。下面的程序通过使用这种技术解决了这个谜题,并且将打印出我们所期望的Black:

public class FadeToBlack {    public static void main(String[] args){        System.out.println(((X.Y)null).Z);    }}
不借助这种有问题的用法,而是通过在一个类声明的extends子句中使用一个被遮掩的类这种方式,你也可以解决本谜题。因为基类总是一种类型,出现在extends子句中的名字从来都不会被解析为变量名。
下面的程序就展示了这项技术,它也会打印出Black: 
public class FadeToBlack {
public class FadeToBlack {       static class Xy extends X.Y{ }          public static void main(String[ ] args){             System.out.println(Xy.Z);    }}
如果你使用的5.0或更新的版本,那么通过在一个类型变量声明的extends子句中使用X.Y这种方式,你也可以解决本谜题: 
   public static <T extends X.Y> void main(String[] args){        System.out.println(T.Z);    }}