5繼承

5繼承

5.1 類扯俱、超類和子類

  • 重用部分代碼,并保留所有域善绎』聘眨“is-a”關(guān)系捎谨,用extends表示。

  • 已存在的類被稱為超類(superclass)憔维、基類(base class)父類(parent class)涛救;新類稱為子類(subclass)派生類(derived class)孩子類(child class)业扒。子類比父類封裝了更多的數(shù)據(jù)检吆,擁有更多的功能。

  • 在設(shè)計(jì)類時(shí)程储,盡可能將通用的功能放到父類中蹭沛,具體特殊用途的放到子類中。有時(shí)需要提供一個(gè)新的方法來覆蓋(override)父類中的方法章鲤。例:
    class Manager extends Employee{
    /**
    Manager繼承于Employee類摊灭,添加了bonus屬性和setBonus方法。
    /
    private double bonus;
    ...
    public void setBonus(double b){
    bonus = b;
    }
    }
    Manager類中添加方法覆蓋Employee中的getSalary()方法:
    public double getSalary(){
    return salary+bonus;//won't work
    }
    該方法無法運(yùn)行咏窿,因?yàn)樽宇愔械姆椒?/em>不能直接地訪問父類的私有域*斟或,如果要訪問,必須借助公有的接口集嵌。改為:
    public double getSalary(){
    double baseSalary = getSalary(); //still won't work
    return baseSalary + bonus;
    }
    仍然無法運(yùn)行萝挤,因?yàn)镸anager類也有一個(gè)getSalary方法御毅,所以該語句會(huì)無限次調(diào)用自己。所以正確的格式為:
    public double getSalary(){
    //super關(guān)鍵字指明調(diào)用父類中的getSalary方法怜珍,而不是當(dāng)前類的這個(gè)方法
    double baseSalary = super.getSalary();
    return baseSalary + bonus;
    }

  • 在子類中可以增加域端蛆、增加方法或覆蓋超類的方法,然而絕不能刪除任何繼承的方法和域酥泛。

  • super在構(gòu)造器中的應(yīng)用:
    public Manager(String n, double s, int year, int month, int day){
    super(n,s,year,month,day);
    bonus = 0;
    }
    super指“調(diào)用超類Employee中含有n今豆、s、year柔袁、month和day參數(shù)的構(gòu)造器”呆躲。我們可以通過super實(shí)現(xiàn)對(duì)超類構(gòu)造器的調(diào)用,使用super調(diào)用構(gòu)造器的語句必須是子類構(gòu)造器的第一條語句捶索。如果子類的構(gòu)造器沒有顯式地調(diào)用超類的構(gòu)造器插掂,則將自動(dòng)地調(diào)用超類默認(rèn)(沒有參數(shù))的構(gòu)造器。如果超類沒有不帶參數(shù)的構(gòu)造器腥例,并且在子類的構(gòu)造器中又沒有顯式地調(diào)用超類的其他構(gòu)造器辅甥,則編譯報(bào)錯(cuò)。

  • this和super很像燎竖。this的兩個(gè)功能:1)引用隱式參數(shù)璃弄;2)調(diào)用該類其他結(jié)構(gòu)的構(gòu)造器。super的兩個(gè)用途:1)調(diào)用超類的方法构回;2)調(diào)用超類的構(gòu)造器夏块。
    Manager boss = new Manager("Carl", 80000, 1987, 12, 15);
    boss.setBonus(5000);
    Employee[] staff = new Employee[3];
    staff[0] = boss;
    staff[1] = new Employee("Harry", 50000, 1989, 10, 1);
    staff[2] = new Employee("Tony", 40000, 1990, 3, 15);

  • 這里的staff[1]和[2]都是Employee對(duì)象,staff[0]是Manager對(duì)象捐凭。(Li:子類對(duì)象可以被認(rèn)為是父類對(duì)象拨扶,反之不可。)

  • boss引用Employee對(duì)象時(shí)茁肠,boss.getSalary()調(diào)用的是Employee中的getSalary方法患民;當(dāng)boss引用Manager對(duì)象時(shí),boss.getSalary()調(diào)用的是Manager中的getSalary方法垦梆。一個(gè)對(duì)象變量可以指示多種實(shí)際類型的現(xiàn)象被稱為多態(tài)(polymorphism)匹颤。在運(yùn)行時(shí)能夠自動(dòng)地選擇調(diào)用哪個(gè)方法的現(xiàn)象稱為動(dòng)態(tài)綁定(dynamic binding)

.1繼承層次

  • 繼承不僅限于一個(gè)層次托猩。由一個(gè)公共超類派生出來的所有類的集合被稱為繼承層次(inheritance hierarchy)印蓖。在繼承層次中,某個(gè)特定的類到其祖先的路徑被稱為該類的繼承鏈(inheritance chain)京腥。
  • 通常赦肃,一個(gè)祖先可以擁有多個(gè)子孫繼承鏈。Java不支持多繼承。
繼承層次
.2多態(tài)
  • 一個(gè)判斷是否應(yīng)該設(shè)計(jì)為繼承關(guān)系的簡單規(guī)則他宛,就是“is-a"規(guī)則船侧,它表明子類的每個(gè)對(duì)象也是超類的對(duì)象√鳎“is-a"規(guī)則的另一種表述法是置換法則镜撩,它表明程序中出現(xiàn)超類的對(duì)象的任何地方都可以用子類對(duì)象置換。例如队塘,可以將一個(gè)子類對(duì)象賦值給超類變量:
    Employee e;
    e = new Employee(...);//Employee object expected
    e = new Manager(...);//can be used as well
  • 在Java中袁梗,對(duì)象變量是多態(tài)的,一個(gè)Employee變量既可以引用一個(gè)Employee類對(duì)象憔古,也可以引用一個(gè)Employee類的任何一個(gè)子類的對(duì)象遮怜。
    Manager boss = new Manager(...);
    Employee[] staff = new Employee[3];
    staff[0] = boss;
  • 變量boss和staff[0]引用同一個(gè)對(duì)象,但編譯器將staff[0]看成Employee對(duì)象投放。這意味著boss.setBonus(5000);成立奈泪,而staff[0].setBonus(5000);無法調(diào)用。不能將一個(gè)超類的引用賦值給予子類變量灸芳。

.3動(dòng)態(tài)綁定

  • 調(diào)用對(duì)象方法的執(zhí)行過程:
    • 1)編譯器查看對(duì)象的聲明類型和方法名,一一列舉該類中所有同名方法和其超類中同名的public方法拜姿。至此烙样,編譯器已獲得所有可能被調(diào)用的候選方法。
    • 2)接下來蕊肥,編譯器將查看調(diào)用方法時(shí)提供的參數(shù)類型谒获。如果在之前獲得的方法中存在一個(gè)與提供的參數(shù)完全匹配,就選擇該方法壁却。這個(gè)過程被稱為重載解析(overloading resolution)批狱。由于允許類型轉(zhuǎn)換,所以這個(gè)過程可能會(huì)很復(fù)雜展东。如果編譯器沒有找到與參數(shù)類型匹配的方法赔硫,或者經(jīng)過類型轉(zhuǎn)換之后有多個(gè)方法與之匹配,就會(huì)報(bào)告一個(gè)錯(cuò)誤盐肃。至此爪膊,編譯器已獲得需要調(diào)用的方法名字和參數(shù)類型。
      • 注:方法的名字和簽名砸王。如果子類中定義了一個(gè)與超類方法相同的方法推盛,那么子類中的這個(gè)方法就覆蓋了超類中的這個(gè)簽名相同的方法。返回類型不是簽名的一部分谦铃。因此在覆蓋方法時(shí)耘成,一定要保證返回類型的兼容性。允許子類覆蓋方法的返回類型定義為原返回類型的子類型。
    • 3)如果是private方法瘪菌、static方法件豌、final方法或者構(gòu)造器,那么編譯器將可以準(zhǔn)確地知道應(yīng)該調(diào)用哪個(gè)方法控嗜,我們將這種調(diào)用方式稱為靜態(tài)綁定(static binding)茧彤。與此對(duì)應(yīng)的是,調(diào)用的方法依賴于隱式參數(shù)的實(shí)際類型疆栏,并且在運(yùn)行時(shí)實(shí)現(xiàn)動(dòng)態(tài)綁定曾掂。
    • 4)當(dāng)程序運(yùn)行,并采用動(dòng)態(tài)綁定調(diào)用方法時(shí)壁顶,虛擬機(jī)一定調(diào)用與x所引用對(duì)象的實(shí)際類型最合適的那個(gè)類的方法珠洗。
    • 每次調(diào)用都要進(jìn)行搜索,時(shí)間開銷相當(dāng)大若专。因此许蓖,虛擬機(jī)預(yù)先為每個(gè)類創(chuàng)建了一個(gè)方法表(method table),其中列出了所有方法的簽名和實(shí)際調(diào)用的方法调衰。這樣一類僅需查表即可膊爪。
    • 動(dòng)態(tài)綁定有一個(gè)非常重要的特征:無需對(duì)現(xiàn)存的代碼進(jìn)行修改,就可以對(duì)程序進(jìn)行拓展嚎莉。
      • 注:在覆蓋一個(gè)方法時(shí)米酬,子類方法不能低于超類方法的可見性。特別是趋箩,如果超類方法是public赃额,子類方法一定聲明為public。如果遺漏叫确,編譯器會(huì)解釋為更嚴(yán)格的訪問權(quán)限跳芳。

.4阻止繼承:final類和方法

  • 不允許擴(kuò)展的類被稱為final類,定義時(shí)用final修飾符表明竹勉。
  • 類中特定的方法也可以被聲明為final飞盆,子類將不能覆蓋這個(gè)方法(final類中的所有方法自動(dòng)地成為final方法)。
    • 域也可以被聲明為final饶米。對(duì)于final域來說桨啃,構(gòu)造對(duì)象之后就不允許改變它們的值了。不過檬输,如果將類聲明為final照瘾,只有其中的方法自動(dòng)地成為final,而不包括域丧慈。
  • 將方法或類聲明為final主要目的是:確保它們不會(huì)在子類中改變語義析命。
  • 動(dòng)態(tài)綁定會(huì)帶來更多的系統(tǒng)開銷主卫。如果一個(gè)方法沒有被覆蓋并且很短,編譯器就能夠?qū)λM(jìn)行優(yōu)化處理鹃愤,這個(gè)過程稱為內(nèi)聯(lián)(inlining)簇搅。例如,內(nèi)聯(lián)調(diào)用e.getName()將被替換為訪問e.name域软吐。

.5強(qiáng)制類型轉(zhuǎn)換

    double x = 3.405;
    int nx = (int)x;
  • 將x的值轉(zhuǎn)換成整數(shù)類型瘩将,舍棄了小數(shù)部分。

  • 有時(shí)需要將某個(gè)類的對(duì)象引用轉(zhuǎn)換成為另一個(gè)類的對(duì)象引用凹耙。進(jìn)行強(qiáng)制類型轉(zhuǎn)換的唯一原因是:在暫時(shí)忽視對(duì)象的實(shí)際類型之后姿现,使用對(duì)象的全部功能。

  • 將一個(gè)值存入變量時(shí)肖抱,編譯器將檢查是否允許該操作备典。將一個(gè)子類的引用賦給一個(gè)超類變量,編譯器是允許的意述。但將一個(gè)超類的引用賦給一個(gè)子類變量提佣,必須進(jìn)行強(qiáng)制類型轉(zhuǎn)換,這樣才能通過檢查荤崇。如果試圖在繼承鏈上進(jìn)行向下的類型轉(zhuǎn)換拌屏,并且謊報(bào)有關(guān)對(duì)象包含的內(nèi)容,會(huì)產(chǎn)生ClassCastException異常天试。

  • 一個(gè)良好的習(xí)慣槐壳,在進(jìn)行類型轉(zhuǎn)換時(shí),先查看一下是否能夠轉(zhuǎn)換成功:
    if(staff[1] instanceof Manager){
    //不可轉(zhuǎn)換便不會(huì)執(zhí)行
    boss = (Manager)staff[1];
    }

  • Date c = (Date) staff[1];將會(huì)產(chǎn)生編譯錯(cuò)誤喜每,因?yàn)镈ate不是Employee的子類。

  • 綜上:1)只能在繼承層次內(nèi)進(jìn)行類型轉(zhuǎn)換雳攘;2)在將超類轉(zhuǎn)換成子類之前带兜,應(yīng)該使用instanceof進(jìn)行檢查。(實(shí)際上通過類型轉(zhuǎn)換調(diào)整對(duì)象的類型并不是一種好的做法)

.6抽象類

  • 如果自下而上在類的繼承層次中上移吨灭,位于上層的類更具有通用性刚照,甚至可能更抽象。從某種角度看喧兄,祖先類更加通用无畔。

  • 使用abstract關(guān)鍵詞,使一個(gè)類完全不需要實(shí)現(xiàn)某個(gè)方法吠冤。為了提高清晰度浑彰,包含一個(gè)或多個(gè)抽象方法的類本身必須被聲明為抽象的。除了抽象方法以外拯辙,抽象類還可以包含具體數(shù)據(jù)和具體方法郭变。
    abstract class Person{
    private String name;
    public Person(String n){
    name = n;
    }
    public abstract String getDescription();
    public String getName(){
    return name;
    }
    }

    • Tips:許多程序員認(rèn)為颜价,在抽象類中布恩那個(gè)包含具體方法。建議盡量使用通用的域和方法(不管是否是抽象的)放在超類(不管是否是抽象類)中诉濒。
  • 抽象方法充當(dāng)占位的角色周伦,它們的具體實(shí)現(xiàn)在子類中。擴(kuò)展抽象類可以有兩個(gè)選擇:1)在抽象類中定義部分抽象類方法或不定義抽象類方法未荒,這樣就必須將子類也標(biāo)記成為抽象類专挪;2)定義全部的抽象方法,這樣一來子類就不是抽象的了片排。

  • 類即使不含抽象方法寨腔,也可以將類聲明為抽象類。抽象類不能被實(shí)例化划纽。也就是說脆侮,如果將一個(gè)類聲明為abstract,就不能創(chuàng)建這個(gè)類的對(duì)象勇劣。但是可以創(chuàng)建一個(gè)具體子類的對(duì)象靖避。需要注意,可以定義一個(gè)抽象類的對(duì)象變量比默,但是它只能引用非抽象子類的對(duì)象幻捏。如:
    Person p = new Student("Vince Vu", "Economics");
    這里的p是一個(gè)抽象類Person的變量,Person引用了一個(gè)非抽象子類Student的實(shí)例命咐。

  • 下面通過抽象類Person擴(kuò)展一個(gè)具體子類Student篡九,在該類中的全部方法都是非抽象方法,所以不再是抽象類:
    class Student extends Person{
    private String major;
    public Student(String n, String m){
    super(n);
    major = m;
    }
    public String getDesription(){
    return "A student majoring in " + major;
    }
    }

  • 測(cè)試程序清單:
    Person[] people = new Person[2];
    people[0] = new Employee(...);
    people[1] = new Student(...);
    for(Person p : people){
    System.out.println(p.getName()+","+p.getDescription());
    }
    由于不能構(gòu)造抽象類Person的對(duì)象醋奠,所以變量永遠(yuǎn)不會(huì)引用Person對(duì)象榛臼,而是引用諸如Employee或Student這樣的具體子類對(duì)象,而這些對(duì)象中都定義了getDescription方法窜司。如果不聲明抽象方法沛善,則無法使用p調(diào)用方法,

.7受保護(hù)訪問

  • 有些時(shí)候塞祈,人們希望超類中的某些方法允許被子類訪問金刁,或允許子類的方法訪問超類的某個(gè)域。為此议薪,需要將這些方法和域聲明為protected尤蛮。在實(shí)際使用中,要謹(jǐn)慎使用protected屬性斯议,否則會(huì)違背OOP的數(shù)據(jù)封裝原則产捞。聲明為protected的方法,只能被子類調(diào)用捅位,其他類無法使用轧葛。例搂抒,Object類中的clone方法。
  • 控制可見性的訪問修飾符:
    • 僅對(duì)本類可見————private
    • 對(duì)所有類可見————public
    • 對(duì)本包和所有子類可見————protected
    • 對(duì)本包可見————默認(rèn)尿扯,無需修飾符

5.2 Object:所有類的超類

  • Object類是Java中的所有類的始祖求晶,在Java中每個(gè)類都是由它擴(kuò)展而來的≈运瘢可以使用Object類型的變量引用類型的對(duì)象:
    Object obj = new Employee("Harry", 35000);
    當(dāng)然芳杏,Object類型的變量只能用于作為各種值得通用持有者。要想對(duì)其中的內(nèi)容進(jìn)行具體的操作辟宗,還需要清楚對(duì)象的原始類型爵赵,并進(jìn)行相應(yīng)的類型轉(zhuǎn)換:
    Employee e = (Employee) obj;
    在Java中,只有基本類型(primitive types)不是對(duì)象泊脐,例如空幻,數(shù)值、字符和布爾類型的值都不是對(duì)象容客。所有的數(shù)組類型秕铛,不管是對(duì)象數(shù)組還是基本類型的數(shù)組都擴(kuò)展于Object類。

.1equals方法

  • Object類中的equals方法用于檢測(cè)一個(gè)對(duì)象是否等于另一個(gè)對(duì)象缩挑。這個(gè)方法將判斷兩個(gè)對(duì)象是否具有相同的引用但两,似乎合情合理,然而對(duì)于多數(shù)類來說供置,這種判斷并沒有什么意義谨湘。如果兩個(gè)對(duì)象的狀態(tài)相等,就認(rèn)為這兩個(gè)對(duì)象是相等的芥丧。利用下面這個(gè)示例演示equals方法的實(shí)現(xiàn)機(jī)制:
    class Employee{
    ...
    public boolean equals(Object otherObject){
    //a quick test to see if the objects are identical
    if(this == otherObject) return true;

              //must return false if the explicit parameter is null
              if(otherObject == null) return false;
    
              //if the classes don't match, they can't be equal
              if(getClass() != otherObject.getClass())
                  return false;
    
              //now we know otherObject is a non-null Employee
              Employee other = (Employee)otehrObject;
    
              //test whether the fields have identical values
              return name.equals(other.name)
                  && salary == other.salary
                  && hireDay.equals(other.hireDay);
          }
      }
    

getClass方法返回一個(gè)對(duì)象所屬的類紧阔。

  • Tips:為了防備name或hireDay可能為null的情況,需要使用Objects.equals方法续担。如果兩個(gè)參數(shù)都為null寓辱,Objects.equals(a,b)調(diào)用將返回ture;如果其中一個(gè)參數(shù)為null赤拒,則返回false;否則诱鞠,如果兩個(gè)參數(shù)都不為null挎挖,則調(diào)用a.equals(b)。利用這個(gè)方法航夺,最后一句要改寫為:
    return Objects.equals(name, other.name)
    && salary == other.salary
    && Objects.equals(hireDay, other.hireDay);

  • 在子類中定義equals方法時(shí)蕉朵,首先調(diào)用超類的equals。如果檢測(cè)失敗阳掐,對(duì)象就不可能相等始衅。如果超類中的域都相等冷蚂,就需要比較子類中的實(shí)例域。
    class Manager extends Employee{
    ...
    public boolean equals(Object otherObject){
    if(!super.equals(otherObject)) return false;
    //super.equals checked that this and otherObject belong to the same class
    Manager other = (Manager)otherObject;
    return bonus == other.bonus;
    }
    }

.2相等測(cè)試與繼承

  • 如果隱式和顯式的參數(shù)不屬于同一個(gè)類汛闸,equals方法將如何處理呢蝙茶?這是一個(gè)很有爭議的問題。在前面的例子中诸老,如果發(fā)現(xiàn)類型不匹配隆夯,equals方法就返回false。但是很多程序員卻喜歡使用instanceof進(jìn)行檢測(cè):
    if(!(otherObject instanceof Employee)) return false;
    這樣做不經(jīng)沒有解決otherObject是子類的情況别伏,并且還有可能招致一些麻煩蹄衷。Java語言規(guī)范要求equals方法具有以下特性:
    • 1)自反性:對(duì)于任何非空引用x,x.equals(x)應(yīng)該返回true
    • 2)對(duì)稱性:對(duì)于任何引用x和y厘肮,并且僅當(dāng)y.equals(x)返回true愧口,x.equals(y)也應(yīng)該返回true
    • 3)傳遞性:對(duì)于任何引用x、y和z类茂,如果x.equals(y)返回true耍属,y.equals(z)返回true,x.equals(z)也應(yīng)該返回true
    • 4)一致性:如果x和y引用的對(duì)象沒有發(fā)生變化大咱,反復(fù)調(diào)用x.equals(y)應(yīng)該返回同樣的結(jié)果
    • 5)對(duì)于任意非空引用x恬涧,x.equals(null)應(yīng)該返回false
  • 這些規(guī)則似乎合情合理,但還是有一些特殊情況碴巾。就對(duì)稱性來說溯捆,但當(dāng)參數(shù)不屬于同一個(gè)類的時(shí)候需要仔細(xì)地思考一下∠闷埃考慮這個(gè)調(diào)用:e.equals(m);提揍。這里的e是一個(gè)Employee對(duì)象,m是一個(gè)Manager對(duì)象煮仇,并且兩個(gè)對(duì)象具有相同的姓名劳跃、薪水和雇傭日期。如果在Employee.equals中用instanceof檢測(cè)浙垫,則返回true刨仑。然而當(dāng)調(diào)用:m.equals(e);也需要返回true。對(duì)稱性不允許這個(gè)方法調(diào)用返回false夹姥,或者拋出異常杉武。這就使得Manager類受到了束縛,這個(gè)類的equals方法必須能夠用自己與任何一個(gè)Employee對(duì)象進(jìn)行比較辙售,而不必考慮經(jīng)理擁有的那部分特有的信息轻抱。
  • 可以從兩個(gè)截然不同的情況看一下這個(gè)問題:
    • 如果子類能夠擁有自己的相等概念,則對(duì)稱性需求將強(qiáng)制采用getClass進(jìn)行檢測(cè)

    • 如果由超類決定相等的概念旦部,那么就可以使用instanceof進(jìn)行檢測(cè)祈搜,這樣可以在不同子類的對(duì)象之間進(jìn)行相等的比較

    • Tips:在Java庫中包含150多個(gè)equals方法的實(shí)現(xiàn)较店,包括使用instanceof檢測(cè)、調(diào)用getClass檢測(cè)容燕、捕獲ClassCastException或者什么也不做梁呈。好像陷入了一種困境。

    • 下面給出編寫一個(gè)完美的equals方法的建議:

      • 1)顯式參數(shù)命名為otherObject缰趋,稍后需要將它轉(zhuǎn)換成另一個(gè)叫做other的變量
      • 2)檢測(cè)this與otherObject是否引用同一個(gè)對(duì)象:
        if(this == otherObject) return true;
        這條語句只是一個(gè)優(yōu)化捧杉。實(shí)際上,這是一種經(jīng)常采用的形式秘血。因?yàn)橛?jì)算這個(gè)等式要比一個(gè)一個(gè)地比較類中的域所付出的代價(jià)小得多
      • 3)檢測(cè)otherObject是否為null味抖,如果為null,則返回false灰粮。這項(xiàng)檢查時(shí)很必要的仔涩。
        if(otherObject == null) return false;
      • 4)比較this與otherObject是否屬于同一個(gè)類。如果equals的語義在每個(gè)子類中有所改變粘舟,就使用getClass檢測(cè):
        if(getClass() != otherObject.getClass()) return false;
        如果所有的子類都擁有統(tǒng)一的語義熔脂,就使用instanceof檢測(cè)
        `if(!(otherObject instanceof ClassName)) return false;
      • 5)將otherObject轉(zhuǎn)換為相應(yīng)的類類型變量:
        ClassName other = (ClassName) otherObject;
      • 6)現(xiàn)在開始對(duì)所有需要比較的域進(jìn)行比較了。使用==比較基本類型域柑肴,使用equals比較對(duì)象域霞揉。如果所有的域都匹配,就返回true晰骑;否則返回false适秩。
        return field1 == other.field1
        && Object.equals(field2, other.field2)
        && ...;
        如果在子類中重新定義equals,就要在其中包含調(diào)用super.equals(other)硕舆。
      • Tips:對(duì)于數(shù)組類型的域秽荞,可以使用靜態(tài)的Arrays.equals方法檢測(cè)相應(yīng)的數(shù)組元素是否相等

.3hashCode方法

  • 散列碼(hash code)是由對(duì)象導(dǎo)出的一個(gè)整數(shù)值。散列碼是沒有規(guī)律的抚官。如果x和y是兩個(gè)不同的對(duì)象扬跋,x.hashCode()與y.hashCode()基本不會(huì)相同。

  • String類使用下列算法計(jì)算散列碼:
    int hash = 0;
    for(int i=0; i<length(); i++){
    hash = 31*hash + charAt(i);
    }

  • 由于hashCode方法定義在Object類中凌节,因此每個(gè)對(duì)象都有一個(gè)默認(rèn)的散列碼钦听,其值為對(duì)象的存儲(chǔ)地址。例:
    String s = "OK";
    StringBuilder sb = new StringBuilder(s);
    System.out.println(s.hashCode()+" "+sb.hashCode());
    String t = new String("OK");
    StringBuilder tb = new StringBuilder(t);
    System.out.println(t.hasCode()+" "+tb.hashCode());

    對(duì)象 散列碼
    s 256
    sb 20526976
    t 256
    tb 20527144

注意倍奢,字符串s與t擁有相同的散列碼彪见,這是因?yàn)樽址纳⒘写a是由內(nèi)容導(dǎo)出的。而字符串緩沖sb與tb卻有著不用的散列碼娱挨,這是因?yàn)樵赟tringBuffer類中沒有定義hashCode方法,它的散列碼是由Object類的默認(rèn)hashCode方法導(dǎo)出的對(duì)象存儲(chǔ)地址捕犬。

  • 如果重新定義equals方法跷坝,就必須重新定義hashCode方法酵镜,以便用戶可以將對(duì)象插入散列表中。
  • hashCode方法應(yīng)該返回一個(gè)整型數(shù)值(也可以是負(fù)數(shù))柴钻,并合理地組合實(shí)例域的散列碼淮韭,以便能能夠讓各個(gè)不同的對(duì)象產(chǎn)生的散列碼更加均勻。
  • 在Java7中還可以做兩個(gè)改進(jìn)贴届。首先靠粪,最好使用null安全的方法Objects.hashCode。如果其參數(shù)為null毫蚓,這個(gè)方法會(huì)返回0占键,否則返回對(duì)參數(shù)調(diào)用hashCode的結(jié)果。還有更好的做法元潘,需要組合多個(gè)散列值時(shí)畔乙,可以調(diào)用Objects.hash并提供多個(gè)參數(shù)。這個(gè)方法會(huì)對(duì)各個(gè)參數(shù)調(diào)用Objects.hashCode翩概,并組合這些散列值牲距。
  • Equals與hashCode的定義必須一致:如果x.equals(y)返回true,那么x.hashCode()就必須與y.hashCode()具有相同的值钥庇。
    • Tips:如果存在數(shù)組類型的域牍鞠,那么可以使用靜態(tài)的Arrays.hashCode方法計(jì)算一個(gè)散列碼,這個(gè)散列碼由數(shù)組元素的散列碼組成评姨。

.4toString方法

  • 用于返回表示對(duì)象值的字符串难述。絕大多數(shù)(但不是全部)的toString方法都遵守這樣的格式:類的名字,隨后是一對(duì)方括號(hào)括起來的域值参咙。下面是Employee類中的toString方法的實(shí)現(xiàn):
    public String toString(){
    return "Employee[name="+name
    +",salary="+salary
    +",hireDay="+hireDay
    +"]";
    }
    實(shí)際上龄广,還可以設(shè)計(jì)得更好一些。最好通過調(diào)用getClass().getName()獲得類名的字符串蕴侧,而不要類名硬加到toString方法中择同。
    public String toString(){
    return getClass().getName()
    +"[name="+name
    +",salary="+salary
    +",hireDay="+hireDay
    +"]";
    }

  • toString方法也可以供子類調(diào)用。如果超類使用了getClass().getName()净宵,那么子類只要調(diào)用super.toString()就可以了敲才。例如,下面是Manager類中的toString方法:
    Class Manager extends Employee{
    ...
    public String toString(){
    return super.toString()
    +"[bonus="+bonus
    +"]";
    }
    }

  • 隨處可見toString方法的主要原因是:只要對(duì)象與一個(gè)字符串通過操作符“+”連接起來择葡,Java編譯就會(huì)自動(dòng)調(diào)用toString方法紧武,以便獲得這個(gè)對(duì)象的字符串描述。例如:
    Point p = new Point(10, 20);
    String message = "The current position is "+p;
    //automatically invokes p.toString()

    • Tips:在調(diào)用x.toString()的地方可以用""+x替代敏储。這條語句是將一個(gè)空字符串與x的字符串表示相連接阻星。這里的x就是x.toString()。與toString不同的是,如果x是基本類型妥箕,這條語句照樣能夠執(zhí)行滥酥。如果x是任意一個(gè)對(duì)象,并調(diào)用System.out.println(x);畦幢,println方法就會(huì)直接地調(diào)用x.toString()坎吻,并打印輸出得到的字符串。
  • Object類定義了toString方法宇葱,用來打印輸出對(duì)象所屬的類名和散列碼瘦真。例如,調(diào)用
    System.out.println(System.out);黍瞧,將輸出:java.io.PrintStream@2f6684诸尽。之所以得到這樣的結(jié)果是因?yàn)镻rintStream類的設(shè)計(jì)者沒有覆蓋toString方法。

    • warning:令人煩惱的是雷逆,數(shù)組繼承了Object類的toString方法弦讽,數(shù)組類型將按照舊的格式打印。例如:
      int[] luckyNumbers = {2,3,5,7,11,13};
      String s = ""+luckyNumbers;
      生成字符串“[I@1a46e30"(前綴[I表明是一個(gè)整型數(shù)組)膀哲。修正的方式是調(diào)用靜態(tài)方法Arrays.toString往产。代碼:
      String s = Arrary.toString(luckyNumber);
      將生成字符串”[2,3,5,7,11,13]"。想要打印多維數(shù)組(即某宪,數(shù)組的數(shù)組)則需要調(diào)用Arrays.deepToString方法仿村。
  • toString方法是一種非常有用的調(diào)試工具。在標(biāo)準(zhǔn)類庫中兴喂,許多類都定義了toString方法蔼囊,以便用戶能夠獲得一些有關(guān)對(duì)象狀態(tài)的必要信息。(之后會(huì)有Log的介紹衣迷。)

    • Tips:強(qiáng)烈建議為自定義的每一個(gè)類增加toString方法畏鼓。這樣做不僅自己受益,而且所有使用這個(gè)類的程序員也會(huì)從日志記錄中受益匪淺壶谒。

5.3泛型數(shù)組列表

  • 在許多語言中云矫,必須在編譯時(shí)就確定整個(gè)數(shù)組的大小。在Java中情況就好多了汗菜。它允許在運(yùn)行時(shí)確定數(shù)組的大小让禀。
    int actualSize = ...;
    Employee[] staff = new Employee[actualSize];
    當(dāng)然,這段代碼并沒有完全解決運(yùn)行時(shí)動(dòng)態(tài)更改數(shù)組的問題陨界。一旦確定了數(shù)組的大小巡揍,改變它就太不容易了。在Java中菌瘪,解決這個(gè)問題最簡單的方法是使用Java中另一個(gè)被稱為ArrayList的類腮敌。它使用起來有點(diǎn)像數(shù)組,但在添加或刪除元素時(shí),具有自動(dòng)調(diào)節(jié)數(shù)組容量的功能缀皱,而不需要為此編寫任何代碼斗这。

  • ArrayList是一個(gè)采用類型參數(shù)(type parameter)泛型類(generic class)。為了指定數(shù)組列表保存的元素對(duì)象類型啤斗,需要用一對(duì)尖括號(hào)將類名括起來加在后面。例如:ArrayList<Employee>赁咙。

  • 聲明和構(gòu)造一個(gè)保存Employee對(duì)象的數(shù)組列表:
    ArrayList<Employee> staff = new ArrayList<Employee>();
    兩邊都使用類型參數(shù)Employee钮莲,有些繁瑣。Java7中彼水,可以省去右邊的類型參數(shù):
    ArrayList<Employee> staff = new ArrayList<>();
    這被稱為“菱形”語法崔拥,因?yàn)榭占饫ㄌ?hào)<>就像一個(gè)菱形跃巡〗嶂矗可以結(jié)合new操作符使用菱形語法读整。編譯器會(huì)檢查新值是什么菌羽。如果賦值給一個(gè)變量声邦,或者傳遞到某個(gè)方法炒辉,或者從某個(gè)方法返回旋圆,編譯器會(huì)檢查這個(gè)變量
    起愈、參數(shù)或者方法的泛型類型拥峦,然后將這個(gè)類型放在<>中贴膘。

    • Tips:Java SE5.0以前的版本沒有提供泛型類,而是有一個(gè)ArrayList類略号,其中保存類型為Object元素刑峡,它是“自適應(yīng)大小”的集合。如果一定要使用老版本Java玄柠,則需要?jiǎng)h掉所有的后綴<...>突梦。在新版中,沒有后綴的ArrayList會(huì)被認(rèn)為是一個(gè)刪除了類型參數(shù)的“原始”類型羽利,仍然可以使用宫患。
  • 使用add方法可以將元素添加到數(shù)組列表中去。例如铐伴,下面展示了如何將雇員對(duì)象添加到數(shù)組列表中的方法:
    staff.add(new Employee("Harry", ...));
    staff.add(new Employee("Tony", ...));
    數(shù)組列表管理著對(duì)象引用的一個(gè)內(nèi)部數(shù)組撮奏。最終,數(shù)組的全部空間有可能被用盡当宴。這就顯現(xiàn)出數(shù)組列表的操作魅力:如果調(diào)用add且內(nèi)部數(shù)組已經(jīng)滿了畜吊,數(shù)組列表就將自動(dòng)地創(chuàng)建一個(gè)更大的數(shù)組,并且將所有的對(duì)象從較小數(shù)組中拷貝到較大數(shù)組中户矢。

  • 如果足夠清楚數(shù)組可能存儲(chǔ)的元素?cái)?shù)量玲献,就可以在填充數(shù)組之前調(diào)用ensureCapacity方法:staff.ensureCapacity(100);,這個(gè)方法將分配一個(gè)包含100個(gè)對(duì)象的內(nèi)部數(shù)組。然后至多調(diào)用100次add捌年,而不用重新分配空間瓢娜。

  • 另外,還可以把初始容器傳遞給ArrayList構(gòu)造器:
    ArrayList<Employee> staff = new ArrayList<>(100);

    • Warning:分配數(shù)組列表:new ArrayList<>(100);//capacity is 100礼预,它與為新數(shù)組分配空間有所不同:new Employee[100];//size is 100
      數(shù)組列表的容量與數(shù)組的大小有一個(gè)非常重要的區(qū)別眠砾。如果為數(shù)組分配100個(gè)元素的存儲(chǔ)空間,數(shù)組就有100個(gè)空位置可以使用托酸。而容量為100個(gè)元素的數(shù)組列表只是擁有保存100個(gè)元素的潛力(實(shí)際上重新分配空間的話褒颈,將會(huì)超過100),但是在最初励堡,甚至是完成初始化構(gòu)造之后谷丸,數(shù)組列表根本就不含任何元素。
  • size方法將返回?cái)?shù)組列表中包含的實(shí)際元素?cái)?shù)目应结。例如:staff.size();刨疼,將返回staff數(shù)組列表的當(dāng)前元素?cái)?shù)量,它等價(jià)于數(shù)組a的a.length鹅龄。

  • 一旦能夠確認(rèn)數(shù)組列表的大小不再發(fā)生變化揩慕,就可以調(diào)用trimToSize方法。這個(gè)方法將存儲(chǔ)區(qū)域的大小調(diào)整為當(dāng)前元素所需要的存儲(chǔ)空間數(shù)目砾层。垃圾回收器將回收多余的存儲(chǔ)空間漩绵。一旦整理了數(shù)組列表的大小,添加新元素就需要花時(shí)間再次移動(dòng)存儲(chǔ)塊肛炮,所以應(yīng)該在確認(rèn)不會(huì)添加任何元素時(shí)止吐,再調(diào)用trimToSize。

.1訪問數(shù)組列表元素

  • 數(shù)組列表自動(dòng)擴(kuò)展容量的便利增加了訪問元素語法的復(fù)雜程度侨糟。其原因是ArrayList類并不是Java程序設(shè)計(jì)語言的一部分碍扔,它只是一個(gè)由某些人編寫且被放在標(biāo)準(zhǔn)庫中的一個(gè)實(shí)用類

  • 使用get和set方法實(shí)現(xiàn)訪問或改變數(shù)組元素的操作,而不使用數(shù)組中的[]語法格式秕重。例如不同,要設(shè)置第i個(gè)元素,可以使用:staff.set(i, harry);溶耘, 等價(jià)于對(duì)數(shù)組a的元素賦值(下標(biāo)從0開始)二拐,a[i] = harry;

    • Tips:只有i小于或等于數(shù)組列表的大小時(shí)凳兵,才能夠調(diào)用list.set(i,x)百新。例如,下面這段代碼是錯(cuò)誤的:
      ArrayList<Employee> list = new ArrayList<>(100);//capacity is 100,size 0
      list.set(0,x);//no element 0 yet
      使用add方法為數(shù)組添加新元素庐扫,而不要使用set方法饭望,它只能替換數(shù)組中已經(jīng)存在的元素內(nèi)容仗哨。
  • 使用:Employee e = staff.get(i);,獲得數(shù)組列表的元素铅辞,等價(jià)于:Employee e = a[i];厌漂。

    • Tips:沒有泛型類時(shí),原始的ArrayList類提供的get方法別無選擇只能返回Object斟珊,因此苇倡,get方法的調(diào)用者必須對(duì)返回值進(jìn)行類型轉(zhuǎn)換:Employee e = (Employee)staff.get(i);。原始的ArrayList存在一定的危險(xiǎn)性囤踩。它的add和set方法允許接受任意類型的對(duì)象雏节。對(duì)于這個(gè)調(diào)用:staff.set(i, new Date());,編譯不會(huì)給出任何警告高职,只有在檢索對(duì)象并試圖對(duì)它進(jìn)行類型轉(zhuǎn)換時(shí),才會(huì)發(fā)現(xiàn)有問題辞州。如果使用ArrayList<Employee>怔锌,編譯器就會(huì)檢測(cè)到這個(gè)錯(cuò)誤。
  • 該技巧可以一舉兩得变过,既可以靈活地?cái)U(kuò)展數(shù)組埃元,又可以方便地訪問數(shù)組元素。首先媚狰,創(chuàng)建一個(gè)數(shù)組岛杀,并添加所有的元素。
    ArrayList<x> list = new ArrayList<>();
    while(...){
    x=...;
    list.add(x);
    }
    執(zhí)行完上述操作后崭孤,使用toArray方法將數(shù)組元素拷貝到一個(gè)數(shù)組中类嗤。
    x[] a = new X[list.size()];
    list.toArray(a);
    除了在數(shù)組列表的尾部追加元素之外,還可以在數(shù)組列表的中間插入元素辨宠,使用帶索引參數(shù)的add方法遗锣。
    int n = staff.size()/2;
    staff.add(n, e);
    為了插入一個(gè)新元素,位于n之后的所有元素都要向后移動(dòng)一個(gè)位置嗤形。如果插入新元素后精偿,數(shù)組列表的大小超過了容量,數(shù)組列表就會(huì)被重新分配存儲(chǔ)空間赋兵。同樣的笔咽,可以從數(shù)組列表中刪除一個(gè)元素:
    Employee e = staff.remove(n);
    位于這個(gè)之后的所有元素都向前移動(dòng)一個(gè)位置,并且對(duì)數(shù)組的大小減1霹期。對(duì)數(shù)組實(shí)施插入和刪除元素的操作其效率比較低叶组。對(duì)于小型數(shù)組來說,這一點(diǎn)不必?fù)?dān)心经伙。但如果數(shù)組存儲(chǔ)的元素比較多扶叉,又經(jīng)常需要在中間位置插入勿锅、刪除元素,就應(yīng)該考慮使用鏈表了枣氧。這將在之后討論溢十。

  • 使用“for each”循環(huán)遍歷數(shù)組列表:
    for(Employee e : staff){
    do something with e
    }
    for(int i=0; i<staff.size(); i++)效果相同。

.2類型化與原始數(shù)組列表的兼容性

  • 假設(shè)有遺留代碼:
    public class EmployeeDB{
    public void update(ArrayList list){...}
    public ArrayList find(String query){...}
    }
    可以將一個(gè)類型化的數(shù)組列表傳遞給update方法达吞,而不需要進(jìn)行任何類型轉(zhuǎn)換张弛。
    ArrayList<Employee> staff = ...;
    employeeDB.update(staff);
    也可以將staff對(duì)象傳遞給update方法。

    • Warning:盡管編譯器沒有給出任何錯(cuò)誤信息或警告酪劫,但是這樣調(diào)用并不太安全吞鸭。在update方法中,添加到數(shù)組列表中的元素可能不是Employee類型覆糟。在對(duì)這些元素進(jìn)行檢索時(shí)就會(huì)出現(xiàn)異常刻剥。聽起來似乎很嚇人,但思考一下就會(huì)發(fā)現(xiàn)滩字。這與在Java中增加泛型之前就是一樣的造虏。虛擬級(jí)的完整性絕對(duì)沒有受到威脅。在這種情形之下麦箍,既沒有降低安全性漓藕,也沒有受益于編譯時(shí)的檢查。
  • 相反的挟裂,將一個(gè)原始ArrayList賦值給一個(gè)類型化ArrayList會(huì)得到一個(gè)警告享钞。
    ArrayList<Employee> result = employeeDB.find(query);//yields warning
    使用類型轉(zhuǎn)換并不能避免出現(xiàn)警告。
    ArrayList<Employee> result = (ArrayList<Employee>)employeeDB.find(query);
    //yields another warning
    這樣將會(huì)得到另一個(gè)警告信息诀蓉,被告知類型轉(zhuǎn)換有誤栗竖。

  • 這就是Java中不盡如人意的參數(shù)類型的限制所帶來的結(jié)果。這時(shí)候不要做什么交排,確定不會(huì)造成嚴(yán)重的后果就可以了划滋。

5.4 對(duì)象包裝器與自動(dòng)裝箱

  • 有時(shí),需要將int這樣的基本類型對(duì)象轉(zhuǎn)換為對(duì)象埃篓。所有的基本類型都有一個(gè)與之對(duì)應(yīng)的類处坪。例如,Integer類對(duì)應(yīng)基本類型int架专。通常同窘,這些類稱為包裝器(wrapper)。這些對(duì)象包裝器類擁有很鮮亮的名字:Integer部脚、Long想邦、Float、Double委刘、Short丧没、Byte鹰椒、Character、Void和Boolean(前6個(gè)派生域公共的超類Number)呕童。對(duì)象包裝器類是不可變的漆际,即一旦構(gòu)造了包裝器,就不允許更改包裝在其中的值夺饲。同時(shí)奸汇,對(duì)象包裝器還是final,因此它們不可以定義子類往声。

  • 假設(shè)想定義一個(gè)整型數(shù)組列表擂找。而尖括號(hào)中的類型參數(shù)不允許是基本類型。也就是說浩销,不允許寫成ArrayList<int>贯涎。這里就用到了Integer對(duì)象包裝器類。
    ArrayList<Integer> list = new ArrayList<>();

  • Java SE5.0之后的另一個(gè)改進(jìn)之處是更加便于添加或獲得數(shù)組元素慢洋。這個(gè)調(diào)用:list.add(3);將自動(dòng)得變成為list.add(Integer.valueOf(3));柬采,這種變換被稱為自動(dòng)裝箱(autoboxing)。相反地且警,當(dāng)將一個(gè)Integer對(duì)象賦值給一個(gè)int值,將會(huì)自動(dòng)地拆箱礁遣。也就是說斑芜,編譯器將語句;int n = list.get(i);翻譯成
    int n = list.get(i).intValue();祟霍。

  • 甚至在算術(shù)表達(dá)式中也能夠自動(dòng)地裝箱和拆箱杏头。例如,可以將自增操作符應(yīng)用于一個(gè)包裝器引用:
    Integer n = 3;
    n++;
    編譯器將自動(dòng)地插入一條對(duì)象拆箱的指令沸呐,然后進(jìn)行自增計(jì)算醇王,最后再將結(jié)果裝箱。

  • 在很多情況下崭添,容易有一種假象寓娩,即基本類型與它們的對(duì)象包裝器是一樣的,只是它們的相等性不同呼渣。==運(yùn)算符也可以應(yīng)用于對(duì)象包裝器對(duì)象棘伴,只不過檢測(cè)的是對(duì)象是否指向同一個(gè)存儲(chǔ)區(qū)域,因此屁置,下面的比較通常不會(huì)成立:
    Integer a = 1000;
    Integer b = 1000;
    if(a == b)...
    然而焊夸,Java實(shí)現(xiàn)卻可能(may)讓它成立。如果經(jīng)常出現(xiàn)的值包裝到同一個(gè)對(duì)象中蓝角,這種比較就有可能成立阱穗。這種不確定的結(jié)果并不是我們希望的饭冬,解決這個(gè)問題的辦法是在兩個(gè)包裝器對(duì)象比較時(shí)調(diào)用equals方法。

    • Tips:自動(dòng)裝箱規(guī)范要求boolean揪阶、byte昌抠、char<=127,介于-128~127之間的short和int被包裝到固定的對(duì)象中遣钳。例如扰魂,如果前面的例子中將a和b初始化為100,對(duì)它們進(jìn)行比較的結(jié)果一定成立蕴茴。
  • 最后強(qiáng)調(diào)一下劝评,拆箱和裝箱是編譯器認(rèn)可的,而不是虛擬機(jī)倦淀。編譯器在生成類的字節(jié)碼時(shí)蒋畜,插入必要的方法調(diào)用。虛擬機(jī)只是執(zhí)行這些字節(jié)碼撞叽。使用數(shù)值對(duì)象包裝器還有另外一個(gè)好處姻成。可以將某些基本方法放置在包裝器中愿棋,例如科展,將一個(gè)數(shù)字字符串轉(zhuǎn)換成數(shù)值】酚辏可以使用:int x = Integer.parseInt(s);才睹,這與Integer對(duì)象沒有任何關(guān)系,parseInt是一個(gè)靜態(tài)方法甘邀。

    • 包裝器類不可以實(shí)現(xiàn)修改參數(shù)值的方法琅攘,原理同第4章中的值傳遞。

5.5 參數(shù)數(shù)量可變的方法

  • Java SE 5.0以前的版本中松邪,每個(gè)Java方法都有固定數(shù)量的參數(shù)坞琴。然而,現(xiàn)在的版本提供了可以用個(gè)可變的參數(shù)數(shù)量調(diào)用的方法(有時(shí)稱為”變參“方法)逗抑。

  • printf方法的定義是這樣的:
    public class PrintStream{
    public PrintStream print(String fmt, Object...args){return format(fmt,args);}
    }
    這里的省略號(hào)...是Java代碼的一部分剧辐,它表明這個(gè)方法可以接收任意數(shù)量的對(duì)象(除fmt參數(shù)之外)。實(shí)際上邮府,printf方法接收兩個(gè)參數(shù)浙于,一個(gè)是格式字符串,另一個(gè)是Object[]數(shù)組挟纱,其中保存著所以參數(shù)(如果調(diào)用者提供的是整型數(shù)組或者是其他基本類型的值羞酗,自動(dòng)裝箱功能將把它們轉(zhuǎn)換成對(duì)象)。現(xiàn)在將掃描fmt字符串紊服,并將第i個(gè)格式說明符域arg[i]的值匹配起來檀轨。換句話說胸竞,對(duì)于printf的實(shí)現(xiàn)者來說,Object...參數(shù)類型與Object[]完全一樣参萄。

  • 編譯器需要對(duì)printf的每次調(diào)用進(jìn)行轉(zhuǎn)換卫枝,以便將參數(shù)綁定到數(shù)組上,并在必要的時(shí)候進(jìn)行自動(dòng)裝箱:
    System.out.printf("%d %s", new Object[]{new Integer(n), "widgets"});
    用戶自己也可以定義可變參數(shù)的方法讹挎,并將參數(shù)指定為任意類型校赤,甚至是基本類型。下面是一個(gè)簡單的示例:其功能為計(jì)算若干個(gè)數(shù)值的最大值筒溃。
    public static double max(double...values){
    double largest Double.MIN_VALUE;
    for(double v : values) if (v > largest) largest = v;
    return largest;
    }
    可以這樣調(diào)用這個(gè)方法:double m = max(3.1, 40.4, -5);马篮,編譯器將new double[]{3.1, 40.4, -5}傳遞給max方法。

5.6 枚舉類

  • 例:public enum Size {SMALL, MEDIUM, LARGE, EXTRA_LARGE};怜奖,實(shí)際上浑测,這個(gè)聲明定義的類型是一個(gè)類,它剛好有4個(gè)實(shí)例歪玲,在此盡量不要構(gòu)造新對(duì)象迁央。因此,在比較兩個(gè)枚舉類型的值時(shí)滥崩,永遠(yuǎn)不需要調(diào)用equals岖圈,直接使用“==”就可以了。

  • 如果需要的話钙皮,可以在枚舉類型中添加一些構(gòu)造器幅狮、方法和域。當(dāng)然株灸,構(gòu)造器只是在構(gòu)造枚舉常量的時(shí)候被調(diào)用。下面是一個(gè)示例:
    public enum Size{
    SMALL("S"),MEDIUM("M"),LARGE("L"),EXTRA_LARGE("XL");
    private String addreviation;
    private Size(String addreviation){this.addreviation = addreviation;}
    public String getAddreviation(){return addreviation;}
    }

  • 所有的枚舉類都是Enum類的子類擎值。它們繼承了這個(gè)類的許多方法慌烧。其中最實(shí)用的是toString,這個(gè)方法能夠返回枚舉類常量名鸠儿。例如屹蚊,Size.SMALL.toString()將返回字符串“SMALL”。

  • toString方法的逆方法是靜態(tài)方法valueOf进每。例如汹粤,語句:
    Size s = Enum.valueOf(Size.class, "SMALL");,將s設(shè)置成Size.SMALL田晚。

  • 每個(gè)枚舉類型都有一個(gè)靜態(tài)的values方法嘱兼,它將返回一個(gè)包含全部枚舉類型值的數(shù)組。例如:
    Size[] values = Size.values();贤徒,將返回包含元素Size.SMALL芹壕,Size.MEDIUM汇四,Size.LARGE,Size.EXTRA_LARGE的數(shù)組踢涌。

  • ordinal方法返回enum聲明中枚舉常量的位置通孽,位置從0開始計(jì)數(shù)。例如:
    Size.MEDIUM.ordinal()返回1睁壁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末背苦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子潘明,更是在濱河造成了極大的恐慌行剂,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钉疫,死亡現(xiàn)場(chǎng)離奇詭異硼讽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)牲阁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門固阁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人城菊,你說我怎么就攤上這事备燃。” “怎么了凌唬?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵并齐,是天一觀的道長。 經(jīng)常有香客問我客税,道長况褪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任更耻,我火速辦了婚禮测垛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘秧均。我一直安慰自己食侮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布目胡。 她就那樣靜靜地躺著锯七,像睡著了一般。 火紅的嫁衣襯著肌膚如雪誉己。 梳的紋絲不亂的頭發(fā)上眉尸,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼效五。 笑死地消,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的畏妖。 我是一名探鬼主播脉执,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼戒劫!你這毒婦竟也來了半夷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤迅细,失蹤者是張志新(化名)和其女友劉穎巫橄,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茵典,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡湘换,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了统阿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彩倚。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖扶平,靈堂內(nèi)的尸體忽然破棺而出帆离,到底是詐尸還是另有隱情,我是刑警寧澤结澄,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布哥谷,位于F島的核電站,受9級(jí)特大地震影響麻献,放射性物質(zhì)發(fā)生泄漏们妥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一勉吻、第九天 我趴在偏房一處隱蔽的房頂上張望监婶。 院中可真熱鬧,春花似錦餐曼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至孕似,卻和暖如春踩娘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工养渴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留雷绢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓理卑,卻偏偏與公主長得像翘紊,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子藐唠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法帆疟,類相關(guān)的語法,內(nèi)部類的語法宇立,繼承相關(guān)的語法踪宠,異常的語法,線程的語...
    子非魚_t_閱讀 31,622評(píng)論 18 399
  • 1.import static是Java 5增加的功能,就是將Import類中的靜態(tài)方法妈嘹,可以作為本類的靜態(tài)方法來...
    XLsn0w閱讀 1,222評(píng)論 0 2
  • 面向?qū)ο笾饕槍?duì)面向過程柳琢。 面向過程的基本單元是函數(shù)。 什么是對(duì)象:EVERYTHING IS OBJECT(萬物...
    sinpi閱讀 1,053評(píng)論 0 4
  • (一)Java部分 1润脸、列舉出JAVA中6個(gè)比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨(dú)云閱讀 7,101評(píng)論 0 62
  • 小余你來看看老師的胳膊津函,好疼呀~??小余緊接著回一句:老師你是得靜脈曲張了嗎??肖粮?(誰家誰家靜脈曲張?jiān)诟觳采希浚????...
    天琪老師閱讀 429評(píng)論 0 0