java final static

參考android listview 聲明ViewHolder內(nèi)部類時,為什么建議使用static關鍵字
這個問題也是我每次面試別人必問的問題之一。其實這個是考靜態(tài)內(nèi)部類和非靜態(tài)內(nèi)部類的主要區(qū)別之一蕴坪。非靜態(tài)內(nèi)部類會隱式持有外部類的引用,就像大家經(jīng)常將自定義的adapter在Activity類里,然后在adapter類里面是可以隨意調(diào)用外部activity的方法的。當你將內(nèi)部類定義為static時,你就調(diào)用不了外部類的實例方法了,因為這時候靜態(tài)內(nèi)部類是不持有外部類的引用的。聲明ViewHolder靜態(tài)內(nèi)部類,可以將ViewHolder和外部類解引用童擎。大家會說一般ViewHolder都很簡單芯砸,不定義為static也沒事吧包帚。確實如此谋梭,但是如果你將它定義為static的纤垂,說明你懂這些含義逃糟。萬一有一天你在這個ViewHolder加入一些復雜邏輯琐谤,做了一些耗時工作织阳,那么如果ViewHolder是非靜態(tài)內(nèi)部類的話刽宪,就很容易出現(xiàn)內(nèi)存泄露。如果是靜態(tài)的話,你就不能直接引用外部類纲酗,迫使你關注如何避免相互引用饶囚。 所以將 ViewHolder內(nèi)部類 定義為靜態(tài)的规惰,是一種好習慣.

本文參考
Java中的static關鍵字解析
Java關鍵字之final和static

一、final

Java語言里,final關鍵字有多種用途,其主題都表示“不可變”衅鹿,但背后的具體內(nèi)容并不一樣。當final關鍵字用于修飾類時表示該類不允許被繼承纬霞;當它用于修飾方法時表示該方法在派生類里不允許被覆寫(override)。當final關鍵字用于修飾變量時表示該變量的值不可變澎蛛;靜態(tài)變量气堕、實例成員變量煮寡、形式參數(shù)和局部變量都可以被final修飾炭菌。

1.final類
當一個類聲明為final類黑低,也就證明這個類是不能夠被繼承的勋眯,即禁止繼承塞蹭,因此final類的成員方法是沒有機會被覆蓋的婉烟,這個final類的功能是完整的。在Java中有很多類是final的,如String卵慰、Interger以及其他包裝類。
final類的好處:不可變類有很多的好處佛呻,它們的對象是只讀的裳朋,可以在多線程環(huán)境下安全的共享,不用額外的開銷吓著。
下面是final類的實例:

final class PersonalLoan{
}
 
class CheapPersonalLoan extends PersonalLoan{ //compilation error: cannot inherit from final class
}

2.final方法
如果一個類不允許其子類覆蓋某個方法鲤嫡,即不能被重寫,則可以把這個方法聲明為final方法绑莺。(類中所有的private方法都隱式的指定為final)暖眼。
使用final方法的原因:

  • 方法鎖定,防止任何繼承類修改它的含義纺裁,確保在繼承中使方法行為保持不變且不被覆蓋诫肠;
  • 效率,將一個方法指明為final欺缘,就是同意編譯器將針對該方法的所有調(diào)用都轉化為內(nèi)嵌調(diào)用(相當于在編譯的時候已經(jīng)靜態(tài)綁定栋豫,不需要在運行時再動態(tài)綁定)杜跷。

下面是final方法的實例:

public class Test1 {
   public static void main(String[] args) { 
   }
   public void f1() { 
      System.out.println("f1"); 
   } 
   //final方法
   public final void f2() { 
      System.out.println("f2"); 
   }
}
public class Test2 extends Test1 { 
   public void f1(){ 
      System.out.println("Test1父類方法f1被覆蓋!"); 
   } 
   public static void main(String[] args) { 
      Test2 t=new Test2(); 
      t.f1(); //子類重寫父類的方法 
      t.f2(); //調(diào)用從父類繼承過來的final方法 
   }
}

