No.2 對象與記憶體控制(記憶體分配)

来源:http://www.cnblogs.com/fang--/archive/2016/12/15/6181887.html
-Advertisement-
Play Games

1.實例變數和類變數 成員變數 VS 局部變數 局部變數(存儲在方法的棧記憶體中) 形參:方法簽名中定義,由方法調用者賦值,隨方法結束而消亡 方法內局部變數:方法內定義,必須在方法內進行顯示初始化,初始化完成後開始生效,隨方法結束而消亡 代碼塊內局部變數:代碼塊內定義,必須代碼塊內進行顯示初始化,初始 ...


1.實例變數和類變數

  成員變數 VS 局部變數

  • 局部變數(存儲在方法的棧記憶體中)
    • 形參:方法簽名中定義,由方法調用者賦值,隨方法結束而消亡
    • 方法內局部變數:方法內定義,必須在方法內進行顯示初始化,初始化完成後開始生效,隨方法結束而消亡
    • 代碼塊內局部變數:代碼塊內定義,必須代碼塊內進行顯示初始化,初始化完成後開始生效,隨代碼塊結束而消亡
  • 成員變數(類體內定義的)
    • 無static:非靜態變數/實例變數;有static:靜態變數/類變數
    • static
      • 從程式角度看,static的作用:將實例成員變為類成員。無static修飾的類的成員,成員屬於類的實例;有static修飾,成員屬於類本身。因此,static只能修飾類的成員
    • 定義成員變數時 要 合法前向引用
      • 類變數的初始化時機總是處於實例變數的初始化時機之前
      • 1 public class RightDef{
        2       // 下麵代碼完全沒有問題
        3       int num1 = num2 + 20;
        4       static  int num2 = 10;           
        5 }
         public class RightDef{
        2       // 非法前向引用
        3       int num1 = num2 + 20;
        4       int num2 = 10;           
        5 }

         public class RightDef{
        2       // 非法前向引用
        3      static int num1 = num2 + 20;
        4      static int num2 = 10;           
        5 }
         
  • 同一個JVM內,每個類只有一個Class對象(可以通過反射獲取該Class對象),但是可以創建多個對象
    • Java允許通過類的實例對象訪問類變數,儘管類變數是屬於類Class對象的(實例對象並不具有類變數);底層是:轉換為通過類Class對象來訪問類變數
  • 實例變數的初始化時機
    • 類中定義實例變數指定初始值;類中的非靜態代碼塊中指定初始值;構造器中指定初始值
    • class Cat{
         String name;
         int age;
         
         // 構造器中指定
         public Cat(String name, int age){
            this.name = name;
            this.age = age;
        }
       
         // 非靜態代碼塊指定
         {
            weight = 2.0;  
         }
          // 定義變數時指定
          double weight = 2.3;
      }
    • 三者的執行順序:經過編譯器處理之後,三者的賦值語句都被合併到構造器中。合併過程中,定義變數語句轉換得到的賦值語句、初始化塊里的語句轉換得到的賦值語句,總是位於構造器的所有語句之前;而前兩種語句編譯合併後的順序與它們在源代碼中的順序相同。父類構造器方法最先調用,然後再合併。javap 命令查看
    • 因此,賦值語句的執行時機為:定義變數時指定,非靜態代碼塊中指定(在創建對象實例時,非靜態代碼塊會執行) 先於  構造器中的賦值語句,至於前兩者的順序與它們的源碼順序相同,二者地位平等。
    • 上例中Cat的weight屬性值為2.3
  • 類變數的初始化時機。只有兩種:定義類變數時指定;靜態初始化塊中。兩者的執行順序為按源碼順序執行。執行完之後再執行另一個
    • 類變數初始化分為兩個階段;
      • 第一階段;分配記憶體,並且此時有預設初始化值(在賦初始值前,實例變數是在構造方法之前,類變數在靜態代碼塊前)
      • 第二階段:賦初始值(本質上是在靜態代碼塊中)

