前言
修飾符怎么使用也是Java基礎(chǔ)中比較重要的知識點,,徹底理解了之后,后面學(xué)習(xí)更高深的東西才能得心應(yīng)手,。今天,以修飾符中比較常見的final為切入點,來談?wù)刦inal的使用的奇淫技巧以及一些相關(guān)的知識點,。學(xué)廢了記得三連哦。
初始化塊
在final的運用中,,經(jīng)常和初始化塊和構(gòu)造器結(jié)合起來一起使用,。上篇文章已經(jīng)介紹完什么是構(gòu)造器,那么現(xiàn)在先來談?wù)勈裁词浅跏蓟瘔K,。
Java會使用構(gòu)造器對對象進行初始化操作,,在使用的構(gòu)造器的時候需要完成初始化整個Java對象的狀態(tài)的功能,然后再將整個完整的Java對象返回給程序使用,。那么,,在Java中,有一個與構(gòu)造器功能類似的東西,,就是初始化塊,,它能夠?qū)ava對象實現(xiàn)初始化操作。
在Java中,,一個類中可以有多個初始化塊,,相同類型的初始化塊的執(zhí)行順序是有要求的,先定義的先執(zhí)行(前面的先執(zhí)行),,后面定義的后執(zhí)行,。
初始化塊的語法其實很簡單了,就是:
修飾符 {
//初始化塊中的可執(zhí)行代碼塊
...
...
...
}
那初始化塊的分類也很簡單,,就分為靜態(tài)初始化塊和非靜態(tài)初始化塊兩種,其中非靜態(tài)初始化塊也叫做普通初始化塊
非靜態(tài)初始化塊
在生成每個對象的時候都會執(zhí)行一次,,可以初始化類的實例變量。非靜態(tài)初始化塊會在類的構(gòu)造器之前執(zhí)行
先來看一段代碼
`public class CodeVald {
` int a = 6;
`//第一個初始化塊
{
int a = 3;
if (this.a > 4) {
System.out.println("codevald的初始化塊: 成員變量a的值大于4");
}
System.out.println("codevald的初始化塊");
}
//第二個初始化塊
{
System.out.println("codevald的第二個初始化塊");
}
//定義一個無參數(shù)的構(gòu)造器
public CodeVald() {
System.out.println("codevald的無參數(shù)構(gòu)造器");
}
public static void main(String[] args) {
new CodeVald();
}
}
}
上面的代碼定義了兩個普通初始化塊和一個構(gòu)造器,,那么執(zhí)行的順序也很簡單了,,先定義的初始化塊先執(zhí)行,然后執(zhí)行后定義的初始化塊,,接著執(zhí)行構(gòu)造器的內(nèi)容
來看下編譯運行結(jié)果
靜態(tài)初始化塊
使用static定義,,當類裝載到系統(tǒng)只會執(zhí)行一次。如果在靜態(tài)初始化塊中想初始化變量的話,,就只能初始化類變量了,,即是由static修飾的數(shù)據(jù)成員。
來看一個靜態(tài)初始化塊、普通初始化塊和構(gòu)造器結(jié)合的例子:
public class JingTai_CodeBlock {
public static void main(String[] args) {
new C();
new C();
}
}
//定義第一個類A,,這是父類
class A {
static {
System.out.println("A的靜態(tài)初始化塊");
}
{
System.out.println("A的普通初始化塊");
}
public A() {
System.out.println("A的無參數(shù)構(gòu)造器");
}
}
//定義一個子類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);
}
}
//定義一個子類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三個類,,他們都提供了靜態(tài)初始化塊和普通初始化塊,并且在類B中使用this()調(diào)用了重載構(gòu)造器,,在C中使用super()顯式地調(diào)用了其父類指定的構(gòu)造器,,接著在main()函數(shù)調(diào)用了兩次new C(),創(chuàng)建兩個C對象。
那么來猜下會輸出什么結(jié)果
先思考五分鐘哦
現(xiàn)在來解釋一下,,這里定義了靜態(tài)初始化塊,,那么會在類地初始化階段執(zhí)行靜態(tài)初始化塊,而不是創(chuàng)建對象的時候才執(zhí)行,,所以靜態(tài)初始化塊總是比普通初始化塊先執(zhí)行,,接著是構(gòu)造器
但是系統(tǒng)在類初始化階段執(zhí)行靜態(tài)初始化塊的時候,不僅會執(zhí)行本類的靜態(tài)初始化塊,,還會一直上溯到j(luò)ava.lang.Object類(所有對象的父類),,如果它包含靜態(tài)初始化塊,先執(zhí)行java.lang.Object類的靜態(tài)初始化塊,,然后執(zhí)行其父類的靜態(tài)初始化塊...執(zhí)行完最后才執(zhí)行該類的靜態(tài)初始化塊,,經(jīng)過上述過程才能完成該類的初始化過程。
第一次創(chuàng)建C對象的時候,,會先運行靜態(tài)初始化塊的內(nèi)容,,但是會先上溯到頂級父類的靜態(tài)初始化塊,所以會先輸出A的靜態(tài)初始化塊,,接著才是B的靜態(tài)初始化塊,,最后是C的靜態(tài)初始化塊。
執(zhí)行完靜態(tài)初始化塊,,一樣先執(zhí)行頂級父類的普通初始化塊,,即輸出A的普通初始化塊,接著執(zhí)行頂級父類的構(gòu)造器代碼,,即輸出A的無參數(shù)構(gòu)造器,。然后輸出父類的普通初始化塊,接著是構(gòu)造器,,所以輸出B的普通初始化塊,因為C的構(gòu)造器調(diào)用的是帶參數(shù)的父類構(gòu)造器,,所以B中會調(diào)用帶參數(shù)的構(gòu)造器B,所以會輸出B的無參數(shù)構(gòu)造器,,B的帶參數(shù)構(gòu)造器,傳入的信息為: codevald,接著執(zhí)行C的普通初始化塊的代碼,,即輸出C的普通初始化塊,,然后是構(gòu)造器的代碼,即C的構(gòu)造器
第二次創(chuàng)建實例C的時候,,因為類C已經(jīng)在虛擬機中存在,,所以無需再初始化C類了,所以靜態(tài)初始化塊的代碼不再執(zhí)行,,而是重復(fù)地執(zhí)行靜態(tài)后面的代碼,。
final修飾符
final可以用來修飾類、變量和方法,,通過final修飾以后,,被修飾的類、方法和變量就表示不可改變的狀態(tài),。
修飾成員變量
成員變量是隨著類的初始化或者對象初始化而初始化的,。當初始化的時候,就會為類的類屬性分配內(nèi)存,,并設(shè)置一個默認值,;當創(chuàng)建對象時,就會為對象的實例屬性分配內(nèi)存,,并分配默認值,。一般來說,都是在普通初始化塊,、靜態(tài)初始化塊,、構(gòu)造器中區(qū)指定初始值的。
那么,,final修飾的屬性,,在哪里聲明初始值是有一定的規(guī)則的,具體如下:
-
修飾類屬性時:可在靜態(tài)靜態(tài)初始化塊中聲明該屬性的初始值
-
修飾實例屬性時: 可在普通初始化塊中或者構(gòu)造器中指定初始值
修飾局部變量
在初始化局部變量的時候,,局部變量必須由程序員顯式地去初始化,。但是使用final修飾地局部變臉既可以指定默認值,也可以不指定默認值,。假如在定義修飾的局部變量時沒有指定默認值,,則可以在后面代碼中對該變量賦予一個指定的初始值。
那么,,現(xiàn)在就final和初始化塊結(jié)合起來,,來看一段代碼
public class UseFinal {
//定義成員變量時指定默認值
final String author = "codevald";
final String str;
final int a;
final static double d;
//初始化塊,可對沒有指定默認值的實例屬性指定初始值
{
str = "Hello";
//由于定義author時已經(jīng)制定了默認值,,因此不能為author重新賦值,,下列語句會導(dǎo)致編譯錯誤
//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);
}
}
運行結(jié)果也很容易就出來了,,但是,這里要注意一點的是,,普通方法不能為final修飾的變量賦值,,會出現(xiàn)編譯錯誤的問題。
來看下運行結(jié)果
總結(jié)一下,,final成員變量(包括實例成員和類成員)必須由程序員顯式地初始化,,系統(tǒng)不會對final成員進行隱式初始化。如果想在初始化塊,、構(gòu)造器中對final的成員變量進行初始化,,那么一定要在初始化之前就訪問該成員變量的值。
final方法
在Java中,,經(jīng)常用final修飾那些不希望被重寫的方法,。所以,如果我們不希望子類重寫父類的某個方法,,就可以使用final修飾該方法,。我們有時候會希望獲取一個Object對象,所用的getClass()方法就是一個final方法,,因為它的設(shè)計者不希望該方法被重寫,,就用final將該方法密封起來。
final修飾的方法只是不能重寫,,但是可以重載,。
public class Incorrect {
public final void test() {
}
}
class Sub extends Incorrect {
//下面的寫法將導(dǎo)致編譯錯誤,不能重寫final修飾的方法
@Override
public void test() {
}
}
編譯程序,,執(zhí)行結(jié)果如下
在Java程序中,,對于private修飾的方法來說,它只在當前類中可見,,所以其子類無法訪問該方法,。所以,如果在子類中定義了一個與父類private方法有相同方法名,、形參列表和返回值類型的方法,,這不是方法重寫,只是重新定義了一個新方法,。不會出現(xiàn)編譯錯誤的問題
例如下面的代碼在子類中重寫父類的 private final方法
public class Invaild {
private final void test() {
}
}
class Sub extends Invaild {
public void test() {}
}
在匿名內(nèi)部類中,,很多時候也會用到final的地方,現(xiàn)在先來系統(tǒng)地談?wù)剝?nèi)部類是啥東西,。
內(nèi)部類
內(nèi)部類指的是在外部類的內(nèi)部再定義一個類,,內(nèi)部類作為外部類的一個成員,是依附在外部類而存在的,。內(nèi)部類可以是靜態(tài)的,,非靜態(tài)的,,可以使用protected和private來修飾,而外部類只能使用public和默認的包訪問權(quán)限,。Java中的內(nèi)部類主要有成員內(nèi)部類,、靜態(tài)內(nèi)部類、局部內(nèi)部類和匿名內(nèi)部類,。
那么內(nèi)部類有什么使用的價值呢?
Java是從JDK1.1開始引入了內(nèi)部類,,內(nèi)部類的主要作用如下:
- 內(nèi)部類提供了更好的封裝,,可以把內(nèi)部類隱藏在外部類之內(nèi),不允許同一個包中的其他類訪問該類
- 內(nèi)部類的成員可以直接訪問外部類的私有數(shù)據(jù),,因為內(nèi)部類被當成了外部類的成員,,同一個類中的成員之間是可以互相訪問的。但外部類不能訪問內(nèi)部類的實現(xiàn)細節(jié),,譬如屬性,。
- 匿名內(nèi)部類適用于那些創(chuàng)建僅使用一次的類
內(nèi)部類是一個編譯時的概念,一旦編譯成功,,外部類和內(nèi)部類就成為完全不同的類,,即生成兩個類的編譯文件,分別是outer.class和outer$inner.class(假如外部類是outer,,內(nèi)部類是inner),。
成員內(nèi)部類
在大多數(shù)的情況下,內(nèi)部類作為成員內(nèi)部類來定義,。成員內(nèi)部類是一種與屬性,、方法、構(gòu)造器和初始化塊相似的類成員,。局部內(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 + ",微信號是 " + wechat);
System.out.println("所屬地區(qū)是 " + area);
}
}
//外部類測試方法
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)部類中可以直接訪問外部類的私有成員,所以其實就是在類FeiJingTaiInner的方法內(nèi)直接訪問外部類的私有屬性,。這是因為在類FeiJingTaiInner內(nèi)部類對象中保存了一個它儲存的外部類對象的引用[當調(diào)用非靜態(tài)內(nèi)部類的實例方法時,,必須有一個非靜態(tài)內(nèi)部類實例,而非靜態(tài)內(nèi)部類實例必須寄居在外部類實例里]
編譯程序,,將會看到在文件路徑下生成了兩個類文件一個是FeiJingTai.class,,另一個是FeiJingTai$FeiJingTaiInner.class
執(zhí)行后的結(jié)果
再來看一段代碼
class MemberInner {// 定義類 MemberInner,這是一個外部類
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)部類對象,,一種是用外部引用的方式,,另一種是調(diào)用方法創(chuàng)建,在execute()方法中,this代表的是創(chuàng)建在堆中的外部對象,,而在內(nèi)部類,,使用this是分別引用內(nèi)部類中的屬性和外部類中的屬性。
看下編譯運行結(jié)果
靜態(tài)內(nèi)部類
如果不需要內(nèi)部類對象與外部類對象之間有聯(lián)系,,則可以將內(nèi)部類聲明為static,。在非靜態(tài)內(nèi)部類中,內(nèi)部類對象通常會保存了一個指向外部類的引用,,如果內(nèi)部類是static時就不用了,,非靜態(tài)內(nèi)部類通常也稱為嵌套類。
嵌套類要注意以下兩點:
- 要創(chuàng)建嵌套類的對象,,不需要外部類的對象
- 不能直接從嵌套類的對象中訪問非靜態(tài)的外部類對象
從一段具體的代碼來分析一下
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)類成員變量
//得生成對象,,再用對象引用去訪問
JingTai a = new JingTai();
System.out.println(a.name_1);
}
}
public static void main(String[] args) {
JingTai.JingTaiInner a = new JingTai.JingTaiInner();
}
}
運行結(jié)果
上面的代碼中,在內(nèi)部類有輸出語句,,然后再外部類創(chuàng)建內(nèi)部類,,但是在內(nèi)部類中,只能直接訪問外部類的靜態(tài)屬性,,要訪問外部類的非靜態(tài)屬性得生成對象,,再用對象的引用去訪問。
所以,,生成一個靜態(tài)內(nèi)部類不需要外部類成員,,這是靜態(tài)內(nèi)部類和成員內(nèi)部類的區(qū)別。靜態(tài)內(nèi)部類可以直接[Outer.Inner inner = new Outer.Inner();],而不需要通過外部類來完成,。這樣子實際上靜態(tài)內(nèi)部類就是一個獨立的類,。
局部內(nèi)部類
在方法中定義的內(nèi)部類就是局部內(nèi)部類。與局部變量相似的是,,局部內(nèi)部類可以訪問當前代碼中的常量和外部類的所有成員,。在Java中,和局部變量一樣,,不能將局部內(nèi)部類定義為public,、private、protected,、和static類型,,并且在定義方法時,只能在方法中聲明final類型的變量,。
看一段代碼
public class LocalInner {
public static void main(String[] args) {
class InnerClass {
String name;
}
class InnerSub extends InnerClass {
String des;
}
//創(chuàng)建局部內(nèi)部類的對象
InnerSub is = new InnerSub();
is.name = "codevald";
is.des = "想起來了嗎,?看完就想起來了";
System.out.println("author: " + is.name + "\n" + "subject: " + is.des);
}
}
編譯運行結(jié)果
匿名類
在實際的項目動手過程,經(jīng)常會看到一個很奇怪的寫法,,直接在代碼中隨機用new新節(jié)一個對象,,然后在new,;里面直接簡單粗暴的加入要執(zhí)行的代碼,這就是匿名類,。好處就是代碼簡潔,、緊湊,不會出現(xiàn)一大段繁雜的類定義代碼,。
在Java程序中,,因為匿名類沒有名字,所以它的創(chuàng)建方式初學(xué)的時候看起來會很懵逼,,創(chuàng)建的格式如下:
new 類/接口名(參數(shù)列表) [實現(xiàn)接口()]{
//匿名內(nèi)部類的類體部分
}
{...}中可以定義變量的名稱,、方法,它跟普通的類一樣,。
因為Java程序中的匿名類沒有名稱,所以不能在其他地方引用,,也不能實例化,,只能使用一次,而且里面不能有構(gòu)造器,。
來看一段代碼
先定義一個抽象父類
public abstract class Author {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract int article();
}
編寫測試類進行測試,,在類中,test()方法接收一個Author類型的參數(shù),同時要先實現(xiàn)類才能new新的類實例,,在方法中直接使用匿名內(nèi)部類新建一個Author實例,。
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)建一個Author實例
@Override
public int article() {
return 2;
}
@Override
public String getName() {
return "codevald";
}
});
}
}
編譯運行結(jié)果
終于講完了,現(xiàn)在要進入主題了,,匿名內(nèi)部類中什么時候會用到final呢,?
使用final形參
在Java中,當我們需要給匿名內(nèi)部類傳遞參數(shù)的時候,,并且如果在內(nèi)部類中使用該形參的時候,,這個形參則必須由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;
{
//實例初始化
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();
}
這里通過實例初始化實現(xiàn)類似構(gòu)造器的功能
來看下運行結(jié)果
枚舉類
在枚舉類中,,使用final的頻率是最頻繁的。什么是枚舉類,?在大多數(shù)情況下,,我們要實例化的類對象是有限的而且固定的,例如季節(jié),,這種實力數(shù)量有限而且固定的類,,在Java中被稱為枚舉類。
我們先來做個有意思的事情,,自己模擬實現(xiàn)一個枚舉類,,在實現(xiàn)枚舉類的時候,,有以下幾個步驟:
- 通過private將構(gòu)造器隱藏起來
- 把此類需要用到的所有實例都用public static final修飾的形式保存起來
- 提供一些靜態(tài)方法允許其他程序根據(jù)特定參數(shù)獲取與之匹配的實例
那么可以定義一個Season類,在里面分別為4個季節(jié)定義4個對象,,這樣類Season就定義為了一個枚舉類,。
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就成為了一個不可變的類,,此類包含了4個static final常量的屬性,,也就代表了該類所能創(chuàng)建的對象。其他程序需要用到Season對象時,,可以用Season.SPRING方式或者getSeason()靜態(tài)方法獲得,。
編寫測試類
public class TestSeason {
public TestSeason(Season s) {
System.out.println(s.getName() + ",是一個" + 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);
}
}
運行結(jié)果
自己模擬完枚舉類后,,會發(fā)現(xiàn)枚舉類其實就是在類編譯的時候,,就生成了相對應(yīng)的靜態(tài)常量,并且構(gòu)造器是對用戶透明的,,它會自己進行初始化,,我們只需要關(guān)心我們需要獲取什么樣的枚舉對象就可以了。
枚舉類型是從JDK1.5開始引入的,,Java引入了一個新的關(guān)鍵字enum來定義枚舉類,。這個enum所定義的類實際上都是繼承自類庫中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枚舉類是一個不可繼承的final類,。枚舉值(RED...)都是Color類型的靜態(tài)常量,,因為枚舉類是class,所以在枚舉類型中也可以有構(gòu)造器,、方法和數(shù)據(jù)域,,但是枚舉類的構(gòu)造器是私有的,它會自己調(diào)用,。
而且在上面的枚舉類中,,重寫了枚舉類Enum的toString()方法,打印出更完整的信息,。
來看下會有什么輸出結(jié)果
在上面的代碼中,,調(diào)用了Enum的ordinal()方法,它會返回枚舉值在枚舉類中的順序,,這個順序是根據(jù)枚舉值在聲明的順序中定的,,所以會輸出"0 1 2 3 4"。
然后調(diào)用了Enum的valueOf()方法,,此方法是和toString()方法對應(yīng)的,,返回帶指定名稱的指定類型的枚舉常量,所以會輸出"BLUE(0,0,255)"。
最后,,可能大家會疑惑,,為什么println輸出會調(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));
}
可以看到,,當要打印一個對象時,,會自動調(diào)用String.valueOf()這個方法。
那么我們再來看下valueof()這個方法的源代碼
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
這段代碼的意思就是,,當傳入的對象為Null時,,會返回一個null,當非null時,,會返回這個obj的toString()方法,,所以,非null時就會調(diào)用toString()方法,,原因我們就知道了,,這就是當我們調(diào)用 print 或者 println 打印一個對象時,它會打印出這個 對象的 toString()的最終根源,。
所以,我覺得平時沒事可以多研究JDK的源代碼,,站在巨人的肩膀上,,看下怎么寫出更簡潔優(yōu)美的代碼。
今天的內(nèi)容就到這里了,,相信看到這里,,你應(yīng)該明白了final大概是怎么用的,什么時候需要用,?!昂媳е?生于毫末?!敝挥姓驹谠O(shè)計者的角度,,從根本上去理解為什么這么設(shè)計,吃透每個基本的知識點,,并且深入研究源碼,,才能讓內(nèi)功更深厚,從而去解決一個又一個更高深的問題,。
|