java面向?qū)ο?/h1>

java繼承

繼承的概念

繼承是java面向?qū)ο缶幊碳夹g(shù)的一塊基石惧互,因?yàn)樗试S創(chuàng)建分等級(jí)層次的類(lèi)建钥。

繼承就是子類(lèi)繼承父類(lèi)的特征和行為,使得子類(lèi)對(duì)象(實(shí)例)具有父類(lèi)的實(shí)例域和方法,或子類(lèi)從父類(lèi)繼承方法戚炫,使得子類(lèi)具有父類(lèi)相同的行為袭厂。

生活中的繼承:

01.jpg

兔子和羊?qū)儆谑巢輨?dòng)物類(lèi)墨吓,獅子和豹屬于食肉動(dòng)物類(lèi)。

食草動(dòng)物和食肉動(dòng)物又是屬于動(dòng)物類(lèi)纹磺。

所以繼承需要符合的關(guān)系是:is-a帖烘,父類(lèi)更通用,子類(lèi)更具體橄杨。

雖然食草動(dòng)物和食肉動(dòng)物都是屬于動(dòng)物秘症,但是兩者的屬性和行為上有差別,所以子類(lèi)會(huì)具有父類(lèi)的一般特性也會(huì)具有自身的特性式矫。

類(lèi)的繼承格式

在 Java 中通過(guò) extends 關(guān)鍵字可以申明一個(gè)類(lèi)是從另外一個(gè)類(lèi)繼承而來(lái)的乡摹,一般形式如下:

類(lèi)的繼承格式
class 父類(lèi) {
}
 
class 子類(lèi) extends 父類(lèi) {
}

為什么需要繼承

接下來(lái)我們通過(guò)實(shí)例來(lái)說(shuō)明這個(gè)需求。

開(kāi)發(fā)動(dòng)物類(lèi)采转,其中動(dòng)物分別為企鵝以及老鼠聪廉,要求如下:

  1. 企鵝:屬性(姓名,id)故慈,方法(吃板熊,睡,自我介紹)
  2. 老鼠:屬性(姓名惯悠,id)邻邮,方法(吃,睡克婶,自我介紹)

企鵝類(lèi):

public class Penguin { 
    private String name; 
    private int id; 
    public Penguin(String myName, int  myid) { 
        name = myName; 
        id = myid; 
    } 
    public void eat(){ 
        System.out.println(name+"正在吃"); 
    }
    public void sleep(){
        System.out.println(name+"正在睡");
    }
    public void introduction() { 
        System.out.println("大家好筒严!我是"         + id + "號(hào)" + name + "."); 
    } 
}

老鼠類(lèi):

public class Mouse { 
    private String name; 
    private int id; 
    public Mouse(String myName, int  myid) { 
        name = myName; 
        id = myid; 
    } 
    public void eat(){ 
        System.out.println(name+"正在吃"); 
    }
    public void sleep(){
        System.out.println(name+"正在睡");
    }
    public void introduction() { 
        System.out.println("大家好丹泉!我是"         + id + "號(hào)" + name + "."); 
    } 
}

從這兩段代碼可以看出來(lái),代碼存在重復(fù)了鸭蛙,導(dǎo)致后果就是代碼量大且臃腫摹恨,而且維護(hù)性不高(維護(hù)性主要是后期需要修改的時(shí)候,就需要修改很多的代碼娶视,容易出錯(cuò))晒哄,所以要從根本上解決這兩段代碼的問(wèn)題,就需要繼承肪获,將兩段代碼中相同的部分提取出來(lái)組成 一個(gè)父類(lèi):

公共父類(lèi):

public class Animal { 
    private String name;  
    private int id; 
    public Animal(String myName, int myid) { 
        name = myName; 
        id = myid;
    } 
    public void eat(){ 
        System.out.println(name+"正在吃"); 
    }
    public void sleep(){
        System.out.println(name+"正在睡");
    }
    public void introduction() { 
        System.out.println("大家好寝凌!我是"         + id + "號(hào)" + name + "."); 
    } 
}

這個(gè)Animal類(lèi)就可以作為一個(gè)父類(lèi),然后企鵝類(lèi)和老鼠類(lèi)繼承這個(gè)類(lèi)之后孝赫,就具有父類(lèi)當(dāng)中的屬性和方法较木,子類(lèi)就不會(huì)存在重復(fù)的代碼,維護(hù)性也提高青柄,代碼也更加簡(jiǎn)潔伐债,提高代碼的復(fù)用性(復(fù)用性主要是可以多次使用,不用再多次寫(xiě)同樣的代碼) 繼承之后的代碼:

企鵝類(lèi):

public class Penguin extends Animal { 
    public Penguin(String myName, int myid) { 
        super(myName, myid); 
    } 
}

老鼠類(lèi):

public class Mouse extends Animal { 
    public Mouse(String myName, int myid) { 
        super(myName, myid); 
    } 
}

繼承的特性

  1. 子類(lèi)擁有父類(lèi)非private的屬性致开,方法峰锁。

  2. 子類(lèi)可以擁有自己的屬性和方法,即子類(lèi)可以對(duì)父類(lèi)進(jìn)行擴(kuò)展双戳。

  3. 子類(lèi)可以用自己的方式實(shí)現(xiàn)父類(lèi)的方法虹蒋。

  4. Java的繼承是單繼承,但是可以多重繼承拣技,單繼承就是一個(gè)子類(lèi)只能繼承一個(gè)父類(lèi)千诬,多重繼承就是,例如A類(lèi)繼承B類(lèi)膏斤,B類(lèi)繼承C類(lèi)徐绑,所以按照關(guān)系就是C類(lèi)是B類(lèi)的父類(lèi),B類(lèi)是A類(lèi)的父類(lèi)莫辨,這是java繼承區(qū)別于C++繼承的一個(gè)特性傲茄。

  5. 提高了類(lèi)之間的耦合性(繼承的缺點(diǎn),耦合度高就會(huì)造成代碼之間的聯(lián)系)沮榜。

繼承關(guān)鍵字

繼承可以使用 extends 和 implements 這兩個(gè)關(guān)鍵字來(lái)實(shí)現(xiàn)繼承盘榨,而且所有的類(lèi)都是繼承于 java.lang.Object,當(dāng)一個(gè)類(lèi)沒(méi)有繼承的兩個(gè)關(guān)鍵字蟆融,則默認(rèn)繼承object(這個(gè)類(lèi)在 java.lang 包中草巡,所以不需要 import)祖先類(lèi)。

extends關(guān)鍵字

在 Java 中型酥,類(lèi)的繼承是單一繼承山憨,也就是說(shuō)查乒,一個(gè)子類(lèi)只能擁有一個(gè)父類(lèi),所以 extends 只能繼承一個(gè)類(lèi)郁竟。

extends 關(guān)鍵字

