繼承
簡單來說就是利用已存在的類構造一個新類,構造的新類複用父類的方法和域。同時,還可以在此基礎上,添加自己的域和方法,這個已存在的類叫做父類(超類或基類)、新類稱爲孩子類(子類或派生類)。
繼承層次
由一個公共超類派生出來的所有類的集合被稱爲繼承層次,在繼承層次中,從某個特定的類到其祖先的路徑被稱爲該類的繼承鏈,通常一個祖先類可以擁有多個子孫繼承鏈。
特點:
- java只支持單繼承,不支持 多繼承。如類A只能有一個父類
- java支持多重繼承,即類B可以繼承A,類C又可以繼承B
注:在通過擴展超類定義子類時,應該將通用的方法放在超類中,而將具有特殊用途的方法放在子類中。
有以下兩個類(來自《java核心技術——卷一》):Empoyee(員工類)、Manager(經理類)。顯然,每一名經理是一名員工,經理類和員工類存在着is-a的關係。類似這種關係可以使用繼承,這樣可以提高代碼的複用性。
//員工類
class Employee
{
//實例域
private String name; //姓名
private double salary; //薪水
private int hireDay; //工齡
//構造器
public Employee()
{
System.out.println("父類無參構造器");
}
public Employee(String name,double salary,int hireDay)
{
this.name=name;
this.salary=salary;
this.hireDay=hireDay;
System.out.println("帶三個參數的父類構造器");
}
//成員方法
public void setSalary(double salary)
{
this.salary=salary;
}
public double getSalary()
{
return salary;
}
}
/*
定義子類的格式:
[修飾符] class 子類名 extends 父類名{
添加的域
添加的方法
}
*/
class Manager extends Employee
{
/*
由於類Manager繼承了類Employee,所以Manager類的對象就有四個域,即
父類的 name、salary、hireDay
子類的 bonus
*/
//添加的域
private double bonus; //獎金
//添加的方法
public void setBonus(double bonus)
{
this.bonus=bonus;
}
//構造器
public Manager()
{
System.out.println("這是子類的無參構造方法");
}
public Manager(String name,double salary,int hireDay,double bonus)
{
System.out.println("子類帶四個參數的構造器");
}
/*覆蓋方法:在父類中的一些方法對子類來說並不一定適合(也稱覆蓋方法)如
在Manager繼承了父類返回工資的方法getSalary(),但是,Manager類的對象工資還應該加上bonus。這時,就可以重寫父類的getSalary()方法。
*/
public double getSalary()
{
return bonus+super.getSalary();
}
}
方法重寫(又稱覆蓋方法):
如果子類出現了和父類一樣的方法聲明(方法名、返回類型和參數列表都相同),則稱子類重寫了父類的方法
在Manager類中,雖然繼承了父類返回工資的方法getSalary(),但是,Manager類的對象工資還應該加上bonus。父類的方法就不適合子類,此時就可以重寫父類的getSalary()方法。如下:
public double getSalary()
{
return bonus+salary;
}
但是上述的方法存在着問題,salary是父類Employee的私有域,只能被自身的方法訪問。如何解決這個問題呢?
- 把父類的實例域salary定義爲非私有的,但是這樣就破壞了Employee的封裝性。
- 調用父類的公有方法來獲得salary,下面的代碼就是採用這樣的方法
public double getSalary()
{
//super.方法(...) 表示調用父類的方法
return bonus+super.getSalary();
}
子類構造器和父類構造器
由於子類的構造方法不能訪問父類的私有域,所以必須利用父類的構造器對這部分私有域進行初始化。調用父類的構造器格式爲
super(....);
如果子類的構造器沒有顯示的調用父類的構造器,則系統會自動調用父類的無參構造。如果父類沒有無參構造器,編譯器將會報錯。
注意:super語句必須是構造方法的第一條語句。
//構造器
public Manager()
{
//super(); //隱含
System.out.println("這是子類的無參構造方法");
}
public Manager(String name,double salary,int hireDay,double bonus)
{
//super(); //隱含
System.out.println("子類帶四個參數的構造器");
}
public class ManagerTest
{
public static void main(String[] args)
{
Manager m1=new Manager();
Manager m2=new Manager("葉葉",3000,1000,4);
}
}
運行結果
final關鍵字
修飾變量:該變量一旦被賦值就不能被修改
- 變量是基本數據類型:一旦賦值就不能修改它的值
- 變量是引用數據類型:一旦引用對象就不能改變其引用的對象,但是可以改變其引用對象的實例域。(引用數據類型存儲的是對象在堆內存中的地址,一旦被final修飾,它存儲的地址就不能改變,但是它所引用的堆內存中飯對象的屬性可以改變)
注:可以將實例域定義爲final,但是這個final域必須在構造方法執行之後被初始化(實際必須在構造方法之前執行,因此通常在定義final變量時直接初始化),且在初始化後不能再對它進行修改
class Student
{
String name;
int age;
final int lag;
{
lag=2;
}
public Student(){
}
public Student(String name,int age)
{
this.name=name;
this.age=age;
//this.lag=2; //error:可能尚未初始化變量lag
}
public String toString()
{
return "名字:"+name+",年齡:"+age;
}
}
class FinalDemo
{
public static void main(String[] args)
{
//final修飾基本數據類型
final int x;
x=1;
System.out.println(x);
//x=45; //error:無法爲最終變量x分配值
//System.out.println(x);
System.out.println("----------------");
//fianl修飾引用數據類型
final Student s1=new Student("葉葉",18);
System.out.println(s1.toString());
s1.name="李**";
s1.age=12;
System.out.println(s1.toString());
//s1=new Studnet("花花",20); //error:無法爲最終變量s1分配值
System.out.println(s1.lag);
}
}
運行結果
修飾方法(final方法):表示該方法不能被覆蓋(或重寫)
修飾類(最終類、final類):阻止繼承,表示該類不允許創建子類,一旦一個類用final修飾,該類所有的方法自動的變爲final方法,但是該類的實例域並不改爲final變量
/*final //error:無法從最終A進行繼承*/ class A
{
public final int num=2;
public final void show() //error:B中的show()無法覆蓋A中的show()
{
}
}
class B extends A
{
public void show()
{
num=34;
System.out.println(num); //error:無法爲最終變量num分配值
}
}