呵呵。Effective Java这本书类似Effective C++的风格,涉及到一些较深入的语言细节,适合有Java基础的人提高Java编码水平,有时间大家可以拿来翻一翻,说不定会有让你恍然大悟的感觉哈。(中英文都有,中文版是潘爱民译的,还不错)顺便支持一下楼主。

解决方案 »

  1.   

    Java里的陷阱不少
    特别是继承那里
      

  2.   

    比如,类A有两个同名函数 void f( Object ) 和 void f( String )
        猜猜看,A.f( null ) 会调用那一个?
    编译期出错,这个应该不算什么问题吧。
      

  3.   

    2、 包级访问权限(就是缺省访问权限)的方法,子类不能访问却能改写(C++中也有类似的问题)。导致的结果是:所有的包级访问权限方法都应该声明为 final ,虽然明知别人无法访问
    楼主在哪里“抄”来的帖子,你试一试,看看能不能改写。
    弄清楚再说话好不好?
      

  4.   

    1、 子类会覆盖父类的同名变量,如果子类可以访问这些变量的话;导致的结果是:尽量把所有的变量声明为private,而通过 get/set 方法让子类访问;否则用户继承一个类,然后不小心覆盖了变量,就会产生一些隐蔽的bug
    这个楼主能不能举个例子,我的理解是,既然父类允许你覆盖,自然就是给子类使用的,和方法的覆盖没有什么区别。
    你可以放大权限,却不能缩小权限,这本身就是 java 语言在继承方面给人带来的方便。虽然帮 java 说了一些好话,但我决不是 java 的卫道者,java 当然也有本身的缺陷,但楼主指出的这些我的确是无法理解。
      

  5.   

    不好意思,刚才发的 post 有问题,变量的覆盖的确是有问题的,sorry
    但在我的测试中,这和包的权限没有什么关系,就是所有的变量,都不会被覆盖(我觉得不应该这样,也许是我哪里出了问题,请高手指点,谢谢)。
    做了一个例子,写出具体的问题(我理解的)做了三个类Test.javapublic class Test {
    public int i;
    public void tt() {
    System.out.println(i);
    }
    }Test2.javapublic class Test2 extends Test{
    public int i;
    public void tt() {
    System.out.println(i);
    }
    }Test3.javapublic class Test3 {
    public static void main(String[] args) {
    Test2 t2=new Test2();
    Test t=t2;
    t2.i=15;
    t.i=10;
    System.out.println(t2.i);
    System.out.println(t.i);
    t2.tt();
    t.tt();
    }
    }Test3 运行的结果
    15
    10
    15
    15
    说明变量并没有覆盖,方法会被覆盖,但变量不会。
      

  6.   

    每一中语言都有它的优点,否则它不可能能在今天依然运用
    同样每个语言也有它本身存在的缺点,不然语言的发展就会匮乏语言之间本来就是相互学习关联的!虽然在学习中会出现很多不如意的地方,但是只要我们
    细心努力的话,问题就会解决了!也许java在应用的时候的确会有这样那样的问题,但是很多时候问题的出现也许不是语言本身的问题,而是运用语言写程序的我们自己的理解以及写的程序的严谨性不够的问题!
      

  7.   

    1、 “覆盖(override)”这个说法可能比较含糊。我说的是,父类变量(private除外)会被子类的同名变量隐藏起来。变量不象方法,没有多态这个概念。变量就是存放在对象的地址空间中的(static除外),所以任何变量是引用的究竟是父类还是子类的同名变量,是在编译期决定的。haha1903(中国人) 的例子中,Test2的对象内存中有两个变量i, 但是具体代码中的i指的是那一个,在编译期就定下来了, java语言规范中讲得很详细。2、 可能我说的不清楚,haha1903(中国人)看看这个例子就知道了。其根本原因是,除了 private 外, package、protect、public的方法都是多态的、运行期才确定的,所以不能访问的方法也有可能被改写(override或者说重载)
    class A {
        void f() { g(); }
        void g() { System.out.println( "A" ); }
    }class B extends A {
        void g() { System.out.println( "B" ); }
    }public class Main {
    public static void main( String[] args ) throws Exception {
        A a = new B();
        a.f();
    }
    }3、 方法的参数在匹配时,是匹配继承最深的子类的那一个。因为 String 是 Object 的子类,而 null 什么都能匹配,所以 f( null ) 会匹配 f( String ) 。f( File ) 会匹配 f( Object ) 。 如果一个都不能匹配,就会编译出错。
      

  8.   

    谢谢 sliant ,是我对 override 理解得不好,我没注意到变量是不能 override 的。小弟才疏学浅,刚才言语之间的冒犯,请见谅。第 2 个例子我会好好看看。第 3 个我就十分不解了,在我这里,如果同时定义
    f(String),f(File),f(Object)
    然后调用的时候用 f(null) 会在编译期出错,而不会像你说的匹配最深的那个子类,为什么呢?
      

  9.   

    2、 包级访问权限(就是缺省访问权限)的方法,子类不能访问却能改写(C++中也有类似的问题)。导致的结果是:所有的包级访问权限方法都应该声明为 final ,虽然明知别人无法访问
    这个具体是什么地方提到的?原文应该只是说因为种种原因除非你有意设定用来继承的方法,其他都要尽量设计为final的。包级访问权限(就是缺省访问权限)的方法,子类不能访问却能改写。这个我没有测试,不过原文真的是这么说的么?不应该啊。包外应该继承都有问题,何来包外的子类一说?
      

  10.   

    变量在C++中其实可以是多态的,这一点java没有提供足够的灵活性 java 的语法比起 C++ 来简单 是java的优点
    ------------------------------------------
    这一点我不认同,如果简化没有带来灵活性,反而是限制了程序员的发挥,这不能算是优点。
    如果说RISC的精简指令集比CISC的复杂指令集有优点,这倒是真的。
    而java的语法比起C++来,没有做到精简,只算是减少。
      

  11.   

    再次声明,第二个是我错了 :-(  包外不能见,所以就不能重载,只能是完全新添一个方法,在 eclipse 中会给警告。而在包内的重载就是普通的重载,就是我举的那个例子。我倒是觉得,基本上所有 java 比 C++ 简化的地方,都是经过了深思熟虑的,比如默认参数, C++ 中这么做:
        void f( int i=99 ) {...}
    但是用同名函数完全可以解决这个问题,java 中都是使用两个函数
        void f( int i ) {...}
        void f() { f(99); }
    这样一样实现了默认参数,虽然麻烦了一点点,但是一目了然,不容易出错。特别是有多个默认参数的时候, C++ 那样很容易出错的。
      

  12.   

    如果定义3个同名函数 f( Object )  f( String )  f( File )  可以看出继承关系是
    String extends Object
    File extends Object
    可见继承树分支了,对 f( null ) 来说, f( String ) 和 f( File ) 都可以匹配,而且不能决定匹配那一个更好,编译会报错但是对于一条线继承下来的,比如:
    f( Object )  f( InputStream )  f( BufferedInputStream )
    就会匹配继承最深的
      

  13.   

    oyd(cpp<JavaIsNotPlatform_Independent>)(MVP) 
    的例子太有意思了。support我还是比较支持 sliant() 重载函数的那个做法,可以 void f(int i) , void f() {int i=99},虽然这样在非常长参数的时候几乎不可行,但非常长参数在我的理解来看,是不是代表了设计不是非常完善,应该把接口分离一下。最后再次谢谢 sliant() 对于 f(null) 的解释,好久以前我也遇到过一次这样的问题,没有太在意,就以为如果全都匹配,就编译不过去,今天才知道,如果参数在一条继承的分支上的时候,就匹配最深的,如果不是 sliant() 的兄提出,也不知道在什么时候会知道这个。^_^
      

  14.   

    我倒是觉得,基本上所有 java 比 C++ 简化的地方,都是经过了深思熟虑的,比如默认参数, C++ 中这么做:
        void f( int i=99 ) {...}
    但是用同名函数完全可以解决这个问题,java 中都是使用两个函数
        void f( int i ) {...}
        void f() { f(99); }
    =========================================================================
    这只能说设计原则不一样。C++是实用主义的,讲究的是最大限度的降低程序员的劳动强度(也可以说是为了代码复用)。
    请看看java中没有的特性,哪一样不是为了此目的?多继承、运算符重载、默认参数、模板
    每一个程序员少写一行代码,全世界的程序员省下来的时间就够开发一个有意义的系统了。而java为了本身的完美,许多地方就忽略了人的价值。
    就拿上面这个例子:
        void f( int i ) {...}
        void f() { f(99); }
    C++一样可以这样,但是如果参数较多觉得这样不方便,程序员还可用
    void f( int i=99 ) {...}
    多体贴呀!C++因为有了众多的特性,导致学习难度加大,这是事实,可是C++的设计思想之一就是不为用不着的特性付出代价。
    你不用这些特性,并不代表你的程序就低人一等了。这种思想导致一些一知半解的程序员,在分明没必要的情况下,去封装了几个对象,强加上继承关系,然后看着还不爽,就给加上模板参数和几个虚基类,最好放到一个名字空间中。好像不这样写程序就会很丢脸一样,而这样的结果呢,发现程序非常糟糕,于是他们开始诟病C++的不是。
      

  15.   

    oyd(cpp<JavaIsNotPlatform_Independent>)(MVP)
    你不用这些特性,并不代表你的程序就低人一等了。这种思想导致一些一知半解的程序员,在分明没必要的情况下,去封装了几个对象,强加上继承关系,然后看着还不爽,就给加上模板参数和几个虚基类,最好放到一个名字空间中。好像不这样写程序就会很丢脸一样,而这样的结果呢,发现程序非常糟糕,于是他们开始诟病C++的不是。
    ------------------------------------------------------------
    说得很好!深有体会。就算在 java 中,也应该这样。比如,不考虑多线程的就不要额外考虑 volitile ,不考虑 serialize 的就不要使用 transient 。写小型的程序, private,package,protect和public都不需要全部用上,只使用 package 和 public ,程序会显得更清晰。写程序的时候,特别是小型程序,应该针对性的选择语言特性的一个子集供自己使用就好
      

  16.   

    oyd(cpp<JavaIsNotPlatform_Independent>)(MVP)
    你先要明白,java并不是不能做,而是不想做
    你说的'多继承、运算符重载、默认参数、模板'
    前三个已经否决,具体原因我想只要关心java.sun.com的不太可能不知道
    并且java分别提出了更有效的解决方案,并且说明了为什么在将来也不会支持
    第四个在此不想浪费口舌我看到你不少贴子,我认为你在罗列自己论据的时候最好先设想一下,为什么java要这样,然后再告诉自己,java不是C++的效仿者,并不是C++能做什么,java就要实现什么。
    至于 sliant() ( )的三个例子
    1,3编译不通过
    2瞎掰我建议你多写写代码
      

  17.   

    allenhe(勤劳的民工)
    虽然我是java的拥护者,但我觉得你否决java和C++的关系太武断了
    一般大家是认为 C/C++/java 是一系的语言,java也是有意模仿 c/c++ 语法的,unix的一位创始人(名字我忘了,d打头的那一位)发表过这个言论,在网上很容易搜到(他建议学习
    4种语言,其中讲到了这一点)另外我举的例子都是可以编译通过的,第2个我理解有误(忘记放到不同的包中去了),但是代码是我亲手编译通过的
      

  18.   

    支持:
    ----------------------------------------------------------------------
    allenhe(勤劳的民工) oyd(cpp<JavaIsNotPlatform_Independent>)(MVP)
    你先要明白,java并不是不能做,而是不想做
    你说的'多继承、运算符重载、默认参数、模板'
    前三个已经否决,具体原因我想只要关心java.sun.com的不太可能不知道
    并且java分别提出了更有效的解决方案,并且说明了为什么在将来也不会支持
    第四个在此不想浪费口舌我看到你不少贴子,我认为你在罗列自己论据的时候最好先设想一下,为什么java要这样,然后再告诉自己,java不是C++的效仿者,并不是C++能做什么,java就要实现什么。
    至于 sliant() ( )的三个例子
    1,3编译不通过
    2瞎掰我建议你多写写代码
    ------------------------------------------------------------赞同!
      

  19.   

    无语。
    我在C++论坛和java论坛都呆过,以下这些话是我的个人体会。
    越新潮的东西,比如java,从事的人就越多,而且没礼貌
    老旧的,难学的,比如C++,大家还能静下心来讨论问题如果编译不通过的话,很可能你根本就没理解问题。
      

  20.   

    C++ 和 Java 其实就类似 Perl 和 Python。
    一个灵活而方便,一个严谨而方便。具体感觉如何,个人都不同罢了。我喜欢 Java 的匿名类、interface、class Class,也喜欢 C++ 的模板 meta-programming、多继承、默认参数,无所谓。有什么好吵的?相比之下,Java 比 C++ 更容易达到实用的程度。这是它的优点,也是缺点:容易让人们急功近利。sliant 最后这话却是有点过激了。