3.final變量
程序中有些數(shù)據(jù)的恒定不變是很有必要的讨阻,比如:

  • 一個永不改變的編譯時常量;
  • 一個在運行時被初始化的值嘹叫,而在程序的后面不希望它被改變嫩絮。
    這種類型的變量只能被賦值一次骡送,一旦被賦值之后,就不能夠再更改了絮记。

有幾點要注意的:

  • 一個既是static又是final的域只占據(jù)一段不能改變的存儲空間,一般用大寫來表示虐先;
  • final使數(shù)值恒定不變怨愤,而當用于對象時,final使引用恒定不變(一旦引用指向一個對象蛹批,就無法再把它改為指向另一個對象)撰洗;

final變量的好處:

  • 提高性能,JVM和Java應用程序都會緩存final變量腐芍;
  • final變量可以在安全的在多線程環(huán)境下進行共享差导,而不需要額外的開銷。
public class Test { 
    public static final int PI = 3.14;//這個變量是只讀的
    public final int INIT; //final空白,必須在初始化對象的時候賦初值 
    public Test(int x) { 
        INIT = x; 
    } 
    public static void main(String[] args) { 
        Test t = new Test(2); 
        //t.PI=3.1415;//出錯,final變量的值一旦給定就無法改變 
        System.out.println(t.INIT); 
    }
}

4.總結

  • 本地變量必須在聲明的時候賦值猪勇;
  • 在匿名類中所有變量都必須是final變量设褐;
  • final方法不能被重寫;
  • final類不能被繼承;
  • final成員變量必須在聲明的時候初始化或者在構造器中初始化助析,否則就會報編譯錯誤犀被;
  • 接口中聲明的所有變量本身是final的;
  • final方法在編譯階段綁定外冀,稱為靜態(tài)綁定(static binding)寡键;
  • 對于集合對象聲明為final指的是引用不能被更改,但是你可以向其中增加雪隧,刪除或者改變內(nèi)容西轩;
二、static

1.static方法
Math類中有很多計算方法脑沿,不需要初始化對象藕畔,就能直接調(diào)用,這正是靜態(tài)方法的主要用途捅伤。靜態(tài)方法由于不依賴于任何對象劫流,也就沒有this,當然也不能訪問類的非靜態(tài)變量和方法丛忆,因為它們需要依賴具體的對象才能調(diào)用祠汇。
如果一個方法和他所在類的實例對象無關,那么它就應該是靜態(tài)的熄诡,反之他就應該是非靜態(tài)的可很。如果我們確實應該使用非靜態(tài)的方法,但是在創(chuàng)建類時又確實只需要維護一份實例時凰浮,就需要用單例模式了我抠。
比如說我們在系統(tǒng)運行時候,就需要加載一些配置和屬性袜茧,這些配置和屬性是一定存在了菜拓,又是公共的,同時需要在整個生命周期中都存在笛厦,所以只需要一份就行纳鼎,這個時候如果需要我再需要的時候new一個,再給他分配值裳凸,顯然是浪費內(nèi)存并且再賦值沒什么意義贱鄙,所以這個時候我們就需要單例模式或靜態(tài)方法去維持一份且僅這一份拷貝,但此時這些配置和屬性又是通過面向對象的編碼方式得到的姨谷,我們就應該使用單例模式逗宁,或者不是面向對象的,但他本身的屬性應該是面對對象的梦湘,我們使用靜態(tài)方法雖然能同樣解決問題瞎颗,但是最好的解決方案也應該是使用單例模式件甥。

2.static類
參考知乎-為什么Java內(nèi)部類要設計成靜態(tài)和非靜態(tài)兩種?
從字面上看言缤,一個被稱為靜態(tài)嵌套類嚼蚀,一個被稱為內(nèi)部類。
從字面的角度解釋是這樣的:
什么是嵌套管挟?嵌套就是我跟你沒關系轿曙,自己可以完全獨立存在,但是我就想借你的殼用一下僻孝,來隱藏一下我自己(真TM猥瑣)导帝。
什么是內(nèi)部?內(nèi)部就是我是你的一部分穿铆,我了解你您单,我知道你的全部,沒有你就沒有我荞雏。(所以內(nèi)部類對象是以外部類對象存在為前提的)