2.父類構造器

  • 隱式調用和顯式調用
    • 只要在程式創建java對象時,系統總是先調用最頂層父類的初始化操作(包括初始化塊和</先於> 構造器),然後依次向下調用所有父類的初始化操作,最終執行本類的初始化操作返回本類的對象實例。
    • 至於先調用父類的哪個構造器?
      • ①super顯示調用父類構造器
      • ②this調用本類重載構造器,然後轉到情形①
      • ③既沒有顯示super,也沒有顯示this。系統在執行子類構造器前, 隱式調用父類的無參構造器。
      • 註意:super/this,用於調用構造器(mine:構造只需一次,並且應該先構造出來,才有其他)。只能用於構造器中,必須作為第一行代碼,只能有一個(不能同時出現),只能調用一次
  • 訪問子類對象的實例變數
    • 在執行構造器代碼之前,對象所占的記憶體已經被分配下來,此時記憶體里的值預設是空值。構造器只是賦初始值。
    • 當變數的編譯類型和運行類型不同時,通過該變數訪問引用對象的實例變數時,實例變數的值由聲明該變數的類型決定(編譯類型);但是當訪問方法時,由實際所引用的對象來決定(運行類型)
  • 訪問被子類重寫的方法
    • 應該避免在父類的構造器中調用被子類重寫過的方法。否則實際運行過程中,構造器調用的將是子類重寫的方法而不是父類的方法
    • 因為:如果調用了,那麼通過子類的構造器創建子類對象實例時,若調用到父類的這個構造器,那麼會導致子類的重寫方法在子類的構造器的所有代碼執行前執行,從而導致子類的重寫方法訪問不到子類的實例變數的值得情形

3.父子實例的記憶體控制

  • 繼承成員變數與繼承方法的區別
    • 對於一個引用類型的變數而言,當通過該變數訪問引用對象的實例變數時,該實例變數的值取決於聲明該變數時的類型;當通過該變數調用所引用對象的方法時,該方法行為取決於他所實際引用的對象的類型
    • why:編譯器會將繼承的方法轉移到子類中,但是並不會將繼承的成員變數轉移到子類中;因此,重寫方法將完全覆蓋父類的方法,實例變數卻不可能覆蓋父類的成員變數,子類中允許與父類中同名的實例變數。
  • 記憶體中子類實例
      • 系統中並沒有父類對象實例,只有子類對象實例,但是子類對象實例中保存了它的所有父類所定義的全部實例變數,因此可以重名。
      • 當程式創建一個子類對象時,系統不僅僅會為該類中定義的實例變數分配記憶體,也會為其父類中定義的所有實例變數分配記憶體,即使父子類中有重名出現(子類會隱藏父類中的同名實例變數,但是不會完全覆蓋)
      • super關鍵字本身並沒有引用任何對象,甚至不能被當成一個真正的引用變數來使用(限定作用)
        • 子類方法不能直接直接使用return super;卻可以直接使用return this放回調用對象
        • 程式不允許直接把super當成變數使用,例如判斷  super ==  a;這條語句將引起編譯錯誤
        • super語句的作用:為了訪問父類中定義的、被隱藏的實例變數/調用父類中定義的、被覆蓋的方法,可以通過super.作為限定來修飾這些實例變數和方法
  • 父、子類的類變數
    • super.作為限定來訪問父類中定義的類變數(也可直接  父類名.屬性值<推薦,可讀性佳>)

