日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

怎么一本正經(jīng)地秀技

 行者花雕 2021-05-06

前言

修飾符怎么使用也是Java基礎(chǔ)中比較重要的知識(shí)點(diǎn),徹底理解了之后,后面學(xué)習(xí)更高深的東西才能得心應(yīng)手。今天,以修飾符中比較常見的final為切入點(diǎn),來談?wù)刦inal的使用的奇淫技巧以及一些相關(guān)的知識(shí)點(diǎn)。學(xué)廢了記得三連哦。

初始化塊

在final的運(yùn)用中,經(jīng)常和初始化塊和構(gòu)造器結(jié)合起來一起使用。上篇文章已經(jīng)介紹完什么是構(gòu)造器,那么現(xiàn)在先來談?wù)勈裁词浅跏蓟瘔K。

Java會(huì)使用構(gòu)造器對(duì)對(duì)象進(jìn)行初始化操作,在使用的構(gòu)造器的時(shí)候需要完成初始化整個(gè)Java對(duì)象的狀態(tài)的功能,然后再將整個(gè)完整的Java對(duì)象返回給程序使用。那么,在Java中,有一個(gè)與構(gòu)造器功能類似的東西,就是初始化塊,它能夠?qū)ava對(duì)象實(shí)現(xiàn)初始化操作。

在Java中,一個(gè)類中可以有多個(gè)初始化塊,相同類型的初始化塊的執(zhí)行順序是有要求的,先定義的先執(zhí)行(前面的先執(zhí)行),后面定義的后執(zhí)行。

初始化塊的語法其實(shí)很簡(jiǎn)單了,就是:



修飾符 {

  //初始化塊中的可執(zhí)行代碼塊
  ...
  ...
  ...
  
}


那初始化塊的分類也很簡(jiǎn)單,就分為靜態(tài)初始化塊非靜態(tài)初始化塊兩種,其中非靜態(tài)初始化塊也叫做普通初始化塊

非靜態(tài)初始化塊

在生成每個(gè)對(duì)象的時(shí)候都會(huì)執(zhí)行一次,可以初始化類的實(shí)例變量。非靜態(tài)初始化塊會(huì)在類的構(gòu)造器之前執(zhí)行

先來看一段代碼



