跳到主要內容

一個 static 還能難得住我?_台中搬家


台中搬家公司費用怎麼算?



擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司



static 是我們日常生活中經常用到的關鍵字,也是 Java 中非常重要的一個關鍵字,static 可以修飾變量、方法、做靜態代碼塊、靜態導包等,下面我們就來具體聊一聊這個關鍵字,我們先從基礎開始,從基本用法入手,然後分析其原理、優化等。


初識 static 關鍵字


static 修飾變量


static 關鍵字表示的概念是 全局的、靜態的,用它修飾的變量被稱為靜態變量


public class TestStatic {

static int i = 10; // 定義了一個靜態變量 i
}

靜態變量也被稱為類變量,靜態變量是屬於這個類所有的。什麼意思呢?這其實就是說,static 關鍵字只能定義在類的 {} 中,而不能定義在任何方法中。



就算把方法中的 static 關鍵字去掉也是一樣的。



static 屬於類所有,由類來直接調用 static 修飾的變量,它不需要手動實例化類進行調用


public class TestStatic {

static int i = 10;

public static void main(String[] args) {
System.out.println(TestStatic.i);
}
}

這裏你需要理解幾個變量的概念



  • 定義在構造方法、代碼塊、方法的變量被稱為實例變量,實例變量的副本數量和實例的數量一樣。

  • 定義在方法、構造方法、代碼塊的變量被稱為局部變量;

  • 定義在方法參數的變量被稱為參數。


詳情參考


static 修飾方法


static 可以修飾方法,被 static 修飾的方法被稱為靜態方法,其實就是在一個方法定義中加上 static 關鍵字進行修飾,例如下面這樣


static void sayHello(){}

《Java 編程思想》在 P86 頁有一句經典的描述


static 方法就是沒有 this 的方法,在 static 內部不能調用非靜態方法,反過來是可以的。而且可以在沒有創建任何對象的前提下,僅僅通過類本身來調用 static 方法,這實際上是 static 方法的主要用途


其中有一句非常重要的話就是 static 方法就是沒有 this 的方法,也就是說,可以在不用創建對象的前提下就能夠訪問 static 方法,如何做到呢?看下面一段代碼



在上面的例子中,由於 staticMethod 是靜態方法,所以能夠使用 類名.變量名進行調用。


因此,如果說想在不創建對象的情況下調用某個方法,就可以將這個方法設置為 static。平常我們見的最多的 static 方法就是 main方 法,至於為什麼 main 方法必須是 static 的,現在應該很清楚了。因為程序在執行 main 方法的時候沒有創建任何對象,因此只有通過類名來訪問。


static 修飾方法的注意事項



  • 首先第一點就是最常用的,不用創建對象,直接類名.變量名 即可訪問;

  • static 修飾的方法內部不能調用非靜態方法;




  • 非靜態方法內部可以調用 static 靜態方法。



static 修飾代碼塊


static 關鍵字可以用來修飾代碼塊,代碼塊分為兩種,一種是使用 {} 代碼塊;一種是 static {} 靜態代碼塊。static 修飾的代碼塊被稱為靜態代碼塊。靜態代碼塊可以置於類中的任何地方,類中可以有多個 static 塊,在類初次被加載的時候,會按照 static 代碼塊的順序來執行,每個 static 修飾的代碼塊只能執行一次。我們會面會說一下代碼塊的加載順序。下面是靜態代碼塊的例子



static 代碼塊可以用來優化程序執行順序,是因為它的特性:只會在類加載的時候執行一次。


static 用作靜態內部類


內部類的使用場景比較少,但是內部類還有具有一些比較有用的。在了解靜態內部類前,我們先看一下內部類的分類



  • 普通內部類

  • 局部內部類

  • 靜態內部類

  • 匿名內部類


靜態內部類就是用 static 修飾的內部類,靜態內部類可以包含靜態成員,也可以包含非靜態成員,但是在非靜態內部類中不可以聲明靜態成員。


靜態內部類有許多作用,由於非靜態內部類的實例創建需要有外部類對象的引用,所以非靜態內部類對象的創建必須依託於外部類的實例;而靜態內部類的實例創建只需依託外部類;


並且由於非靜態內部類對象持有了外部類對象的引用,因此非靜態內部類可以訪問外部類的非靜態成員;而靜態內部類只能訪問外部類的靜態成員;



  • 內部類需要脫離外部類對象來創建實例

  • 避免內部類使用過程中出現內存溢出


