Java中super與this用法及底層原理解析
理解 super 和 this 關(guān)鍵字的基本概念
在 Java 面向?qū)ο缶幊讨校?code>this和super絕非簡單的語法糖,它們是連接對(duì)象實(shí)例與類繼承體系的核心橋梁。深入理解這兩個(gè)關(guān)鍵字,不僅能解決日常開發(fā)中的變量沖突、繼承復(fù)用問題,更能幫助我們看透 Java 對(duì)象的內(nèi)存布局與初始化邏輯。本文將從底層原理、復(fù)雜場景和反編譯視角,重新解讀this與super的本質(zhì)。
this 關(guān)鍵字:對(duì)象自我引用的底層邏輯
this的本質(zhì)是當(dāng)前對(duì)象的內(nèi)存引用,在實(shí)例方法被調(diào)用時(shí)由 JVM 自動(dòng)傳遞。從字節(jié)碼層面看,所有實(shí)例方法的第一個(gè)參數(shù)都是this(只是編譯器隱式處理),這也是this能直接訪問當(dāng)前對(duì)象成員的底層原因。
當(dāng)成員變量與局部變量同名時(shí),this的作用是明確指定訪問對(duì)象堆內(nèi)存中的成員變量,而非棧內(nèi)存中的局部變量
public class User {
private String name; // 堆內(nèi)存中(對(duì)象成員)
public void setName(String name) { // 棧內(nèi)存中(局部變量)
this.name = name; // 明確將棧中name的值賦值給堆中name
}
}反編譯字節(jié)碼顯示:
aload_0加載 this 引用aload_1加載局部變量 nameputfield將局部變量值賦給堆內(nèi)存中的成員變量
構(gòu)造方法調(diào)用鏈:this (...) 的初始化邏輯
this(...)在構(gòu)造方法中調(diào)用其他構(gòu)造方法,本質(zhì)是復(fù)用初始化邏輯,避免代碼冗余。但需注意:JVM 在執(zhí)行構(gòu)造方法時(shí),會(huì)先完成所有this(...)調(diào)用,最終執(zhí)行最底層的構(gòu)造方法,再逐層返回
public class Order {
public Order(String id, String product, int count, double price) {
this.id = id;
System.out.println("全參構(gòu)造初始化");
}
public Order(String id, String product, int count) {
this(id, product, count, 0.0);
System.out.println("省略price的構(gòu)造");
}
public Order(String id) {
this(id, "未知商品", 1);
System.out.println("僅id的構(gòu)造");
}
}輸出順序:
- 全參構(gòu)造初始化
- 省略price的構(gòu)造
- 僅id的構(gòu)造
鏈?zhǔn)秸{(diào)用與 Builder 模式:this 作為返回值
this作為方法返回值時(shí),實(shí)際返回的是當(dāng)前對(duì)象的引用,這使得連續(xù)調(diào)用同一對(duì)象的方法成為可能,是 Builder 模式的核心實(shí)現(xiàn)手段。
public class UserBuilder {
public UserBuilder setName(String name) {
this.name = name;
return this;
}
public User build() {
return new User(name, age, email);
}
}優(yōu)勢:相比傳統(tǒng)的 setter 方法,鏈?zhǔn)秸{(diào)用使對(duì)象構(gòu)建邏輯更清晰,且能在編譯期避免 “未完成初始化” 的問題。
內(nèi)部類中的 this:區(qū)分內(nèi)外層實(shí)例
在非靜態(tài)內(nèi)部類中,this默認(rèn)指向內(nèi)部類實(shí)例;若需訪問外部類實(shí)例,需用外部類名.this。這是因?yàn)?strong>非靜態(tài)內(nèi)部類會(huì)隱式持有外部類的引用
public class Outer {
public class Inner {
public void show() {
System.out.println(this.innerField); // 內(nèi)部類字段
System.out.println(Outer.this.outerField); // 外部類字段
}
}
}
內(nèi)存隱患:非靜態(tài)內(nèi)部類持有外部類引用可能導(dǎo)致內(nèi)存泄漏(如 Activity 中的內(nèi)部類未及時(shí)釋放)。此時(shí)可改用靜態(tài)內(nèi)部類,并通過弱引用持有外部類
super 關(guān)鍵字:繼承體系中的父類引用
super的本質(zhì)是當(dāng)前對(duì)象中父類部分的引用。在 JVM 中,子類對(duì)象的內(nèi)存布局會(huì)包含父類的所有成員(私有成員不可直接訪問,但存在于內(nèi)存中),super就是訪問這部分內(nèi)存的入口
訪問父類成員:突破子類的 “覆蓋” 屏蔽
當(dāng)子類與父類存在同名成員(變量或方法)時(shí),子類的成員會(huì) “屏蔽” 父類的成員,而super能繞過屏蔽,直接訪問父類版
class Parent {
String name = "Parent";
public void print() {
System.out.println("Parent print");
}
}
class Child extends Parent {
String name = "Child";
@Override
public void print() {
System.out.println("Child print");
}
public void show() {
System.out.println(name); // Child
System.out.println(super.name); // Parent
print(); // Child print
super.print(); // Parent print
}
}構(gòu)造方法中的 super(...):父類初始化的強(qiáng)制要求
子類構(gòu)造方法必須先調(diào)用父類構(gòu)造方法,父類成員需先初始化。
多層繼承下,super(...) 逐層向上調(diào)用直到 Object 類的無參構(gòu)造。
class Grandparent {
public Grandparent() {
System.out.println("Grandparent");
}
}
class Parent extends Grandparent {
public Parent() {
System.out.println("Parent");
}
}
class Child extends Parent {
public Child() {
System.out.println("Child");
}
}輸出順序:
- Grandparent
- Parent
- Child
動(dòng)態(tài)綁定中的 super:繞過子類重寫的 “后門”
在多態(tài)場景下,子類對(duì)象向上轉(zhuǎn)型為父類引用時(shí),調(diào)用的方法是子類重寫后的版本(動(dòng)態(tài)綁定)。而super.方法()能強(qiáng)制調(diào)用父類的原始方法,不受動(dòng)態(tài)綁定影響。
class Animal {
public void eat() {
System.out.println("動(dòng)物進(jìn)食");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨頭");
}
public void parentEat() {
super.eat(); // 直接調(diào)用父類的eat
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Dog(); // 向上轉(zhuǎn)型
animal.eat(); // 動(dòng)態(tài)綁定:調(diào)用Dog的eat(輸出“狗吃骨頭”)
Dog dog = new Dog();
dog.parentEat(); // 調(diào)用Animal的eat(輸出“動(dòng)物進(jìn)食”)
}
}字節(jié)碼差異
普通方法調(diào)用(如animal.eat())使用invokevirtual指令,觸發(fā)動(dòng)態(tài)綁定;super.eat()使用invokespecial指令,直接調(diào)用父類方法,不經(jīng)過動(dòng)態(tài)綁定。
this 與 super 的深層對(duì)比:從內(nèi)存到執(zhí)行機(jī)制
| 維度 | this 關(guān)鍵字 | super 關(guān)鍵字 |
|---|---|---|
| 本質(zhì)引用 | 指向當(dāng)前對(duì)象的完整內(nèi)存地址 | 指向當(dāng)前對(duì)象中父類成員的內(nèi)存區(qū)域 |
| 編譯期處理 | 轉(zhuǎn)換為aload_0指令(加載當(dāng)前對(duì)象引用) | 轉(zhuǎn)換為invokespecial指令(調(diào)用父類成員) |
| 訪問權(quán)限 | 可訪問當(dāng)前類的所有成員(包括繼承的) | 只能訪問父類的非私有成員(private 不可見) |
| 在構(gòu)造方法中的作用 | 調(diào)用當(dāng)前類其他構(gòu)造,形成初始化鏈 | 調(diào)用父類構(gòu)造,確保父類成員先初始化 |
| 與多態(tài)的關(guān)系 | 指向?qū)嶋H對(duì)象(多態(tài)下仍為子類實(shí)例) | 繞過多態(tài),直接訪問父類版本 |
關(guān)鍵原理:對(duì)象的內(nèi)存布局與 super 的定位
當(dāng)創(chuàng)建子類對(duì)象時(shí),JVM 會(huì)在堆內(nèi)存中分配一塊連續(xù)空間,包含:
- 父類的所有成員變量(按繼承層次排列);
- 子類的成員變量;
- 對(duì)象頭(包含類型指針等信息)。
super的作用就是定位到這塊內(nèi)存中 “父類成員” 的起始位置,而this則指向整個(gè)對(duì)象的起始位置。
子類對(duì)象內(nèi)存布局:
[父類成員變量區(qū)][子類成員變量區(qū)][對(duì)象頭]
↑ ↑
super指向 this指向(整個(gè)對(duì)象起始)
避坑指南:this 與 super 的常見錯(cuò)誤場景
靜態(tài)方法中使用 this/super:編譯報(bào)錯(cuò)的底層原因 靜態(tài)方法屬于類,而非實(shí)例,其字節(jié)碼中沒有this參數(shù)。因此在靜態(tài)方法中使用this或super,編譯器會(huì)直接報(bào)錯(cuò)。
public class Test {
public static void method() {
// this.name; // 編譯錯(cuò)誤:static方法無this
// super.method(); // 編譯錯(cuò)誤:static方法無super
}
}
構(gòu)造方法中調(diào)用被子類重寫的方法:隱藏的邏輯陷阱 若父類構(gòu)造方法中調(diào)用了一個(gè)被子類重寫的方法,會(huì)導(dǎo)致子類對(duì)象未完成初始化時(shí)就執(zhí)行子類方法,可能引發(fā)空指針異常。
class Parent {
public Parent() {
init();
}
public void init() {
System.out.println("Parent init");
}
}
class Child extends Parent {
private String name;
@Override
public void init() {
System.out.println(name.length());
}
public static void main(String[] args) {
new Child();
}
}解決方案:父類構(gòu)造中避免調(diào)用可被重寫的方法(可將方法聲明為private或final)。
this (...) 與 super (...) 共存:構(gòu)造方法的語法禁區(qū) this(...)和super(...)都必須放在構(gòu)造方法的第一行,因此二者不能同時(shí)出現(xiàn)。
public class Test {
public Test() {
// this(1); 與super()不能同時(shí)存在
super();
}
public Test(int a) {}
}實(shí)戰(zhàn)進(jìn)階:this 與 super 在設(shè)計(jì)模式中的應(yīng)用
模板方法模式:super 調(diào)用父類骨架邏輯 模板方法模式中,父類定義算法骨架,子類重寫具體步驟。通過super調(diào)用父類模板方法,確保算法結(jié)構(gòu)不變。
abstract class AbstractProcessor {
public final void process() {
step1();
step2();
step3();
}
protected void step1() {
System.out.println("固定步驟1");
}
protected abstract void step2();
protected void step3() {
System.out.println("固定步驟3");
}
}
class ConcreteProcessor extends AbstractProcessor {
@Override
protected void step2() {
System.out.println("子類實(shí)現(xiàn)的步驟2");
}
public static void main(String[] args) {
AbstractProcessor processor = new ConcreteProcessor();
processor.process();
}
}裝飾器模式:this 傳遞當(dāng)前裝飾對(duì)象 裝飾器模式中,裝飾類通過this將自身傳遞給被裝飾對(duì)象,實(shí)現(xiàn)功能增強(qiáng)的鏈?zhǔn)蒋B加。
interface Component {
void operation();
}
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("基礎(chǔ)功能");
}
}
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
}
class LogDecorator extends Decorator {
public LogDecorator(Component component) {
super(component);
}
@Override
public void operation() {
System.out.println("日志開始");
component.operation();
System.out.println("日志結(jié)束");
}
}
class TimeDecorator extends Decorator {
public TimeDecorator(Component component) {
super(component);
}
@Override
public void operation() {
long start = System.currentTimeMillis();
component.operation();
System.out.println("耗時(shí):" + (System.currentTimeMillis() - start));
}
}
public class Test {
public static void main(String[] args) {
Component component = new TimeDecorator(
new LogDecorator(new ConcreteComponent())
);
component.operation();
}
}總結(jié)
this和super是 Java 面向?qū)ο蟮?“隱形支柱”:
this是對(duì)象的 “自我認(rèn)知”,讓實(shí)例能清晰訪問自身成員、復(fù)用構(gòu)造邏輯,并支撐鏈?zhǔn)秸{(diào)用等優(yōu)雅設(shè)計(jì);super是子類對(duì)父類的 “繼承契約”,確保父類初始化優(yōu)先,同時(shí)提供訪問父類成員的安全通道,是繼承與多態(tài)的基礎(chǔ)。
深入理解二者的關(guān)鍵,在于看透其背后的內(nèi)存模型與執(zhí)行機(jī)制 ——this指向?qū)ο笳w,super指向?qū)ο笾械母割惒糠郑吖餐瑯?gòu)成了 Java 對(duì)象的完整邏輯。
到此這篇關(guān)于Java中super與this的深入解析的文章就介紹到這了,更多相關(guān)java super與this內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java利用opencv實(shí)現(xiàn)用字符展示視頻或圖片的方法
這篇文章主要介紹了Java利用opencv實(shí)現(xiàn)用字符展示視頻或圖片的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
Java利用Spire.Presentation for Java實(shí)現(xiàn)高效處理圖片轉(zhuǎn)PPT
在日常工作或?qū)W習(xí)中,我們經(jīng)常需要將大量的圖片整理成演示文稿,本文將聚焦于如何利用Spire.Presentation for Java這一強(qiáng)大的Java庫,輕松實(shí)現(xiàn)圖片到PowerPoint的轉(zhuǎn)換,希望對(duì)大家有所幫助2025-10-10
mybatis-plus 新增/修改如何實(shí)現(xiàn)自動(dòng)填充指定字段
這篇文章主要介紹了mybatis-plus 新增/修改實(shí)現(xiàn)自動(dòng)填充指定字段方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
try-cache-finally讀取文件錯(cuò)誤try-with-resources使用方法
這篇文章主要為大家介紹了try-cache-finally讀取文件錯(cuò)誤try-with-resources使用方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
Java 騰訊驗(yàn)證碼平臺(tái)使用實(shí)例
這篇文章主要介紹了Java 騰訊驗(yàn)證碼平臺(tái)使用實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
使用redis的increment()方法實(shí)現(xiàn)計(jì)數(shù)器功能案例
這篇文章主要介紹了使用redis的increment()方法實(shí)現(xiàn)計(jì)數(shù)器功能案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-11-11
解決spring項(xiàng)目找不到Aspect依賴注解的問題
這篇文章主要介紹了解決spring項(xiàng)目找不到Aspect依賴注解的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06

