對於物件導向的程式設計語言來說,類別毫無疑問是其最重要的基礎。抽象、封裝、繼承、多態這四大特性都離不開類,只有存在類,才能體現物件導向程式設計的特點,今天我們就來了解一些類別與繼承的相關知識。首先,我們來講述一下與類別的初始化相關的東西,然後再從幾個方面闡述繼承這一大特性。以下是本文的目錄大綱:
一.你了解類嗎?
二.你了解繼承嗎?
三.常見的面試筆試題
一.你了解類嗎?
在Java中,類文件是以.java為後綴的代碼文件,在每個類文件中最多只允許出現一個public類,當有public類的時候,類文件的名稱必須和public類的名稱相同,若不存在public,則類別檔案的名稱可以為任意的名稱(當然以數字開頭的名稱是不允許的)。
在類別內部,對於成員變量,如果在定義的時候沒有進行顯示的賦值初始化,則Java會保證類別的每個成員變數都得到恰當的初始化:
1)對於 char、short、byte、intint 、long、float、double等基本資料型別的變數會預設初始化為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"); } }
輸出結果為:
bread
meal
二.你了解繼承嗎?
繼承是所有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 constructor
shape constructor
circle draw constructor
circle 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 constructor
circle constructor
shapethis is circle
shape
这道题主要考察了隐藏和覆盖的区别(当然也和多态相关,在后续博文中会继续讲到)。
覆盖只针对非静态方法(终态方法不能被继承,所以就存在覆盖一说了),而隐藏是针对成员变量和静态方法的。这2者之间的区别是:覆盖受RTTI(Runtime type identification)约束的,而隐藏却不受该约束。也就是说只有覆盖方法才会进行动态绑定,而隐藏是不会发生动态绑定的。在Java中,除了static方法和final方法,其他所有的方法都是动态绑定。因此,就会出现上面的输出结果。
更多Java:類別與繼承相关文章请关注PHP中文网!

本文討論了使用Maven和Gradle進行Java項目管理,構建自動化和依賴性解決方案,以比較其方法和優化策略。

本文使用Maven和Gradle之類的工具討論了具有適當的版本控制和依賴關係管理的自定義Java庫(JAR文件)的創建和使用。

本文討論了使用咖啡因和Guava緩存在Java中實施多層緩存以提高應用程序性能。它涵蓋設置,集成和績效優勢,以及配置和驅逐政策管理最佳PRA

本文討論了使用JPA進行對象相關映射,並具有高級功能,例如緩存和懶惰加載。它涵蓋了設置,實體映射和優化性能的最佳實踐,同時突出潛在的陷阱。[159個字符]

Java的類上載涉及使用帶有引導,擴展程序和應用程序類負載器的分層系統加載,鏈接和初始化類。父代授權模型確保首先加載核心類別,從而影響自定義類LOA


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Atom編輯器mac版下載
最受歡迎的的開源編輯器

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

SublimeText3漢化版
中文版,非常好用

WebStorm Mac版
好用的JavaScript開發工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器