4.final修飾符

  • final修飾的變數
    • 普通實例變亮,有預設初始值,但是final修飾的必須顯示的賦初始化值
    • 本質上final實例變數只能在構造器中顯示賦值
    • 本質上final類變數只能在靜態初始化塊中進行賦值
    • final修飾的局部變數,需要顯示賦值,不能修改
  • 執行“巨集替換”的變數
    • 對於一個final修飾的變數而言(不管是類變數、實例變數、還是局部變數),如果定義該final變數時就指定初始值,而且這個初始值可以再編譯時就確定下來(直接量,基本的算術表達式,字元串拼接<包括隱士類型轉換,但是顯式轉換則不可以><沒有訪問變數,沒有調用方法><編譯器很笨,不能有調用(變數/方法/...)>),那麼這個final變數本質上將不再是變數,而是相當於一個直接量。(可以通過 “==” 方法驗證)
    • 即“巨集變數”,編譯器會 把程式中 用到該變數的方法直接替換為改變數的值
    • final變數只有在定義變數時指定初始值才會有“巨集變數”效果,在其他地方指定則不會有(與編譯器 比較 笨 脫不了干係)
  • final方法不能被重寫
    • 如果子類中並不能訪問到父類中的某方法(比如限定符),那麼子類中定義的同名方法並不屬於對父類中方法的重寫(@override驗證),只是一個普通的方法,那麼就自然沒有final一說了
  • 內部類中的局部變數
    • 局部內部類<方法體中/代碼塊中>(包括匿名內部類)訪問方法體內的局部變數(其他內部類<靜態/非靜態內部類>也訪問不到方法體內的局部變數),必須用final修飾
    • why:局部內部類產生的隱式“閉包”將使局部變數脫離它所在的方法而繼續存在(擴大作用域)
    • 內部類可能擴大局部變數的作用域(eg. 內部類中新建線程,在新線程中調用局部變數),為了避免局部變數所在方法執行完畢仍然能夠隨意更改局部變數值引起的極大混亂,編譯器要求所有被內部類訪問的局部變數都必須使用final修飾
  • 吼吼吼

 

 

 

 

 


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

-Advertisement-
Play Games
更多相關文章
  • ASP.NET Core 中間件(Middleware)Diagnostics使用。對於中間件的介紹可以查看之前的文章ASP.NET Core 開發-中間件(Middleware)。 Diagnostics中間件,主要功能是用於報告和處理ASP.NET Core中的異常和錯誤信息,以及診斷Entit ...
  • 在日常的開發中,運行定時任務基本上已經是很普遍的需求了,可以通過windows服務+timer組件來實現,也可以使用第三方框架來集成,Quartz.NET就是一款從JAVA的Quartz移植過來的一個不錯的作業調度組件,但是當我們把作業都寫好,並部署完成的時候,管理成為了很麻煩的事情,因此我基於Qu ...
  • 今日問題: 請問主程式輸出結果是什麼?(點擊以下“【Java每日一題】20161216”查看20161215問題解析) 題目原發佈於公眾號、簡書:【Java每日一題】20161216,【Java每日一題】20161216 註:weknow團隊近期開通並認證了分答,歡迎大家收聽,有問題也歡迎到分答來咨 ...
  • 本文面向php語言的laravel框架的用戶,介紹一些laravel框架裡面容器管理方面的使用要點。文章很長,但是內容應該很有用,希望有需要的朋友能看到。php經驗有限,不到位的地方,歡迎幫忙指正。 1. laravel容器基本認識 laravel框架是有一個容器框架,框架應用程式的實例就是一個超大 ...
  • 我要寫幾篇隨筆,為準備學習Java作為自己第一門電腦編程語言的同學總結我認為必需、但在一般的Java教材/課程中並不教授的預備知識。但我並不教你Java本身。我(但願可以)幫你弄明白隨便一本Java教材第一章第一節講的“標準版”、“企業版”、“虛擬機”、“垃圾回收”是什麼。理想的讀者是中學生、家庭 ...
  • ...
  • 電腦網路的分類: 區域網(LAN) 指在一個較小地理範圍內的各種電腦網路設備互聯在一起的通信網路,可以包括一個或多個子網,通常局限在幾千米的範圍之內。 城域網(MAN) 主要由城域範圍內的各個區域網之間互連構成。 廣域網(WAN) 由距離較遠的區域網與城域網互聯構成的通信網路,通常是除了電腦設 ...
  • 題目:古典問題:3個月起每個月都生一對兔子,小兔子長到第三個月後每個月又生一對兔子,假如兔子都不死,問每個月的兔子總數為多少? 分析:首先我們要明白題目的意思指的是每個月的兔子總對數;假設將兔子分為小中大三種,兔子從出生後三個月後每個月就會生出一對兔子, 那麼我們假定第一個月的兔子為小兔子,第二個月 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...