public class ClassDemo {

private int a = 10;
private static int b = 20;

static class StaticClass{
public static int c = 30;
public int d = 40;

public static void print(){
//下面代碼會報錯,靜態內部類不能訪問外部類實例成員
//System.out.println(a);

//靜態內部類只可以訪問外部類類成員
System.out.println("b = "+b);

}

public void print01(){
//靜態內部內所處的類中的方法,調用靜態內部類的實例方法,屬於外部類中調用靜態內部類的實例方法
StaticClass sc = new StaticClass();
sc.print();
}
}
}

靜態導包


不知道你注意到這種現象沒有,比如你使用了 java.util 內的工具類時,你需要導入 java.util 包,才能使用其內部的工具類,如下



但是還有一種導包方式是使用靜態導包,靜態導入就是使用 import static 用來導入某個類或者某個包中的靜態方法或者靜態變量。


import static java.lang.Integer.*;

public class StaticTest {

public static void main(String[] args) {
System.out.println(MAX_VALUE);
System.out.println(toHexString(111));
}
}

static 進階知識


我們在了解了 static 關鍵字的用法之後,來看一下 static 深入的用法,也就是由淺入深,慢慢來,前戲要夠~


台中搬家遵守搬運三大原則,讓您的家具不再被破壞!



台中搬家公司推薦超過30年經驗,首選台中大展搬家




關於 static 的所屬類


static 所修飾的屬性和方法都屬於類的,不會屬於任何對象;它們的調用方式都是 類名.屬性名/方法名,而實例變量和局部變量都是屬於具體的對象實例。


static 修飾變量的存儲位置


首先,先來認識一下 JVM 的不同存儲區域。




  • 虛擬機棧 : Java 虛擬機棧是線程私有的數據區,Java 虛擬機棧的生命周期與線程相同,虛擬機棧也是局部變量的存儲位置。方法在執行過程中,會在虛擬機棧種創建一個 棧幀(stack frame)


  • 本地方法棧: 本地方法棧也是線程私有的數據區,本地方法棧存儲的區域主要是 Java 中使用 native 關鍵字修飾的方法所存儲的區域


  • 程序計數器:程序計數器也是線程私有的數據區,這部分區域用於存儲線程的指令地址,用於判斷線程的分支、循環、跳轉、異常、線程切換和恢復等功能,這些都通過程序計數器來完成。


  • 方法區:方法區是各個線程共享的內存區域,它用於存儲虛擬機加載的 類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據,也就是說,static 修飾的變量存儲在方法區中


  • : 堆是線程共享的數據區,堆是 JVM 中最大的一塊存儲區域,所有的對象實例,包括實例變量都在堆上進行相應的分配。



static 變量的生命周期


static 變量的生命周期與類的生命周期相同,隨類的加載而創建,隨類的銷毀而銷毀;普通成員變量和其所屬的生命周期相同。


static 序列化


我們知道,序列化的目的就是為了 把 Java 對象轉換為字節序列。對象轉換為有序字節流,以便其能夠在網絡上傳輸或者保存在本地文件中。


聲明為 static 和 transient 類型的變量不能被序列化,因為 static 修飾的變量保存在方法區中,只有堆內存才會被序列化。而 transient 關鍵字的作用就是防止對象進行序列化操作。


類加載順序


我們前面提到了類加載順序這麼一個概念,static 修飾的變量和靜態代碼塊在使用前已經被初始化好了,類的初始化順序依次是


加載父類的靜態字段 -> 父類的靜態代碼塊 -> 子類靜態字段 -> 子類靜態代碼塊 -> 父類成員變量(非靜態字段)


-> 父類非靜態代碼塊 -> 父類構造器 -> 子類成員變量 -> 子類非靜態代碼塊 -> 子類構造器


static 經常用作日誌打印


我們在開發過程中,經常會使用 static 關鍵字作為日誌打印,下面這行代碼你應該經常看到


private static final Logger LOGGER = LogFactory.getLoggger(StaticTest.class);

然而把 static 和 final 去掉都可以打印日誌


private final Logger LOGGER = LogFactory.getLoggger(StaticTest.class);
private Logger LOGGER = LogFactory.getLoggger(StaticTest.class);

但是這種打印日誌的方式存在問題


對於每個 StaticTest 的實例化對象都會擁有一個 LOGGER,如果創建了1000個 StaticTest 對象,則會多出1000個Logger 對象,造成資源的浪費,因此通常會將 Logger 對象聲明為 static 變量,這樣一來,能夠減少對內存資源的佔用。


static 經常用作單例模式


由於單例模式指的就是對於不同的類來說,它的副本只有一個,因此 static 可以和單例模式完全匹配。


下面是一個經典的雙重校驗鎖實現單例模式的場景