public class Animal { 
    private String name;   
    private int id; 
    public Animal(String myName, String myid) { 
        //初始化屬性值
    } 
    public void eat() {  //吃東西方法的具體實(shí)現(xiàn)  } 
    public void sleep() { //睡覺(jué)方法的具體實(shí)現(xiàn)  } 
} 
 
public class Penguin  extends  Animal{ 
}

implements關(guān)鍵字

使用 implements 關(guān)鍵字可以變相的使java具有多繼承的特性玛迄,使用范圍為類(lèi)繼承接口的情況,可以同時(shí)繼承多個(gè)接口(接口跟接口之間采用逗號(hào)分隔)棚亩。

implements 關(guān)鍵字

public interface A {
    public void eat();
    public void sleep();
}
 
public interface B {
    public void show();
}
 
public class C implements A,B {
}

super 與 this 關(guān)鍵字

super關(guān)鍵字:我們可以通過(guò)super關(guān)鍵字來(lái)實(shí)現(xiàn)對(duì)父類(lèi)成員的訪(fǎng)問(wèn)蓖议,用來(lái)引用當(dāng)前對(duì)象的父類(lèi)。

this關(guān)鍵字:指向自己的引用讥蟆。

class Animal {
  void eat() {
    System.out.println("animal : eat");
  }
}
 
class Dog extends Animal {
  void eat() {
    System.out.println("dog : eat");
  }
  void eatTest() {
    this.eat();   // this 調(diào)用自己的方法
    super.eat();  // super 調(diào)用父類(lèi)方法
  }
}
 
public class Test {
  public static void main(String[] args) {
    Animal a = new Animal();
    a.eat();
    Dog d = new Dog();
    d.eatTest();
  }
}

輸出結(jié)果為:

animal : eat
dog : eat
animal : eat

final關(guān)鍵字

final 關(guān)鍵字聲明類(lèi)可以把類(lèi)定義為不能繼承的勒虾,即最終類(lèi);或者用于修飾方法瘸彤,該方法不能被子類(lèi)重寫(xiě):

聲明類(lèi):

final class 類(lèi)名 {//類(lèi)體}

聲明方法:

修飾符(public/private/default/protected) final 返回值類(lèi)型 方法名(){//方法體}

注:實(shí)例變量也可以被定義為 final从撼,被定義為 final 的變量不能被修改。被聲明為 final 類(lèi)的方法自動(dòng)地聲明為 final钧栖,但是實(shí)例變量并不是 final

構(gòu)造器

子類(lèi)不能繼承父類(lèi)的構(gòu)造器(構(gòu)造方法或者構(gòu)造函數(shù)),但是父類(lèi)的構(gòu)造器帶有參數(shù)的婆翔,則必須在子類(lèi)的構(gòu)造器中顯式地通過(guò)super關(guān)鍵字調(diào)用父類(lèi)的構(gòu)造器并配以適當(dāng)?shù)膮?shù)列表拯杠。

如果父類(lèi)有無(wú)參構(gòu)造器,則在子類(lèi)的構(gòu)造器中用super調(diào)用父類(lèi)構(gòu)造器不是必須的啃奴,如果沒(méi)有使用super關(guān)鍵字潭陪,系統(tǒng)會(huì)自動(dòng)調(diào)用父類(lèi)的無(wú)參構(gòu)造器。

class SuperClass {
  private int n;
  SuperClass(){
    System.out.println("SuperClass()");
  }
  SuperClass(int n) {
    System.out.println("SuperClass(int n)");
    this.n = n;
  }
}
class SubClass extends SuperClass{
  private int n;
  
  SubClass(){
    super(300);
    System.out.println("SubClass");
  }  
  
  public SubClass(int n){
    System.out.println("SubClass(int n):"+n);
    this.n = n;
  }
}
public class TestSuperSub{
  public static void main (String args[]){
    SubClass sc = new SubClass();
    SubClass sc2 = new SubClass(200); 
  }
}

輸出結(jié)果為:

SuperClass(int n)
SubClass
SuperClass()
SubClass(int n):200

Java 重寫(xiě)(Override)與重載(Overload)

重寫(xiě)(Override)

重寫(xiě)是子類(lèi)對(duì)父類(lèi)的允許訪(fǎng)問(wèn)的方法的實(shí)現(xiàn)過(guò)程進(jìn)行重新編寫(xiě), 返回值和形參都不能改變最蕾。即外殼不變依溯,核心重寫(xiě)!

重寫(xiě)的好處在于子類(lèi)可以根據(jù)需要瘟则,定義特定于自己的行為黎炉。 也就是說(shuō)子類(lèi)能夠根據(jù)需要實(shí)現(xiàn)父類(lèi)的方法。

重寫(xiě)方法不能拋出新的檢查異炒着。或者比被重寫(xiě)方法申明更加寬泛的異常。例如: 父類(lèi)的一個(gè)方法申明了一個(gè)檢查異常 IOException,但是在重寫(xiě)這個(gè)方法的時(shí)候不能拋出 Exception 異常三椿,因?yàn)?Exception 是 IOException 的父類(lèi)沃疮,只能拋出 IOException 的子類(lèi)異常。

在面向?qū)ο笤瓌t里菌赖,重寫(xiě)意味著可以重寫(xiě)任何現(xiàn)有方法缭乘。實(shí)例如下:

TestDog.java 文件代碼:

class Animal{
   public void move(){
      System.out.println("動(dòng)物可以移動(dòng)");
   }
}
 
class Dog extends Animal{
   public void move(){
      System.out.println("狗可以跑和走");
   }
}
 
public class TestDog{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal 對(duì)象
      Animal b = new Dog(); // Dog 對(duì)象
 
      a.move();// 執(zhí)行 Animal 類(lèi)的方法
 
      b.move();//執(zhí)行 Dog 類(lèi)的方法
   }
}

以上實(shí)例編譯運(yùn)行結(jié)果如下:

動(dòng)物可以移動(dòng)
狗可以跑和走

在上面的例子中可以看到,盡管b屬于Animal類(lèi)型琉用,但是它運(yùn)行的是Dog類(lèi)的move方法堕绩。

這是由于在編譯階段策幼,只是檢查參數(shù)的引用類(lèi)型。

然而在運(yùn)行時(shí)逛尚,Java虛擬機(jī)(JVM)指定對(duì)象的類(lèi)型并且運(yùn)行該對(duì)象的方法垄惧。

因此在上面的例子中,之所以能編譯成功绰寞,是因?yàn)锳nimal類(lèi)中存在move方法到逊,然而運(yùn)行時(shí),運(yùn)行的是特定對(duì)象的方法滤钱。

思考以下例子:

TestDog.java 文件代碼:

class Animal{
   public void move(){
      System.out.println("動(dòng)物可以移動(dòng)");
   }
}
 
class Dog extends Animal{
   public void move(){
      System.out.println("狗可以跑和走");
   }
   public void bark(){
      System.out.println("狗可以吠叫");
   }
}
 
public class TestDog{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal 對(duì)象
      Animal b = new Dog(); // Dog 對(duì)象
 
      a.move();// 執(zhí)行 Animal 類(lèi)的方法
      b.move();//執(zhí)行 Dog 類(lèi)的方法
      b.bark();
   }
}

以上實(shí)例編譯運(yùn)行結(jié)果如下:

TestDog.java:30: cannot find symbol
symbol  : method bark()
location: class Animal
                b.bark();
                 ^

該程序?qū)伋鲆粋€(gè)編譯錯(cuò)誤觉壶,因?yàn)閎的引用類(lèi)型Animal沒(méi)有bark方法。

方法的重寫(xiě)規(guī)則

  1. 參數(shù)列表必須完全與被重寫(xiě)方法的相同件缸;
  2. 返回類(lèi)型必須完全與被重寫(xiě)方法的返回類(lèi)型相同铜靶;
  3. 訪(fǎng)問(wèn)權(quán)限不能比父類(lèi)中被重寫(xiě)的方法的訪(fǎng)問(wèn)權(quán)限更低。例如:如果父類(lèi)的一個(gè)方法被聲明為public他炊,那么在子類(lèi)中重寫(xiě)該方法就不能聲明為protected争剿。
  4. 父類(lèi)的成員方法只能被它的子類(lèi)重寫(xiě)。
  5. 聲明為final的方法不能被重寫(xiě)痊末。
  6. 聲明為static的方法不能被重寫(xiě)蚕苇,但是能夠被再次聲明。
  7. 子類(lèi)和父類(lèi)在同一個(gè)包中凿叠,那么子類(lèi)可以重寫(xiě)父類(lèi)所有方法涩笤,除了聲明為private和final的方法。
  8. 子類(lèi)和父類(lèi)不在同一個(gè)包中盒件,那么子類(lèi)只能夠重寫(xiě)父類(lèi)的聲明為public和protected的非final方法蹬碧。
  9. 重寫(xiě)的方法能夠拋出任何非強(qiáng)制異常,無(wú)論被重寫(xiě)的方法是否拋出異常炒刁。但是恩沽,重寫(xiě)的方法不能拋出新的強(qiáng)制性異常,或者比被重寫(xiě)方法聲明的更廣泛的強(qiáng)制性異常切心,反之則可以飒筑。
  10. 構(gòu)造方法不能被重寫(xiě)。
  11. 如果不能繼承一個(gè)方法绽昏,則不能重寫(xiě)這個(gè)方法协屡。

Super關(guān)鍵字的使用

當(dāng)需要在子類(lèi)中調(diào)用父類(lèi)的被重寫(xiě)方法時(shí),要使用super關(guān)鍵字全谤。

TestDog.java 文件代碼:

class Animal{
   public void move(){
      System.out.println("動(dòng)物可以移動(dòng)");
   }
}
 
class Dog extends Animal{
   public void move(){
      super.move(); // 應(yīng)用super類(lèi)的方法
      System.out.println("狗可以跑和走");
   }
}
 