`public class CodeVald {

`  int a = 6;

    `//第一個(gè)初始化塊

    {


      int a = 3;
      if (this.a > 4) {

        System.out.println("codevald的初始化塊:  成員變量a的值大于4");

      }


      System.out.println("codevald的初始化塊");


    }

    //第二個(gè)初始化塊

    {

      System.out.println("codevald的第二個(gè)初始化塊");


    }

    //定義一個(gè)無參數(shù)的構(gòu)造器

    public CodeVald() {

      System.out.println("codevald的無參數(shù)構(gòu)造器");

    }

    public static void main(String[] args) {

      new CodeVald();


    } 



  }

  
}


上面的代碼定義了兩個(gè)普通初始化塊和一個(gè)構(gòu)造器,那么執(zhí)行的順序也很簡(jiǎn)單了,先定義的初始化塊先執(zhí)行,然后執(zhí)行后定義的初始化塊,接著執(zhí)行構(gòu)造器的內(nèi)容

來看下編譯運(yùn)行結(jié)果

靜態(tài)初始化塊

使用static定義,當(dāng)類裝載到系統(tǒng)只會(huì)執(zhí)行一次。如果在靜態(tài)初始化塊中想初始化變量的話,就只能初始化類變量了,即是由static修飾的數(shù)據(jù)成員。

來看一個(gè)靜態(tài)初始化塊、普通初始化塊和構(gòu)造器結(jié)合的例子:


public class JingTai_CodeBlock {

    public static void main(String[] args) {


      new C();
      new C();


    }

}


//定義第一個(gè)類A,這是父類

class A {


    static {


      System.out.println("A的靜態(tài)初始化塊");


    }

    {

      System.out.println("A的普通初始化塊");

    }

    public A() {

      System.out.println("A的無參數(shù)構(gòu)造器");

    }


}

//定義一個(gè)子類B

class B extends A {

    static {

      System.out.println("B的靜態(tài)初始化塊");


    }

    {

      System.out.println("B的普通初始化塊");


    }

    public B() {

      System.out.println("B的無參數(shù)構(gòu)造器");

    }

    public B(String message) {

      this();

      //通過this()調(diào)用無參數(shù)的構(gòu)造器(即重載的構(gòu)造器)
      System.out.println("B的帶參數(shù)構(gòu)造器,傳入的信息為: " + message);



    }

}

//定義一個(gè)子類C


class C extends B {


    static {

      System.out.println("C的靜態(tài)初始化塊");

    }

    {

      System.out.println("C的普通初始化塊");


    }

    public C() {

      //通過super調(diào)用父類中帶參數(shù)的構(gòu)造器

      super("codevald");
      System.out.println("C的構(gòu)造器");



    }

}

上述代碼定義了A、B、C三個(gè)類,他們都提供了靜態(tài)初始化塊和普通初始化塊,并且在類B中使用this()調(diào)用了重載構(gòu)造器,在C中使用super()顯式地調(diào)用了其父類指定的構(gòu)造器,接著在main()函數(shù)調(diào)用了兩次new C(),創(chuàng)建兩個(gè)C對(duì)象。

那么來猜下會(huì)輸出什么結(jié)果

先思考五分鐘哦

現(xiàn)在來解釋一下,這里定義了靜態(tài)初始化塊,那么會(huì)在類地初始化階段執(zhí)行靜態(tài)初始化塊,而不是創(chuàng)建對(duì)象的時(shí)候才執(zhí)行,所以靜態(tài)初始化塊總是比普通初始化塊先執(zhí)行,接著是構(gòu)造器

但是系統(tǒng)在類初始化階段執(zhí)行靜態(tài)初始化塊的時(shí)候,不僅會(huì)執(zhí)行本類的靜態(tài)初始化塊,還會(huì)一直上溯到j(luò)ava.lang.Object類(所有對(duì)象的父類),如果它包含靜態(tài)初始化塊,先執(zhí)行java.lang.Object類的靜態(tài)初始化塊,然后執(zhí)行其父類的靜態(tài)初始化塊...執(zhí)行完最后才執(zhí)行該類的靜態(tài)初始化塊,經(jīng)過上述過程才能完成該類的初始化過程。

第一次創(chuàng)建C對(duì)象的時(shí)候,會(huì)先運(yùn)行靜態(tài)初始化塊的內(nèi)容,但是會(huì)先上溯到頂級(jí)父類的靜態(tài)初始化塊,所以會(huì)先輸出A的靜態(tài)初始化塊,接著才是B的靜態(tài)初始化塊,最后是C的靜態(tài)初始化塊。

執(zhí)行完靜態(tài)初始化塊,一樣先執(zhí)行頂級(jí)父類的普通初始化塊,即輸出A的普通初始化塊,接著執(zhí)行頂級(jí)父類的構(gòu)造器代碼,即輸出A的無參數(shù)構(gòu)造器。然后輸出父類的普通初始化塊,接著是構(gòu)造器,所以輸出B的普通初始化塊,因?yàn)镃的構(gòu)造器調(diào)用的是帶參數(shù)的父類構(gòu)造器,所以B中會(huì)調(diào)用帶參數(shù)的構(gòu)造器B,所以會(huì)輸出B的無參數(shù)構(gòu)造器,B的帶參數(shù)構(gòu)造器,傳入的信息為: codevald,接著執(zhí)行C的普通初始化塊的代碼,即輸出C的普通初始化塊,然后是構(gòu)造器的代碼,即C的構(gòu)造器

第二次創(chuàng)建實(shí)例C的時(shí)候,因?yàn)轭怌已經(jīng)在虛擬機(jī)中存在,所以無需再初始化C類了,所以靜態(tài)初始化塊的代碼不再執(zhí)行,而是重復(fù)地執(zhí)行靜態(tài)后面的代碼。

final修飾符

final可以用來修飾類、變量和方法,通過final修飾以后,被修飾的類、方法和變量就表示不可改變的狀態(tài)。

修飾成員變量

成員變量是隨著類的初始化或者對(duì)象初始化而初始化的。當(dāng)初始化的時(shí)候,就會(huì)為類的類屬性分配內(nèi)存,并設(shè)置一個(gè)默認(rèn)值;當(dāng)創(chuàng)建對(duì)象時(shí),就會(huì)為對(duì)象的實(shí)例屬性分配內(nèi)存,并分配默認(rèn)值。一般來說,都是在普通初始化塊、靜態(tài)初始化塊、構(gòu)造器中區(qū)指定初始值的。

那么,final修飾的屬性,在哪里聲明初始值是有一定的規(guī)則的,具體如下:

  • 修飾類屬性時(shí):可在靜態(tài)靜態(tài)初始化塊中聲明該屬性的初始值
  • 修飾實(shí)例屬性時(shí): 可在普通初始化塊中或者構(gòu)造器中指定初始值

修飾局部變量

在初始化局部變量的時(shí)候,局部變量必須由程序員顯式地去初始化。但是使用final修飾地局部變臉既可以指定默認(rèn)值,也可以不指定默認(rèn)值。假如在定義修飾的局部變量時(shí)沒有指定默認(rèn)值,則可以在后面代碼中對(duì)該變量賦予一個(gè)指定的初始值。

那么,現(xiàn)在就final和初始化塊結(jié)合起來,來看一段代碼


public class UseFinal {

//定義成員變量時(shí)指定默認(rèn)值

final String author = "codevald";
final String str;
final int a;
final static double d;

//初始化塊,可對(duì)沒有指定默認(rèn)值的實(shí)例屬性指定初始值

{

str = "Hello";

//由于定義author時(shí)已經(jīng)制定了默認(rèn)值,因此不能為author重新賦值,下列語句會(huì)導(dǎo)致編譯錯(cuò)誤

//author = "CodeVald"


}




static {

//在靜態(tài)初始化塊中為類屬性指定初始值

d = 2.1;


}

public UseFinal() {

a = 21;

}


public void useFinal() {

//普通方法不能為fina修飾的成員變量指定初始化值

//d = 2.1;


}



public static void main(String[] args) {

UseFinal useFinal = new UseFinal();
System.out.println(useFinal.author);
System.out.println(useFinal.str);
System.out.println(useFinal.a);
System.out.println(useFinal.d);



}


}

運(yùn)行結(jié)果也很容易就出來了,但是,這里要注意一點(diǎn)的是,普通方法不能為final修飾的變量賦值,會(huì)出現(xiàn)編譯錯(cuò)誤的問題。

來看下運(yùn)行結(jié)果

總結(jié)一下,final成員變量(包括實(shí)例成員和類成員)必須由程序員顯式地初始化,系統(tǒng)不會(huì)對(duì)final成員進(jìn)行隱式初始化。如果想在初始化塊、構(gòu)造器中對(duì)final的成員變量進(jìn)行初始化,那么一定要在初始化之前就訪問該成員變量的值。

final方法

在Java中,經(jīng)常用final修飾那些不希望被重寫的方法。所以,如果我們不希望子類重寫父類的某個(gè)方法,就可以使用final修飾該方法。我們有時(shí)候會(huì)希望獲取一個(gè)Object對(duì)象,所用的getClass()方法就是一個(gè)final方法,因?yàn)樗脑O(shè)計(jì)者不希望該方法被重寫,就用final將該方法密封起來。

final修飾的方法只是不能重寫,但是可以重載。


public class Incorrect {

public final void test() {



  }


}


class Sub extends Incorrect {

//下面的寫法將導(dǎo)致編譯錯(cuò)誤,不能重寫final修飾的方法

@Override 


public void test() {




  }


}


編譯程序,執(zhí)行結(jié)果如下

在Java程序中,對(duì)于private修飾的方法來說,它只在當(dāng)前類中可見,所以其子類無法訪問該方法。所以,如果在子類中定義了一個(gè)與父類private方法有相同方法名、形參列表和返回值類型的方法,這不是方法重寫,只是重新定義了一個(gè)新方法。不會(huì)出現(xiàn)編譯錯(cuò)誤的問題

例如下面的代碼在子類中重寫父類的 private final方法


public class Invaild {

private final void test() {



}

}


class Sub extends Invaild {

public void test() {}


}

在匿名內(nèi)部類中,很多時(shí)候也會(huì)用到final的地方,現(xiàn)在先來系統(tǒng)地談?wù)剝?nèi)部類是啥東西。

內(nèi)部類

內(nèi)部類指的是在外部類的內(nèi)部再定義一個(gè)類,內(nèi)部類作為外部類的一個(gè)成員,是依附在外部類而存在的。內(nèi)部類可以是靜態(tài)的,非靜態(tài)的,可以使用protected和private來修飾,而外部類只能使用public和默認(rèn)的包訪問權(quán)限。Java中的內(nèi)部類主要有成員內(nèi)部類、靜態(tài)內(nèi)部類、局部?jī)?nèi)部類和匿名內(nèi)部類。

那么內(nèi)部類有什么使用的價(jià)值呢?

Java是從JDK1.1開始引入了內(nèi)部類,內(nèi)部類的主要作用如下:

  • 內(nèi)部類提供了更好的封裝,可以把內(nèi)部類隱藏在外部類之內(nèi),不允許同一個(gè)包中的其他類訪問該類
  • 內(nèi)部類的成員可以直接訪問外部類的私有數(shù)據(jù),因?yàn)閮?nèi)部類被當(dāng)成了外部類的成員,同一個(gè)類中的成員之間是可以互相訪問的。但外部類不能訪問內(nèi)部類的實(shí)現(xiàn)細(xì)節(jié),譬如屬性。
  • 匿名內(nèi)部類適用于那些創(chuàng)建僅使用一次的類

內(nèi)部類是一個(gè)編譯時(shí)的概念,一旦編譯成功,外部類和內(nèi)部類就成為完全不同的類,即生成兩個(gè)類的編譯文件,分別是outer.class和outer$inner.class(假如外部類是outer,內(nèi)部類是inner)。

成員內(nèi)部類

在大多數(shù)的情況下,內(nèi)部類作為成員內(nèi)部類來定義。成員內(nèi)部類是一種與屬性、方法、構(gòu)造器和初始化塊相似的類成員。局部?jī)?nèi)部類和匿名內(nèi)部類都不是類成員。Java中的成員內(nèi)部類分別是靜態(tài)內(nèi)部類和非靜態(tài)內(nèi)部類。使用static修飾的就是靜態(tài)內(nèi)部類,沒有使用static修飾的成員內(nèi)部類就是非靜態(tài)內(nèi)部類.

非靜態(tài)內(nèi)部類

來看一段代碼



public class FeiJingTai {

private String area;


//重載構(gòu)造器

public FeiJingTai() {


}

public FeiJingTai(String area) {

this.area = area;

}

//定義內(nèi)部類

private class FeiJingTaiInner {

//內(nèi)部類的屬性

private String name;
private String wechat;

public FeiJingTaiInner(String name,String wechat) {

this.name = name;
this.wechat = wechat;


}

//內(nèi)部方法

public void info() {


System.out.println("CodeVald的作者是 " + name + ",微信號(hào)是 " + wechat);
System.out.println("所屬地區(qū)是 " + area);



}


}

//外部類測(cè)試方法

public void test() {


FeiJingTaiInner a = new FeiJingTaiInner("codevald","valdcode");
a.info();

}


public static void main(String[] args) {


FeiJingTai a = new FeiJingTai("廣東廣州");
a.test();


}



}

在上面的代碼中,可以看到在非靜態(tài)內(nèi)部類中可以直接訪問外部類的私有成員,所以其實(shí)就是在類FeiJingTaiInner的方法內(nèi)直接訪問外部類的私有屬性。這是因?yàn)樵陬怓eiJingTaiInner內(nèi)部類對(duì)象中保存了一個(gè)它儲(chǔ)存的外部類對(duì)象的引用[當(dāng)調(diào)用非靜態(tài)內(nèi)部類的實(shí)例方法時(shí),必須有一個(gè)非靜態(tài)內(nèi)部類實(shí)例,而非靜態(tài)內(nèi)部類實(shí)例必須寄居在外部類實(shí)例里]

編譯程序,將會(huì)看到在文件路徑下生成了兩個(gè)類文件一個(gè)是FeiJingTai.class,另一個(gè)是FeiJingTai$FeiJingTaiInner.class

執(zhí)行后的結(jié)果

再來看一段代碼


class MemberInner {// 定義類 MemberInner,這是一個(gè)外部類
    private String name = "codevald";

    public void execute() {
        // 在外部類中創(chuàng)建成員內(nèi)部類
        InnerClass innerClass = this.new InnerClass();
    }

    /** 成員內(nèi)部類 */
    public class InnerClass {
        // 內(nèi)部類可以創(chuàng)建與外部類同名的成員變量
        private String name = "codevald";

        public void execute() {
            System.out.println(this.name);
            // 在內(nèi)部類中使用外部類成員變量的方法
            System.out.println(MemberInner.this.name);
        }
    }

    public static void main(String[] args) {
        MemberInner.InnerClass innerClass = new MemberInner().new InnerClass();
        innerClass.execute();
    }
}

在上面的代碼中,使用了兩種方式創(chuàng)建內(nèi)部類對(duì)象,一種是用外部引用的方式,另一種是調(diào)用方法創(chuàng)建,在execute()方法中,this代表的是創(chuàng)建在堆中的外部對(duì)象,而在內(nèi)部類,使用this是分別引用內(nèi)部類中的屬性和外部類中的屬性。

看下編譯運(yùn)行結(jié)果

靜態(tài)內(nèi)部類

如果不需要內(nèi)部類對(duì)象與外部類對(duì)象之間有聯(lián)系,則可以將內(nèi)部類聲明為static。在非靜態(tài)內(nèi)部類中,內(nèi)部類對(duì)象通常會(huì)保存了一個(gè)指向外部類的引用,如果內(nèi)部類是static時(shí)就不用了,非靜態(tài)內(nèi)部類通常也稱為嵌套類

嵌套類要注意以下兩點(diǎn):

  • 要?jiǎng)?chuàng)建嵌套類的對(duì)象,不需要外部類的對(duì)象
  • 不能直接從嵌套類的對(duì)象中訪問非靜態(tài)的外部類對(duì)象

從一段具體的代碼來分析一下


public class JingTai {

private String name_1 = "codevald";
private static String name_2 = "codevald";

static class JingTaiInner {

private static String name;

public static void main(String[] args) {

//可以輸出外部類的靜態(tài)類成員變量

System.out.println(name_2);

//System.out.println(name_1);
//不可以直接輸出外部類的非靜態(tài)類成員變量

//得生成對(duì)象,再用對(duì)象引用去訪問

JingTai a = new JingTai();
System.out.println(a.name_1);




}


}

public static void main(String[] args) {

JingTai.JingTaiInner a = new JingTai.JingTaiInner();



}



}

運(yùn)行結(jié)果

上面的代碼中,在內(nèi)部類有輸出語句,然后再外部類創(chuàng)建內(nèi)部類,但是在內(nèi)部類中,只能直接訪問外部類的靜態(tài)屬性,要訪問外部類的非靜態(tài)屬性得生成對(duì)象,再用對(duì)象的引用去訪問。

所以,生成一個(gè)靜態(tài)內(nèi)部類不需要外部類成員,這是靜態(tài)內(nèi)部類和成員內(nèi)部類的區(qū)別。靜態(tài)內(nèi)部類可以直接[Outer.Inner inner = new Outer.Inner();],而不需要通過外部類來完成。這樣子實(shí)際上靜態(tài)內(nèi)部類就是一個(gè)獨(dú)立的類。

局部?jī)?nèi)部類

在方法中定義的內(nèi)部類就是局部?jī)?nèi)部類。與局部變量相似的是,局部?jī)?nèi)部類可以訪問當(dāng)前代碼中的常量和外部類的所有成員。在Java中,和局部變量一樣,不能將局部?jī)?nèi)部類定義為public、private、protected、和static類型,并且在定義方法時(shí),只能在方法中聲明final類型的變量。

看一段代碼


public class LocalInner {

public static void main(String[] args) {


class InnerClass {

String name;

}

class InnerSub extends InnerClass {

String des;


}

//創(chuàng)建局部?jī)?nèi)部類的對(duì)象

InnerSub is = new InnerSub();
is.name = "codevald";
is.des = "想起來了嗎?看完就想起來了";
System.out.println("author: " + is.name + "\n" + "subject: " + is.des);

}

}

編譯運(yùn)行結(jié)果

匿名類

在實(shí)際的項(xiàng)目動(dòng)手過程,經(jīng)常會(huì)看到一個(gè)很奇怪的寫法,直接在代碼中隨機(jī)用new新節(jié)一個(gè)對(duì)象,然后在new;里面直接簡(jiǎn)單粗暴的加入要執(zhí)行的代碼,這就是匿名類。好處就是代碼簡(jiǎn)潔、緊湊,不會(huì)出現(xiàn)一大段繁雜的類定義代碼。

在Java程序中,因?yàn)槟涿悰]有名字,所以它的創(chuàng)建方式初學(xué)的時(shí)候看起來會(huì)很懵逼,創(chuàng)建的格式如下:


new 類/接口名(參數(shù)列表) [實(shí)現(xiàn)接口()]{
     //匿名內(nèi)部類的類體部分
}

{...}中可以定義變量的名稱、方法,它跟普通的類一樣。

因?yàn)镴ava程序中的匿名類沒有名稱,所以不能在其他地方引用,也不能實(shí)例化,只能使用一次,而且里面不能有構(gòu)造器。

來看一段代碼

先定義一個(gè)抽象父類


public abstract class Author {

private String name;

public String getName() {

return name;

}


public void setName(String name) {

this.name = name;

}

public abstract int article();


}

編寫測(cè)試類進(jìn)行測(cè)試,在類中,test()方法接收一個(gè)Author類型的參數(shù),同時(shí)要先實(shí)現(xiàn)類才能new新的類實(shí)例,在方法中直接使用匿名內(nèi)部類新建一個(gè)Author實(shí)例。


public class NiMing {

public void test(Author author) {

System.out.println("這是" + author.getName() + "的第" + author.article() + "篇原創(chuàng)作品.");

}


public static void main(String[] args) {

NiMing test = new NiMing();
test.test(new Author() {

//使用匿名內(nèi)部類來創(chuàng)建一個(gè)Author實(shí)例

@Override 

public int article() {

return 2;

}


@Override 

public String getName() {

return "codevald";

}


});

}


}

編譯運(yùn)行結(jié)果

終于講完了,現(xiàn)在要進(jìn)入主題了,匿名內(nèi)部類中什么時(shí)候會(huì)用到final呢?

使用final形參

在Java中,當(dāng)我們需要給匿名內(nèi)部類傳遞參數(shù)的時(shí)候,并且如果在內(nèi)部類中使用該形參的時(shí)候,這個(gè)形參則必須由final修飾的。即該匿名內(nèi)部類所在方法的形參必須加上final修飾符。

編寫一段代碼


public class NiMing_Final {

public static void main(String[] args) {

NiMing_Final niming = new NiMing_Final();
Inner inner = niming.getInner("codevald",true);
System.out.println("這是" + inner.getName() + "的第" + inner.article() + "篇原創(chuàng)作品");



}



public Inner getInner(final String name,boolean isOriginal) {

return new Inner() {

private String nameStr = name;
private int article;

{

//實(shí)例初始化

if (isOriginal) {

article = 2;

} else {

article = 0;

}


}


@Override 

public String getName() {

return nameStr;

}

@Override 

public int article() {

return article;

}




};


}


}



interface Inner {

String getName();
int article();


}

這里通過實(shí)例初始化實(shí)現(xiàn)類似構(gòu)造器的功能

來看下運(yùn)行結(jié)果

枚舉類

在枚舉類中,使用final的頻率是最頻繁的。什么是枚舉類?在大多數(shù)情況下,我們要實(shí)例化的類對(duì)象是有限的而且固定的,例如季節(jié),這種實(shí)力數(shù)量有限而且固定的類,在Java中被稱為枚舉類。

我們先來做個(gè)有意思的事情,自己模擬實(shí)現(xiàn)一個(gè)枚舉類,在實(shí)現(xiàn)枚舉類的時(shí)候,有以下幾個(gè)步驟:

  • 通過private將構(gòu)造器隱藏起來
  • 把此類需要用到的所有實(shí)例都用public static final修飾的形式保存起來
  • 提供一些靜態(tài)方法允許其他程序根據(jù)特定參數(shù)獲取與之匹配的實(shí)例

那么可以定義一個(gè)Season類,在里面分別為4個(gè)季節(jié)定義4個(gè)對(duì)象,這樣類Season就定義為了一個(gè)枚舉類。


public class Season {

//將Season定義成不可變得,將其屬性定義成final

private final String name;
private final String description;

public static final Season SPRING = new Season("春天","綠肥紅瘦");
public static final Season SUMMER = new Season("夏天","驕陽似火");
public static final Season FALL = new Season("秋天","天高云淡");
public static final Season WINTER = new Season("冬天","惟余莽莽");

//構(gòu)造器一定要定義為private屬性

private Season(String name,String description) {

this.name = name;
this.description = description;

}

//也可以通過getSeason()獲取枚舉常量

public static final Season getSeason(int seasonValue) {

switch(seasonValue) {

case 1:

return SPRING;



case 2:

return SUMMER;


case 3:

return FALL;


case 4:

return WINTER;



default:

return null;

}


}


public String getName() {

return this.name;


}


public String getDescription() {

return description;


}

}

類Season就成為了一個(gè)不可變的類,此類包含了4個(gè)static final常量的屬性,也就代表了該類所能創(chuàng)建的對(duì)象。其他程序需要用到Season對(duì)象時(shí),可以用Season.SPRING方式或者getSeason()靜態(tài)方法獲得。

編寫測(cè)試類


public class TestSeason {

public TestSeason(Season s) {

System.out.println(s.getName() + ",是一個(gè)" + s.getDescription() + "的季節(jié)");

}


public static void main(String[] args) {



new TestSeason(Season.SPRING);
new TestSeason(Season.SUMMER);
new TestSeason(Season.FALL);
new TestSeason(Season.WINTER);

}

}

運(yùn)行結(jié)果

自己模擬完枚舉類后,會(huì)發(fā)現(xiàn)枚舉類其實(shí)就是在類編譯的時(shí)候,就生成了相對(duì)應(yīng)的靜態(tài)常量,并且構(gòu)造器是對(duì)用戶透明的,它會(huì)自己進(jìn)行初始化,我們只需要關(guān)心我們需要獲取什么樣的枚舉對(duì)象就可以了。

枚舉類型是從JDK1.5開始引入的,Java引入了一個(gè)新的關(guān)鍵字enum來定義枚舉類。這個(gè)enum所定義的類實(shí)際上都是繼承自類庫中Enum(java.lang.Enum)的子類,它繼承了Enum中許多有用的方法。

來繼續(xù)看一段代碼


public enum Color {

RED(255,0,0),BLUE(0,0,255),BLACK(0,0,0),YELLOW(255,255,0),GREEN(0,255,0);

private Color(int redValue,int greenValue,int blueValue) {

this.redValue = redValue;
this.greenValue = greenValue;
this.blueValue = blueValue;

}


@Override 

public String toString() {

//覆蓋了父類Enum的toString()方法

return super.toString() + "(" + redValue + "," + greenValue + "," + blueValue + ")";

}




//自定義數(shù)據(jù)域

private int redValue;
private int greenValue;
private int blueValue;

}

上面的Color枚舉類是一個(gè)不可繼承的final類。枚舉值(RED...)都是Color類型的靜態(tài)常量,因?yàn)槊杜e類是class,所以在枚舉類型中也可以有構(gòu)造器、方法和數(shù)據(jù)域,但是枚舉類的構(gòu)造器是私有的,它會(huì)自己調(diào)用。

而且在上面的枚舉類中,重寫了枚舉類Enum的toString()方法,打印出更完整的信息。

來看下會(huì)有什么輸出結(jié)果

在上面的代碼中,調(diào)用了Enum的ordinal()方法,它會(huì)返回枚舉值在枚舉類中的順序,這個(gè)順序是根據(jù)枚舉值在聲明的順序中定的,所以會(huì)輸出"0 1 2 3 4"。

然后調(diào)用了Enum的valueOf()方法,此方法是和toString()方法對(duì)應(yīng)的,返回帶指定名稱的指定類型的枚舉常量,所以會(huì)輸出"BLUE(0,0,255)"。

最后,可能大家會(huì)疑惑,為什么println輸出會(huì)調(diào)用重寫的toString()方法呢?

別急,讓我來一一分析一下。

直接看Java相關(guān)類的源代碼就可以分析出來了。

先來看下System.out.println和System.out.print的源代碼


public void println(Object x){ 
  String s = String.valueOf(x); 
     synchronized (this) 
      { 
      print(s); 

      newLine();

      } 





   public void print(Object obj) { 
    write(String.valueOf(obj));
  } 

可以看到,當(dāng)要打印一個(gè)對(duì)象時(shí),會(huì)自動(dòng)調(diào)用String.valueOf()這個(gè)方法。

那么我們?cè)賮砜聪聉alueof()這個(gè)方法的源代碼


public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

這段代碼的意思就是,當(dāng)傳入的對(duì)象為Null時(shí),會(huì)返回一個(gè)null,當(dāng)非null時(shí),會(huì)返回這個(gè)obj的toString()方法,所以,非null時(shí)就會(huì)調(diào)用toString()方法,原因我們就知道了,這就是當(dāng)我們調(diào)用 print 或者 println 打印一個(gè)對(duì)象時(shí),它會(huì)打印出這個(gè) 對(duì)象的 toString()的最終根源。

所以,我覺得平時(shí)沒事可以多研究JDK的源代碼,站在巨人的肩膀上,看下怎么寫出更簡(jiǎn)潔優(yōu)美的代碼。


今天的內(nèi)容就到這里了,相信看到這里,你應(yīng)該明白了final大概是怎么用的,什么時(shí)候需要用?!昂媳е?生于毫末?!敝挥姓驹谠O(shè)計(jì)者的角度,從根本上去理解為什么這么設(shè)計(jì),吃透每個(gè)基本的知識(shí)點(diǎn),并且深入研究源碼,才能讓內(nèi)功更深厚,從而去解決一個(gè)又一個(gè)更高深的問題。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多