Java常见陷阱(一)

 陷阱一:什么时候“被覆盖的”方法并非真的被覆盖

class Super

{

    static String greeting(){

             return "Super Say Hello";

    }

    String name(){

             return "My Name is Super";

    }

}

class Sub extends Super

{

    static String greeting(){

             return "Sub Say Hello World";

    }

    String name(){

             return "My Name is Sub";

    }

}

public class  Test

{

    public static void main(String[] args)

    {

             Super s1=new Sub();

             System.out.println("Super s1=new Sub():"+s1.greeting()+"\t"+s1.name());

             Super s2=new Super();

             System.out.println("Super s2=new Super()"+s2.greeting()+"\t"+s2.name());

             Sub s3=new Sub();

             System.out.println("Sub s3=new Sub():"+s3.greeting()+"\t"+s3.name());

             //Sub s4=new Super();//错误

             //System.out.println("Sub s4=new Super():"+s4.greeting()+"\t"+s4.name());

    }

}

结果:

Super s1=new Sub():Super Say Hello,     My Name is Sub

Super s2=new Super()Super Say Hello,    My Name is Super

Sub s3=new Sub():Sub Say Hello World,   My Name is Sub

         记住一个原则:“实例方法被覆盖,静态方法被隐藏”

 

陷阱二:String.equals()方法与“==”运算符的用法比较

    class Test

{

    public static void main(String[] args)

    {

             String s1="language";

             String s2=new String("language");

             String s3="langu"+"age";

             System.out.println("s1.equals(s2):"+s1.equals(s2));

             System.out.println("s1.equals(s3):"+s1.equals(s3));

             System.out.println("s2.equals(s3):"+s2.equals(s3));

             System.out.println("s1==s2:"+(s1==s2));

             System.out.println("s1==s3:"+(s1==s3));

             System.out.println("s2==s3:"+(s2==s3));

    }

}

结果:

s1.equals(s2):true

s1.equals(s3):true

s2.equals(s3):true

s1==s2:false

s1==s3:true

s2==s3:false

         String.equals()方法比较的是字符串的内容。使用equals()方法,会对字符串中的所有字符,一个接一个地进行比较,如果完全向导,那么就会返回true。运算符“==”比较的是String实例的引用。当字符串由多个字符串常量连接而成时。如s3,他同样在编译期就被解析称为一个字符串常量。Java会设置两个变量的应用为同一个常量的引用。在常量池中,Java会跟踪所有的字符串常量。

         常量池指的是在编译期被确定,并保存在已编译的.class文件中的一些数据。它包含了有关于方法、类、接口等等,当然还有字符串常量的信息。当JVM装载了这个.class文件,变量s1s3被解析,JVM执行了一项名为常量池解析的操作。该项操作针对字符串的处理过程,百科一下步骤:

Ø  如果另一个常量池入口被标记为CONSTANT_String,并且指出,同样的Unicode字符序列已经被解析,那么这项操作结果就是之前的常量池入口创建的String实例的引用。

Ø  否则,如果intern()方法已经被这个常量池描述的一个包含同样Unicode字符序列的String实例调用过,那么这项操作的结果就是那个相同String实例的引用。

Ø  否则,一个新的String实例会被创建。它包含了CONSTANT_String入口描述的Unicode字符序列;这个String实例就是该项操作的结果。

 

import java.io.*;

class  StringTest

{

        public static void main(String[] args) throws Exception

        {

                 String sFile ="out.txt";

                 String s1=readerStringFile(sFile);

                 String s2=readerStringFile(sFile);

                 System.out.println("s1==s2:"+(s1==s2));

                 System.out.println("s1.equals(s2):"+s1.equals(s2));

                 s1.intern();

                 s2.intern();

                 System.out.println("s1==s2:"+(s1==s2));

                 System.out.println("s1==s2.intern():"+(s1==s2.intern()));

        }

        public static String readerStringFile(String sFile)throws Exception{

                 File f=new File("D:\\"+sFile);

                 FileInputStream iput=new FileInputStream(f);

                 byte b[]=new byte[(int)f.length()];//将所有内容都读入此数组之中

                 for(int i=0;i<f.length();i++)

                 {

                          b[i]=(byte)iput.read();

                 }

                 iput.close();

                 String ss=new String(b);

                 return ss;

        }

}

结果:

s1==s2:false

s1.equals(s2):true

s1==s2:false

s1==s2.intern():true

         总的来说,在执行等式比较时,应该实战使用String.equals()方法而不是“==”运算符语句String.equals()String.intern()得到的结果是一致的。

 

陷阱三:Java是强制类型的语言

         平时看到的诶呀标识类型的数值如1,2,765等等均可以看做int类型,若标识了1l34d54f等等就不能看做int类型而是看成它们特有的类型。

        

