Java繼承特性以及重寫現象的記憶體分析
今天我們說一下Java面向物件中的一個特性-繼承,然後做一下他的記憶體分析,理解一下重新現象的情況。
怎麼理解繼承?
下面先介紹一下怎麼理解繼承的特性,繼承呢在Java中的關鍵是extends,那麼其實所謂的繼承是比較簡單的也是很好理解的,Java中如果一個類繼承了父類,那麼我們就說他們是一個繼承的關係,那麼被繼承的那個類的所有屬性,繼承者都是存在的,除了構造器,構造器是不可以被繼承的,舉個例子:
package com.gaojizu.TestExtends; /** * 測試繼承 繼承誰的類,那麼他具有的所有屬性繼承者都有 除了構造器 構造器是不可以繼承的 java * 的類只有單繼承,介面是有多繼承的,如果沒有定義繼承,我們預設的都是繼承Object他是我們的祖類 * 是在java.lang.Object * @author admin * */ //動物 public class Animals { String eyes; public void run() { System.out.println("我可以跑"); } public void eat() { System.out.println("我可以吃"); } } //哺乳動物 class Mammel extends Animals{ public void taisheng() { System.out.println("我是胎生"); } }
寫一個測試類:
package com.gaojizu.TestExtends;
/**
* 測試繼承
* @author admin
*
*/
public class Test {
public static void main(String[] args) {
Mammel m = new Mammel();
m.eat();
}
}
輸出結果:我可以吃!
那麼我們可以看到,我寫的Mammel 也就是哺乳動物的類中是沒有eat()方法的,eat方法是在他的父類中的,所以說他是擁有了父親的方法,這是很簡單的,但凡瞭解Java的人基本都是明白的,前面說了,構造器是不可以被繼承的,我們可以測試一下!
public class Animals {
String eyes;
public void run() {
System.out.println("我可以跑");
}
public void eat() {
System.out.println("我可以吃");
}
/**
* 建立一個構造器
*/
public Animals() {
}
public Animals(String name) {
}
}
那麼在測試的Mammel類裡面我們是不可以呼叫到這個構造方法的。
講了很多廢話,我們今天主要是做記憶體分析的, 不過呢考慮到有些人對繼承現象比較暈,所以簡單的做一個介紹。
什麼是重寫?
所謂的重寫就是說,我們拿到父類的方法以後,滿足不了我們的需求,需要自己定義內容的時候,我們可以將父類的方法重新定義,從而呢實現一個覆蓋的現象!舉個例子:
public class Animals {
String eyes;
public void run() {
System.out.println("我可以跑");
}
public void eat() {
System.out.println("我可以吃");
}
public Animals() {
}
public Animals(String name) {
}
}
//哺乳動物
class Mammel extends Animals{
//重寫父類的eat方法
public void eat() {
System.out.println("我可以燒火吃飯");
}
public void taisheng() {
System.out.println("我是胎生");
}
}
測試一下:
Mammel m = new Mammel();
m.eat();
//輸出:我可以燒火吃飯
那麼之前的eat就被覆蓋了,畢竟是人嘛,總不能吃不煮的食物吧,高階動物嘛!
這裡有人就說了,我兩個都想用怎麼辦呢?就是我不僅僅要改變父類的實現內容,我還要使用父類自己的實現內容,是不是可以呢?可以的, 我們每一個方法都是有兩個隱式引數的,一個是this一個是super,我們可以測試一下:
//哺乳動物
class Mammel extends Animals{
public void eat() {
super.eat();
System.out.println("我可以燒火吃飯");
}
public void taisheng() {
System.out.println("我是胎生");
}
}
測試一下:
Mammel m = new Mammel();
m.eat();
//輸出: 我可以吃 我可以燒火吃飯
ok到這裡概念就基本介紹完了,下面我們根據程式碼看一下記憶體的情況:
哦,這裡有一點是忘記說了的,就是我們不管寫不寫繼承,就是說不管我們一個類是不是寫了extends他都是預設繼承基類的,什麼是基類的,就是類的祖先-Object,怎麼確定呢?前面我們說了,既然繼承了,就一定是有他父類的方法的,對不打,那麼我們不寫關鍵字,看看是不是可以使用Object的方法就行了,我們先看一下他有哪些方法:
舉個例子:
寫個不做繼承的類:
//測試基類的使用
class TestObject{
}
測試一下:
TestObject t = new TestObject();
t.toString();
System.out.println(t.toString());
我們可以看到,我們是可以使用toString方法的,說明我們是可以直接繼承基類的方法的。
下面我們畫張圖進行簡單的分析一下:
畫的不好,將就看一下,不要太辣眼睛...
我們可以看到,一個類被創建出來,例項化一個物件以後,我們在使用的時候他會先找到父類,父類會繼續找他的父類,為什麼呢?可以看到我每一個類的下面都寫了一個 super(),為什麼呢?我之前是不是說了,每一個方法都是有一個隱式引數的,this和super,this指向的是本類,super指向的就是父類,那麼這裡程式碼會首先走super(),這個super必須放在程式碼的第一行,否則是錯的,即使你不寫,JVM也會幫你自動建立一個super(),程式碼走到super繼續向上找父類,直到找到Object基類結束,那麼記憶體裡面的分佈情況就是右邊畫的,最下面的是Paxing類,那麼他就有上面所有的方法和屬性,我們可以一級一級的想嘛,他有父類的所有方法和屬性,除構造器以外的,那麼就是有Anmals的所有屬性和方法,那麼Anmils又繼承了Object類,他就有Object的所有屬性和方法,自然Paxing就有所有的屬性和方法了,所以是包含的關係。
就是說如果是一個類沒有寫任何的繼承,他就是基類的直接兒子,也就是他是直接從屬於Object類的,這裡再說一下super關鍵字,他指向的是直接父類的方法,不是祖父的方法。也就是說隸屬於直接上級!
但是他是不是可以重寫祖父的類的方法呢?當然是可以的,舉個例子:
public class Animals {
String eyes;
public void run() {
System.out.println("我可以跑");
}
public void eat() {
System.out.println("我可以吃");
}
public void hun() {
System.out.println("woshi 孫子的測試");
}
public Animals() {
}
public Animals(String name) {
}
}
//哺乳動物
class Mammel extends Animals{
public void eat() {
super.eat();
System.out.println("我可以燒火吃飯");
}
public void taisheng() {
System.out.println("我是胎生");
}
}
//繼續繼承
class Testsunzi extends Mammel{
//測試重寫祖類的方法
public void hun() {
System.out.println("我是新的測試孫子的函式");
}
public void eat() {
System.out.println("我是孫子");
}
}
測試一下可以看出來:
Testsunzi tt = new Testsunzi();
tt.eat();
tt.hun();
輸出的是:
是和我們想的一樣的,所以是沒有問題的。
到這裡基本就是結束了,其實繼承的特性是很厲害的,他的作用很多,但是主要的是為了提高程式碼的複用性,這個不用說了,大家都是知道的。
分析記憶體的一個好處是可以幫助我們更好的理解程式碼的執行情況,對於我們理解程式碼也是由幫助的,其實對於除錯程式碼記憶解決常見的錯誤也是很有幫助的。
喜歡我的文章的看了一關注我,我寫的可能不是很深,但是會慢慢的深入,這畢竟是一個學習的過程,所以可以一起交流!
謝謝閱讀!