程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> No.2 對象與內存控制(內存分配),no.2對象

No.2 對象與內存控制(內存分配),no.2對象

編輯:JAVA綜合教程

No.2 對象與內存控制(內存分配),no.2對象


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修飾
  • 吼吼吼

 

 

 

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved