找回密码
 FreeOZ用户注册
查看: 2032|回复: 19
打印 上一主题 下一主题

[论坛技术] 无法理解的几个Java问题 —— 小Key出品

[复制链接]
跳转到指定楼层
1#
发表于 2-7-2009 11:11:32 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?FreeOZ用户注册

x
问题一:继承/实现接口后变量重名的问题

1. 重名变量不因为重名而编译出错
  1.   1 interface IT { int x = 1; }
  2.   2 interface IT2 { int x = 2; }
  3.   3
  4.   4 public class MC implements IT, IT2 {
  5.   5     public static void main(String [] args) {
  6.   6     //  System.out.println(++x);
  7.   7     }
  8.   8 }
复制代码
这个sample程序里,由于注释了第6行,所以编译是不会出错的。
也就是说,如果你implements的接口中存在变量重名,其本身是不会引起问题的。

但如果把第6行的注释去掉,则出现错误。

2. 重名问题不因为静态或非静态而改变
  1.   1 interface IT { int x = 1; }
  2.   2 class CT { int x = 0; }
  3.   3
  4.   4 public class MC extends CT implements IT{
  5.   5     public static void main(String [] args) {
  6.   6     //  System.out.println(++x);
  7.   7     }
  8.   8 }
复制代码
上面的这个程序和第一个sample有相同的问题,虽然int x在class CT中是非静态变量,
按某种理解来说,在main中不会调用到CT.x。然而还是出现了重名,如果去掉Line-6的注释的话。
然而,Line-6有注释,也就是没有explicitly使用重名了的变量的话,问题则不存在。

[ 本帖最后由 key 于 2-7-2009 11:50 编辑 ]
回复  

使用道具 举报

2#
发表于 2-7-2009 11:31:43 | 只看该作者
这个应该就是龟定吧,语言对name space的范围和覆盖规则,怎么定就怎么来喽。
回复  

使用道具 举报

3#
发表于 2-7-2009 11:31:47 | 只看该作者
第一个好理解,不用就不冲突

第二个比较搞,觉得编译器应该再smart一点,报也也应该报 x in IT cannot be changed 之类的错误啊
回复  

使用道具 举报

4#
发表于 2-7-2009 11:32:33 | 只看该作者
interface 中定义的变量缺省是public static final 的。所以:
程序1中,必须要IT1.X 或IT2.X才能避免编译错误。同时不能++x,因为是它是final的。

程序2中的line 6。java compiler应该知道此x是static,应该取之interface而不是class的instance variable. 可能是javac定的bug。
回复  

使用道具 举报

5#
 楼主| 发表于 2-7-2009 11:37:32 | 只看该作者

问题二:Autoboxing带来的理解问题

J2se 5带来的Autoboxing的确很方便,但也带来了理解上的问题。赋值与函数传递我估计大家都不会有问题,但下面两个东西呢?

Sample 1: 相等还是不等?
  1. System.out.println(1 == new Integer(1));
复制代码
Sample 2: true or false?
  1. System.out.println(1 instanceof Integer);
复制代码
Sample 3: true of false?
  1. System.out.printf("%b", 0);
复制代码
Sample 4: 值是什么?
  1. int x = 1;
  2. System.out.println(x.intValue());
复制代码
Sample 5: 希望你没有疯掉
  1.   6         byte x = 1;
  2.   7         Byte bx = 1;
  3.   8         Byte bx2 = new Byte(1);
  4.   9
  5. 10         System.out.println(x + ", " + bx + ", " + bx2);
复制代码
Sample1编译没有问题,答案是true
Sample2不能通过编译。
Sample 3通过编译,运行没有问题,结果是true,原因是 0 被autoboxing成Integer(0),是一个对象,并且不是null,所以显示true
Sample 4编译出错,原因是int并没有方法
Sample 5的Line6, 7都没有问题,但8是错的。严格来说,Sample 5不是autoboxing问题,而是numeric auto-promotion问题。
回复  

使用道具 举报

