西西河

主题:【游戏】不看不知道,一看吓一跳!!! -- Highway

共:💬47 🌺6 新:
分页树展主题 · 全看首页 上页
/ 4
下页 末页
  • 家园 【游戏】不看不知道,一看吓一跳!!!

    兄弟我自以为在Java上还是有一两年功力的,结果没想到尽然让这两个小题给蒙蔽了,汉啊!!!点看全图

    外链图片需谨慎,可能会被源头改

    怎么样,你也下来玩玩?

    问题一

    有人写了这么段程序,意思是想无限循环,大家看看能不能Work

    while (i <= j && j <= i && i != j) {

    }

    问题二

    有这么两个Class,

    public class PrintWords {

    public static void main(String[] args) {

    System.out.println(Words.FIRST + " " +

    Words.SECOND + " " + Words.THIRD);

    }

    }

    public class Words {

    public static final String FIRST = "the";

    public static final String SECOND = null;

    public static final String THIRD = "set";

    }

    你编译以一下,没问题是吧。好,现在改动一下Words Class,如下

    public class Words {

    public static final String FIRST = "physics";

    public static final String SECOND = "chemistry";

    public static final String THIRD = "biology";

    }

    然后再把Words Class编译一下,再次运行,结果会是什么样的呢?

    不要上Google找答案,自己想想看!!!

    • 家园 My 2 cents

      问题一: 是Java设计的问题. 操作符重载应由程序员实现, 除了最基本的外Java不应该狗拿耗子多管闲事, javac对这段代码应该直接报错.

      问题二: 是程序员的问题. 关键字final会导致编译器做优化处理, 这是完全合理的.

      问题三: 还是Java的问题. C/C++编译器会在预处理时将86400000000 hard coded into object file, 根本不会等到运行时再去计算MICROS_PER_DAY. 而且中间结果存放在int里也是很弱智的设计.

      • 家园 My 1.99 cents 8-)

        问题一出得不好,变量名故意引人往错处想.

        真正编程时很难想象熟手会写出这样的程

        序,但就题论题,生手熟手都可能答错.属于

        刁钻的trick question.

        问题二责任归谁要看程序类型.如果程序的设

        计用途就是hot swappable,(比如在web applet 里),

        那么程序员不应该用'final'.如果是stand-alone

        application,错误应该在编译管理:有dependency

        的class没有重新编译.C/C++的makefile没写好也

        会出现同样问题.如果用ant或eclipse,两个class

        都会重新编译的.

        问题三是编译器的问题.编译器在做constant

        substitution的除法时是直接把两个变量的定义

        字符串展开再计算的.如果先求两个变量的值再

        代入就不会错了.估计是把gcc的现成算法照抄了

        • 家园 第二题答得比较不错。第一题没有切中要害,

          第三题的解释不是很对。

          • 家园 补充回答

            个人观点:问题一不算JAVA语言设计的大错.

            只看给出的这段代码,有人可能觉得auto-unboxed

            <=, >= 与普通的!=混用容易制造bug.但我觉得

            真正写程序时并不容易出bug,因为首先要查的

            就是i和j的类型.

            第三个问题一开始想岔了.查了一下书发现编译

            器没错.(Section 4.2.2, Java Language Specification).

            仔细想了想,这样规定也有它的道理.

      • 家园 再补充两句,C/C++其实和Java一样“笨”。

        不信你自己试试看。(注意,C/C++的long long和Java的long一样,是64位的长整数)

        #include <iostream>
        
        //static or nor doesn't matter
        static long long MILL_PER_DAY = 60 * 60 * 24 * 1000;
        static long long MACRO_PER_DAY = 60 * 60 * 24 * 1000 * 1000;
        
        int main(int args, char *argv[])
        {
        	printf("The result = %d",MACRO_PER_DAY/MILL_PER_DAY);
        }

        我用的是gcc编译器,结果和java一样,是5。(IDE是免费的Dev-C++ 4.9.9.2)

        如果用微软的Visual C++(VS 2005),结果也是5。不过编译的时候会给出警告。

        Warning 1	warning C4307: '*' : integral constant overflow	c:\temp\longdivison\longdivison\longdivison.cpp	7

        奇怪吗?

        • 家园 FT, 的确如此

          #include <iostream>

          using namespace std;

          long long MILL_PER_DAY = 60 * 60 * 24 * 1000;

          long long MACRO_PER_DAY = 60LL * 60 * 24 * 1000 * 1000;

          main()

          {

          cout << MILL_PER_DAY << endl;

          cout << MACRO_PER_DAY << endl;

          cout << MACRO_PER_DAY/MILL_PER_DAY << endl;

          }

          结果就对了(M$的那个__int64也一样). 看来C/C+里加一个L还不够, 得加两个!

          如果不用中间结果:

          #include <iostream>

          using namespace std;

          long long MILL_PER_DAY = 86400000;

          long long MACRO_PER_DAY = 86400000000;

          main()

          {

          cout << MILL_PER_DAY << endl;

          cout << MACRO_PER_DAY << endl;

          cout << MACRO_PER_DAY/MILL_PER_DAY << endl;

          }

          gcc会报错, cl会正确编译运行.

          虽然C/C++也很土, 不过至少不会用16-bit int去存32-bit long的中间结果. C是70年代设计的, C++是80年代设计的, Java是90年代设计的, 评判标准当然不一样了.

      • 家园 有些问题你可能不知道。第一题在以前一直都是错误的。但

        问题是Java 5.0引入了boxing/un-boxing feature。于是出现了一些“不伦不类”的东西。Java 5.0中有好多东西并不是非常自然的,完全是出于市场上的需要。因为.net有,而Java没有,那在市场宣传上会显得很难看。

        第二题有些争议。因为java的最根本本质是dynamic linking。编译器那样优化,性能上是有所提高,但违反了Basic principle,合适吗?

        第三题你Java的看法是不对的。事实上,Java和C/C++一样,是先将那两个变量算出来作为常量的。但Java没有“智能”,不知道将中间结果放到long临时变量中。

        反编译的Java code是这样的。

        public class LongDivision
        {
            public LongDivision() {}
        
            public static void main(String args[])
            {
                System.out.println(5L);
            }
        
            private static final long MILLIS_PER_DAY = 0x5265c00L;
            private static final long MICROS_PER_DAY = 0x1dd76000L;
        }
        

        在.NET中,编译的时候,编译器会给出错误提示The operation overflows at compile time in checked mode 。看来这点上,.NET要聪明一些。

        • 家园 发错了, 空贴
        • 家园 呵呵, 我们是从程序员的角度来看问题的

          marketing的事我们不管. 第一题里的boxing/unboxing也许在某些情况下有用, 但这个功能实在是可有可无. 真正复杂的类操作符还是得由程序员来重载, 而象Integer这样的我宁可用基本数据类型.

          第二题和Java Language Specification的13.4.8和14.20小节里的例子一样, 书中指出了误用final关键字会导致break compatibility with existing binaries. 既然人家都那么说了, 只好怪程序员读书不仔细了.

          当然这样的设计是值得推敲的. 在实际中如果Words和PrintWords是由不同的程序员/公司开发的, 后者也许只能得到Words.class或是不知道前者修改了Words.java, 麻烦就来了. 这有点类似"DLL Hell".

          C++里的const关键字就没有此问题. 下面a.cpp和b.cpp完全可以分开独立编译, 怎么修改a.cpp都没事. C也是一样, 用extern const char*不会使编译器做错误优化. 其实常量在C/C++里面都是用头文件定义, 我对Java不提供#define这样的directives很有意见.

          h.h:

          #include <stdio.h>

          class Words {

          public:

          static const char* FIRST;

          static const char* SECOND;

          static const char* THIRD;

          };

          a.cpp

          #include "h.h"

          const char* Words::FIRST="the";

          const char* Words::SECOND=NULL;

          const char* Words::THIRD="set";

          b.cpp

          #include "h.h"

          main()

          {

          printf("%s\n",Words::FIRST);

          printf("%s\n",Words::SECOND);

          printf("%s\n",Words::THIRD);

          }

          所以此题对Java和程序员各打五十大板.

          第三题说明不光是java.exe而且javac.exe也是用int保存中间结果. 这太土了, 简直是个bug, 自动类型转换从来是就高不就低的.

          P.S. 看了Highway的大作后发现C/C++也一样土, 真是没想到啊. 不知Fortran是否会好一些?

    • 家园 题目给的有问题阿

      第一个题目没有说明i和j是什么数据类型的,如果照Highway兄的解释,那就是这个题目的前提条件没有给够。如果i和j都是long或者其他简单数据类型的话,应该是不会死循环的。如果i和j都是object类型的话,程序员是应该提供合适的比较方法,否则系统用默认的方法比较,出了错就是程序员自己的责任了。这个在C++bible等OO Programing的书里面应该都是讲过的。第二个题目,怎么看都觉得把他称为java5。0的feature太过牵强了:)

      • 家园 你有所不知。在以前的Java中,&lt;=,&gt;=这些操作符只能用于原始数据类型

        也就是int,long这一类的东西。

        Java 5.0中出现了box/un-boxing feature,这个题就是让你主意这个feature带来的新变化。不能用原来的思维考虑了。

        • 家园 这个倒是,不过

          不过在我所知道的OO语言中(C++,C#...),object的比较一般都是要求programmer提供比较函数或操作符,语言本身只提供基本的相等和不等的比较。所以不管这个box/unbox是不是java5。0的新feature,如果看到object之间的比较的case,就应该check一下所使用的比较操作符或者是比较函数是否是想要的。我想这个在以前的java的版本中应该也是成立的吧:)另外,Highway兄给的那个例子有点特殊,Integer这种object类型很有迷惑性的阿,如果i和j是什么Employee型的东西,估计大家就能一眼看出关键所在了:)

分页树展主题 · 全看首页 上页
/ 4
下页 末页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河