public class Singleton {

private static volatile Singleton singleton;

private Singleton() {}

public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

來對上面代碼做一個簡單的描述


使用 static 保證 singleton 變量是靜態的,使用 volatile 保證 singleton 變量的可見性,使用私有構造器確保 Singleton 不能被 new 實例化。


使用 Singleton.getInstance() 獲取 singleton 對象,首先會進行判斷,如果 singleton 為空,會鎖住 Singletion 類對象,這裡有一些小夥伴們可能不知道為什麼需要兩次判斷,這裏來解釋下


如果線程 t1 執行到 singleton == null 后,判斷對象為 null,此時線程把執行權交給了 t2,t2 判斷對象為 null,鎖住 Singleton 類對象,進行下面的判斷和實例化過程。如果不進行第二次判斷的話,那麼 t1 在進行第一次判空后,也會進行實例化過程,此時仍然會創建多個對象。


類的構造器是否是 static 的


這個問題我相信大部分小夥伴都沒有考慮過,在 Java 編程思想中有這麼一句話 類的構造器雖然沒有用 static 修飾,但是實際上是 static 方法,但是並沒有給出實際的解釋,但是這個問題可以從下面幾個方面來回答



  • static 最簡單、最方便記憶的規則就是沒有 this 引用。而在類的構造器中,是有隱含的 this 綁定的,因為構造方法是和類綁定的,從這個角度來看,構造器不是靜態的。

  • 從類的方法這個角度來看,因為 類.方法名不需要新創建對象就能夠訪問,所以從這個角度來看,構造器也不是靜態的

  • 從 JVM 指令角度去看,我們來看一個例子


public class StaticTest {

public StaticTest(){}

public static void test(){

}

public static void main(String[] args) {
StaticTest.test();
StaticTest staticTest = new StaticTest();
}
}

我們使用 javap -c 生成 StaticTest 的字節碼看一下


public class test.StaticTest {
public test.StaticTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return

public static void test();
Code:
0: return

public static void main(java.lang.String[]);
Code:
0: invokestatic #2 // Method test:()V
3: new #3 // class test/StaticTest
6: dup
7: invokespecial #4 // Method "<init>":()V
10: astore_1
11: return
}


我們發現,在調用 static 方法時是使用的 invokestatic 指令,new 對象調用的是 invokespecial 指令,而且在 JVM 規範中 https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokestatic 說到




從這個角度來講,invokestatic 指令是專門用來執行 static 方法的指令;invokespecial 是專門用來執行實例方法的指令;從這個角度來講,構造器也不是靜態的。


本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

台中搬家公司費用怎麼算?



擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司





Orignal From: 一個 static 還能難得住我?_台中搬家

留言

這個網誌中的熱門文章

Python 併發總結,多線程,多進程,異步IO

1 測量函數運行時間 import time def profile(func): def wrapper(*args, ** kwargs): import time start = time.time() func( *args, ** kwargs) end = time.time() print ' COST: {} ' .format(end - start) return wrapper @profile def fib(n): if n<= 2 : return 1 return fib(n-1) + fib(n-2 ) fib( 35 )   2 啟動多個線程,並等待完成   2.1 使用threading.enumerate() import threading for i in range(2 ): t = threading.Thread(target=fib, args=(35 ,)) t.start() main_thread = threading.currentThread() for t in threading.enumerate(): if t is main_thread: continue t.join()   2.2 先保存啟動的線程 threads = [] for i in range(5 ): t = Thread(target=foo, args= (i,)) threads.append(t) t.start() for t in threads: t.join()   3 使用信號量,限制同時能有幾個線程訪問臨界區 from threading import Semaphore import time sema = Semaphor...

高雄十大包子名店出爐

, 圖文:吳恩文 高雄包子大賽落幕了,我只能就我個人意見, 介紹一下前十名這些包子,但是不能代表其他四位評審的意見,雖然身為評審長,我通常不會第一個表示意見,以免影響其他評審, 我主要工作是負責發問。   這次參賽的素包子很少,而且都不夠細致,又偏油,我不愛, 但是第一名的甜芝麻包-熔岩黑金包,竟然是素食得名- 漢來蔬食巨蛋店。   這包子賣相太好,竹炭粉的黑色外皮刷上金粉,一上桌,眾人驚呼, 搶拍照,內餡是芝麻餡,混一點花生醬增稠,加入白糖芝麻油, 熔岩爆漿的程度剛剛好,我一直以為芝麻要配豬油才行、 但是選到好的黑芝麻油一樣不減香醇, 當下有二位評審就想宅配回家。   尤其特別的是,黑芝麻餡室溫易化,師傅必須要輪班躲在冷藏室內, 穿著大外套才能包,一天包不了多少,我笑說,漢來美食,集團餐廳那麼多,實力雄厚,根本是「 奧運選手報名參加村裡運動會」嘛,其他都是小包子店啊, 但是沒辦法,顯然大家都覺得它好看又好吃, 目前限定漢來蔬食高雄巨蛋店,二顆88元,可以冷凍宅配, 但是要排一陣子,因為供不應求,聽說,四月份, 台北sogo店開始會賣。   第二名的包子,左營寬來順早餐店,顯然平易近人的多,一顆肉包, 十塊錢,是所有參賽者中最便宜的,當然,個頭也小, 它的包子皮明顯和其他不同,灰灰的老麵,薄但紮實有嚼勁, 肉餡新鮮帶汁,因為打了些水,味道極其簡單,就是蔥薑,塩, 香油,薑味尤其明顯,是老眷村的味道, 而特別的是老闆娘是台灣本省人, 當年完全是依據眷村老兵的口味一步一步調整而來,沒有加什麼糖、 五香粉,胡椒粉,油蔥酥。就是蔥薑豬肉和老麵香,能得名, 應該是它的平實無華,鮮美簡單,打動人心。   這是標準的心靈美食,可以撫慰人心,得名之前,寛來順已經天天排隊,現在,恐怕要排更久了, 建議大家六七點早點上門。   第三名,「專十一」很神奇,我記得比賽最後, 大家連吃了幾家不能引起共鳴的包子,有些累,到了專十一, 就坐著等包子,其他評審一吃,就催我趕快試,我一吃, 也醒了大半。   它的包子皮厚薄適中,但是高筋麵粉高些,老麵加一點點酵母, 我心中,它的皮屬一屬二,至於餡又多又好吃,蛋黃還是切丁拌入, 不是整顆放,吃起來「美味、均衡、飽滿」。一顆二十元。   老闆是陸軍專科十一期畢業取名專十一,...

韋伯連續劇終於更新 期待第一季順利完結