6#
 楼主| 发表于 2-7-2009 11:40:01 | 只看该作者
原帖由 yuba 于 2-7-2009 10:31 发表
第一个好理解,不用就不冲突

第二个比较搞,觉得编译器应该再smart一点,报也也应该报 x in IT cannot be changed 之类的错误啊


第二个是名字冲突,即使我把x做为right value,一样的冲突
回复  

使用道具 举报

7#
 楼主| 发表于 2-7-2009 12:00:29 | 只看该作者

问题三:throw & catch exceptions

下面的程序:
Sample 1
  1. try {
  2.    throw new RuntimeException();
  3. }catch(InterruptedException ie){
  4.   System.out.println("InterruptedException");
  5. }catch(RuntimeException re){
  6.   System.out.println("RuntimeException");
  7. }
复制代码
Sample 2
  1. try {
  2.    throw new RuntimeException(new InterruptedException()));
  3. }catch(InterruptedException ie){
  4.   System.out.println("InterruptedException");
  5. }catch(RuntimeException re){
  6.   System.out.println("RuntimeException");
  7. }
复制代码
Sample 3:
  1. try {
  2.    throw new InterruptedException();
  3. }catch(InterruptedException ie){
  4.   System.out.println("InterruptedException");
  5. }catch(RuntimeException re){
  6.   System.out.println("RuntimeException");
  7. }
复制代码
Sample 4
  1.         try{
  2.             Throwable t = new InterruptedException();
  3.             throw t;
  4.         }catch(InterruptedException ie){
  5.             System.out.println("InterruptedException");
  6.         }
复制代码
Sample 5
  1. public void meth() throws InterruptedException {
  2.   throw new RuntimeException();
  3. }
复制代码
Sample 6
  1. class Super { public void test() throws InterruptedException {}}
  2. class Derived extends Super { public void test() throws RuntimeException {}}
复制代码
Sample 1出错,原因是InterruptedException是checked exception,没有throw则不能catch
Sample 2出错,原因相同
Sample 3正常
Sample 4出错,原因是必须catch Throwable
Sample 5正常
Sample 6正常,但如果把Derived和Super换个位置则出错。
回复  

使用道具 举报

8#
发表于 2-7-2009 12:09:43 | 只看该作者
sample 6出错是因为,子类override的函数不能声明父类没有声明的checked exception
所以父类没有InterruptedException的话,子类不能声明throws它
回复  

使用道具 举报

9#
 楼主| 发表于 2-7-2009 12:23:42 | 只看该作者

问题四:可怕的enum带来的问题

enum给我们带来了方便。从Effective Java可以看出设计者对于enum的期望。
然而enum的出现,也带来了我们对于Java语言理解上的问题。

内部定义的enum都是static的
无论enum的前面有没有static modifier,enum都是static。所以不能在method内部对enum进行定义。
(recall一下static nested class的约束),也不能在inner class内部定义(再recall一下static nested class的约束)

enum之所以是static的,可以从其语言特性进行理解:enum的数据元素必须是static的。由于inner class(虽然我们
不能把enum绝对等同于class,但类比是有用的)不能包含static元素,所以enum必须是static的。(这是个人观点)

enum不能new
这个用过enum的人都应该知道吧。

enum可以有constructor
不能new但可以有constructor,这个很让初用enum的同学吃惊。但这是事实。
enum的constructor的参数由其数据元素带进来。

enum可以有方法
这个就不说了。

enum在值声明时必须用“类”名打头,在switch的时候不可以用“类”名打头,必须直接使用数据元素
这个龟定让我很想杀人。
  1. enum XX { A, B, C };

  2. public static void main(String [] args){
  3.   XX a = A;

  4.   switch(a) {
  5.      case XX.A: System.out.println("A"); break;
  6.   }
  7. }
复制代码
上面的程序两个地方都出错。没有道理,这是龟定,上头的龟定。

