Java开发网 Java开发网
注册 | 登录 | 帮助 | 搜索 | 排行榜 | 发帖统计  

您没有登录

» Java开发网 » Java SE 综合讨论区  

按打印兼容模式打印这个话题 打印话题    把这个话题寄给朋友 寄给朋友    该主题的所有更新都将Email到你的邮箱 订阅主题
flat modethreaded modego to previous topicgo to next topicgo to back
作者 [转贴]对初学者很有用的文章
jinco



发贴: 0
积分: 0
于 2004-03-25 09:41 user profilesend a private message to usersearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
经常听到这样的对话:"你会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;}
^

但是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编程的人常常遇到的一个问题。以下这段代码,编译会出现错误。
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)

......未完,待续

呵呵,同志们,这是一位高手的力作。可不是本人的原创。


why edited on 2004-03-27 20:38


话题树型展开
人气 标题 作者 字数 发贴时间
4187 [转贴]对初学者很有用的文章 jinco 8168 2004-03-25 09:41
4065 Re:[转贴]对初学者很有用的文章 caoyi 7 2004-03-27 17:44
3537 Re:[转贴]对初学者很有用的文章 PrimeJava 4 2004-03-27 20:28
3580 Re:[转贴]对初学者很有用的文章 bluepure 328 2004-03-29 11:05
3331 Re:[转贴]对初学者很有用的文章 hohaischooldays 378 2004-03-30 16:00
3276 Re:[转贴]对初学者很有用的文章 fangquan 29 2004-04-06 15:27

flat modethreaded modego to previous topicgo to next topicgo to back
  已读帖子
  新的帖子
  被删除的帖子
Jump to the top of page

   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2021 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号-1
客服电话 18559299278    客服信箱 714923@qq.com    客服QQ 714923