  地球天文學界的跳票大王詹姆斯·韋伯空間望遠鏡 (James Webb Space Telescope,縮寫為 JWST)自 1996 年以來斷斷續續不按劇本演出的連續劇終於讓焦慮的觀眾們又等到了一次更新:五層遮陽罩測試順利完成。 裝配完成的韋伯望遠鏡與好夥伴遮陽罩同框啦。Credit: NASA   嚴格的測試是任何空間任務順利成功的重中之重。遮陽罩,這個韋伯望遠鏡異常重要的親密夥伴,要是無法正常運轉的話,韋伯的這一季天文界連續劇說不準就要一直拖更了。   詹姆斯·韋伯空間望遠鏡是歷史上造出的最先進的空間望遠鏡。它不僅是一架紅外望遠鏡,還具有特別高的靈敏度。但想要達到辣么高的靈敏度來研究系外行星和遙遠的宇宙童年,韋伯童鞋必須非常"冷靜",體溫升高的話,靈敏度會大大折損。這個時候,遮陽罩就要大顯身手啦。   遮陽罩在韋伯的設計中至關重要。韋伯望遠鏡會被發射到拉格朗日 L2 點,運行軌道很高,遠離太陽、地球與月球。太陽是韋伯的主要熱量干擾的來源,其次是地球與月球。遮陽罩會有效阻斷來自這三大熱源的能量並保護韋伯維持在工作溫度正常運轉。這個工作溫度指的是零下 220 攝氏度(-370 華氏度;50 開爾文)。 上圖中我們可以看出,韋伯望遠鏡的配置大致可分為兩部分:紅色較熱的一面溫度為 85 攝氏度,藍色較冷的一面溫度達到零下 233 攝氏度。紅色的這部分中,儀器包括太陽能板、通信設備、計算機、以及轉向裝置。藍色部分的主要裝置包括鏡面、探測器、濾光片等。Credit: STSci.   遮陽罩的那一部分和望遠鏡的鏡面這部分可以產生非常極端的溫差。遮陽的這面溫度可以達到 110 攝氏度,足以煮熟雞蛋,而背陰處的部分溫度極低,足以凍結氧氣。   工程師們剛剛完成了五層遮陽罩的測試,按照韋伯在 L2 時的運行狀態安裝了遮陽罩。L2 距離地球約 160 萬公里。NASA 表示這些測試使用了航天器的自帶系統來展開遮陽罩,測試目前都已成功完成。韋伯望遠鏡遮陽罩負責人 James Cooper 介紹說這是遮陽罩"第一次在望遠鏡系統的电子設備的控制下展開。儘管這個任務非常艱巨,難度高,但測試順利完成,遮陽罩展開時的狀態非常驚艷"。   遮陽罩由五層 Kapton 製成。Kapton 是一種聚酰亞胺薄膜材料, 耐高溫絕...