Java關(guān)鍵字static的6個(gè)常見(jiàn)誤區(qū)及解決方法
static 是 Java 入門最早接觸、卻也是誤解最多的關(guān)鍵字。很多同學(xué)只會(huì)用它寫靜態(tài)常量、靜態(tài)方法,但對(duì)生命周期、繼承、重寫、初始化順序、this 能否使用、線程安全等認(rèn)知模糊,日常寫 Bug、面試翻車,大多都是踩了 static 的坑。
誤區(qū)一:static 屬于對(duì)象,而不是屬于類
錯(cuò)誤認(rèn)知
靜態(tài)變量、靜態(tài)方法是歸每個(gè)對(duì)象所有,創(chuàng)建越多實(shí)例,靜態(tài)變量就有多份副本。
正確原理
1. static 修飾的成員,全局只一份,歸屬于類,不屬于任何對(duì)象。
2. 類加載階段就初始化,優(yōu)先于對(duì)象存在,所有實(shí)例共享同一個(gè)靜態(tài)變量。
3. 無(wú)論 new 多少個(gè)對(duì)象,靜態(tài)變量?jī)?nèi)存中永遠(yuǎn)只有一份。
反例代碼
public class StaticDemo {
public static int count = 0;
public StaticDemo(){
count++;
}
}連續(xù) new 三次對(duì)象,count 直接累加到 3,而不是每個(gè)對(duì)象各自為 1。
結(jié)論
靜態(tài)資源全局唯一、對(duì)象共享,跟實(shí)例數(shù)量無(wú)關(guān)。
誤區(qū)二:靜態(tài)方法可以被重寫(Override)
錯(cuò)誤認(rèn)知
父類有靜態(tài)方法,子類寫一個(gè)同名同參靜態(tài)方法,就是重寫。
正確原理
1. 靜態(tài)方法不能被重寫,只能被隱藏(Hide)。
2. 靜態(tài)方法編譯期靜態(tài)綁定,由引用類型決定調(diào)用哪個(gè)方法,而不是運(yùn)行時(shí)對(duì)象類型。
3. 多態(tài)對(duì) static 完全失效。
示例對(duì)比
class Father{
public static void show(){
System.out.println("父類靜態(tài)方法");
}
}
class Son extends Father{
public static void show(){
System.out.println("子類靜態(tài)方法");
}
}
public class Test{
public static void main(String[] args) {
Father f = new Son();
f.show(); // 輸出:父類靜態(tài)方法
}
}如果是普通實(shí)例方法,會(huì)走多態(tài)調(diào)用子類;靜態(tài)方法看引用類型,不看實(shí)際對(duì)象。
結(jié)論
靜態(tài)方法只可隱藏、不可重寫,不具備多態(tài)特性。
誤區(qū)三:靜態(tài)方法中可以直接使用 this、super
錯(cuò)誤認(rèn)知
普通方法能用 this,靜態(tài)方法也能用 this 訪問(wèn)成員。
正確原理
1. this、super 代表當(dāng)前實(shí)例對(duì)象。
2. 靜態(tài)方法屬于類,不需要對(duì)象就能調(diào)用,執(zhí)行時(shí)可能根本沒(méi)有實(shí)例。
3. 因此靜態(tài)方法禁止使用 this、super,也不能直接調(diào)用普通實(shí)例方法、直接訪問(wèn)實(shí)例變量。
編譯報(bào)錯(cuò)代碼
public static void test(){
this.xxx(); // 編譯直接報(bào)錯(cuò)
}結(jié)論
靜態(tài)上下文與實(shí)例無(wú)關(guān),不能出現(xiàn)任何對(duì)象相關(guān)關(guān)鍵字。
誤區(qū)四:靜態(tài)代碼塊只會(huì)執(zhí)行一次,隨便寫都沒(méi)問(wèn)題
錯(cuò)誤認(rèn)知
靜態(tài)代碼塊隨時(shí)都會(huì)執(zhí)行,或者每次 new 對(duì)象都會(huì)執(zhí)行。
正確原理
1. 靜態(tài)代碼塊:類加載時(shí)執(zhí)行,全局只執(zhí)行一次。
2. 執(zhí)行時(shí)機(jī)早于構(gòu)造器、早于普通代碼塊。
3. 只會(huì)在類首次被加載、主動(dòng)使用時(shí)觸發(fā)一次,后續(xù) new 對(duì)象不再執(zhí)行。
4. 坑:靜態(tài)塊里不要寫耗時(shí)邏輯、數(shù)據(jù)庫(kù)連接、死循環(huán),會(huì)直接拖慢項(xiàng)目啟動(dòng)、導(dǎo)致卡死。
執(zhí)行順序
靜態(tài)代碼塊 → 普通代碼塊 → 構(gòu)造方法
結(jié)論
靜態(tài)塊隨類加載只跑一次,適合初始化常量、加載配置,禁止寫耗時(shí)業(yè)務(wù)邏輯。
誤區(qū)五:static 變量天然線程安全
錯(cuò)誤認(rèn)知
靜態(tài)變量全局只有一份,自帶同步,多線程修改也不會(huì)有并發(fā)問(wèn)題。
正確原理
1. static 只是全局共享一份內(nèi)存,不具備任何原子性、可見(jiàn)性、有序性保障。
2. 多線程同時(shí)讀寫靜態(tài)變量,依然會(huì)出現(xiàn)臟數(shù)據(jù)、覆蓋丟失、競(jìng)態(tài)條件。
3. 靜態(tài)變量反而更容易引發(fā)并發(fā) Bug,因?yàn)樗芯€程共享同一份。
并發(fā)錯(cuò)誤示例
public class CountUtil{
public static int num = 0;
public static void add(){
num++;
}
}多線程循環(huán)累加,結(jié)果一定小于預(yù)期,典型線程不安全。
正確做法
靜態(tài)變量并發(fā)修改需要:synchronized / AtomicInteger / Lock 保證線程安全。
結(jié)論
static ≠ 線程安全,共享變量反而更容易出并發(fā)問(wèn)題。
誤區(qū)六:可以在普通代碼塊/構(gòu)造器中給靜態(tài)變量隨意初始化
錯(cuò)誤認(rèn)知
靜態(tài)變量在哪賦值都一樣,構(gòu)造器、普通代碼塊隨便改。
正確原理
1. 靜態(tài)變量屬于類,本該在:靜態(tài)代碼塊、聲明處初始化。
2. 在構(gòu)造器、普通代碼塊給靜態(tài)變量賦值:
- 每 new 一次對(duì)象,就會(huì)重新覆蓋靜態(tài)值;
- 極易造成全局狀態(tài)被意外篡改,隱藏極深的 Bug。
3. 違背設(shè)計(jì)語(yǔ)義:靜態(tài)資源不該依賴實(shí)例創(chuàng)建來(lái)初始化。
坑示例
public class User{
public static String type;
public User(){
type = "user";
}
}只要 new 一次,就強(qiáng)行覆蓋靜態(tài)變量,全局狀態(tài)被隨意改動(dòng)。
結(jié)論
靜態(tài)變量盡量在聲明或靜態(tài)塊初始化,不要在構(gòu)造器、實(shí)例代碼塊中賦值。
全文總結(jié)
這 6 個(gè)誤區(qū),既是日常開(kāi)發(fā)高頻坑,也是 Java 面試常問(wèn)原題。
1. static 歸類不歸對(duì)象,全局唯一共享;
2. 靜態(tài)方法不可重寫、只能隱藏,無(wú)多態(tài);
3. 靜態(tài)上下文不能用 this / super,不能直接訪問(wèn)實(shí)例成員;
4. 靜態(tài)代碼塊類加載僅執(zhí)行一次,禁止耗時(shí)邏輯;
5. static 變量不自帶線程安全,多線程必須加同步;
6. 靜態(tài)變量盡量靜態(tài)環(huán)境初始化,不要在構(gòu)造器中亂賦值。
以上就是Java關(guān)鍵字static的6個(gè)常見(jiàn)誤區(qū)及解決方法的詳細(xì)內(nèi)容,更多關(guān)于Java static關(guān)鍵字常見(jiàn)誤區(qū)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java開(kāi)發(fā)到底為什么要用 IoC 和 AOP
這篇文章主要介紹了Java開(kāi)發(fā)到底為什么要用 IoC 和 AOP,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下2021-02-02
SpringBoot如何獲取當(dāng)前操作用戶的id/信息
在一般性的基設(shè)需求中,有需要獲取當(dāng)前用戶操作記錄的情況,也就是說(shuō)我們需要記錄當(dāng)前用戶的信息,如:id、昵稱、賬號(hào)等信息,這篇文章主要介紹了SpringBoot獲取當(dāng)前操作用戶的id/信息,需要的朋友可以參考下2023-10-10
Java中comparator接口和Comparable接口的比較解析
這篇文章主要介紹了Java中comparator接口和Comparable接口的比較解析,Java提供了一個(gè)用于比較的接口Comparator和Comparable接口,提供了一個(gè)比較的方法,所有實(shí)現(xiàn)該接口的類,都動(dòng)態(tài)的實(shí)現(xiàn)了該比較方法,需要的朋友可以參考下2023-08-08
Jedis出現(xiàn)connection timeout問(wèn)題解決方法(JedisPool連接池使用實(shí)例)
這篇文章主要介紹了Jedis出現(xiàn)connection timeout問(wèn)題解決方法,使用Jedis的JedisPool連接池解決了這個(gè)問(wèn)題,需要的朋友可以參考下2014-05-05
簡(jiǎn)單了解JAVA public class與class區(qū)別
這篇文章主要介紹了簡(jiǎn)單了解JAVA public class與class區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
@Valid和@Validated注解校驗(yàn)以及異常處理方式
在Javaweb開(kāi)發(fā)中,防止數(shù)據(jù)庫(kù)惡意攻擊是至關(guān)重要的,盡管前端校驗(yàn)可以起到一定的篩選作用,但通過(guò)工具如postman直接對(duì)后端發(fā)起請(qǐng)求的情況仍然需要后端進(jìn)行嚴(yán)格的數(shù)據(jù)校驗(yàn),Java生態(tài)下,@Valid注解配合SpringBoot提供了一個(gè)便捷高效的后端數(shù)據(jù)校驗(yàn)方案2024-11-11
SpringBoot3整合SpringDoc實(shí)現(xiàn)在線接口文檔的詳細(xì)過(guò)程
這篇文章主要介紹了SpringBoot3整合SpringDoc實(shí)現(xiàn)在線接口文檔的詳細(xì)過(guò)程,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-06-06

