Java繼承中的轉型及其記憶體分配

来源:http://www.cnblogs.com/jycboy/archive/2016/04/10/5373690.html
-Advertisement-
Play Games

看書的時候被一段代碼能凌亂啦,代碼是這樣的: 運行結果: 瘋狂Java講義Parent 在這個代碼中,抽象父類People定義了兩個變數和一個getName()方法,子類student也定義了兩個和父類同名的變數,把父類的隱藏。 關於這段代碼的兩個困惑:1.子類實例化時必須首先實例化父類對象,而父類 ...


看書的時候被一段代碼能凌亂啦,代碼是這樣的:

package 繼承;

abstract class People
    {
        public String tag = "瘋狂Java講義";         //
        public String name = "Parent";
        String getName(){
            return name;
        }
        
    }
    class Student extends People
    {
        //定義一個私有的tag實例變數來隱藏父類的tag實例變數
        String tag = "輕量級Java EE企業應用實戰";         //
        public String name = "Student";
    }
    public class HideTest2
    {
        public static void main(String[] args)
        {
            Student d = new Student();
            //將d變數顯式地向上轉型為Parent後,即可訪問tag實例變數
            //程式將輸出:“瘋狂Java講義”
            System.out.println(((People)d).tag);         //
            System.out.println(d.getName());  //parent
        }
    }

運行結果:

瘋狂Java講義
Parent

在這個代碼中,抽象父類People定義了兩個變數和一個getName()方法,子類student也定義了兩個和父類同名的變數,把父類的隱藏。

關於這段代碼的兩個困惑:1.子類實例化時必須首先實例化父類對象,而父類是抽象類,不能有對象。那到底子類實例化時產不產生父類對象???

                                  2.d.getName();//返回的是parent,而不是student.不應該把父類的隱藏麽??

書中是這麼解釋的:

  Student對象會保存兩份實例變數,一份是people中定義的實例變數,一份是Student中定義的實例變數,d變數引用一個Student對象,記憶體示意圖如下:

  

將d向上轉型為Parent對象,在通過它訪問name變數是允許的,也就是輸出“parent”。

但看著他的解釋還是有點不明白,說的不是很清楚,又去網上搜了下:

 

java 子類實例化時是否同時存在一個父類對象.

  2011-10-14 19:53 提問者: luoyuehao89 瀏覽次數:602次
java 子類實例化時是否同時存在一個父類對象.
假如父類A中有個int a = 1;
子類B繼承A,同時B中覆蓋個int a = 2;

運行:
A test = new B();
system.out.println(test.a);

結果是1,是父類中的屬性.這個時候是否存在父類對象,我的理解是存在的.
我又試,把父類用抽象abstract修飾,按理說abstract累不能實例化吧,肯定不能得到父類中的a屬性,結果還是一樣的.
怎麼理解.

問題補充:

是不是創建子類對象,肯定會出現一個父類的對象?
精彩回答
不會產生父類對象,只是用了父類的構造函數而已,並不是用到構造函數就會產生對象,構造函數只是起對象初始化作用的,而不是起產生對象作用的,如果new A();即只有new語句才會產生父類A的對象。
變數是靜態綁定 ,方法是動態綁定。 這裡面變數在編譯期間實現了變數調用語句與變數定義賦值語句的綁定,綁定的自然是父類的,因為調用時類型是父類的,所以值是父類中定義的值 
其實你可以這麼理解  創建了一個子類對象時,在子類對象記憶體中,有兩份這個變數,一份繼承自父類,一份子類。 
絕對不會產生父類對象,父類中的成員被繼承到子類對象中,用指向子類對象的父類引用調用父類成員,只不過是從 子類對象記憶體空間中找到那個被繼承來的父類成員,也就是說實質是用子類對象調用變數a,這樣就可以解釋成員必須通過對象調用的規定,只不過這時調用的是子類對象中的繼承自父類的a(子類對象中有兩個a,一個繼承自父類,一個屬於自己)

哎,話說的有些亂。  這個問題也困惑我很久,上網查詢發現很多人是錯誤的,最後找到幾篇好的文章才明白,可能很多java老手也都會犯“產生父類對象”這個錯誤,最近才搞明白。
你自己想想,如果產生父類對象,如果父類是抽象類,抽象類允許產生對象嗎?所以這種說法不嚴謹

動態綁定定義

  動態綁定是指在執行期間(非編譯期)判斷所引用對象的實際類型,根據其實際的類型調用其相應的方法

靜態綁定與動態綁定

  除了限制訪問,訪問方式也決定哪個方法將被子類調用或哪個屬性將被子類訪問. 函數調用與函數本身的關聯,以及成員訪問與變數記憶體地址間的關係,稱為綁定. 
電腦語言中有兩種主要的綁定方式,靜態綁定和動態綁定. 靜態綁定發生於數據結構和數據結構間,程式執行之前. 靜態綁定發生於編譯期, 因此不能利用任何運行期的信息.
它針對函數調用與函數的主體,或變數與記憶體中的區塊.. 動態綁定則針對運行期產生的訪問請求,只用到運行期的可用信息. 在面向對象的代碼中,動態綁定意味著決定哪個方法被調用或哪個屬性被訪問,
將基於這個類本身而不基於訪問範圍.

