问题一:我声明了什么! 



1. String s = "Hello world!";


许多人都做过这样的事情,但是,我们到底声明了什么?回答通常是:一个String,内容是"Hello world!"。这样模糊的回答通常是概念不清的根源。如果要准确的回答,一半的人大概会回答错误。 


这个语句声明的是一个指向对象的引用,名为“s”,可以指向类型为String的任何对象,目前指向"Hello world!"这个String类型的对象。这就是真正发生的事情。我们并没有声明一个String对象,我们只是声明了一个只能指向String对象的引用变量。所以,如果在刚才那句语句后面,如果再运行一句: 



1. String string = s;



我们是声明了另外一个只能指向String对象的引用,名为string,并没有第二个对象产生,string还是指向原来那个对象,也就是,和s指向同一个对象。 



问题二:"=="和equals方法究竟有什么区别?

 



==操作符专门用来比较变量的值是否相等。 


1. int a=10;   
2. int b=10;



则a==b将是

true 。 



但: 



    1. String a=new String("foo");   
    2. String b=new String("foo");



    则a==b将返回

    false 。 



    对象变量其实是一个引用,它们的值是指向对象所在的内存地址,而不是对象本身。a和b都使用了new操作符,意味着将在内存中产生两个内容为"foo"的字符串,既然是“两个”,它们自然位于不同的内存地址。a和b的值其实是两个不同的内存地址的值,所以使用"=="操作符,结果会是false。虽然,a和b所指的对象,它们的内容都是"foo",应该是“相等”,但是==操作符并不涉及到对象内容的比较。 



    对象内容的比较应使用Object对象的equals方法。 


    1. boolean
    2. return this==o;   
    3. }


    Object对象默认使用了==操作符。所以如果你自创的类没有覆盖equals方法,那你的类使用equals和使用==会得到同样的结果。同样也可以看出,Object的equals方法没有达到equals方法应该达到的目标:比较两个对象内容是否相等。因为答案应该由类的创建者决定,所以Object把这个任务留给了类的创建者。 



    例如: 



    1. Class MyClass{   
    2. private
    3. ...   
    4. boolean
    5. return true;  
    6. }   
    7. }



    MyClass类中的equals方法覆盖了父类object类的equals方法。这个实现会导致无论MyClass类的实例内容如何,它们之间的比较永远返回true。 


    所以当使用equals方法判断对象的内容是否相等,请不要想当然。因为可能你认为相等,而这个类的创建者不这样认为,而类的equals方法的实现是由他掌握的。 



    问题三:String和StringBuffer的区别

     



    String类被设计成

    不可变(immutable)类 ,所以它的所有对象都是不可变对象。请看下列代码: 



    1. String s = "Hello";   
    2. s = s + " world!";



    s所指向的对象是否改变了呢?在这段代码中,s原先指向一个String对象,内容是"Hello",然后我们对s进行了+操作,那么

    s所指向的那个对象是否发生了改变呢? 答案是没有。这时, s不指向原来那个对象了,而指向了另一个String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了 。 



    所以,如果经常对字符串进行各种各样的修改,使用String类来代表字符串的话会引起很大的内存开销。因为String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。 



    同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都new一个String。例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样做: 




      1. public class
      2. private
      3. ...   
      4. public
      5. s = "Initial Value";   
      6. }   
      7. ...   
      8. }




      而非 



      1. s = new String("Initial Value");

      后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以

      对于内容相同的字符串,只要一个String对象来表示就可以了 。也就说,多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象。 


      上面的结论还基于这样一个事实:

      对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。  


      至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即StringBuffer。 



      问题四:逻辑与/或 和 短路与/或的区别

       



      逻辑操作符:&(与运算),^(异或运算),|(或运算)。 


      短路逻辑操作符:&&(并且),||(或者). 


      短路与/或运算符和逻辑与/或一样可以实现逻辑运算,但是此时有一个重要的区别:

      用逻辑与/或运算时,不管操作符两边的条件表达式成不成立,它都要进行运算判断,而短路与/或运算不一样了,如果通过左侧的操作数就可以进行它们需要的判断,那么它就不会再计算右侧的操作数了 ,请看下面的例子: 



        1. ......  
        2. double value=0;  
        3. if(value!=0 && 1/value<1000){  
        4. System.out.println("The value is not too small.");  
        5. }  
        6. else{  
        7. System.out.println("The value is too small.");  
        8. }  
        9. ......



        运行结果: 


        The value is too small.

         


        结果分析: 


        照理说应会出现除数为0的错误,但由于条件逻辑操作符是短路操作符,显然,value!=0条件不成立,立即就可作出判断应执行else后的语句,所以它就不再会运算判断1/value<1000了.如果不懂请再看一例: 



        1. ......  
        2. double int1=0,int2=1,int3=1;  
        3. if(int1!=0 & (int2=2)==1){  
        4. }  
        5. System.out.println("int2="+int2);  
        6. if(int1!=0 && (int3=2)==1){  
        7. }  
        8. System.out.println("int3="+int3);  
        9. ......




        运行结果: 


        int2=2.0 

        int3=1.0

         



        问题五:实例变量与类变量(静态变量)

         



        可以通过两种方法在类中存储数据──作为实例变量和类变量.

        实例变量是特定于对象的 ,如果你有两个对象(即一个类的两个实例),每一个对象中的实例变量独立于另一个对象中的实例变量的;另一方面,两个对象的类变量均指向相同的数据,并因此面保存相同的值,换句话说, 类变量被类中的所有对象共享 ,类变量在声明时比实例变量多一个static. 


        1. class
        2. public int intdata=0;//intdata在这儿是实例变量
        3. }  
        4. public class
        5. public static void
        6. Data a,b;  
        7. a=new
        8. b=new
        9. a.intdata=1;  
        10. System.out.println("b.indata="+b.intdata);  
        11. }  
        12. }


        运行结果: 


        b.intdata=0

         


        结果分析: 


        可以看出,a.intdata的值虽然变了,但并没有影响b.intdata. 



        但是如果在Data类中声明intdata时,在其前面加上static就变成类变量了(即:public static int intdata=0;),则此时运行结果会变为:

        b.intdata=1  


        这次a.intdata值的改变可把b.intdata影响了,事实上,对象a和b的类变量均指向相同的数据,所有值一样,这就是类变量的作用. 



        问题六:实例方法,类方法(静态方法),构造器方法

         



        我们通常所说的方法系指实例方法。类方法(即静态方法)与实例方法最大的区别是:在形式上

        类方法多一个static,在用法上不必创建对象就可直接调用类方法(而实例方法却一定要先创建对象,再通过对象调用) 。 

        1. class
        2. static int addNum(int op1,int
        3. return
        4. }  
        5. }  
        6. public class
        7. public static void
        8. //直接用类名作为对象调用类方法
        9. System.out.println("addem(2,2)="+Add.addNum(2,2));  
        10. }   
        11. }



        注: 也可按通常的方法,即先创建对象,再调用方法,不过,这时static就无任何意义了。 



        构造器方法,它是用来初始化对象中的数据的一种方法,创建很容易,只需在类中加上一个与这个类同名的方法,不需要在前面加任何访问说明符或者返回类型,另外,构造器也一样可以像方法一样传递参数. 





        1. class
        2. private String data1;//事先声明
        3. Data(String s){  
        4. data1=s; /*通过接收数据来初始化变量.(注:不能在构造器内声明变量,事先在外就要声明.)*/
        5. }  
        6. public
        7. return
        8. }  
        9. }  
        10. public class
        11. public static void
        12. /*通过传递参数调用构造器新建一个对象,再通过对象调用方法得到数据*/
        13. System.out.println((new Data("I love you")).getData());  
        14. }  
        15. }

        问题七:接口与类

         



        类是对一类特定对象的规格说明,我们可以定义类,然后创建类的对象,通过创建类的对象来组合所有属于该类的组件,而接口不能这样做.而接口实质上就是一个常量和抽象方法的集合,要使用一个接口,就需要在类中实现这个接口,然后作为类定义的一部分,编写接口中声明的每一个方法,接口中的方法永远是public,abstract,接口中的常量永远是public static和final,因此不需要为它们说明属性.因为在Java中不支持多重继承,但是,可以用接口来实现类似的功能,这是接口的重要作用之一。 

        1. interface anyone{ //定义一个接口
        2. final double PI=3.1416;  
        3. void setNumber(int
        4. int
        5. }




          1. interface anyother{ //定义另一个接口
          2. void
          3. String getString();  
          4. }

          1. class Test implement anyone,anyother{ //定义一个类,并使用两个接口
          2. int
          3. String str;  
          4. public
          5. }  
          6. void setNumber(int
          7. this.number=number;  
          8. }  
          9. void
          10. this.str=str;  
          11. }  
          12. void int
          13. }//可以为一个空实现.
          14. void
          15. } 
          16. }

          =============================





          补充:静态代码块的执行



          class Parent{
          	static String name = "hello";
          	{
          		System.out.println("parent block");
          	}
          	static{
          		System.out.println("parent static block");
          	}
          	public Parent(){
          		System.out.println("parent constructor");
          	}
          }
          
          class Child extends Parent{
          	static String childName = "hello";
          	{
          		System.out.println("child block");
          	}
          	static{
          		System.out.println("child static block");
          	}
          	public Child(){
          		System.out.println("child constructor");
          	}
          }
          
          class TestStatic{
          	public static void main(String[] args){
          		new Child();
          		new Child();
          	}
          }



          运行结果:


          parent static block 
            
           child static block 
            
           parent block 
            
           parent constructor 
            
           child block 
            
           child constructor 
            
           parent block 
            
           parent constructor 
            
           child block 
            
           child constructor



          分析:



          代码块(子类) ---->非静态代码块(父类)---->构造方法(父类)---->非静态方法(子类)---->构造方法(子类)



          静态代码块会被自动执行,且就算产生类的多个实例对象,其中的静态代码块只被执行一次。



          3. 子类的构造方法,不管带不带参数,默认会先去找父类的不带参数构造方法,如果父类没有不带参数构造方法,子类必须用 super关键字来调用父类带参数的构造方法,否则编译不能通过