Java類與繼承

轉載於http://www.cnblogs.com/dolphin0520/p/3803432.htmlhtml

在Java中,類文件是以.java爲後綴的代碼文件,在每一個類文件中最多隻容許出現一個public類,當有public類的時候,類文件的名稱必須和public類的名稱相同,若不存在public,則類文件的名稱能夠爲任意的名稱(固然以數字開頭的名稱是不容許的)。java

  在類內部,對於成員變量,若是在定義的時候沒有進行顯示的賦值初始化,則Java會保證類的每一個成員變量都獲得恰當的初始化:面試

  1)對於  char、short、byte、int、long、float、double等基本數據類型的變量來講會默認初始化爲0(boolean變量默認會被初始化爲false);ide

  2)對於引用類型的變量,會默認初始化爲null。this

  若是沒有顯示地定義構造器,則編譯器會自動建立一個無參構造器,可是要記住一點,若是顯示地定義了構造器,編譯器就不會自動添加構造器。注意,全部的構造器默認爲static的。spa

 下面咱們着重講解一下 初始化 順序:3d

  當程序執行時,須要生成某個類的對象,Java執行引擎會先檢查是否加載了這個類,若是沒有加載,則先執行類的加載再生成對象,若是已經加載,則直接生成對象。code

  在類的加載過程當中,類的static成員變量會被初始化,另外,若是類中有static語句塊,則會執行static語句塊。static成員變量和static語句塊的執行順序同代碼中的順序一致。記住,在Java中,類是按需加載,只有當須要用到這個類的時候,纔會加載這個類,而且只會加載一次。看下面這個例子就明白了:htm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public  class  Test {
     public  static  void  main(String[] args)  throws  ClassNotFoundException {
         
         Bread bread1 =  new  Bread();
         Bread bread2 =  new  Bread();
     }
}
 
 
class  Bread {
     static {
         System.out.println( "Bread is loaded" );
     }
     public  Bread() {
         System.out.println( "bread" );
     }
}

  運行這段代碼就會發現"Bread is loaded"只會被打印一次。對象

  在生成對象的過程當中,會先初始化對象的成員變量,而後再執行構造器。也就是說類中的變量會在任何方法(包括構造器)調用以前獲得初始化,即便變量散步於方法定義之間。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  class  Test {
     public  static  void  main(String[] args)  {
         new  Meal();
     }
}
 
 
class  Meal {
     
     public  Meal() {
         System.out.println( "meal" );
     }
     
     Bread bread =  new  Bread();
}
 
class  Bread {
     
     public  Bread() {
         System.out.println( "bread" );
     }
}

  輸出結果爲:

  View Code

二.你瞭解繼承嗎?

  繼承是全部OOP語言不可缺乏的部分,在java中使用extends關鍵字來表示繼承關係。當建立一個類時,老是在繼承,若是沒有明確指出要繼承的類,就老是隱式地從根類Object進行繼承。好比下面這段代碼:

1
2
3
4
5
6
7
8
9
10
11
class  Person {
     public  Person() {
         
     }
}
 
class  Man  extends  Person {
     public  Man() {
         
     }
}

  類Man繼承於Person類,這樣一來的話,Person類稱爲父類(基類),Man類稱爲子類(導出類)。若是兩個類存在繼承關係,則子類會自動繼承父類的方法和變量,在子類中能夠調用父類的方法和變量。在java中,只容許單繼承,也就是說 一個類最多隻能顯示地繼承於一個父類。可是一個類卻能夠被多個類繼承,也就是說一個類能夠擁有多個子類。