public class TestDog{
   public static void main(String args[]){
 
      Animal b = new Dog(); // Dog 對(duì)象
      b.move(); //執(zhí)行 Dog類(lèi)的方法
 
   }
}

以上實(shí)例編譯運(yùn)行結(jié)果如下:

動(dòng)物可以移動(dòng)
狗可以跑和走

重載(Overload)

重載(overloading) 是在一個(gè)類(lèi)里面肤晓,方法名字相同,而參數(shù)不同。返回類(lèi)型可以相同也可以不同补憾。

每個(gè)重載的方法(或者構(gòu)造函數(shù))都必須有一個(gè)獨(dú)一無(wú)二的參數(shù)類(lèi)型列表漫萄。

最常用的地方就是構(gòu)造器的重載。

重載規(guī)則

  1. 被重載的方法必須改變參數(shù)列表(參數(shù)個(gè)數(shù)或類(lèi)型或順序不一樣)盈匾;
  2. 被重載的方法可以改變返回類(lèi)型腾务;
  3. 被重載的方法可以改變?cè)L問(wèn)修飾符;
  4. 被重載的方法可以聲明新的或更廣的檢查異常削饵;
  5. 方法能夠在同一個(gè)類(lèi)中或者在一個(gè)子類(lèi)中被重載岩瘦。
  6. 無(wú)法以返回值類(lèi)型作為重載函數(shù)的區(qū)分標(biāo)準(zhǔn)。
Overloading.java 文件代碼:
public class Overloading {
    public int test(){
        System.out.println("test1");
        return 1;
    }
 
    public void test(int a){
        System.out.println("test2");
    }   
 
    //以下兩個(gè)參數(shù)類(lèi)型順序不同
    public String test(int a,String s){
        System.out.println("test3");
        return "returntest3";
    }   
 
    public String test(String s,int a){
        System.out.println("test4");
        return "returntest4";
    }   
 
    public static void main(String[] args){
        Overloading o = new Overloading();
        System.out.println(o.test());
        o.test(1);
        System.out.println(o.test(1,"test3"));
        System.out.println(o.test("test4",1));
    }
}

重寫(xiě)與重載之間的區(qū)別

區(qū)別點(diǎn) 重載方法 重寫(xiě)方法
參數(shù)列表 必須修改 一定不能修改
返回類(lèi)型 可以修改 一定不能修改
異常 可以修改 可以減少或刪除窿撬,一定不能拋出新的或者更廣的異常
訪(fǎng)問(wèn) 可以修改 一定不能做更嚴(yán)格的限制(可以降低限制)

總結(jié)

方法的重寫(xiě)(Overriding)和重載(Overloading)是java多態(tài)性的不同表現(xiàn)启昧,重寫(xiě)是父類(lèi)與子類(lèi)之間多態(tài)性的一種表現(xiàn),重載可以理解成多態(tài)的具體表現(xiàn)形式劈伴。

  1. 方法重載是一個(gè)類(lèi)中定義了多個(gè)方法名相同,而他們的參數(shù)的數(shù)量不同或數(shù)量相同而類(lèi)型和次序不同,則稱(chēng)為方法的重載(Overloading)密末。
  2. 方法重寫(xiě)是在子類(lèi)存在方法與父類(lèi)的方法的名字相同,而且參數(shù)的個(gè)數(shù)與類(lèi)型一樣,返回值也一樣的方法,就稱(chēng)為重寫(xiě)(Overriding)。
  3. 方法重載是一個(gè)類(lèi)的多態(tài)性表現(xiàn),而方法重寫(xiě)是子類(lèi)與父類(lèi)的一種多態(tài)性表現(xiàn)跛璧。

java多態(tài)

多態(tài)是同一個(gè)行為具有多個(gè)不同表現(xiàn)形式或形態(tài)的能力严里。

多態(tài)就是同一個(gè)接口,使用不同的實(shí)例而執(zhí)行不同操作追城,如圖所示:

03.png

多態(tài)性是對(duì)象多種表現(xiàn)形式的體現(xiàn)田炭。

現(xiàn)實(shí)中,比如我們按下 F1 鍵這個(gè)動(dòng)作:

  1. 如果當(dāng)前在 Flash 界面下彈出的就是 AS 3 的幫助文檔漓柑;
  2. 如果當(dāng)前在 Word 下彈出的就是 Word 幫助;
  3. 在 Windows 下彈出的就是 Windows 幫助和支持叨吮。
    同一個(gè)事件發(fā)生在不同的對(duì)象上會(huì)產(chǎn)生不同的結(jié)果辆布。

多態(tài)的優(yōu)點(diǎn)

  1. 消除類(lèi)型之間的耦合關(guān)系
  2. 可替換性
  3. 可擴(kuò)充性
  4. 接口性
  5. 靈活性
  6. 簡(jiǎn)化性

多態(tài)存在的三個(gè)必要條件

  1. 繼承
  2. 重寫(xiě)
  3. 父類(lèi)引用指向子類(lèi)對(duì)象
    比如:
Parent p = new Child();

當(dāng)使用多態(tài)方式調(diào)用方法時(shí),首先檢查父類(lèi)中是否有該方法茶鉴,如果沒(méi)有锋玲,則編譯錯(cuò)誤;如果有涵叮,再去調(diào)用子類(lèi)的同名方法惭蹂。

多態(tài)的好處:可以使程序有良好的擴(kuò)展,并可以對(duì)所有類(lèi)的對(duì)象進(jìn)行通用處理割粮。

以下是一個(gè)多態(tài)實(shí)例的演示盾碗,詳細(xì)說(shuō)明請(qǐng)看注釋?zhuān)?/p>

Test.java 文件代碼:

public class Test {
    public static void main(String[] args) {
      show(new Cat());  // 以 Cat 對(duì)象調(diào)用 show 方法
      show(new Dog());  // 以 Dog 對(duì)象調(diào)用 show 方法
                
      Animal a = new Cat();  // 向上轉(zhuǎn)型  
      a.eat();               // 調(diào)用的是 Cat 的 eat
      Cat c = (Cat)a;        // 向下轉(zhuǎn)型  
      c.work();        // 調(diào)用的是 Cat 的 work
  }  
            
    public static void show(Animal a)  {
      a.eat();  
        // 類(lèi)型判斷
        if (a instanceof Cat)  {  // 貓做的事情 
            Cat c = (Cat)a;  
            c.work();  
        } else if (a instanceof Dog) { // 狗做的事情 
            Dog c = (Dog)a;  
            c.work();  
        }  
    }  
}
 
abstract class Animal {  
    abstract void eat();  
}  
  
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃魚(yú)");  
    }  
    public void work() {  
        System.out.println("抓老鼠");  
    }  
}  
  
class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨頭");  
    }  
    public void work() {  
        System.out.println("看家");  
    }  
}

執(zhí)行以上程序,輸出結(jié)果為:

吃魚(yú)
抓老鼠
吃骨頭
看家
吃魚(yú)
抓老鼠

虛方法

我們將介紹在Java中舀瓢,當(dāng)設(shè)計(jì)類(lèi)時(shí)廷雅,被重寫(xiě)的方法的行為怎樣影響多態(tài)性。

我們已經(jīng)討論了方法的重寫(xiě),也就是子類(lèi)能夠重寫(xiě)父類(lèi)的方法航缀。

當(dāng)子類(lèi)對(duì)象調(diào)用重寫(xiě)的方法時(shí)商架,調(diào)用的是子類(lèi)的方法,而不是父類(lèi)中被重寫(xiě)的方法芥玉。

要想調(diào)用父類(lèi)中被重寫(xiě)的方法蛇摸,則必須使用關(guān)鍵字super。

Employee.java 文件代碼:

/* 文件名 : Employee.java */
public class Employee {
   private String name;
   private String address;
   private int number;
   public Employee(String name, String address, int number) {
      System.out.println("Employee 構(gòu)造函數(shù)");
      this.name = name;
      this.address = address;
      this.number = number;
   }
   public void mailCheck() {
      System.out.println("郵寄支票給: " + this.name
       + " " + this.address);
   }
   public String toString() {
      return name + " " + address + " " + number;
   }
   public String getName() {
      return name;
   }
   public String getAddress() {
      return address;
   }
   public void setAddress(String newAddress) {
      address = newAddress;
   }
   public int getNumber() {
     return number;
   }
}

假設(shè)下面的類(lèi)繼承Employee類(lèi):

