您好,登錄后才能下訂單哦!
對于面向對象的程序設計語言來說,類毫無疑問是其最重要的基礎。抽象、封裝、繼承、多態這四大特性都離不開類,只有存在類,才能體現面向對象編程的特點,今天我們就來了解一些類與繼承的相關知識。
一、Java類簡介
在Java中,類文件是以.java為后綴的代碼文件,在每個類文件中最多只允許出現一個public類,當有public類的時候,類文件的名稱必須和public類的名稱相同,若不存在public,則類文件的名稱可以為任意的名稱(當然以數字開頭的名稱是不允許的)。
對于類的成員變量,如果在定義的時候沒有進行顯示的賦值初始化,則Java會保證類的每個成員變量都得到恰當的初始化:
1)對于 char、short、byte、int、long、float、double等基本數據類型的變量來說會默認初始化為0(boolean變量默認會被初始化為false);我們在定義類時,類的成員變量可以不用賦初始值,因為在類加載時JVM已經給類變量賦了默認的初始值,但是類的局部變量(方法中定義的變量)必須先賦初始值后才能使用,否則會報錯。
2)對于引用類型的變量,會默認初始化為null。
如果沒有顯示地定義構造器,則編譯器會自動創建一個無參構造器,但是要記住一點,如果顯示地定義了構造器,編譯器就不會自動添加構造器。
下面我們著重講解一下初始化順序:
當程序執行時,需要生成某個類的對象,Java執行引擎會先檢查是否加載了這個類,如果沒有加載,則先執行類的加載再生成對象,如果已經加載,則直接生成對象。
在類的加載過程中,類的static成員變量會被先加載,另外如果類中有static語句塊,則會執行static語句塊。static成員變量和static語句塊的執行順序同代碼中的順序一致。記住,在Java中,類是按需加載,只有當需要用到這個類的時候,才會加載這個類,并且只會加載一次。
在生成對象的過程中,會先初始化對象的成員變量,然后再執行構造器。也就是說類中的變量會在任何方法(包括構造器)調用之前得到初始化。以下通過示例說明類的初始化順序:
/** * Java初始化執行順序 */ public class InitSeq { private static int var; static { System.out.println("static block"); } public InitSeq() { System.out.println("init class"); } private Inner inner = new Inner(); public static void main(String[] args) { InitSeq initSeq = new InitSeq(); Inner inner1 = new Inner(); System.out.println(var); } } class Inner { static { System.out.println("inner static block"); } public Inner() { System.out.println("inner class init"); } }
以上程序的執行結果為:
static block inner static block inner class init init class inner class init 0
結果說明:在加載InitSeq類的時候,首先執行static塊中的內容,然后初始化類成員變量inner,Inner類中的static塊在類加載后執行且static塊只有在類加載時執行,所以雖然Inner類實例化了兩次(inner、inner1),但static塊只會執行一次,最后執行構造器函數完成類的初始化,類成員變量var沒有顯示的賦值,默認初始值為0。
二、Java繼承
繼承是所有面向對象語言不可缺少的部分,在java中使用extends關鍵字來表示繼承關系。Objece類是所有類的父類,當創建一個類時如果沒有明確指出要繼承的類,就總是隱式地從根類Object進行繼承,子類可以直接訪問父類中非私有的屬性和方法,繼承提高了代碼的復用性,繼承讓類與類直接產生了關系,提供了多態的前提;
需要特別說明的是:Java只支持單繼承,也就是說 一個類最多只能顯示地繼承于一個父類。但是一個類卻可以被多個類繼承,也就是說一個類可以擁有多個子類。
1、子類繼承父類的成員變量
當子類繼承了某個類之后,便可以使用父類中的成員變量,但是并不是完全繼承父類的所有成員變量。具體的原則如下:
a、子類能夠繼承父類中public和protected修飾的成員變量,不能繼承父類中private修飾的成員變量,也無法訪問父類中private修飾的成員變量(只能通過getter方法訪問)
b、對于父類中包訪問權限的成員變量,如果父類和子類在同一個包下,那么子類可以繼承,否則子類不能繼承。
c、對于父類中可以繼承的成員變量,如果子類中出現了同名的成員變量,則會發生隱藏現象,即子類的變量會屏蔽父類的成員變量,如果想要訪問父類同名的成員變量則需要使用關鍵字super
/** * 父類 */ public class FatherClazz { /** * 姓名 */ public String name; /** * 性別 */ protected String sex; /** * 年齡 */ private int age; } /** * 子類 */ public class SonClazz extends FatherClazz{ /** * 父類中存在相同的變量 * 子類中會屏蔽父類的變量 */ public String name; /** * 使用super關鍵字訪問父類同名變量 */ public SonClazz() { this.name = "son"; super.name = "father"; this.sex = "男"; } }
2、子類繼承父類的方法
方法的繼承和成員變量的繼承類似
a、子類能夠繼承父類中public和protected修飾的方法,不能繼承父類中private修飾的方法,也無法訪問父類中private修飾的方法
b、對于父類中包訪問權限的方法,如果父類和子類在同一個包下,那么子類可以繼承,否則子類不能繼承。
c、對于父類中可以繼承的成員變量,如果子類中出現了相同的方法,則會發生覆蓋現象,即子類的方法會覆蓋父類的方法,如果想要訪問父類相同的方法則需要使用關鍵字super
/** * 父類 */ public class FatherClazz { /** * 姓名 */ public String name; /** * 性別 */ protected String sex; /** * 年齡 */ private int age; public void sayHello() { System.out.println("hello fatherClazz, My Name is " + name); } } /** * 子類 */ public class SonClazz extends FatherClazz{ /** * 父類中存在相同的變量 * 子類中會屏蔽父類的變量 */ public String name; /** * 使用super關鍵字訪問父類同名變量 */ public SonClazz() { this.name = "son"; super.name = "father"; this.sex = "男"; } public static void main(String[] args) { SonClazz sonClazz = new SonClazz(); sonClazz.sayHello(); sonClazz.extendMethod(); } /** * 子類方法覆蓋父類方法 */ @Override public void sayHello() { // 訪問父類方法,使用super關鍵字 super.sayHello(); System.out.println("hello sonClazz, My Name is " + name); } }
執行結果為:
hello fatherClazz, My Name is father hello sonClazz, My Name is son this extend method
3、子類初始化說明
子類在初始化時,會先初始化父類,但是子類是不能夠繼承父類的構造器,如果父類的構造器都是帶有參數的,則必須在子類的構造器中顯示地通過super關鍵字調用父類的構造器并配以適當的參數列表。否則無法初始化父類。如果父類有無參構造器,則在子類的構造器中用super關鍵字調用父類構造器不是必須的,如果沒有使用super關鍵字,系統會自動調用父類的無參構造器。
/** * 父類 */ public class FatherClazz { /** * 姓名 */ public String name; /** * 性別 */ protected String sex; /** * 年齡 */ private int age; /** * 帶參數的構造函數 */ public FatherClazz(String name) { this.name = name; System.out.println("fatherClazz init"); } public void sayHello() { System.out.println("hello fatherClazz, My Name is " + name); } public void extendMethod() { System.out.println("this extend method"); } } /** * 子類 */ public class SonClazz extends FatherClazz{ /** * 父類中存在相同的變量 * 子類中會屏蔽父類的變量 */ public String name; /** * 使用super關鍵字訪問父類同名變量 */ public SonClazz() { // 由于父類的構造函數帶參數,且父類中沒有無參的構造函數 // 那么在子類的構造函數中必須使用super()顯示的調用父類的構造函數,完成父類的初始化 // 如果父類中有無參的構造函數,則無需使用super調用構造函數,JVM默認使用無參的構造函數完成初始話 super("father"); this.name = "son"; this.sex = "男"; System.out.println("sonClazz init"); } public static void main(String[] args) { SonClazz sonClazz = new SonClazz(); sonClazz.sayHello(); sonClazz.extendMethod(); } /** * 子類方法覆蓋父類方法 */ @Override public void sayHello() { // 訪問父類方法,使用super關鍵字 super.sayHello(); System.out.println("hello sonClazz, My Name is " + name); } }
執行結果為:
fatherClazz init sonClazz init hello fatherClazz, My Name is father hello sonClazz, My Name is son this extend method
結果分析:在初始化子類時,會先初始化父類,由于父類沒有無參的構造函數,所有在子類的構造函數中使用super(),顯示的調用了父類的構造函數完成了父類的初始化。需要特別強調的是:父類的構造器調用以及初始化過程一定在子類的前面
static變量、static塊加載順序額外說明,示例如下:
示例一:
/** * static加載說明 */ public class TestClazz extends Base{ static{ System.out.println("test static"); } public TestClazz(){ System.out.println("testClazz constructor"); } public static void main(String[] args) { new TestClazz(); } } class Base{ private Test test = new Test(); static{ System.out.println("base static"); } public Base(){ System.out.println("base constructor"); } } class Test { public Test() { System.out.println("test constructor"); } }
執行結果為:
base static test static test constructor base constructor testClazz constructor
結果說明:執行TestClazz的main方法時,首先需要加載TestClazz類,由于TestClazz類繼承了Base所以要先加載Base類,加載類時會加載static變量和static塊,所以先執行了Base類中的static塊,Base類加載完成后,加載TestClazz類,執行TestClazz中的static塊,類加載完成后,執行new TestClazz(),進行類的實例化,由于繼承了Base類,同樣需要先實例化Base類,Base中定義了成員變量test,需要先示例化成員變量,然后執行構造函數,完成Base類的實例化,最后執行TestClazz類的構造函數,完成TestClazz類的實例化。
示例二:
/** * static加載說明 */ public class TestClazz extends Base{ static{ System.out.println("test static"); } public TestClazz(){ System.out.println("testClazz constructor"); } public static void main(String[] args) { new TestClazz(); } } class Base{ // test變量為static類型,在類加載的時候,就會完成類的示例化 private static Test test = new Test(); static{ System.out.println("base static"); } public Base(){ System.out.println("base constructor"); } } class Test { public Test() { System.out.println("test constructor"); } }
執行結果:
test constructor base static test static base constructor testClazz constructor
結果說明:示例一和示例二唯一的區別就是,Base類中的test變量類型變為static類型,結果就是在加載Base類時就將test變量實例化了。
由于篇幅有限文章中的代碼無法一一列出,如需獲取全部的源碼信息,可關注微信公眾號 布衣暖,回復 java基礎 獲取全部的源碼信息
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。