權限修飾

 

  1.子類繼承父類的成員變量

  當子類繼承了某個類以後,即可以使用父類中的成員變量,可是並非徹底繼承父類的全部成員變量。具體的原則以下:

  1)可以繼承父類的public和protected成員變量;不可以繼承父類的private成員變量;

  2)對於父類的包訪問權限成員變量,若是子類和父類在同一個包下,則子類可以繼承;不然,子類不可以繼承;

  3)對於子類能夠繼承的父類成員變量,若是在子類中出現了同名稱的成員變量,則會發生隱藏現象,即子類的成員變量會屏蔽掉父類的同名成員變量。若是要在子類中訪問父類中同名成員變量,須要使用super關鍵字來進行引用。

  2.子類繼承父類的方法

  一樣地,子類也並非徹底繼承父類的全部方法。

  1)可以繼承父類的public和protected成員方法;不可以繼承父類的private成員方法;

  2)對於父類的包訪問權限成員方法,若是子類和父類在同一個包下,則子類可以繼承;不然,子類不可以繼承;

  3)對於子類能夠繼承的父類成員方法,若是在子類中出現了同名稱的成員方法,則稱爲覆蓋,即子類的成員方法會覆蓋掉父類的同名成員方法。若是要在子類中訪問父類中同名成員方法,須要使用super關鍵字來進行引用。

  注意:隱藏和覆蓋是不一樣的。隱藏是針對成員變量和靜態方法的,而覆蓋是針對普通方法的。(後面會講到)

  3.構造器

  子類是不可以繼承父類的構造器,可是要注意的是,若是父類的構造器都是帶有參數的,則必須在子類的構造器中顯示地經過super關鍵字調用父類的構造器並配以適當的參數列表。若是父類有無參構造器,則在子類的構造器中用super關鍵字調用父類構造器不是必須的,若是沒有使用super關鍵字,系統會自動調用父類的無參構造器。看下面這個例子就清楚了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class  Shape {
     
     protected  String name;
     
     public  Shape(){
         name =  "shape" ;
     }
     
     public  Shape(String name) {
         this .name = name;
     }
}
 
class  Circle  extends  Shape {
     
     private  double  radius;
     
     public  Circle() {
         radius =  0 ;
     }
     
     public  Circle( double  radius) {
         this .radius = radius;
     }
     
     public  Circle( double  radius,String name) {
         this .radius = radius;
         this .name = name;
     }
}

  這樣的代碼是沒有問題的,若是把父類的無參構造器去掉,則下面的代碼必然會出錯:

  改爲下面這樣就好了:

  4.super

  super主要有兩種用法:

  1)super.成員變量/super.成員方法;

  2)super(parameter1,parameter2....)

  第一種用法主要用來在子類中調用父類的同名成員變量或者方法;第二種主要用在子類的構造器中顯示地調用父類的構造器,要注意的是,若是是用在子類構造器中,則必須是子類構造器的第一個語句。

三.常見的面試筆試題

1.下面這段代碼的輸出結果是什麼?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public  class  Test {
     public  static  void  main(String[] args)  {
         new  Circle();
     }
}
 
class  Draw {
     
     public  Draw(String type) {
         System.out.println(type+ " draw constructor" );
     }
}
 
class  Shape {
     private  Draw draw =  new  Draw( "shape" );
     
     public  Shape(){
         System.out.println( "shape constructor" );
     }
}
 
class  Circle  extends  Shape {
     private  Draw draw =  new  Draw( "circle" );
     public  Circle() {
         System.out.println( "circle constructor" );
     }
}
  View Code

  這道題目主要考察的是類繼承時構造器的調用順序和初始化順序。要記住一點:父類的構造器調用以及初始化過程必定在子類的前面。因爲Circle類的父類是Shape類,因此Shape類先進行初始化,而後再執行Shape類的構造器。接着纔是對子類Circle進行初始化,最後執行Circle的構造器。

2.下面這段代碼的輸出結果是什麼?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public  class  Test {
     public  static  void  main(String[] args)  {
         Shape shape =  new  Circle();
         System.out.println(shape.name);
         shape.printType();
         shape.printName();
     }
}
 
class  Shape {
     public  String name =  "shape" ;
     
     public  Shape(){
         System.out.println( "shape constructor" );
     }
     
     public  void  printType() {
         System.out.println( "this is shape" );
     }
     
     public  static  void  printName() {
         System.out.println( "shape" );
     }
}
 
class  Circle  extends  Shape {
     public  String name =  "circle" ;
     
     public  Circle() {
         System.out.println( "circle constructor" );
     }
     
     public  void  printType() {
         System.out.println( "this is circle" );
     }
     
     public  static  void  printName() {
         System.out.println( "circle" );
     }
}
  View Code

  這道題主要考察了隱藏和覆蓋的區別(固然也和多態相關,在後續博文中會繼續講到)。

  覆蓋只針對非靜態方法(終態方法不能被繼承,因此就存在覆蓋一說了),而隱藏是針對成員變量和靜態方法的。這2者之間的區別是:覆蓋受RTTI(Runtime type  identification)約束的,而隱藏卻不受該約束。也就是說只有覆蓋方法纔會進行動態綁定,而隱藏是不會發生動態綁定的。在Java中,除了static方法和final方法,其餘全部的方法都是動態綁定。所以,就會出現上面的輸出結

相關文章
相關標籤/搜索