Java面向對象(高級)-- 類中屬性賦值的位置及過程

文章目錄

  • 一、賦值順序
    • (1)賦值的位置及順序
    • (2)舉例
    • (3)字節碼文件
    • (4)進一步探索
    • (5)最終賦值順序
    • (6)實際開發如何選
  • 二、(超綱)關于字節碼文件中的<init>
  • 三、面試題
    • (1)面試題1
    • (2)面試題2
    • (3)面試題3
    • (4)面試題4

一、賦值順序

(1)賦值的位置及順序

  • 可以給類的非靜態的屬性(即實例變量)賦值的位置有:

① 默認初始化

② 顯式初始化

⑤ 代碼塊中初始化

③ 構造器中初始化

#############################

④ 有了對象以后,通過"對象.屬性"或"對象.方法"的方法進行賦值

(造對象之前叫初始化,造對象之后叫賦值)

  • 執行的先后順序:

① - ② - ③ - ④

代碼塊中初始化應該放在哪?

image.png

(2)舉例

【舉例】

先看一段代碼:

package yuyi06;/*** ClassName: FieldTest* Package: yuyi06* Description:** @Author 雨翼輕塵* @Create 2023/11/19 0019 16:25*/
public class FieldTest {public static void main(String[] args) {Order o1=new Order();System.out.println(o1.orderId); //1}
}class Order{int orderId=1;}

輸出結果:

image.png

這個很簡單,顯而易見。

現在整一個代碼塊,看一下它和顯式賦值誰先誰后。

public class FieldTest {public static void main(String[] args) {Order o1=new Order();System.out.println(o1.orderId); //2}
}class Order{int orderId=1;{orderId=2;}}

輸出結果:

image.png

那么一定是先有1,后有2。所以代碼塊初始化肯定是在顯示初始化之后


接下來是構造器和代碼塊。

創建一個空參構造器,那么在創建對象的時候一定會調用它。

ublic class FieldTest {public static void main(String[] args) {Order o1=new Order();System.out.println(o1.orderId); //3}
}class Order{int orderId=1;{orderId=2;}public Order(){orderId=3;}}

輸出結果:

image.png

結果是3,所以3將2覆蓋了。所以代碼塊初始化在構造器初始化前面

所以目前來看,執行順序是這樣的:① - ② - ⑤ - ③ - ④

(3)字節碼文件

將光標放在Order類中,看一下字節碼文件。

插件在這里:

image.png

先運行然后重新編譯一下,確保生成的字節碼文件和代碼一致。

image.png

然后點擊這個即可:

image.png

構造器會以<init>方法的方式呈現在字節碼文件中,如下:

image.png

看一下代碼:

image.png

方法里面對應的是個棧幀,棧幀里面會放局部變量,

aload_0 就是指局部變量第0個位置–this,表示當前正在創建的對象,通過aload_0調用現在的方法。

如下:

image.png

畫個圖解釋一下Code的意思:

image.png

(4)進一步探索

根據上面得出來的結論,代碼塊賦值在顯式賦值之后,那么將它們倆的代碼換個位置呢?

如下:

public class FieldTest {public static void main(String[] args) {Order o1=new Order();System.out.println(o1.orderId); }
}class Order{{orderId=2;}int orderId=1;public Order(){//orderId=3;}}

輸出結果:

image.png

怎么是1了呢?肯定是先有2,后有1。

看字節碼文件:

image.png

這樣來看,代碼塊賦值又先于了顯示賦值。

剛才的① - ② - ⑤ - ③ - ④ 明顯不太對。

②和⑤就是看誰先聲明,誰就先執行。

所以應該是這樣的:① - ②/⑤ - ③ - ④


💬為啥將代碼塊寫在顯示賦值上面,不會報錯,這時候變量還未聲明啊?

其實這個地方一直有個誤區,舉個例子:

image.png

可以看到,在eat()方法中可以調用sleep()方法。

若是按照剛才的說法,先有eat(),后有sleep(),怎么一上來就可以sleep(),此時sleep()還沒有聲明啊,但是怎么沒有報錯?

我們只需要考慮,編譯的時候,會看到eat()里面調用了sleep()方法,這個方法找一下有沒有,發現有,那能確保調用sleep()的時候,內存中有嗎?

其實在加載類的時候(將類放入了方法區),其實sleep()也好,eat()也好,方法都加載了的。

所以只需要確保調用這個方法之前,這個方法加載了就行

回到這里:

//代碼塊賦值
{orderId=2;
}//顯示賦值
int orderId=1;

現在這種情況也可以用類似的方式去解釋,以后再說類加載的詳細過程,現在就說最核心的點。

orderId在整個類的加載中有一個過程,在其中某一個環節,就已經將orderId給加載了,而且還給了一個默認賦值0,這個時候orderId屬性就已經有了。在后續的環節中,才開始做顯示賦值和代碼塊的賦值

現在是先有代碼塊的賦值,那么就將orderId改為2,后面又顯示賦值,將它改為1。

一般習慣將代碼塊寫顯示賦值的下面

(5)最終賦值順序

可以給類的非靜態的屬性(即實例變量)賦值的位置有:

① 默認初始化

② 顯式初始化 或 ⑤ 代碼塊中初始化

③ 構造器中初始化

#############################

④ 有了對象以后,通過"對象.屬性"或"對象.方法"的方法進行賦值

(造對象之前叫初始化,造對象之后叫賦值)

執行的先后順序

① - ②/⑤ - ③ - ④

(6)實際開發如何選

💬 給實例變量賦值的位置很多,開發中如何選?

  • 顯示賦值:比較適合于每個對象的屬性值相同的場景。
  • 構造器中賦值:比較適合于每個對象的屬性值不相同的場景(通過形參的方式給它賦值)。
  • 非靜態代碼塊:用的比較少,在構造器里面基本能完成。
  • 靜態代碼塊:靜態(與類相關)屬性不會選擇在構造器(與對象相關)中賦值。靜態的變量要么默認賦值,要么顯示賦值,要么代碼塊中賦值。

二、(超綱)關于字節碼文件中的

(超綱)關于字節碼文件中的的簡單說明(通過插件jclasslib bytecode viewer查看)

剛才查看字節碼文件的時候,可以看到,這里做個簡單說明,便于大家理解。

🚗說明

<init>方法在字節碼文件中可以看到。每個方法都對應著一個類的構造器。(類中聲明了幾個構造器就會有幾個

既然構造器和一 一對應,在字節碼文件中也看不到“構造器”這一項。

image.png

所以構造器就是以方法的形式呈現在字節碼文件中的。

比如這里聲明了倆構造器:

class Order{{orderId=2;}int orderId=1;public Order(){//orderId=3;}public Order(int orderId){this.orderId=orderId;}public void eat(){sleep();}public void sleep(){}}

看一下字節碼文件有兩個,如下:

image.png

點開第二個,一起來看一下它的Code:

image.png

角標為1的值:

image.png

所以通過第二個有參構造器去造對象的時候,也會有顯示賦值和代碼塊的執行,然后才是構造器。對應字節碼文件中就是方法。

②編寫的代碼中的構造器在編譯以后就會以<init>方法的方式呈現。(方法和構造器不是一回事)

<init>方法內部的代碼包含了實例變量的顯示賦值、代碼塊中的賦值和構造器中的代碼

<init>方法用來初始化當前創建的對象的信息的

構造器和方法不是一回事,字節碼文件中沒有“構造器”,是以方法的形式呈現的。

三、面試題

(1)面試題1

下面代碼輸出結果是?

package yuyi06;//技巧:由父及子,靜態先行。class Root{//靜態代碼塊static{System.out.println("Root的靜態初始化塊");}//非靜態代碼塊{System.out.println("Root的普通初始化塊");}//構造器public Root(){super();System.out.println("Root的無參數的構造器");}
}class Mid extends Root{static{System.out.println("Mid的靜態初始化塊");}{System.out.println("Mid的普通初始化塊");}public Mid(){System.out.println("Mid的無參數的構造器");}public Mid(String msg){//通過this調用同一類中重載的構造器this();System.out.println("Mid的帶參數構造器,其參數值:"+ msg);}
}class Leaf extends Mid{static{System.out.println("Leaf的靜態初始化塊");}{System.out.println("Leaf的普通初始化塊");}public Leaf(){//通過super調用父類中有一個字符串參數的構造器super("雨翼輕塵");System.out.println("Leaf的構造器");}
}public class LeafTest{public static void main(String[] args){new Leaf(); //涉及到當前類,以及它的父類、父類的父類的加載包括相應功能的執行//    System.out.println();//    new Leaf();}
}

🤸分析

new Leaf(); 涉及到當前類,以及它的父類、父類的父類的加載包括相應功能的執行。

分析先后執行的順序。

上面的類中,分別都有靜態代碼塊、非靜態代碼塊和構造器。

首先應該是靜態代碼塊進行類加載的時候,一定先加載父類的,然后才是子類

之前說的方法的重寫,一定是先有父類的方法,才能覆蓋它。(先加載父類)

當我們通過leaf()造對象,首先會通過super()找到父類。(沒有寫也是super)

畫個圖看一下邏輯:

image.png

所以最先加載的類是Object,只不過改不了代碼,也沒有輸出語句,

所以看似好像沒加載,其實是先加載它,其次是Root類,然后就是Root類里面的static代碼塊,下面的非靜態代碼塊和無參構造器就別先執行了,因為要先將類的加載都執行了

如下:

image.png

所以,看一下執行結果:(前面三行是“靜態初始化塊”)

image.png

類的加載就完成了。

下面才涉及造對象。

靜態加載之后,先去new了一個leaf(),然后執行super(),一直到最上層的Root類,先考慮它的構造器的加載(涉及到非靜態結構的加載,然后才是子類),代碼塊的執行又早于構造器,所以會先輸出代碼塊中的內容。

剛才說到調用的過程如下:

image.png

輸出的話,就是反過來:

image.png

運行結果如下:

image.png

技巧:由父及子,靜態先行。(先加載父類,后加載子類,靜態結構早于非靜態(init方法)的,非靜態代碼塊的執行又早于構造器的執行)

方法包括代碼塊的,每個構造器都默認調用父類的構造器。

方法不是通過對象.去調用的,而是自動執行的。

(2)面試題2

下面代碼輸出結果是?

class HelloA {public HelloA() {System.out.println("HelloA");}{System.out.println("I'm A Class");}static {System.out.println("static A");}
}class HelloB extends HelloA {public HelloB() {System.out.println("HelloB");}{System.out.println("I'm B Class");}static {System.out.println("static B");}}public class Test01 {public static void main(String[] args) {new HelloB();}
}

🤸分析

畫個圖演示一下:
image.png

執行輸出順序:①->②->③->④->⑤->⑥

先將類的加載搞定。

HelloA中,有靜態先調用靜態,輸出“static A”,

然后回到HelloA中,調用靜態,輸出“static B”。

然后考慮當前要創建的對象的構造器HelloB(),此時第一行會調用super(),

調用HelloA()構造器。

再HelloA()構造器中,有非靜態代碼塊,先執行它,輸出“I’m A Class”,

然后輸出構造器中“HelloA”。

super()執行結束之后,回到HelloB(),此時HelloB類中也有非靜態代碼塊,

所以先輸出代碼塊中“I’m B Class”,最后輸出HelloB()構造器中“HelloB”。

👻代碼運行結果

image.png

(3)面試題3

下面代碼輸出結果是?

public class Test02 {static int x, y, z;static {int x = 5;x--;}static {x--;}public static void method() {y = z++ + ++z;}public static void main(String[] args) {System.out.println("x=" + x);z--;method();System.out.println("result:" + (z + y + ++z));}
}

🤸分析

畫個圖:(執行順序:①->②->③->④->⑤->⑥)

image.png

👻輸出結果:

image.png

(4)面試題4

下面代碼輸出結果是?

public class Test03 {public static void main(String[] args) {Sub s = new Sub();}
}
class Base{Base(){method(100);}{System.out.println("base");}public void method(int i){System.out.println("base : " + i);}
}
class Sub extends Base{Sub(){super.method(70);}{System.out.println("sub");}public void method(int j){System.out.println("sub : " + j);}
}

🤸分析

畫個圖:(執行順序:①->②->③->④->⑤->⑥->⑦->⑧)

image.png

🚗調試

大家也可以自行調試,這里就做個示范。

問題4.gif

👻輸出結果:

image.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/160928.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/160928.shtml
英文地址,請注明出處:http://en.pswp.cn/news/160928.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

1992-2021年省市縣經過矯正的夜間燈光數據(GNLD、VIIRS)

1992-2021年省市縣經過矯正的夜間燈光數據&#xff08;GNLD、VIIRS&#xff09; 1、時間&#xff1a;1992-2021年3月&#xff0c;其中1992-2013年為年度數據&#xff0c;2013-2021年3月為月度數據 2、來源&#xff1a;DMSP、VIIRS 3、范圍&#xff1a;分區域匯總&#xff1a…

SpringBoot : ch05 整合Mybatis

前言 隨著Java Web應用程序的快速發展&#xff0c;開發人員需要越來越多地關注如何高效地構建可靠的應用程序。Spring Boot作為一種快速開發框架&#xff0c;旨在簡化基于Spring的應用程序的初始搭建和開發過程。而MyBatis作為一種優秀的持久層框架&#xff0c;提供了對數據庫…

【Linux】-進程間通信-共享內存(SystemV),詳解接口函數以及原理(使用管道處理同步互斥機制)

&#x1f496;作者&#xff1a;小樹苗渴望變成參天大樹&#x1f388; &#x1f389;作者宣言&#xff1a;認真寫好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee? &#x1f49e;作者專欄&#xff1a;C語言,數據結構初階,Linux,C 動態規劃算法&#x1f384; 如 果 你 …

中低壓MOSFET 2N7002T 60V 300mA 雙N通道 采用SOT-523封裝形式

2N7002KW小電流雙N通道MOSFET&#xff0c;電壓60V電流300mA&#xff0c;采用SOT-523封裝形式。低Ros (on)的高密度單元設計&#xff0c;堅固可靠&#xff0c;具有高飽和電流能力&#xff0c;ESD防護門HBM2KV。可應用于直流/直流轉換器&#xff0c;電池開關等產品應用上。

Redis JDBC

1、導入依賴&#xff1a; <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.4.3</version></dependency> 2、連接JDBC public class JedisDemo05 {public static void main(String[]…

成為AI產品經理——AI產品經理工作全流程

一、業務背景 背景&#xff1a;日常排球訓練&#xff0c;中考排球項目和排球體測項目耗費大量人力成本和時間成本。 目標&#xff1a;開發一套用于實時檢測排球運動并進行排球墊球計數和姿勢分析的軟件。 二、產品工作流程 我們這里對于產品工作流程的關鍵部分進行講解&…

「Docker」如何在蘋果電腦上構建簡單的Go云原生程序「MacOS」

介紹 使用Docker開發Golang云原生應用程序&#xff0c;使用Golang服務和Redis服務 注&#xff1a;寫得很詳細 為方便我的朋友可以看懂 環境部署 確保已經安裝Go、docker等基礎配置 官網下載鏈接直達&#xff1a;Docker官網下載 Go官網下載 操作步驟 第一步 創建一個…

Java 多線程之 DCL(Double-Checked Locking)

文章目錄 一、概述二、使用方法 一、概述 DCL&#xff08;Double-Checked Locking&#xff09;是一種用于在多線程環境下實現延遲初始化的技術。它結合了懶加載&#xff08;Lazy Initialization&#xff09;和線程安全性&#xff0c;用于在需要時創建單例對象或共享資源。它的…

什么是SEO?(初學者建議收藏)

前言 在這個充滿機遇和挑戰的時代&#xff0c;人們不斷追求著更好的生活和更高的成就。無論是個人還是企業&#xff0c;都需要不斷提升自己的競爭力&#xff0c;才能在激烈的市場競爭中獲得成功。因此&#xff0c;我們需要不斷學習和成長&#xff0c;學會適應變化和面對挑戰。…

汽車智能座艙/智能駕駛SOC -2

第二篇&#xff08;筆記&#xff09;。 未來智能汽車電子電氣將會是集中式架構&#xff08;車載數據中心&#xff09;虛擬化技術&#xff08;提供車載數據中心靈活性和安全性&#xff09;這個幾乎是毋庸置疑的了。國際大廠也否紛紛布局超算芯片和車載數據中心平臺。但是演進需…

日期格式轉化成星期幾部署到linux顯示英文

異常收集 原因&#xff1a;解決辦法仰天大笑出門去&#xff0c;我輩豈是蓬蒿人 傳入一個時間獲取這個時間對應的是星期幾&#xff0c;在開發環境&#xff08;window系統&#xff09;中顯示為星期幾&#xff0c;部署到服務器&#xff08;linux系統&#xff09;中會顯示英文的時間…

serverless開發實戰

.yml格式 YAML&#xff08;YAML Ain’t Markup Language&#xff09;是一種人類可讀的數據序列化格式&#xff0c;它使用簡潔的結構和縮進來表示數據。它被廣泛用于配置文件和數據交換的場景&#xff0c;具有易讀性和易寫性的特點。 serverless.yml配置 在項目的根目錄下增加…

Youtube新手運營——你需要的技巧與工具

對于有跨境意向的內容創作者或者品牌企業來說&#xff0c;YouTube是因其巨大的潛在受眾群和商業價值成為最值得投入變現與營銷計劃的平臺。 據統計&#xff0c;98% 的美國人每月訪問 YouTube&#xff0c;近三分之二的人每天訪問。但是&#xff0c;YouTube還遠未達到過度飽和的…

Leetcode—53.最大子數組和【中等】

2023每日刷題&#xff08;三十四&#xff09; Leetcode—53.最大子數組和 前綴和算法思想 參考靈茶山艾府 實現代碼 #define MAX(a, b) ((a > b) ? (a) : (b)) #define MIN(a, b) ((a < b) ? (a) : (b)) int maxSubArray(int* nums, int numsSize) {int ans INT_…

VMware 16 Pro 安裝以及下載

1、下載地址&#xff1a; https://www.aliyundrive.com/s/nj3PSD4TN9G 2、安裝文件 右擊打開 下一步 密鑰&#xff1a;ZF3R0-FHED2-M80TY-8QYGC-NPKYF 到此&#xff0c;安裝完畢

postgreSQL如何快速查詢大表數據量

文章目錄 場景方案結果 場景 我有一個非常大的表&#xff0c;估計幾百萬或者幾千萬。 我開始使用了 select count(*) from my_table_javapub 方式&#xff0c;查詢非常慢。 如何解決&#xff1f;&#xff1f;&#xff1f; 方案 如果你需要更快地獲取表中的行數&#xff0c…

93.STL-系統內置仿函數

目錄 算術仿函數 關系仿函數 邏輯仿函數 C 標準庫中提供了一些內置的函數對象&#xff0c;也稱為仿函數&#xff0c;它們通常位于 <functional> 頭文件中。以下是一些常見的系統內置仿函數&#xff1a; 算術仿函數 功能描述&#xff1a; 實現四則運算其中negate是一元…

Java游戲之飛翔的小鳥

前言 飛翔的小鳥 小游戲 可以作為 java入門階段的收尾作品 &#xff1b; 需要掌握 面向對象的使用以及了解 多線程&#xff0c;IO流&#xff0c;異常處理&#xff0c;一些java基礎等相關知識。一 、游戲分析 1. 分析游戲邏輯 &#xff08;1&#xff09;先讓窗口顯示出來&#x…

騰訊待辦導出的文件在哪找?支持打開ics文件的提醒待辦工具

您使用過騰訊待辦嗎&#xff1f;如果您在平常使用的提醒待辦工具為騰訊待辦&#xff0c;想必近期您打開這款提醒待辦工具時會看到提示您及時導出數據的提示。騰訊旗下的騰訊待辦應用&#xff0c;應業務發展方向調整將于2023年12月20日全面停止運營并下架該應用。 面對突如其來…