Salary.java 文件代碼:

/* 文件名 : Salary.java */
public class Salary extends Employee
{
   private double salary; // 全年工資
   public Salary(String name, String address, int number, double salary) {
       super(name, address, number);
       setSalary(salary);
   }
   public void mailCheck() {
       System.out.println("Salary 類(lèi)的 mailCheck 方法 ");
       System.out.println("郵寄支票給:" + getName()
       + " 灿巧,工資為:" + salary);
   }
   public double getSalary() {
       return salary;
   }
   public void setSalary(double newSalary) {
       if(newSalary >= 0.0) {
          salary = newSalary;
       }
   }
   public double computePay() {
      System.out.println("計(jì)算工資赶袄,付給:" + getName());
      return salary/52;
   }
}

現(xiàn)在我們仔細(xì)閱讀下面的代碼,嘗試給出它的輸出結(jié)果:

VirtualDemo.java 文件代碼:

/* 文件名 : VirtualDemo.java */
public class VirtualDemo {
   public static void main(String [] args) {
      Salary s = new Salary("員工 A", "北京", 3, 3600.00);
      Employee e = new Salary("員工 B", "上海", 2, 2400.00);
      System.out.println("使用 Salary 的引用調(diào)用 mailCheck -- ");
      s.mailCheck();
      System.out.println("\n使用 Employee 的引用調(diào)用 mailCheck--");
      e.mailCheck();
    }
}

以上實(shí)例編譯運(yùn)行結(jié)果如下:

Employee 構(gòu)造函數(shù)
Employee 構(gòu)造函數(shù)
使用 Salary 的引用調(diào)用 mailCheck -- 
Salary 類(lèi)的 mailCheck 方法 
郵寄支票給:?jiǎn)T工 A 砸烦,工資為:3600.0

使用 Employee 的引用調(diào)用 mailCheck--
Salary 類(lèi)的 mailCheck 方法 
郵寄支票給:?jiǎn)T工 B 弃鸦,工資為:2400.0

例子解析

  1. 實(shí)例中,實(shí)例化了兩個(gè) Salary 對(duì)象:一個(gè)使用 Salary 引用 s幢痘,另一個(gè)使用 Employee 引用 e唬格。

  2. 當(dāng)調(diào)用 s.mailCheck() 時(shí),編譯器在編譯時(shí)會(huì)在 Salary 類(lèi)中找到 mailCheck()颜说,執(zhí)行過(guò)程 JVM 就調(diào)用 Salary 類(lèi)的 mailCheck()购岗。

  3. 因?yàn)?e 是 Employee 的引用,所以調(diào)用 e 的 mailCheck() 方法時(shí)门粪,編譯器會(huì)去 Employee 類(lèi)查找 mailCheck() 方法 喊积。

  4. 在編譯的時(shí)候,編譯器使用 Employee 類(lèi)中的 mailCheck() 方法驗(yàn)證該語(yǔ)句玄妈, 但是在運(yùn)行的時(shí)候乾吻,Java虛擬機(jī)(JVM)調(diào)用的是 Salary 類(lèi)中的 mailCheck() 方法。

以上整個(gè)過(guò)程被稱(chēng)為虛擬方法調(diào)用拟蜻,該方法被稱(chēng)為虛擬方法绎签。

Java中所有的方法都能以這種方式表現(xiàn),因此酝锅,重寫(xiě)的方法能在運(yùn)行時(shí)調(diào)用诡必,不管編譯的時(shí)候源代碼中引用變量是什么數(shù)據(jù)類(lèi)型。

多態(tài)的實(shí)現(xiàn)方式

方式一:重寫(xiě):

這個(gè)內(nèi)容已經(jīng)在上一章節(jié)詳細(xì)講過(guò)搔扁,就不再闡述爸舒,詳細(xì)可訪(fǎng)問(wèn):Java 重寫(xiě)(Override)與重載(Overload)。

方式二:接口

  1. 生活中的接口最具代表性的就是插座稿蹲,例如一個(gè)三接頭的插頭都能接在三孔插座中扭勉,因?yàn)檫@個(gè)是每個(gè)國(guó)家都有各自規(guī)定的接口規(guī)則,有可能到國(guó)外就不行苛聘,那是因?yàn)閲?guó)外自己定義的接口類(lèi)型剖效。

  2. java中的接口類(lèi)似于生活中的接口嫉入,就是一些方法特征的集合,但沒(méi)有方法的實(shí)現(xiàn)璧尸。具體可以看 java接口 這一章節(jié)的內(nèi)容咒林。

方式三:抽象類(lèi)和抽象方法

Java抽象類(lèi)

在面向?qū)ο蟮母拍钪校械膶?duì)象都是通過(guò)類(lèi)來(lái)描繪的爷光,但是反過(guò)來(lái)垫竞,并不是所有的類(lèi)都是用來(lái)描繪對(duì)象的,如果一個(gè)類(lèi)中沒(méi)有包含足夠的信息來(lái)描繪一個(gè)具體的對(duì)象蛀序,這樣的類(lèi)就是抽象類(lèi)欢瞪。

抽象類(lèi)除了不能實(shí)例化對(duì)象之外,類(lèi)的其它功能依然存在徐裸,成員變量遣鼓、成員方法和構(gòu)造方法的訪(fǎng)問(wèn)方式和普通類(lèi)一樣。

由于抽象類(lèi)不能實(shí)例化對(duì)象重贺,所以抽象類(lèi)必須被繼承骑祟,才能被使用。也是因?yàn)檫@個(gè)原因气笙,通常在設(shè)計(jì)階段決定要不要設(shè)計(jì)抽象類(lèi)次企。

父類(lèi)包含了子類(lèi)集合的常見(jiàn)的方法,但是由于父類(lèi)本身是抽象的潜圃,所以不能使用這些方法缸棵。

在Java中抽象類(lèi)表示的是一種繼承關(guān)系,一個(gè)類(lèi)只能繼承一個(gè)抽象類(lèi)谭期,而一個(gè)類(lèi)卻可以實(shí)現(xiàn)多個(gè)接口堵第。

抽象類(lèi)

在Java語(yǔ)言中使用abstract class來(lái)定義抽象類(lèi)。如下實(shí)例:

Employee.java 文件代碼:

/* 文件名 : Employee.java */
public abstract class Employee
{
   private String name;
   private String address;
   private int number;
   public Employee(String name, String address, int number)
   {
      System.out.println("Constructing an Employee");
      this.name = name;
      this.address = address;
      this.number = number;
   }
   public double computePay()
   {
     System.out.println("Inside Employee computePay");
     return 0.0;
   }
   public void mailCheck()
   {
      System.out.println("Mailing a check to " + this.name
       + " " + this.address);
   }
   public String toString()
   {
      return name + " " + address + " " + number;
   }
   public String getName()
   {
      return name;
   }
   public String getAddress()
   {
      return address;
   }
   public void setAddress(String newAddress)
   {
      address = newAddress;
   }
   public int getNumber()
   {
     return number;
   }
}

注意到該Employee類(lèi)沒(méi)有什么不同隧出,盡管該類(lèi)是抽象類(lèi)型诚,但是它仍然有3個(gè)成員變量,7個(gè)成員方法和1個(gè)構(gòu)造方法鸳劳。 現(xiàn)在如果你嘗試如下的例子:

AbstractDemo.java 文件代碼:

/* 文件名 : AbstractDemo.java */
public class AbstractDemo
{
   public static void main(String [] args)
   {
      /* 以下是不允許的,會(huì)引發(fā)錯(cuò)誤 */
      Employee e = new Employee("George W.", "Houston, TX", 43);
 
      System.out.println("\n Call mailCheck using Employee reference--");
      e.mailCheck();
    }
}

當(dāng)你嘗試編譯AbstractDemo類(lèi)時(shí)也搓,會(huì)產(chǎn)生如下錯(cuò)誤:

Employee.java:46: Employee is abstract; cannot be instantiated
      Employee e = new Employee("George W.", "Houston, TX", 43);
                   ^
1 error

繼承抽象類(lèi)

我們能通過(guò)一般的方法繼承Employee類(lèi):

Salary.java 文件代碼:

