| |
经常听到这样的对话:"你会weblogic, oracle, xml, kylix, 等等吗?不会?你好差。这么先进的技术怎么能不会?"往往我们也能在看到很多简历,自称精通某某,某某某技术, 乍一看下来,简直是绝顶高手,精通了所有流行的先进技术。但是我常常想,就算是不吃饭不睡觉也不可能在短短时间里精通那么多范围极广,博大精深的技术啊。而我自己,却常常在实际工作中遇到一些问题,让我不得不想起基础知识的重要性。我在这里不是要打击大家学习先进技术的热情,而是为了强调一下基础知识的重要。比如,有很多的java程序员在使用jbuilder, weblogic, websphere, silverstream,写普通的java程序或者写j2ee, corba结构的程序。在但是,在掌握先进技术的同时,我们也要注重一下基础的修炼,免得不断出现本可以避免的错误。我将陆续写一系列的文章,关于java编程常见问题。这些,都是我在日常工作中积累下来的一些笔记,不成体系(我尽量将它们按照范围不同组织一下), 严格的说,不能叫做文章吧。大家随便看看。呵呵。 (内容) 先来看一个常见的错误。 public boolean testans(string ans, int n){ boolean rslt; if( ans.equalsignorecase("yes") && n > 5) rslt = true; return rslt; } 程序逻辑方面并没有问题,但是编译的时候会出现错误提示 : variable rslt might not have been initialized return rslt; ^ 这是因为,当if条件为false的时候,rslt可能会没有被赋予初值,而return的时候则会出错。java编译器很聪明的检查了这一错误并在编译的时候给予了提示。这需要你在声明rslt的时候或者在返回rslt之前给它赋值。 比如: public boolean testans(string ans, int n){ boolean rslt = false; if( ans.equalsignorecase("yes") && n > 5) rslt = true; return rslt; } 相关的问题还有: public boolean testans(string ans, int n){ boolean rslt = true; while(false){rslt = false;} for(;false;){rslt = false;} if(false) {rslt = false;} return rslt; } 则java编译器会提示 unreachable statement while(false){rslt = false;} ^ unreachable statement for(;false;){rslt = false;}[/code] ^ 但是if(false)这一段则没有错误提示,编译通过。这也是要注意的一点。 第二个例子:class object 中有一个方法equals() public boolean object.equals(object) 它检查object reference是否相同,也就是说是否指向同一个对象。如果是,则返回true, 否则返回false. 而每一个继承class object的类都会override这个方法。比如在long, integer等class中,equals比较该ojbect是否相应的是long, integer类型。如果类型相同,值比较所包裹的值是否相同。如果相同,则返回true, 否则返回false.要注意的是,返回false并不说明所包裹的值不相同,也可能是类型不同。比如下面代码:
long l = new long(7); integer j = new integer(7); if(l.equals(j)) system.out.println("equal"); else system.out.println("not equal"); 编译成功,但是输出为not equal, 这就是因为类型不同, 不是同为integer或者 同为long.
再看一下使用instanceof要注意的问题. instanceof是判断一个对象的引用(reference) 是否某一类型。比如 integer i = new integer(0); system.out.println( i instanceof integer); 返回为true,因为i是一个integer的对象的引用。 integer i = new integer(0); system.out.println( i instanceof long); 则返回为false, 因为i不是一个long的对象的引用。 但是, integer i = null; system.out.println( i instanceof integer); 返回值为false. 这是因为i的值为null, null不是任何对象的引用。这是需要注意的。
第四个问题,是在邮件列表,news groups中提到次数比较多的一个问题,也是很多初学java编程的人常常遇到的一个问题。以下这段代码,编译会出现错误。 [code]byte x = 100; switch(x) { case 100: case 200: case 300: } 编译器提示 a1.java:6: possible loss of precision found : int required: byte case 200: ^ a1.java:7: possible loss of precision found : int required: byte case 300: ^ 2 errors
因为x为byte类型,但是300超过了byte类型的最大值127, 所以出现了错误。这段代码相当于是 if(x == 100) ... else if(x == 200) ... else if(x == 300) ... 这样子看错误原因就比较明显了。 类似的我们还有这样的代码: short x = 200; switch(x) { case 70000: case 10: case 1: } 编译也会出错,提示case 7000:这一行类型不匹配。
关于primitive类型的赋值问题,还有以下两个需要注意的问题: 这段代码 byte b = 1; short s = b; 不会出错。因为,byte为8 bits, 而short为16 bits,将byte类型的数值赋予给short类型的变量不会引起数值精度问题。但是 short s = 1; byte b = s; 则不能正确编译。因为这样赋值可能导致s所含数值的高8 bit被舍弃,因而数值不正确。这样需要我们在写程序的时候指定 byte b = (byte)s;
以便通知编译器,"嘿, 如果有精度的损失,那是我自愿的,你不必担心!"这样编译就不会出错了。同样的, short s = 1; byte b = 1; b = b + s; 不能正确编译。需要我们在写程序的时候指定 b = (byte)(b + s); 这些错误很多人现在都会避免了。但是这样子的代码是否会编译错误呢? short s = 1; byte b = 1; b += s; 照以上的解释,你一定认为这段代码不能正确编译。但是如果你实际编译一下则会发现,它编译通过了!为什么呢?这是因为, +=, -=, *=, /=, %=这类操作符号比较特殊,对于编译器来说,他们相当于 b += s; -------> b = (byte)(b + s); 呵呵,有意思吧?
inner class的问题,更多的人感到迷惑。 比如,inner class是否能够有static的变量? 一般的说法是,static inner class能够有static的变量,而non-static inner class则不能有static的变量。但是,如果这样: class outer { class inner { static final int i = 999; } } 编译通过。而你不能这么写: static final int i; 就是说,一定要在声明它的时候赋予初值, 因为i是final的,它的值不能再被改变。 关于inner class, 还有一个代码也很能说明一些问题。
public class testclass { public testclass(int i) { }
public void m1() { testclass al = new testclass(10) { public void actionperformed(actionevent e) { } }; } } 这说明了一下四个问题: 1. 类中的方法也可以有inner classes.(但是他们不能有static inner classes). 2. inner class 能够继承包含它的外部class. 3. 匿名 inner class 可以继承一个 class 或者 implements 一个 interface。 4. 匿名 inner class 可以有初始化参数。(如果这个class继承的基类有相应的 constructor 的话。)
再来看看动态联编 ( dyanamic link )的问题。 考虑这段代码:
class base{ public void whoami() { system.out.println("base"); } } class derived extends base{ public void whoami() { system.out.println("derived"); } }
class test{ public static void main(string[] args) { base b = new base(); derived d = new derived(); b.whoami(); d.whoami(); base b2 = d; b2.whoami(); } } 当然大家很清楚,b.whoami()打印base, 而d.whoami()打印derived.但是,b2.whoami()打印什么呢?也就是说,b2.whoami()将调用那一个方法呢?是基类的还是派生类的?运行以后看到,调用的是派生类的方法。这是因为java是在运行过程中采用了动态联编的方法,在运行时刻来决定该reference指向的什么类的对象,从而决定调用哪一个类的方法,而不是根据reference的类型来决定调用哪一个类的方法。从而可以使我们通过这一机制来完成多样化的程序。打印的结果将是derived。 再来看另外一个类似的例子: class base{ int i = 100; public void print() { system.out.println(i); } } class derived extends base{ int i = 999; public void print() { system.out.println(i); } }
class test{ public static void main(string[] args) { base b = new base();
derived d = new derived(); b.print(); system.out.print(b.i); d.print(); system.out.print(d.i); base b2 = d; b2.print(); system.out.print(b2.i); } } 很简单的,前面的调用: b.print(); 打印100 system.out.print(b.i); 打印100 d.print(); 打印999 system.out.print(d.i); 打印999. 但是 system.out.print(b2.i); 打印什么呢?结果是100, 而不是999。这是因为,在动态联编过程中,只是函数参与了,而对于类中的变量,则还是以静态的方式编译的,就是说,编译器是凭借reference的类型来决定类变量的。b2是base类型的reference, 所以b2.i 是基类中的变量的值。
关于动态联编,还有2个要注意的问题: class base{ } class derived extends base{ public void print() { system.out.println("derived"); } }
class test{ public static void main(string[] args) { base b = new base(); derived d = new derived(); d.print(); base b2 = d; b2.print();//出错 } } 为什么呢?因为在编译过程中,编译器会检查基类中是否有print()方法,如果没有,则会报错。注意:动态联编出现在运行过程中,而不是编译过程中。
class base{ int i = 100; public void print() { system.out.println(i); } } class derived extends base{ int i = 999; public void print() { system.out.println(i); } }
class test{ public static void main(string[] args) { base b = new base(); derived d = new derived(); b.print(); system.out.print(b.i); d.print(); system.out.print(d.i); base b2 = d; b2.print(); system.out.print(b2.i); derived d2 = b;//出错 d2.print();//出错 system.out.print(d2.i);//出错 } } 这是因为,在编译过程中,derived类型的reference可以赋给base类型的reference. 而base类型的reference则不可以赋给derived类型的reference.如果要这么做, 则需要在赋值的过程中指定 derived d2 = (derived)b; 编译才能通过。这和primitive types的赋值是一样的道理。而编译完成后在运行时刻还需要做进一步的检验,如果类型不能匹配,则会抛出 classcastexception. exception in thread "main" java.lang.classcastexception: base at test.main(a2.java:12)
......未完,待续
|
|