陷阱四:那是构造函数吗?

         构造函数是没有返回类型的,而且构造函数是不能用staticfinalabstract等关键字修饰。

         每一个类都有构造函数,只是有的是隐性的,我们看到的是显式的构造函数。构造函数是可以多态的。构造函数也使可以用privata修饰的。

 

陷阱四:不能访问被覆盖的方法

         可以通过包含关键字super的方法调用表达式来访问被覆盖的方法。注意:尝试用全局名或强制转换为分类型,以求访问一个被覆盖的,是无效的。

         无法访问中被子类覆盖的方法原则,仅仅适用于实例方法,也就是非静态方法。即使父类中的静态方法被子类“覆盖”(并非真正的覆盖,而是子类也具有与父类中同名的方法)了,它们仍然能被被访问。强制转换为父类的类型就能达到这个目的。

 

陷阱五:避免落入“隐藏变量成员”的陷阱

public class  WWW

{

         public  String s="Hello Java";

         public  void  wMM(){

                  System.out.println("***"+s+”***”);

         }

         public static void main(String[] args)

         {

                   WWW w=new WWW();

                   w.wMM();

         }

}

public class  WWW

{

         public static String s="Hello Java";

         public static void  wMM(){

                  System.out.println("***"+s+"***");

         }

         public void mWW(){

                   wMM();

         }

         public static void main(String[] args)

         {

                   wMM();

                   WWW w=new WWW();

                   w.mWW();

         }

}

         结果1          ***Hello Java***

         结果2          ***Hello Java***

***Hello Java***

         非静态方法可以调用非静态方法和静态方法,但是静态方法不可调用非静态方法。

 

不同类型的Java变量

         Java一共有6种变量类型:

Ø  类变量:包括在类中声明的静态数据成员以及在接口体中声明的静态或非静态数据成员。

Ø  实例变量:在类中声明的非静态变量。

Ø  方法参数:是用来传入一个方法体的。

Ø  构造函数参数:是用来传入一个构造函数体的。

Ø  异常处理器参数:用来传入一个try语句的catch块中的。

Ø  局部变量:是在一个代码块中或一个for语句中声明的变量。

注意:变量成员是指类变量和实例变量。

 

变量的作用范围

         变量的作用范围指的其实是一块代码块。

         实例变量和类变量的作用范围就是它们的类或接口的整体。

         方法参数的作用范围就是整个方法体。

         构造函数参数作用范围就是整个构造函数体。

         异常处理器参数作用范围是catch语句。

         局部变量的作用范围是它被声明的所在代码块。

 

何种变量能被隐藏

         实例变量和类变量能被隐藏。局部变量和各种参数永远不会被隐藏的。假如用一个同名的局部变量去隐藏一个参数,编译器将会报错。

   public class Test

{

   public static void mWW(String s){

            String s="Hello Java"; //错误

   }

   public static void main(String[] args)

   {

            mWW("******");

   }

}

                   结果:Test.java:4: 错误已在mWW(String)中定义s

                         String s="Hello Java";

 

实例变量和类变量如何被隐藏

         同名的局部变量或者同名的参数,可以隐藏掉变量成员的一部分作用范围,变量成员也能被子类的同名变量成员隐藏。与一个变量成员同名的局部变量,将在其作用范围内,隐藏掉这个成员变量。与一个变量成员同名的方法参数,将在方法体中隐藏掉这个变量成员。与一个变量成员同名的构造函数参数,将在构造函数体中隐藏掉这个变量成员。以此类推,与一个变量成员同名的异常处理器参数,将在catch语句块中隐藏掉这个变量成员。

 

如何访问被隐藏的变量

         通过全局名,可以访问大多数的变量成员。关键字“this”可以限定一个正被局部变量隐藏的实例变量。关键字“super”可以限定一个正被子类隐藏的实例变量。类变量特可以被限定,只要在该类的名字与该类变量的名字之间加上“.”即可。

 

变量隐藏与方法隐藏的区别

         它们之间最重要的区别是:一个类的实例,无法通过使用全局变量,或者强制转换自己为其父类的类型,以访问其父类中被覆盖的方法。

         数据覆盖与方法覆盖的另外一个不同就是静态方法不能覆盖父类的实例方法,而静态变量却可以隐藏父类的一个同名实例变量。相同地,实例方法也不能覆盖父类的同名方法静态方法,而变量成员却可以隐藏父类同名变量成员,不论父类的这个同名变量成员是类还是实例变量。


本文转自 梦朝思夕 51CTO博客,原文链接:http://blog.51cto.com/qiangmzsx/852877

上一篇:我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。


下一篇:10个你可能不知道的JavaScript小技巧