/* 文件名 : Salary.java */
public class Salary extends Employee
{
   private double salary; //Annual salary
   public Salary(String name, String address, int number, double
      salary)
   {
       super(name, address, number);
       setSalary(salary);
   }
   public void mailCheck()
   {
       System.out.println("Within mailCheck of Salary class ");
       System.out.println("Mailing check to " + getName()
       + " with salary " + salary);
   }
   public double getSalary()
   {
       return salary;
   }
   public void setSalary(double newSalary)
   {
       if(newSalary >= 0.0)
       {
          salary = newSalary;
       }
   }
   public double computePay()
   {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
}

盡管我們不能實(shí)例化一個(gè)Employee類(lèi)的對(duì)象赏廓,但是如果我們實(shí)例化一個(gè)Salary類(lèi)對(duì)象,該對(duì)象將從 Employee 類(lèi)繼承7個(gè)成員方法傍妒,且通過(guò)該方法可以設(shè)置或獲取三個(gè)成員變量幔摸。

AbstractDemo.java 文件代碼:

/* 文件名 : AbstractDemo.java */
public class AbstractDemo
{
   public static void main(String [] args)
   {
      Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
      Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
 
      System.out.println("Call mailCheck using Salary reference --");
      s.mailCheck();
 
      System.out.println("\n Call mailCheck using Employee reference--");
      e.mailCheck();
    }
}

以上程序編譯運(yùn)行結(jié)果如下:

Constructing an Employee
Constructing an Employee
Call mailCheck using  Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0

Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.

抽象方法

如果你想設(shè)計(jì)這樣一個(gè)類(lèi),該類(lèi)包含一個(gè)特別的成員方法颤练,該方法的具體實(shí)現(xiàn)由它的子類(lèi)確定既忆,那么你可以在父類(lèi)中聲明該方法為抽象方法。

Abstract關(guān)鍵字同樣可以用來(lái)聲明抽象方法,抽象方法只包含一個(gè)方法名患雇,而沒(méi)有方法體跃脊。

抽象方法沒(méi)有定義,方法名后面直接跟一個(gè)分號(hào)苛吱,而不是花括號(hào)酪术。

public abstract class Employee
{
   private String name;
   private String address;
   private int number;
   
   public abstract double computePay();
   
   //其余代碼
}

聲明抽象方法會(huì)造成以下兩個(gè)結(jié)果:

  1. 如果一個(gè)類(lèi)包含抽象方法,那么該類(lèi)必須是抽象類(lèi)翠储。
  2. 任何子類(lèi)必須重寫(xiě)父類(lèi)的抽象方法绘雁,或者聲明自身為抽象類(lèi)。

繼承抽象方法的子類(lèi)必須重寫(xiě)該方法援所。否則庐舟,該子類(lèi)也必須聲明為抽象類(lèi)。最終住拭,必須有子類(lèi)實(shí)現(xiàn)該抽象方法挪略,否則,從最初的父類(lèi)到最終的子類(lèi)都不能用來(lái)實(shí)例化對(duì)象废酷。

如果Salary類(lèi)繼承了Employee類(lèi)瘟檩,那么它必須實(shí)現(xiàn)computePay()方法:

Salary.java 文件代碼:

/* 文件名 : Salary.java */
public class Salary extends Employee
{
   private double salary; // Annual salary
  
   public double computePay()
   {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
 
   //其余代碼
}

抽象類(lèi)總結(jié)規(guī)定

  1. 抽象類(lèi)不能被實(shí)例化(初學(xué)者很容易犯的錯(cuò)),如果被實(shí)例化澈蟆,就會(huì)報(bào)錯(cuò)墨辛,編譯無(wú)法通過(guò)。只有抽象類(lèi)的非抽象子類(lèi)可以創(chuàng)建對(duì)象趴俘。

  2. 抽象類(lèi)中不一定包含抽象方法睹簇,但是有抽象方法的類(lèi)必定是抽象類(lèi)。

  3. 抽象類(lèi)中的抽象方法只是聲明寥闪,不包含方法體太惠,就是不給出方法的具體實(shí)現(xiàn)也就是方法的具體功能。

  4. 構(gòu)造方法疲憋,類(lèi)方法(用static修飾的方法)不能聲明為抽象方法凿渊。

  5. 抽象類(lèi)的子類(lèi)必須給出抽象類(lèi)中的抽象方法的具體實(shí)現(xiàn),除非該子類(lèi)也是抽象類(lèi)缚柳。

java封裝

在面向?qū)ο蟪淌皆O(shè)計(jì)方法中埃脏,封裝(英語(yǔ):Encapsulation)是指一種將抽象性函式接口的實(shí)現(xiàn)細(xì)節(jié)部份包裝、隱藏起來(lái)的方法秋忙。

封裝可以被認(rèn)為是一個(gè)保護(hù)屏障彩掐,防止該類(lèi)的代碼和數(shù)據(jù)被外部類(lèi)定義的代碼隨機(jī)訪(fǎng)問(wèn)。

要訪(fǎng)問(wèn)該類(lèi)的代碼和數(shù)據(jù)灰追,必須通過(guò)嚴(yán)格的接口控制堵幽。

封裝最主要的功能在于我們能修改自己的實(shí)現(xiàn)代碼狗超,而不用修改那些調(diào)用我們代碼的程序片段。

適當(dāng)?shù)姆庋b可以讓程式碼更容易理解與維護(hù)朴下,也加強(qiáng)了程式碼的安全性努咐。

封裝的優(yōu)點(diǎn)

  1. 良好的封裝能夠減少耦合。

  2. 類(lèi)內(nèi)部的結(jié)構(gòu)可以自由修改桐猬。

  3. 可以對(duì)成員變量進(jìn)行更精確的控制麦撵。

  4. 隱藏信息,實(shí)現(xiàn)細(xì)節(jié)溃肪。

實(shí)現(xiàn)Java封裝的步驟

1.修改屬性的可見(jiàn)性來(lái)限制對(duì)屬性的訪(fǎng)問(wèn)(一般限制為private)免胃,例如:

public class Person {
    private String name;
    private int age;
}

這段代碼中,將 name 和 age 屬性設(shè)置為私有的惫撰,只能本類(lèi)才能訪(fǎng)問(wèn)羔沙,其他類(lèi)都訪(fǎng)問(wèn)不了,如此就對(duì)信息進(jìn)行了隱藏厨钻。

2.對(duì)每個(gè)值屬性提供對(duì)外的公共方法訪(fǎng)問(wèn)扼雏,也就是創(chuàng)建一對(duì)賦取值方法,用于對(duì)私有屬性的訪(fǎng)問(wèn)夯膀,例如:

public class Person{
    private String name;
    private int age;

    public int getAge(){
      return age;
    }

    public String getName(){
      return name;
    }

    public void setAge(int age){
      this.age = age;
    }

    public void setName(String name){
      this.name = name;
    }
}

采用 this 關(guān)鍵字是為了解決實(shí)例變量(private String name)和局部變量(setName(String name)中的name變量)之間發(fā)生的同名的沖突诗充。

實(shí)例

讓我們來(lái)看一個(gè)java封裝類(lèi)的例子:

EncapTest.java 文件代碼:

/* 文件名: EncapTest.java */
public class EncapTest{
 
   private String name;
   private String idNum;
   private int age;
 
   public int getAge(){
      return age;
   }
 
   public String getName(){
      return name;
   }
 
   public String getIdNum(){
      return idNum;
   }
 
   public void setAge( int newAge){
      age = newAge;
   }
 
   public void setName(String newName){
      name = newName;
   }
 
   public void setIdNum( String newId){
      idNum = newId;
   }
}

以上實(shí)例中public方法是外部類(lèi)訪(fǎng)問(wèn)該類(lèi)成員變量的入口。

通常情況下诱建,這些方法被稱(chēng)為getter和setter方法蝴蜓。

因此,任何要訪(fǎng)問(wèn)類(lèi)中私有成員變量的類(lèi)都要通過(guò)這些getter和setter方法俺猿。

通過(guò)如下的例子說(shuō)明EncapTest類(lèi)的變量怎樣被訪(fǎng)問(wèn):

RunEncap.java 文件代碼:

/* F文件名 : RunEncap.java */
public class RunEncap{
   public static void main(String args[]){
      EncapTest encap = new EncapTest();
      encap.setName("James");
      encap.setAge(20);
      encap.setIdNum("12343ms");
 
      System.out.print("Name : " + encap.getName()+ 
                             " Age : "+ encap.getAge());
    }
}

以上代碼編譯運(yùn)行結(jié)果如下:

Name : James Age : 20

java接口

接口(英文:Interface)茎匠,在JAVA編程語(yǔ)言中是一個(gè)抽象類(lèi)型,是抽象方法的集合押袍,接口通常以interface來(lái)聲明诵冒。一個(gè)類(lèi)通過(guò)繼承接口的方式,從而來(lái)繼承接口的抽象方法谊惭。

接口并不是類(lèi)汽馋,編寫(xiě)接口的方式和類(lèi)很相似,但是它們屬于不同的概念圈盔。類(lèi)描述對(duì)象的屬性和方法豹芯。接口則包含類(lèi)要實(shí)現(xiàn)的方法。

除非實(shí)現(xiàn)接口的類(lèi)是抽象類(lèi)药磺,否則該類(lèi)要定義接口中的所有方法。

接口無(wú)法被實(shí)例化煤伟,但是可以被實(shí)現(xiàn)癌佩。一個(gè)實(shí)現(xiàn)接口的類(lèi)木缝,必須實(shí)現(xiàn)接口內(nèi)所描述的所有方法,否則就必須聲明為抽象類(lèi)围辙。另外我碟,在 Java 中,接口類(lèi)型可用來(lái)聲明一個(gè)變量姚建,他們可以成為一個(gè)空指針矫俺,或是被綁定在一個(gè)以此接口實(shí)現(xiàn)的對(duì)象。

接口與類(lèi)相似點(diǎn):

  1. 一個(gè)接口可以有多個(gè)方法掸冤。
  2. 接口文件保存在 .java 結(jié)尾的文件中厘托,文件名使用接口名。
  3. 接口的字節(jié)碼文件保存在 .class 結(jié)尾的文件中稿湿。
  4. 接口相應(yīng)的字節(jié)碼文件必須在與包名稱(chēng)相匹配的目錄結(jié)構(gòu)中铅匹。

接口與類(lèi)的區(qū)別:

  1. 接口不能用于實(shí)例化對(duì)象。
  2. 接口沒(méi)有構(gòu)造方法饺藤。
  3. 接口中所有的方法必須是抽象方法包斑。
  4. 接口不能包含成員變量,除了 static 和 final 變量涕俗。
  5. 接口不是被類(lèi)繼承了罗丰,而是要被類(lèi)實(shí)現(xiàn)。
  6. 接口支持多繼承再姑。

接口特性

  1. 接口中每一個(gè)方法也是隱式抽象的,接口中的方法會(huì)被隱式的指定為 public abstract(只能是 public abstract萌抵,其他修飾符都會(huì)報(bào)錯(cuò))。
  2. 接口中可以含有變量询刹,但是接口中的變量會(huì)被隱式的指定為 public static final 變量(并且只能是 public谜嫉,用 private 修飾會(huì)報(bào)編譯錯(cuò)誤)。
  3. 接口中的方法是不能在接口中實(shí)現(xiàn)的凹联,只能由實(shí)現(xiàn)接口的類(lèi)來(lái)實(shí)現(xiàn)接口中的方法沐兰。

抽象類(lèi)和接口的區(qū)別

  1. 抽象類(lèi)中的方法可以有方法體,就是能實(shí)現(xiàn)方法的具體功能蔽挠,但是接口中的方法不行住闯。
  2. 抽象類(lèi)中的成員變量可以是各種類(lèi)型的,而接口中的成員變量只能是 public static final 類(lèi)型的澳淑。
  3. 接口中不能含有靜態(tài)代碼塊以及靜態(tài)方法(用 static 修飾的方法)比原,而抽象類(lèi)是可以有靜態(tài)代碼塊和靜態(tài)方法。
  4. 一個(gè)類(lèi)只能繼承一個(gè)抽象類(lèi)杠巡,而一個(gè)類(lèi)卻可以實(shí)現(xiàn)多個(gè)接口量窘。

接口的聲明

接口的聲明語(yǔ)法格式如下:

[可見(jiàn)度] interface 接口名稱(chēng) [extends 其他的類(lèi)名] {
        // 聲明變量
        // 抽象方法
}

Interface關(guān)鍵字用來(lái)聲明一個(gè)接口。下面是接口聲明的一個(gè)簡(jiǎn)單例子氢拥。

NameOfInterface.java 文件代碼:

/* 文件名 : NameOfInterface.java */
import java.lang.*;
//引入包
 
public interface NameOfInterface
{
   //任何類(lèi)型 final, static 字段
   //抽象方法
}

接口有以下特性:

  1. 接口是隱式抽象的蚌铜,當(dāng)聲明一個(gè)接口的時(shí)候锨侯,不必使用abstract關(guān)鍵字。
  2. 接口中每一個(gè)方法也是隱式抽象的冬殃,聲明時(shí)同樣不需要abstract關(guān)鍵字囚痴。
  3. 接口中的方法都是公有的。

Animal.java 文件代碼:

/* 文件名 : Animal.java */
interface Animal {
   public void eat();
   public void travel();
}

接口的實(shí)現(xiàn)

當(dāng)類(lèi)實(shí)現(xiàn)接口的時(shí)候审葬,類(lèi)要實(shí)現(xiàn)接口中所有的方法深滚。否則,類(lèi)必須聲明為抽象的類(lèi)涣觉。

類(lèi)使用implements關(guān)鍵字實(shí)現(xiàn)接口痴荐。在類(lèi)聲明中,Implements關(guān)鍵字放在class聲明后面旨枯。

實(shí)現(xiàn)一個(gè)接口的語(yǔ)法蹬昌,可以使用這個(gè)公式:

Animal.java 文件代碼:

...implements 接口名稱(chēng)[, 其他接口, 其他接口..., ...] ...

MammalInt.java 文件代碼:

/* 文件名 : MammalInt.java */
public class MammalInt implements Animal{
 
   public void eat(){
      System.out.println("Mammal eats");
   }
 
   public void travel(){
      System.out.println("Mammal travels");
   } 
 
   public int noOfLegs(){
      return 0;
   }
 
   public static void main(String args[]){
      MammalInt m = new MammalInt();
      m.eat();
      m.travel();
   }
}

以上實(shí)例編譯運(yùn)行結(jié)果如下:

Mammal eats
Mammal travels

重寫(xiě)接口中聲明的方法時(shí),需要注意以下規(guī)則:

  1. 類(lèi)在實(shí)現(xiàn)接口的方法時(shí)攀隔,不能拋出強(qiáng)制性異常皂贩,只能在接口中,或者繼承接口的抽象類(lèi)中拋出該強(qiáng)制性異常昆汹。
  2. 類(lèi)在重寫(xiě)方法時(shí)要保持一致的方法名明刷,并且應(yīng)該保持相同或者相兼容的返回值類(lèi)型。
  3. 如果實(shí)現(xiàn)接口的類(lèi)是抽象類(lèi)满粗,那么就沒(méi)必要實(shí)現(xiàn)該接口的方法辈末。

在實(shí)現(xiàn)接口的時(shí)候,也要注意一些規(guī)則:

  1. 一個(gè)類(lèi)可以同時(shí)實(shí)現(xiàn)多個(gè)接口映皆。
  2. 一個(gè)類(lèi)只能繼承一個(gè)類(lèi)挤聘,但是能實(shí)現(xiàn)多個(gè)接口。
  3. 一個(gè)接口能繼承另一個(gè)接口捅彻,這和類(lèi)之間的繼承比較相似组去。

接口的繼承

一個(gè)接口能繼承另一個(gè)接口,和類(lèi)之間的繼承方式比較相似步淹。接口的繼承使用extends關(guān)鍵字从隆,子接口繼承父接口的方法。

下面的Sports接口被Hockey和Football接口繼承:

// 文件名: Sports.java
public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}
 
// 文件名: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}
 
// 文件名: Hockey.java
public interface Hockey extends Sports
{
   public void homeGoalScored();
   public void visitingGoalScored();
   public void endOfPeriod(int period);
   public void overtimePeriod(int ot);
}

Hockey接口自己聲明了四個(gè)方法缭裆,從Sports接口繼承了兩個(gè)方法键闺,這樣,實(shí)現(xiàn)Hockey接口的類(lèi)需要實(shí)現(xiàn)六個(gè)方法澈驼。

相似的辛燥,實(shí)現(xiàn)Football接口的類(lèi)需要實(shí)現(xiàn)五個(gè)方法,其中兩個(gè)來(lái)自于Sports接口。