名称冲突?
  1.   1 public class EnumTest {
  2.   2     public static enum AA { hello, world };
  3.   3     public static enum BB { hello, world };
  4.   4
  5.   5     public static void main(String [] args)
  6.   6     {
  7.   7         AA x;
  8.   8         BB y;
  9.   9
  10. 10         AA x1, y1;
  11. 11
  12. 12         x1 = Enum.valueOf(AA.class, "hello");
  13. 13         y1 = Enum.valueOf(AA.class, "world");
  14. 14
  15. 15         switch(x1) {
  16. 16             case hello:
  17. 17             case world:
  18. 18         }
  19. 19     }
  20. 20 }
复制代码
上面的程序一切正常

名称冲突?
  1.   1 public class EnumTest {
  2.   2     public static enum AA { hello, world };
  3.   3
  4.   4     public static void main(String [] args)
  5.   5     {
  6.   6         AA x;
  7.   7
  8.   8         AA x1;
  9.   9
  10. 10         x1 = Enum.valueOf(AA.class, "hello");
  11. 11
  12. 12         int hello = 100;
  13. 13         int world = 200;
  14. 14
  15. 15         switch(x1) {
  16. 16             case hello:
  17. 17                 System.out.println(hello); break;
  18. 18             case world:
  19. 19                 System.out.println(world); break;
  20. 20         }
  21. 21     }
  22. 22 }
复制代码
答案是:上面的程序一切正常,运行结果是
100
回复  

使用道具 举报

10#
 楼主| 发表于 2-7-2009 12:42:59 | 只看该作者

问题五:当autoboxing遇上Varargs时

试分析下面的程序的编译结果
Sample 1
  1.   1 public class VarArgAutoboxingTest {
  2.   2     public void test(Integer ... ks){ }
  3.   3     public void test(int ... ks){ }
  4.   4
  5.   5     public void test() {
  6.   6         test(1);
  7.   7     }
  8.   8 }
复制代码
Sample 2
  1.   1 public class VarArgAutoboxingTest {
  2.   2     public void test(Integer ... ks){ }
  3.   3     public void test(int ... ks){ }
  4.   4     public void test(int x) { }
  5.   5
  6.   6     public void test() {
  7.   7         test(1);
  8.   8     }
  9.   9 }
复制代码
Sample 3
  1.   1 public class VarArgAutoboxingTest {
  2.   2     public void test(Integer ... ks){ }
  3.   3     public void test(int ... ks){ }
  4.   4     public void test(Integer x) { }
  5.   5
  6.   6     public void test() {
  7.   7         test(1);
  8.   8     }
  9.   9 }
复制代码
Sample 4
  1.   1 public class VarArgAutoboxingTest {
  2.   2     public void test(int ... ks){ }
  3.   3     public void test(int[] ks){}
  4.   4
  5.   5     public void test() {
  6.   6         test(1);
  7.   7     }
  8.   8 }
复制代码
Sample 5
  1.   1 public class VarArgAutoboxingTest {
  2.   2     public void test(int ... ks){ }
  3.   3
  4.   4     class X extends VarArgAutoboxingTest {
  5.   5         public int test(int [] ks){ return 0; }
  6.   6     }
  7.   7
  8.   8     public void test() {
  9.   9         test(1);
  10. 10     }
  11. 11 }
复制代码
Sample 6
  1.   1 public class VarArgAutoboxingTest {
  2.   2     public void test(int[] ks){ }
  3.   3
  4.   4     public void test() {
  5.   5         test(1);
  6.   6     }
  7.   7 }
复制代码
Sample 1: 出错,原因,autoboxing之后不知道对应哪个
Sample 2: 没问题
Sample 3: 没问题
Sample 4: 出错,原因是int...与int []不能同时出现
Sample 5: 出错,原因是int[]被看成和int...同一个signature,这里被编译器认为是overridden
Sample 6: 出错,原因是 1 不能做为int[]的传入参数。。。如果没有Sample4-5的判断,我估计绝大部分人在Sample 6问题上不会犹豫。对比4-6,你是不是有点想杀了jcp那群混蛋呢?
回复  

使用道具 举报