注意android listview 聲明ViewHolder內(nèi)部類:

    // ViewHolder靜態(tài)類  
    static class ViewHolder {  
        public ImageView img;  
        public TextView title;  
        public TextView content;  
    } 

參考android listview 聲明ViewHolder內(nèi)部類時虐秦,為什么建議使用static關鍵字
這個問題也是我每次面試別人必問的問題之一。其實這個是考靜態(tài)內(nèi)部類和非靜態(tài)內(nèi)部類的主要區(qū)別之一凤优。非靜態(tài)內(nèi)部類會隱式持有外部類的引用悦陋,就像大家經(jīng)常將自定義的adapter在Activity類里,然后在adapter類里面是可以隨意調(diào)用外部activity的方法的筑辨。當你將內(nèi)部類定義為static時俺驶,你就調(diào)用不了外部類的實例方法了,因為這時候靜態(tài)內(nèi)部類是不持有外部類的引用的棍辕。聲明ViewHolder靜態(tài)內(nèi)部類暮现,可以將ViewHolder和外部類解引用。大家會說一般ViewHolder都很簡單楚昭,不定義為static也沒事吧栖袋。確實如此,但是如果你將它定義為static的抚太,說明你懂這些含義栋荸。萬一有一天你在這個ViewHolder加入一些復雜邏輯,做了一些耗時工作凭舶,那么如果ViewHolder是非靜態(tài)內(nèi)部類的話,就很容易出現(xiàn)內(nèi)存泄露爱沟。如果是靜態(tài)的話帅霜,你就不能直接引用外部類,迫使你關注如何避免相互引用呼伸。 所以將 ViewHolder內(nèi)部類 定義為靜態(tài)的身冀,是一種好習慣.

另外钝尸,使用使用靜態(tài)內(nèi)部類實現(xiàn)單例模式,這種方法也是《Effective Java》上所推薦的

public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

這種寫法仍然使用JVM本身機制保證了線程安全問題搂根;由于 SingletonHolder 是私有的珍促,除了 getInstance() 之外沒有辦法訪問它,因此它是懶漢式的剩愧;同時讀取實例的時候不會進行同步猪叙,沒有性能缺陷;也不依賴 JDK 版本仁卷。

3.static變量
參考Java中靜態(tài)變量的適用場景
靜態(tài)變量被所有的對象所共享穴翩,在內(nèi)存中只有一個副本,它當且僅當在類初次加載時會被初始化锦积。而非靜態(tài)變量是對象所擁有的芒帕,在創(chuàng)建對象的時候被初始化,存在多個副本丰介,各個對象擁有的副本互不影響背蟆。
另外,注意在C/C++中static是可以作用域局部變量的哮幢,但是在Java中切記:static是不允許用來修飾局部變量带膀。不要問為什么,這是Java語法的規(guī)定家浇。

public class WeekB{

      static class Data { 

             private int week;
             private String name;
             Data(int i, String s) {
                   week= i;
                   name = s;
            }

      }

      static Data weeks[] = {
             new Data(1, "Monday"),
             new Data(2, "Tuesay"),
             new Data(3, "Wednesday"),
             new Data(4, "Thursday"),
             new Data(5, "Friday"),
             new Data(6, "Saturday"),
             new Data(7, "Sunday")
      };

      public static void main(String args[]) {

             final int N = 10000;
             WeekB weekinstance;
             for (int i = 1; i <= N; i++){
                   weekinstance = new WeekB ();
             }
     }

}