接口的多繼承

在Java中挎塌,類(lèi)的多繼承是不合法畅铭,但接口允許多繼承,勃蜘。

在接口的多繼承中extends關(guān)鍵字只需要使用一次,在其后跟著繼承接口假残。 如下所示:

public interface Hockey extends Sports, Event

以上的程序片段是合法定義的子接口缭贡,與類(lèi)不同的是,接口允許多繼承辉懒,而 Sports及 Event 可能定義或是繼承相同的方法

標(biāo)記接口

最常用的繼承接口是沒(méi)有包含任何方法的接口阳惹。

標(biāo)識(shí)接口是沒(méi)有任何方法和屬性的接口.它僅僅表明它的類(lèi)屬于一個(gè)特定的類(lèi)型,供其他代碼來(lái)測(cè)試允許做一些事情。

標(biāo)識(shí)接口作用:簡(jiǎn)單形象的說(shuō)就是給某個(gè)對(duì)象打個(gè)標(biāo)(蓋個(gè)戳)眶俩,使對(duì)象擁有某個(gè)或某些特權(quán)莹汤。

例如:java.awt.event 包中的 MouseListener 接口繼承的 java.util.EventListener 接口定義如下:

package java.util;
public interface EventListener
{}

沒(méi)有任何方法的接口被稱(chēng)為標(biāo)記接口。標(biāo)記接口主要用于以下兩種目的:

建立一個(gè)公共的父接口:

正如EventListener接口颠印,這是由幾十個(gè)其他接口擴(kuò)展的Java API纲岭,你可以使用一個(gè)標(biāo)記接口來(lái)建立一組接口的父接口。例如:當(dāng)一個(gè)接口繼承了EventListener接口线罕,Java虛擬機(jī)(JVM)就知道該接口將要被用于一個(gè)事件的代理方案止潮。

向一個(gè)類(lèi)添加數(shù)據(jù)類(lèi)型:

這種情況是標(biāo)記接口最初的目的,實(shí)現(xiàn)標(biāo)記接口的類(lèi)不需要定義任何接口方法(因?yàn)闃?biāo)記接口根本就沒(méi)有方法)钞楼,但是該類(lèi)通過(guò)多態(tài)性變成一個(gè)接口類(lèi)型喇闸。

Java 包(package)

為了更好地組織類(lèi),Java 提供了包機(jī)制询件,用于區(qū)別類(lèi)名的命名空間燃乍。

包的作用

  1. 把功能相似或相關(guān)的類(lèi)或接口組織在同一個(gè)包中,方便類(lèi)的查找和使用宛琅。

  2. 如同文件夾一樣刻蟹,包也采用了樹(shù)形目錄的存儲(chǔ)方式。同一個(gè)包中的類(lèi)名字是不同的夯秃,不同的包中的類(lèi)的名字是可以相同的座咆,當(dāng)同時(shí)調(diào)用兩個(gè)不同包中相同類(lèi)名的類(lèi)時(shí),應(yīng)該加上包名加以區(qū)別仓洼。因此介陶,包可以避免名字沖突。

  3. 包也限定了訪(fǎng)問(wèn)權(quán)限色建,擁有包訪(fǎng)問(wèn)權(quán)限的類(lèi)才能訪(fǎng)問(wèn)某個(gè)包中的類(lèi)哺呜。

Java 使用包(package)這種機(jī)制是為了防止命名沖突,訪(fǎng)問(wèn)控制箕戳,提供搜索和定位類(lèi)(class)某残、接口国撵、枚舉(enumerations)和注釋?zhuān)╝nnotation)等。

包語(yǔ)句的語(yǔ)法格式為:

package pkg1[.pkg2[.pkg3…]];

例如,一個(gè)Something.java 文件它的內(nèi)容

package net.java.util
public class Something{
   ...
}

那么它的路徑應(yīng)該是 net/java/util/Something.java 這樣保存的玻墅。 package(包) 的作用是把不同的 java 程序分類(lèi)保存介牙,更方便的被其他 java 程序調(diào)用。

一個(gè)包(package)可以定義為一組相互聯(lián)系的類(lèi)型(類(lèi)澳厢、接口环础、枚舉和注釋?zhuān)瑸檫@些類(lèi)型提供訪(fǎng)問(wèn)保護(hù)和命名空間管理的功能剩拢。

以下是一些 Java 中的包:

  1. java.lang-打包基礎(chǔ)的類(lèi)
  2. java.io-包含輸入輸出功能的函數(shù)

開(kāi)發(fā)者可以自己把一組類(lèi)和接口等打包线得,并定義自己的包。而且在實(shí)際開(kāi)發(fā)中這樣做是值得提倡的徐伐,當(dāng)你自己完成類(lèi)的實(shí)現(xiàn)之后贯钩,將相關(guān)的類(lèi)分組,可以讓其他的編程者更容易地確定哪些類(lèi)办素、接口角雷、枚舉和注釋等是相關(guān)的。

由于包創(chuàng)建了新的命名空間(namespace)性穿,所以不會(huì)跟其他包中的任何名字產(chǎn)生命名沖突谓罗。使用包這種機(jī)制,更容易實(shí)現(xiàn)訪(fǎng)問(wèn)控制季二,并且讓定位相關(guān)類(lèi)更加簡(jiǎn)單檩咱。

創(chuàng)建包

創(chuàng)建包的時(shí)候,你需要為這個(gè)包取一個(gè)合適的名字胯舷。之后刻蚯,如果其他的一個(gè)源文件包含了這個(gè)包提供的類(lèi)、接口桑嘶、枚舉或者注釋類(lèi)型的時(shí)候炊汹,都必須將這個(gè)包的聲明放在這個(gè)源文件的開(kāi)頭。

包聲明應(yīng)該在源文件的第一行逃顶,每個(gè)源文件只能有一個(gè)包聲明讨便,這個(gè)文件中的每個(gè)類(lèi)型都應(yīng)用于它。

如果一個(gè)源文件中沒(méi)有使用包聲明以政,那么其中的類(lèi)霸褒,函數(shù),枚舉盈蛮,注釋等將被放在一個(gè)無(wú)名的包(unnamed package)中废菱。

例子

讓我們來(lái)看一個(gè)例子,這個(gè)例子創(chuàng)建了一個(gè)叫做animals的包。通常使用小寫(xiě)的字母來(lái)命名避免與類(lèi)殊轴、接口名字的沖突衰倦。

在 animals 包中加入一個(gè)接口(interface):

Animal.java 文件代碼:

/* 文件名: Animal.java */
package animals;
 
interface Animal {
   public void eat();
   public void travel();
}

接下來(lái),在同一個(gè)包中加入該接口的實(shí)現(xiàn):

MammalInt.java 文件代碼:

package animals;
 
/* 文件名 : MammalInt.java */
public class MammalInt implements Animal{
 
   public void eat(){
      System.out.println("Mammal eats");
   }
 
   public void travel(){
      System.out.println("Mammal travels");
   } 
 
   public int noOfLegs(){
      return 0;
   }
 
   public static void main(String args[]){
      MammalInt m = new MammalInt();
      m.eat();
      m.travel();
   }
}

然后旁理,編譯這兩個(gè)文件樊零,并把他們放在一個(gè)叫做animals的子目錄中。 用下面的命令來(lái)運(yùn)行:

$ mkdir animals
$ cp Animal.class  MammalInt.class animals
$ java animals/MammalInt
Mammal eats
Mammal travel

import 關(guān)鍵字

為了能夠使用某一個(gè)包的成員孽文,我們需要在 Java 程序中明確導(dǎo)入該包淹接。使用 "import" 語(yǔ)句可完成此功能。

在 java 源文件中 import 語(yǔ)句應(yīng)位于 package 語(yǔ)句之后叛溢,所有類(lèi)的定義之前,可以沒(méi)有劲适,也可以有多條楷掉,其語(yǔ)法格式為:

import package1[.package2…].(classname|*);

如果在一個(gè)包中,一個(gè)類(lèi)想要使用本包中的另一個(gè)類(lèi)霞势,那么該包名可以省略烹植。

例子

下面的 payroll 包已經(jīng)包含了 Employee 類(lèi),接下來(lái)向 payroll 包中添加一個(gè) Boss 類(lèi)愕贡。Boss 類(lèi)引用 Employee 類(lèi)的時(shí)候可以不用使用 payroll 前綴草雕,Boss類(lèi)的實(shí)例如下。