11#
发表于 2-7-2009 13:59:03 | 只看该作者
原帖由 key 于 2-7-2009 11:23 发表
enum给我们带来了方便。从Effective Java可以看出设计者对于enum的期望。
然而enum的出现,也带来了我们对于Java语言理解上的问题。

内部定义的enum都是static的
无论enum的前面有没有static modifier,enum都是 ...



我觉得可以这样来理解enum,应该就不难了。
1。enum{}里面的A跟外面的“A”根本上是不同的,是两个冬冬,即使它们表面上value相等。
2。从上面的解释就不难理解你的问题了,
  - 定义 XX a = A;  如果A是从别的地方来的根本没有意义,必须是XX里面的A。所以必须XX.A
  - 在switch里面,一样,你去比较别的地方来的A是没有意义的,so the compiler defines all the "case" must come from the XX, for your convenience, you do not need to qualify the A (XX.A) within the switch scope.
回复  

使用道具 举报

12#
发表于 2-7-2009 14:05:24 | 只看该作者
你研究的很深,我不懂。
回复  

使用道具 举报

13#
 楼主| 发表于 2-7-2009 14:15:09 | 只看该作者
原帖由 felix100 于 2-7-2009 12:59 发表



我觉得可以这样来理解enum,应该就不难了。
1。enum{}里面的A跟外面的“A”根本上是不同的,是两个冬冬,即使它们表面上value相等。
2。从上面的解释就不难理解你的问题了,
  - 定义 XX a = A;  如果A是从别 ...


我觉得"for your convenience"的理由太弱了(不是说你,而是说jsr/jls)。
另外,不知道你有没有注意到,EnumTest.java编译出来后,有一个EnumTest$1.class,有同学能帮我解释一下这个$1吗?
回复  

使用道具 举报

14#
发表于 2-7-2009 16:08:58 | 只看该作者
Enumtest.class
Enumtest$AA.class 这不是那个内部类吗?enum和class都是内部类吧。
回复  

使用道具 举报

15#
发表于 2-7-2009 20:02:47 | 只看该作者
I hope the following concepts could help for java enum.

1. enum is a class which implicitly extends the java.lang.enum.

2. A is a static instance of enum type XX.

3. The actual value of A is "A", but A is a complex object, A is not a constant (public static String).

4. A instance of enum type XX can only compare with instances of XX, comparing with other type is not making sense regardless of the value of the enum, eg. Comparing "XX.A" with "XX2.A". (switch statement is a kind of comparison).

5. Within the switch block, the qualifier "XX" is not necessary because instance "a" can only compare with any XX instances which are defined in the enum {} block. If you try "case D" or "case XX2.A" (already define a XX2 enum), both are illegal. I guess this is a hard rule set in the compiler.

I think the compiler try to reduce the rumtime error for programmers.

6. Imagine the implementation of enum XX is like the following, but of course not fully equivalent

   public class XX entends java.lang.enum {
        public static final XX A = new XX("A");
        public static final XX B = new XX("B");
           public static final XX C = new XX("C");

        // an array to support positioning
       
        private XX() {};
        private XX(String s){....};

        .......
   }

7. In "XX a = XX.A;", the XX qualifier is necessary base on the 6. because you are using a "public static final" from XX class.

8. You can consider the switch block is a kind of special case or a bonus the compiler offers. (no need the XX qualifier).

[ 本帖最后由 felix100 于 2-7-2009 19:06 编辑 ]
回复  

使用道具 举报

16#
 楼主| 发表于 2-7-2009 22:59:10 | 只看该作者
原帖由 laysman 于 2-7-2009 15:08 发表
Enumtest.class
Enumtest$AA.class 这不是那个内部类吗?enum和class都是内部类吧。



EnumTest$1.class,不是EnumTest$AA.class,是anonymous inner class来的
回复  

使用道具 举报

17#
发表于 2-7-2009 23:03:44 | 只看该作者
Java的enum看起来好变态啊,是谁说C++更晦涩难懂来着
回复  

使用道具 举报