在類WeekB中本砰,在Data weeks[]之前添加了static關鍵字,將該對象變量聲明為靜態(tài)的钢悲,因此當你創(chuàng)建10000個WeekB對象時系統(tǒng)中只保存著該對象的一份拷貝点额,而且該類的所有對象實例共享這份拷貝,這無疑節(jié)約了大量的不必要的內(nèi)存開銷.
那么是不是我們應該盡量地多使用靜態(tài)變量呢莺琳?其實不是這樣的还棱,因為靜態(tài)變量生命周期較長,而且不易被系統(tǒng)回收惭等,因此如果不能合理地使用靜態(tài)變量珍手,就會適得其反,造成大量的內(nèi)存浪費辞做,所謂過猶不及琳要。因此,建議在具備下列全部條件的情況下秤茅,盡量使用靜態(tài)變量:
(1)變量所包含的對象體積較大稚补,占用內(nèi)存較多。
(2)變量所包含的對象生命周期較長框喳。
(3)變量所包含的對象數(shù)據(jù)穩(wěn)定课幕。
(4)該類的對象實例有對該變量所包含的對象的共享需求厦坛。
如果變量不具備上述特點建議你不要輕易地使用靜態(tài)變量,以免弄巧成拙乍惊。

4.static代碼塊
static塊可以置于類中的任何地方杜秸,類中可以有多個static塊。在類初次被加載的時候润绎,會按照static塊的順序來執(zhí)行每個static塊撬碟,并且只會執(zhí)行一次。

class Person{
    private Date birthDate;
     
    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }
     
    boolean isBornBoomer() {
        Date startDate = Date.valueOf("1946");
        Date endDate = Date.valueOf("1964");
        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
    }
}

isBornBoomer是用來這個人是否是1946-1964年出生的凡橱,而每次isBornBoomer被調(diào)用的時候小作,都會生成startDate和birthDate兩個對象,造成了空間浪費稼钩,如果改成這樣效率會更好:

class Person{
    private Date birthDate;
    private static Date startDate,endDate;
    static{
        startDate = Date.valueOf("1946");
        endDate = Date.valueOf("1964");
    }
     
    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }
     
    boolean isBornBoomer() {
        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
    }
}

因此顾稀,很多時候會將一些只需要進行一次的初始化操作都放在static代碼塊中進行。

三坝撑、筆試題

1.下面這段代碼的輸出結果是什么静秆?

public class Test extends Base{
 
    static{
        System.out.println("test static");
    }
     
    public Test(){
        System.out.println("test constructor");
    }
     
    public static void main(String[] args) {
        new Test();
    }
}
 
class Base{
     
    static{
        System.out.println("base static");
    }
     
    public Base(){
        System.out.println("base constructor");
    }
}
base static
test static
base constructor
test constructor

在執(zhí)行開始,先要尋找到main方法巡李,因為main方法是程序的入口抚笔,但是在執(zhí)行main方法之前,必須先加載Test類侨拦,而在加載Test類的時候發(fā)現(xiàn)Test類繼承自Base類殊橙,因此會轉去先加載Base類,在加載Base類的時候狱从,發(fā)現(xiàn)有static塊膨蛮,便執(zhí)行了static塊。在Base類加載完成之后季研,便繼續(xù)加載Test類敞葛,然后發(fā)現(xiàn)Test類中也有static塊,便執(zhí)行static塊与涡。在加載完所需的類之后惹谐,便開始執(zhí)行main方法。在main方法中執(zhí)行new Test()的時候會先調(diào)用父類的構造器驼卖,然后再調(diào)用自身的構造器氨肌。
2.這段代碼的輸出結果是什么?

public class Test {
    Person person = new Person("Test");
    static{
        System.out.println("test static");
    }
     
    public Test() {
        System.out.println("test constructor");
    }
     
    public static void main(String[] args) {
        new MyClass();
    }
}
 
class Person{
    static{
        System.out.println("person static");
    }
    public Person(String str) {
        System.out.println("person "+str);
    }
}
 
 
class MyClass extends Test {
    Person person = new Person("MyClass");
    static{
        System.out.println("myclass static");
    }
     
    public MyClass() {
        System.out.println("myclass constructor");
    }
}
test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor

首先加載Test類酌畜,因此會執(zhí)行Test類中的static塊儒飒。接著執(zhí)行new MyClass(),而MyClass類還沒有被加載檩奠,因此需要加載MyClass類桩了。在加載MyClass類的時候,發(fā)現(xiàn)MyClass類繼承自Test類埠戳,但是由于Test類已經(jīng)被加載了井誉,所以只需要加載MyClass類,那么就會執(zhí)行MyClass類的中的static塊整胃。在加載完之后颗圣,就通過構造器來生成對象。而在生成對象的時候屁使,必須先初始化父類的成員變量在岂,因此會執(zhí)行Test中的Person person = new Person(),而Person類還沒有被加載過蛮寂,因此會先加載Person類并執(zhí)行Person類中的static塊蔽午,接著執(zhí)行父類的構造器,完成了父類的初始化酬蹋,然后就來初始化自身了及老,因此會接著執(zhí)行MyClass中的Person person = new Person(),最后執(zhí)行MyClass的構造器范抓。
3.這段代碼的輸出結果是什么骄恶?

public class Test {
     
    static{
        System.out.println("test static 1");
    }
    public static void main(String[] args) {
         
    }
     
    static{
        System.out.println("test static 2");
    }
}
test static 1
test static 2

雖然在main方法中沒有任何語句,但是還是會輸出匕垫,原因上面已經(jīng)講述過了僧鲁。另外,static塊可以出現(xiàn)類中的任何地方(只要不是方法內(nèi)部象泵,記住寞秃,任何方法內(nèi)部都不行),并且執(zhí)行是按照static塊的順序執(zhí)行的单芜。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜕该,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子洲鸠,更是在濱河造成了極大的恐慌堂淡,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扒腕,死亡現(xiàn)場離奇詭異绢淀,居然都是意外死亡,警方通過查閱死者的電腦和手機瘾腰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門皆的,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蹋盆,你說我怎么就攤上這事费薄∠跞” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵楞抡,是天一觀的道長伟众。 經(jīng)常有香客問我,道長召廷,這世上最難降的妖魔是什么凳厢? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮竞慢,結果婚禮上先紫,老公的妹妹穿的比我還像新娘。我一直安慰自己筹煮,他們只是感情好遮精,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著寺谤,像睡著了一般仑鸥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上变屁,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天眼俊,我揣著相機與錄音,去河邊找鬼粟关。 笑死疮胖,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的闷板。 我是一名探鬼主播澎灸,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼遮晚!你這毒婦竟也來了性昭?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤县遣,失蹤者是張志新(化名)和其女友劉穎糜颠,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萧求,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡其兴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了夸政。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片元旬。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出匀归,到底是詐尸還是另有隱情坑资,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布穆端,位于F島的核電站盐茎,受9級特大地震影響,放射性物質發(fā)生泄漏徙赢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一探越、第九天 我趴在偏房一處隱蔽的房頂上張望狡赐。 院中可真熱鬧,春花似錦钦幔、人聲如沸枕屉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搀擂。三九已至,卻和暖如春卷玉,著一層夾襖步出監(jiān)牢的瞬間哨颂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工相种, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留威恼,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓寝并,卻偏偏與公主長得像箫措,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子衬潦,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 問:談談 Java 中 final镀岛、finally弦牡、finalize 的區(qū)別? 答: final 是一個修飾符哎媚,如...
    Little丶Jerry閱讀 122評論 0 0
  • 1. Java基礎部分 基礎部分的順序:基本語法喇伯,類相關的語法,內(nèi)部類的語法拨与,繼承相關的語法稻据,異常的語法,線程的語...
    子非魚_t_閱讀 31,598評論 18 399
  • 一:java概述:1,JDK:Java Development Kit捻悯,java的開發(fā)和運行環(huán)境匆赃,java的開發(fā)工...
    ZaneInTheSun閱讀 2,635評論 0 11
  • Java關鍵字final 在設計程序時,出于效率或者設計的原因今缚,有時候希望某些數(shù)據(jù)是不可改變的算柳。這時候可以使用fi...
    獅_子歌歌閱讀 738評論 1 4
  • (一)Java部分 1、列舉出JAVA中6個比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨云閱讀 7,083評論 0 62