Boss.java 文件代碼:

package payroll;
 
public class Boss
{
   public void payEmployee(Employee e)
   {
      e.mailCheck();
   }
}

如果 Boss 類(lèi)不在 payroll 包中又會(huì)怎樣固以?Boss 類(lèi)必須使用下面幾種方法之一來(lái)引用其他包中的類(lèi)墩虹。

使用類(lèi)全名描述,例如:

payroll.Employee

用 import 關(guān)鍵字引入憨琳,使用通配符 "*"

import payroll.*;

使用 import 關(guān)鍵字引入 Employee 類(lèi):

import payroll.Employee;

注意:

類(lèi)文件中可以包含任意數(shù)量的 import 聲明诫钓。import 聲明必須在包聲明之后,類(lèi)聲明之前篙螟。

package 的目錄結(jié)構(gòu)

類(lèi)放在包中會(huì)有兩種主要的結(jié)果:

  1. 包名成為類(lèi)名的一部分菌湃,正如我們前面討論的一樣。
  2. 包名必須與相應(yīng)的字節(jié)碼所在的目錄結(jié)構(gòu)相吻合遍略。
    下面是管理你自己 java 中文件的一種簡(jiǎn)單方式:

將類(lèi)惧所、接口等類(lèi)型的源碼放在一個(gè)文本中,這個(gè)文件的名字就是這個(gè)類(lèi)型的名字绪杏,并以.java作為擴(kuò)展名下愈。例如:

// 文件名 :  Car.java
 
package vehicle;
 
public class Car {
   // 類(lèi)實(shí)現(xiàn)  
}

接下來(lái),把源文件放在一個(gè)目錄中蕾久,這個(gè)目錄要對(duì)應(yīng)類(lèi)所在包的名字驰唬。

....\vehicle\Car.java

現(xiàn)在,正確的類(lèi)名和路徑將會(huì)是如下樣子:

  1. 類(lèi)名 -> vehicle.Car

  2. 路徑名 -> vehicle\Car.java (在 windows 系統(tǒng)中)

通常,一個(gè)公司使用它互聯(lián)網(wǎng)域名的顛倒形式來(lái)作為它的包名.例如:互聯(lián)網(wǎng)域名是 runoob.com叫编,所有的包名都以 com.runoob 開(kāi)頭辖佣。包名中的每一個(gè)部分對(duì)應(yīng)一個(gè)子目錄。

例如:有一個(gè) com.runoob.test 的包搓逾,這個(gè)包包含一個(gè)叫做 Runoob.java 的源文件卷谈,那么相應(yīng)的,應(yīng)該有如下面的一連串子目錄:

....\com\runoob\test\Runoob.java

編譯的時(shí)候霞篡,編譯器為包中定義的每個(gè)類(lèi)世蔗、接口等類(lèi)型各創(chuàng)建一個(gè)不同的輸出文件,輸出文件的名字就是這個(gè)類(lèi)型的名字朗兵,并加上 .class 作為擴(kuò)展后綴污淋。 例如:

// 文件名: Runoob.java
 
package com.runoob.test;
public class Runoob {
      
}
class Google {
      
}

現(xiàn)在,我們用-d選項(xiàng)來(lái)編譯這個(gè)文件余掖,如下:

$javac -d . Runoob.java

這樣會(huì)像下面這樣放置編譯了的文件:

.\com\runoob\test\Runoob.class
.\com\runoob\test\Google.class

你可以像下面這樣來(lái)導(dǎo)入所有 \com\runoob\test\ 中定義的類(lèi)寸爆、接口等:

import com.runoob.test.*;

編譯之后的 .class 文件應(yīng)該和 .java 源文件一樣,它們放置的目錄應(yīng)該跟包的名字對(duì)應(yīng)起來(lái)盐欺。但是赁豆,并不要求 .class 文件的路徑跟相應(yīng)的 .java 的路徑一樣。你可以分開(kāi)來(lái)安排源碼和類(lèi)的目錄冗美。

<path-one>\sources\com\runoob\test\Runoob.java
<path-two>\classes\com\runoob\test\Google.class

這樣魔种,你可以將你的類(lèi)目錄分享給其他的編程人員,而不用透露自己的源碼粉洼。用這種方法管理源碼和類(lèi)文件可以讓編譯器和java 虛擬機(jī)(JVM)可以找到你程序中使用的所有類(lèi)型节预。

類(lèi)目錄的絕對(duì)路徑叫做 class path。設(shè)置在系統(tǒng)變量 CLASSPATH 中属韧。編譯器和 java 虛擬機(jī)通過(guò)將 package 名字加到 class path 后來(lái)構(gòu)造 .class 文件的路徑心铃。

<path- two>\classes 是 class path,package 名字是 com.runoob.test,而編譯器和 JVM 會(huì)在 <path-two>\classes\com\runoob\test 中找 .class 文件挫剑。

一個(gè) class path 可能會(huì)包含好幾個(gè)路徑去扣,多路徑應(yīng)該用分隔符分開(kāi)。默認(rèn)情況下樊破,編譯器和 JVM 查找當(dāng)前目錄愉棱。JAR 文件按包含 Java 平臺(tái)相關(guān)的類(lèi),所以他們的目錄默認(rèn)放在了 class path 中哲戚。

設(shè)置 CLASSPATH 系統(tǒng)變量

用下面的命令顯示當(dāng)前的CLASSPATH變量:
  1. Windows 平臺(tái)(DOS 命令行下):C:> set CLASSPATH
  2. UNIX 平臺(tái)(Bourne shell 下):# echo $CLASSPATH
刪除當(dāng)前CLASSPATH變量?jī)?nèi)容:
  1. Windows 平臺(tái)(DOS 命令行下):C:> set CLASSPATH=
  2. UNIX 平臺(tái)(Bourne shell 下):# unset CLASSPATH; export CLASSPATH
設(shè)置CLASSPATH變量:
  1. Windows 平臺(tái)(DOS 命令行下): C:> set CLASSPATH=C:\users\jack\java\classes
  2. UNIX 平臺(tái)(Bourne shell 下):# CLASSPATH=/home/jack/java/classes; export CLASSPATH

文章摘自菜鳥(niǎo)教程http://www.runoob.com/java/java-tutorial.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末奔滑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子顺少,更是在濱河造成了極大的恐慌朋其,老刑警劉巖王浴,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異梅猿,居然都是意外死亡氓辣,警方通過(guò)查閱死者的電腦和手機(jī)隙畜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)邓嘹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)躏升,“玉大人怀估,你說(shuō)我怎么就攤上這事∠碛模” “怎么了猜欺?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵箱玷,是天一觀的道長(zhǎng)颖低。 經(jīng)常有香客問(wèn)我絮吵,道長(zhǎng),這世上最難降的妖魔是什么忱屑? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任蹬敲,我火速辦了婚禮,結(jié)果婚禮上想幻,老公的妹妹穿的比我還像新娘。我一直安慰自己话浇,他們只是感情好脏毯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著幔崖,像睡著了一般食店。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赏寇,一...
    開(kāi)封第一講書(shū)人閱讀 51,231評(píng)論 1 299
  • 那天吉嫩,我揣著相機(jī)與錄音,去河邊找鬼嗅定。 笑死自娩,一個(gè)胖子當(dāng)著我的面吹牛渠退,可吹牛的內(nèi)容都是我干的忙迁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼碎乃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼姊扔!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起梅誓,我...
    開(kāi)封第一講書(shū)人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤恰梢,失蹤者是張志新(化名)和其女友劉穎佛南,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體嵌言,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嗅回,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呀页。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妈拌。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蓬蝶,靈堂內(nèi)的尸體忽然破棺而出尘分,到底是詐尸還是另有隱情,我是刑警寧澤丸氛,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布培愁,位于F島的核電站,受9級(jí)特大地震影響缓窜,放射性物質(zhì)發(fā)生泄漏定续。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一禾锤、第九天 我趴在偏房一處隱蔽的房頂上張望私股。 院中可真熱鬧,春花似錦恩掷、人聲如沸倡鲸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)峭状。三九已至,卻和暖如春逼争,著一層夾襖步出監(jiān)牢的瞬間优床,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工誓焦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胆敞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓杂伟,卻偏偏與公主長(zhǎng)得像竿秆,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子稿壁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354