18#
 楼主| 发表于 2-7-2009 23:07:35 | 只看该作者
原帖由 felix100 于 2-7-2009 19:02 发表
I hope the following concepts could help for java enum.

1. enum is a class which implicitly extends the java.lang.enum.

2. A is a static instance of enum type XX.

3. The actual value of A is  ...


其实我不是说这些东西我不能理解,或者理解起来有多难。
但从某个角度来说,这些理解,很大程度上是建立在“死背Java规则”之上,
而某些规则和另一些规则又存在矛盾,你必须记住这些互相矛盾的规则,
比如int ... 与 int [],这个就很明显了。

至于enum,其起源应该是Effective Java中提到的Enum的改进发展起来。
我很早之前读过这个书,大概明白其思路。
然而,作为一种新的数据类型,设计得还不是太完备。
比如switch(a) { cast A:break; case B: break; }之类的
有一定的创新,但作为从其他语言过度过来,或者刚刚接触enum类据类型的
程序员,要完全想透还是不容易。
回复  

使用道具 举报

19#
 楼主| 发表于 2-7-2009 23:12:15 | 只看该作者
原帖由 coredump 于 2-7-2009 22:03 发表
Java的enum看起来好变态啊,是谁说C++更晦涩难懂来着


不让你看我写的垃圾文章,给你十天时间看Java 1.5新语法,十天后给你一份题,如果你只凭教科书能拿50%的分数,我马上崇拜你,哈哈
这个“你”不是特指core同学,而是所有之前没有认真接触J2se 1.5新语法的同学。

或者你看看我写的那个Collection,其中有一部分说到Java自己crack自己的语法。我觉得这种做法很不厚道。

比较起来C++是一个严谨的计算机语言体系,Java,至少Java 5开始,是一个实用体系。
回复  

使用道具 举报

20#
 楼主| 发表于 2-7-2009 23:31:23 | 只看该作者

问题六:protected能被Derived类直接使用?

了解Java语言的人都知道,access modifier的排序是:
public > protected > default > private

protected描述的对象能在子类中被使用。看下面的程序:
  1. class BX {
  2.     protected void test() {
  3.         System.out.println("Hello");
  4.     }
  5. }

  6. public class B extends BX{
  7.     public static void main(String [] args){
  8.         BX bx = new BX();
  9.         bx.test();
  10.     }
  11. }
复制代码
这个程序没有问题。再请看,如果把程序分成两个文件,一个是BX.java,另一个是B.java
BX.java
  1.   1 package bx;
  2.   2
  3.   3 public class BX {
  4.   4     protected void test() {
  5.   5         System.out.println("hello");
  6.   6     }
  7.   7 }
复制代码
B.java
  1. 1 package b;
  2. 2
  3. 3 import bx.*;
  4. 4
  5. 5 public class B extends BX {
  6. 6     public static void main(String [] args){
  7. 7         BX bx = new BX();
  8. 8         bx.test();
  9. 9     }
  10. 10 }
复制代码
上面的程序编译是出错的。问题出在Line-8。它和前面的程序的最大不同,
是两个类处在不同的package下。这样一来,虽然B extends BX,但
在static方法下还是不能用protected描述的方法。

事实上,用protected描述的数据成员也不能用,static nested class,
如果放在非静态成员函数中,如果使用BX bx = new BX();这样的形式生成对象再
访问其protected成员,还是不行,但如果用this或直接访问其成员还是可以的,
只是nested class还是不行,下面的代码放在class B中:
  1.   6     public void dd() {
  2.   7         test();
  3.   8         x = 10;
  4.   9         PBX pbx = new PBX();
  5. 10     }
复制代码
第9行还是出错,其他是正确的。
回复  

使用道具 举报

您需要登录后才可以回帖 登录 | FreeOZ用户注册

本版积分规则

小黑屋|手机版|Archiver|FreeOZ论坛

GMT+11, 6-3-2025 05:24 , Processed in 0.053672 second(s), 35 queries , Gzip On, Redis On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表