(轉)Java:類與繼承

來源:互聯網
上載者:User

標籤:程式   類成員變數   type   als   對象   構造   調用   prot   xtend   

原文地址:

http://www.cnblogs.com/dolphin0520/p/3803432.html

     對於物件導向的程式設計語言來說,類毫無疑問是其最重要的基礎。抽象、封裝、繼承、多態這四大特性都離不開類,只有存在類,才能體現物件導向編程的特點,今天我們就來瞭解一些類與繼承的相關知識。首先,我們講述一下與類的初始化相關的東西,然後再從幾個方面闡述繼承這一大特性。

一.你瞭解類嗎?

  在Java中,類檔案是以.java為尾碼的代碼檔案,在每個類檔案中最多隻允許出現一個public類,當有public類的時候,類檔案的名稱必須和public類的名稱相同,若不存在public,則類檔案的名稱可以為任意的名稱(當然以數字開頭的名稱是不允許的)。

  在類內部,對於成員變數,如果在定義的時候沒有進行顯示的賦值初始化,則Java會保證類的每個成員變數都得到恰當的初始化:

  1)對於  char、short、byte、int、long、float、double等基礎資料型別 (Elementary Data Type)的變數來說會預設初始化為0(boolean變數預設會被初始化為false);

  2)對於參考型別的變數,會預設初始化為null。

  如果沒有顯示地定義構造器,則編譯器會自動建立一個無參構造器,但是要記住一點,如果顯示地定義了構造器,編譯器就不會自動添加構造器。注意,所有的構造器預設為static的。

  下面我們著重講解一下 初始化 順序:

  當程式執行時,需要產生某個類的對象,Java執行引擎會先檢查是否載入了這個類,如果沒有載入,則先執行類的載入再產生對象,如果已經載入,則直接產生對象。

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

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"只會被列印一次。

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

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");    }}

輸出結果為:

breadmeal
二.你瞭解繼承嗎?

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

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關鍵字,系統會自動調用父類的無參構造器。看下面這個例子就清楚了:

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.下面這段代碼的輸出結果是什嗎?

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");    }}
shape draw constructorshape constructorcircle draw constructorcircle constructor

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

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


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");    }}

shape constructorcircle constructorshapethis is circleshape

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

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


 

(轉)Java:類與繼承

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.