誰有更好的解釋,說得更清楚點,歡迎留言。。Thanks
這個解釋的不錯:http://bbs.csdn.net/topics/390896785
解釋
如下:
子類在創建實例後,類初始化方法會調用父類的初始化方法(除了java.lang.Object類,因為java.lang.Object類沒有父類),而這種調用會逐級追述,直到java.lang.Object的初始化方法。
這個地方我說的是初始化方法,而不是構造方法,因為構造方法是相對於java源程式而言,而編譯後的class文件是初始化方法即" <init>"方法(紅色部分為方法名),
初始化方法是由java源程式的三個部分組成的,一個部分是成員欄位後的直接的初始化語句,例如private int i=0;private Date date=new Date();等等,第二個部分是由初始化塊組成,例如:

Java code
   public class Test{
 private int i=0;//初始化第一部分 
//以下大括弧內為初始化第二部分
 { this.i=4; //do something...... } }
第三個部分就是java源代碼中的構造方法中的代碼,java源代碼中有幾個構造方法,那麼class文件中就有幾個初始化方法,編譯器會把第一部分與第二部分分別複製到每個初始化方法的前端,然後把初始化
方法對應參數的構造方法的代碼複製到相應初始化方法中(這裡說的複製其實應該說是編譯,不過為了讓你更好理解所以如此說).
那麼說初始化方法如何追述其父類的,這也關係到初始化方法的結構,初始化方法的執行順序以及結構就如上所說,但是每個初始化方法的第一個執行指令就是調用另外一個初始化方法,
這個初始化方法可能是自身類某個初始化方法,例如你的構造函數中第一句有類似this(...)這種語句,那麼初始化方法就會調用自身類的指定構造方法;如果你的構造方法中沒有指定構造方法調用,
那麼初始化方法會預設調用父類無參數初始化方法,如果你的子類第一句為 super(....),那麼初始化方法會調用父類指定初始化方法。這種調用過程會遞歸進行調用,直到這個類是java.lang.Object類。
調用初始化方法並不代表會生成對象,你的java代碼中出現new關鍵字加上構造方法的調用,只會生成一個對象,其父類對象不會生成,所以調用父類為抽象類的構造方法完全是合理的。
而且初始化方法對於虛擬機來說只是一個名稱叫做" <init>"的普通方法,區別隻是生成對象以後調用而已(sun 的jdk私有包中有繞過構造方法生成對象的方式,可以證明之上說法,具體如何我這裡不陳述)。
然後回答你的第二個問題,抽象類中的構造方法其實是用來給繼承的子類來用的,因為構造方法相當於初始化方法,當子類調用構造方法時必須調用父類構造方法,
所以你可以在子類產生對象時抽象類中按需求初始化抽象類中的欄位以及執行一些初始化代碼。其實並不是一定要生成某個類的實例才調用構造方法,子類也需要調用父類構造方法。
而生成實例也並不一定會調用構造方法,在某些特殊實現中或者特殊情況下,生成實例不會調用構造方法。而調用了構造方法也不一定就生成了一個實例,但是那一定是一個實例調用的,就像一個普通的實例方法一樣。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在C語言中,函數名也是指針變數,比如創建了一個add(int n,int m)的函數的同時也創建了一個名為add的指針變數,因此我們可以把函數指針當作一種類型為它賦值、當作參數傳遞等操作 C語言創建函數指針的公式: 返回類型 (*指針變數)(參數類型) 雖然函數指針是指針,但是在使用的時候我們可以省 ...
  • 線程 1.線程的實現 (1)實現線程主要有3中方式:使用內核線程實現,使用用戶線程實現和使用用戶線程加輕量級進程混合實現。 (2)使用內核線程實現 內核線程就是直接由操作系統內核支持的線程,這種線程由內核來完成線程切換,內核通過操縱調度器對線程進行調度,並負責將線程映射到處理器上。 局限性:由於是基 ...
  • 1.request.getSession().getServletContext().getResourceAsStream("/WEB-INF/classes/a.txt""); / 相對對於項目的根路徑 2.getClass().getClassLoader().getResourceAsStr ...
  • 無論是BS還是CS得項目,沒有資料庫是不行的。 本文是對python對mysql的操作的總結。適合有一定基礎的開發者,最好是按部就班學習的人閱讀。因為我認為人生不能永遠都是從零開始,那簡直就是災難。 對於python2.7 和mysql5.7.11 的安裝或使用遇到問題儘量查閱官網的文檔。 pyth ...
  • Git for PyCharm Using PyCharm's Git integration locally:http://confluence.jetbrains.com/display/PYH/Using+PyCharm%27s+Git+integration+locally 創建git集 p... ...
  • #!/usr/bin/env python #coding:utf-8 #定義字典 dic = { "上海":{ "浦東":["張江","唐鎮"], "嘉定":["婁塘","封浜"], "寶山":["羅店","月浦"] }, "北京":{ "海澱":["上地","中關村"], "朝陽":[" ...
  • 本文原創作者:pipi-changing 本文原創出處:http://www.cnblogs.com/pipi-changing/ 本文版權歸作者和博客園共有,未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接 ,否則保留追究法律責任的權利。 函數的定義 什麼是函數? •函數就是定義在類 ...
  • python編碼總結: 1).首先python有兩種格式的字元串,str和unicode,其中unicode相當於位元組碼那樣,可以跨平臺使用。 str轉化為unicode可以通過unicode(),u,str.decode三種方式 unicode轉化為str,如果有中文的話,一般通過encode的方 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...