Java中的繼承與多态
繼承和多态是Java語言面向對象程式設計的兩個特點。下面主要講述繼承和多态的概念和用法,以及super關鍵字的使用。同時進一步接受構造函數的用法。
1、繼承
由一個已定義的類中派生出一個新類,稱為繼承。利用繼承可以先建立一個公共類,這個類具有多個項目的共同屬性,然後一些具體的類繼承該類,同時再加上自己特有的屬性。
1)、超類和子類的關系
如果類A時從另一個類B中派生出來的,那麼稱A為子類或派生類、B為超類或父類。子類可以繼承父類所有的成員變量和方法,同時自己還可以定義自己的成員變量和方法。
如果要建立一個類的子類,隻需要在類聲明中加入extends子句:
Class test02 extends test01{}
下面是子類test02繼承了父類test01,并在測試類test03中調用父類的變量和方法:
public class test01{
String userName;
int userId;
int userAge;
String userAddress;
void print()
{
System.out.println("姓名:" + userName + "ID号:" + userId + "年齡:" + userAge + "住址:" + userAddress);
}
}
public class test02 extends test01{
boolean userSex;
void print1()
{
if (userSex) {
System.out.println("性别:" + "男");
}
else
{
System.out.println("性别:" + "女");
}
}
}
public class test03 {
public static void main(String[] args)
{
test01 t1 = new test01();
test02 t2 = new test02();
t1.userName = "小武靈靈";
t1.userId = 41009161;
t1.userAge = 22;
t1.userAddress = "陝西";
System.out.println("超類輸出為:");
t1.print();
t2.userName = "靈靈小武";
t2.userId = 16190014;
t2.userAge = 22;
t2.userAddress = "西陝";
t2.userSex = true;
System.out.println("子類輸出為:");
t2.print();
t2.print1();
}
}
2)、成員變量能否被繼承
子類會繼承其父類的所有的成員變量和成員方法,但是在其父類中被聲明為private的成員變量和方法不能被繼承。大家可以将上面的test01中的int userId;改為private int userId,可以看到test03中t2.userId = 16190014;這句會提示錯誤。
3)、對象在超類中與子類中的使用
假如有兩個類:類A是類B的子類。C是類A的一個引用,它不但可以指向類A的對象,而且可以指向類B的對象。當C指向類B的對象時,它可以調用類B從類A中繼承的方法或成員變量,但是不能調用類B中新增的成員變量或方法,下面是一個子類對象調用父類對象方法的執行個體:
public static void main(String[] args)
{
//父類的引用指向子類的對象
test01 t1 = new test02();
//建立類test02的一個對象
test02 t2 = new test02();
t1.userName = "小武靈靈";
t1.userId = 1001;
t1.userAge = 22;
t1.userAddress = "陝西";
System.out.println("這是一個父類的引用指向子類的對象");
t1.print();
System.out.println("這是子類特有的屬性和方法:");
t2.userSex = true;
//調用子類特有的方法
t2.print1();
}
2、父類
在子類中經常使用父類的成員變量,或在重寫的方法中使用父類中被重寫的方法,這時就要通路父類的成員變量或調用父類的方法。Java中通過使用super關鍵字來實作對父類的通路。
Super的用途主要有二:調用父類的構造函數;用來通路被子類的成員隐藏(重定義)的超類成員。
1)、調用父類的構造函數
子類通過在方法中使用super(參數)調用父類的構造函數。其中參數指父類的構造函數所需的參數。同時在子類中super()作為第一句執行。下面是一個使用super方法調用父類的構造方法的執行個體:
public class test01{
String bookName;
int bookId;
int bookPrice;
public test01() { //設定預設值
bookName = "戲遊記";
bookId = 1001;
bookPrice = 20;
}
public test01(test01 t)//對象作為構造函數的參數
{
bookName = t.bookName;
bookId = t.bookId;
bookPrice = t.bookPrice;
}
//初始化對象
public test01(String bookName, int bookId, int bookPrice) {
this.bookName = bookName;
this.bookId = bookId;
this.bookPrice = bookPrice;
}
public void print()
{
System.out.println("書名:" + bookName + " 序号: " + bookId + " 價格:" + bookPrice);
}
}
public class test02 extends test01{
String BookBadder;
public test02() {//設定預設值
super();
BookBadder = "科學出版社";
}
public test02(test02 t) { //對象作為構造函數的參數
super(t);
BookBadder = t.BookBadder;
}
//初始化對象
public test02(String bookName , int bookId , int bookPrice , String bookBadder) {
super(bookName , bookId , bookPrice);
BookBadder = bookBadder;
}
}
public class test03 {
public static void main(String[] args)
{
test02 t1 = new test02();//執行個體化一個對象
test02 t2 = new test02("三國演義", 1002, 10, "山東教育出版社");
test02 t3 = new test02(t2);
System.out.println(t1.BookBadder);
t1.print();
System.out.println(t2.BookBadder);
t2.print();
t3.print();
t3.BookBadder = "中國國際出版集團";
System.out.println(t3.BookBadder);
t3.print();
}
}
在Java中,有時還會遇到子類中的成員變量或方法與父類中的成員變量或方法同名。在下面會用一個預設的方法,當一個子類中沒有使用super時,系統會自動調用父類中的預設的無參構造函數。下面是一個系統自動調用父類中的無參構造方法的執行個體:
class test01
{
public test01() {
System.out.println("這是一個父類");
}
}
class test02 extends test01
{
public test02() {
System.out.println("test02繼承了父類test01");
}
}
public class test03 extends test02
{
public test03() {
System.out.println("test03繼承了test02");
}
public static void main(String[] args) {
new test03();
}
}
子類不能繼承父類的構造函數,隻能使用super關鍵字在子類的構造函數中調用父類的構造函數。在Java中每一個構造函數都必須調用父類的構造函數作為第一條語句,當子類的構造函數中沒有明确地使用super關鍵字時,那麼系統會自動調用父類中預設的構造函數。
2)、通路超類的成員變量及方法
通過super()也可以通路超類的成員變量和方法,其今本格式為:
Super.[方法或成員變量];
這種形式多用于子類在隐藏了父類的構造函數或重寫了父類的方法的情況。下面的執行個體将通過super變量給父類的變量指派:
class test01
{
int a;
int b;
}
class test02 extends test01
{
int a , b;
public test02(int x, int y , int z , int q) {
//使用super調用父類中被子類隐藏的成員
super.a = x;
super.b = y;
//初始化子類中的成員
a = z;
b = q;
}
void print()
{
System.out.println(super.a + "");
System.out.println(super.b + "");
System.out.println(a + "");
System.out.println(b + "");
}
}
public class test03
{
public static void main(String[] args) {
test02 test02 = new test02(1, 2, 3, 4);
test02.print();
}
}
3)、多層次的繼承
使用extends可以建立任意多層繼承關系的類層次結構。我們已使用了由一個超類和子類組成的簡單類層次結構。下面是一個多層次繼承的執行個體,為了讓大家更好地了解繼承,我在關鍵位置加入了輸出:
class test01
{
String bookName;
int bookId;
int bookPrice;
public test01() {
System.out.println("1");
bookName = "戲遊記";
bookId = 1001;
bookPrice = 20;
System.out.println("2");
}
test01(test01 t)
{
System.out.println("3");
bookName = t.bookName;
bookId = t.bookId;
bookPrice = t.bookPrice;
System.out.println("4");
}
test01(String name , int id , int price)
{
System.out.println("5");
bookName = name;
bookId = id;
bookPrice = price;
System.out.println("6");
}
void print() {
System.out.println("書名:" + bookName + " 序号:" + bookId + "價格:" + bookPrice);
}
}
class test02 extends test01
{
String bookBadder;
public test02() {
super();
System.out.println("8");
bookBadder = "科學出版社";
}
test02(test02 t)
{
super(t);
System.out.println("9");
bookBadder = t.bookBadder;
}
test02(String x , int y , int z , String q)
{
super(x, y , z);
System.out.println("10");
bookBadder = q;
}
}
class Factory extends test02
{
String factory;
public Factory() {
super();
System.out.println("11");
factory = "達成印刷廠";
}
Factory(Factory f)
{
super(f);
System.out.println("12");
factory = f.factory;
}
Factory(String x , int y , int z , String q , String p)
{
super(x, y , z , q);
System.out.println("13");
factory = p;
}
}
public class test03
{
public static void main(String[] args) {
System.out.println("14");
Factory f1 = new Factory();
System.out.println("15");
Factory f2 = new Factory("三國演義" , 1002 , 42 , "希望出版社" , "曙光印刷廠");
System.out.println("16");
Factory f3 = new Factory(f2);
System.out.println("17");
System.out.println(f1.bookBadder);
System.out.println(f1.factory);
f1.print();
System.out.println(f2.bookBadder);
System.out.println(f2.factory);
f2.print();
f3.print();
}
}
3、重載
很多人認為重載是重寫的“兄弟“,大部分書上也是将重寫和重載放在一起講解,不過大家一定要知道重寫和重載是完全不同的兩樣東西。重載和重寫有一個很相似的地方,就是他們都展現了Java的優越性。重載大大減輕了程式員的負擔,讓程式員不再需要記住那麼多的方法名稱。
1)、重載的定義:
同一個類中兩個或多個方法可以共享一個相同的名稱,隻要它們的參數不同即可,這就是重載的概念。重載也是多态性的方法之一。下面是一個簡單的print方法重載:
class Factory
{
String name;
int age;
void print()
{
System.out.println("姓名為:" + name + " 年齡為:" + age);
}
void print(String a , int b)//重載方法print
{
System.out.println("姓名為:" + a + " 年齡為" + b);
}
void print(String a , int b , int c)
{
System.out.println("姓名為:" + a + " 年齡為:" + b + "ID号為:" + c);
}
void print(String a , int b , double c)
{
System.out.println("姓名為:" + a + " 年齡為:" + b + " ID号為:" + c);
}
}
public class test03
{
public static void main(String[] args) {
Factory factory = new Factory();
factory.name = "小武靈靈";
factory.age = 22;
factory.print();
factory.print("小武", 22);
factory.print("小武靈", 22, 1001);
factory.print("靈靈小武" , 22 , 1002);
}
}
2)、重載規則
在調用重載方法時,确定要調用哪個參數是基于其參數的。如果是int參數調用該方法,則調用帶int的重載方法。如果是double參數調用該方法,則調用帶double的重載方法。總之參數決定重載方法的調用。
4、重寫
1)、重寫的定義
重寫是展現Java優越性的地方之一。重寫是建立在Java繼承關系中的,它使得Java的語言結構更加豐富。由于父類的方法不能滿足新的要求,是以需要在子類中修改從父類中繼承的方法,同時還可以定義自己的方法,這就是重寫的概念。通過下面的執行個體可以具體滴了解重寫的過程:
class Factory
{
void print()
{
System.out.println("這是父類的方法");
}
}
public class test03 extends Factory
{
public static void main(String[] args) {
test03 test03 = new test03();
test03.print();
}
void print()
{
System.out.println("這是一個子類重寫後的方法");
}
}
重寫的目的有二:修改父類中的方法;對父類的方法進行擴充或在子類中定義具體的、特定的行為;下面的代碼程式是以一個學校的名稱、人數為例,編寫了一個重寫的類中的方法,子類重寫父類中的方法主要就是為了可以修改父類中的方法:
class test01
{
String name;
int id , number;
void print()
{
System.out.println("學校名為:" + name + " 序号為:" + id + " 人數為:" + number);
}
public test01(String name , int id , int number) {
this.name = name;
this.id = id;
this.number = number;
}
}
class test02 extends test01
{
String adder;
public test02(String name, int id, int number , String adder) {
super(name, id, number);
this.adder = adder;
}
void print()
{
System.out.println("學校名為:" + name + " 學号為:" + id + "人數為:" + number
+ " 位址: " + adder);
}
}
public class test03 {
public static void main(String[] args)
{
test02 t = new test02("陝西師範大學", 1001, 1002, "師大路");
t.print();
}
}
2)、重寫規則
超類中的方法并不是在任何情況下都可以重寫的。當父類中的方法的通路控制修飾符為private時,該方法隻能被自己的類所通路,不能被外部的類所通路,是以在子類中不能重寫該方法。如果定義父類中的方法的通路控制修飾符為public,在子類中該方法被定義為private,在編譯時仍然報錯,因為在Java中規定重寫方法不能比被重寫方法有着更嚴格的通路權限。在子類中不可以重寫父類中用private修飾的方法。具體執行個體可以将上面的代碼test01中print()方法前加入private。
對于從父類繼承的抽象方法,子類沒有任何選擇,必須設計該方法,除非該子類也是抽象的。父類的抽象方法必須被其非抽象的子類具體實作,是以抽象方法是必須重寫的方法。下面是一個子類實作父類中未定義的抽象方法的執行個體:
abstract class test01
{
int a , b;
public test01(int x , int y) {
a = x;
b = y;
}
abstract void print();//定義了一個抽象方法
}
abstract class test02 extends test01
{
int c;
public test02(int x, int y , int z) {
super(x, y);
c = z;
}
abstract void print();
}
class test03 extends test02
{
int d;
public test03(int x, int y, int z , int h) {
super(x, y, z);
d = h;
}
void print()//重寫抽象方法
{
System.out.println(a + b + c + d);
}
}
public class test {
public static void main(String[] args)
{
test03 t = new test03(1, 2, 3, 4);
t.print();
}
}
支援多态性是重寫的另一個方面,多态性是面向對象程式設計的特性之一,一方面所有的子類繼承了它們父類所有的元素,另一方面所有的子類也可以靈活定義自己的方法。重寫是Java實作多态性的”一種接口,多種方法”的一種方法。下面是執行個體示範多個子類都可以對父類中的方法進行重寫:
abstract class test01
{
int a , b;
public test01(int x , int y) {
a = x;
b = y;
}
abstract void print();//定義了一個抽象方法
}
class test02 extends test01
{
int c;
public test02(int x, int y , int z) {
super(x, y);
c = z;
}
void print() {
System.out.println(a + b + c);
}
}
class test03 extends test01
{
int d;
public test03(int x, int y, int z) {
super(x, y);
d = z;
}
void print()//重寫抽象方法
{
System.out.println(a*b*d);
}
}
public class test {
public static void main(String[] args)
{
test02 t2 = new test02(1, 2, 3);
test03 t3 = new test03(1, 2, 3);
t2.print();
t3.print();
}
}
在一個代碼中,有時會同時遇到重寫和重載,重寫是指在子類中修改從父類中繼承的方法,同時還可以定義自己的方法。而重載是指在一個子類中具有相同方法名而參數卻不同。由于兩者都規定必須有相同的方法名,是以有時特别容易混淆。下面是一個重寫與重載并存的執行個體:
class test01
{
int a , b;
public test01(int x , int y) {
a = x;
b = y;
}
int print()
{
return a + b;
}
int print(int x , int y)//方法重載
{
return x + y;
}
}
class test02 extends test01
{
public test02(int x, int y) {
super(x, y);
}
int print()//重寫方法print()
{
return a;
}
double print(int a , double b) //test02定義了一個自己的方法
{
return a + b;
}
}
public class test {
public static void main(String[] args)
{
test01 t1 = new test01(1, 2);
test02 t2 = new test02(2, 4);
System.out.println(t1.print());
System.out.println(t1.print(2, 3));
System.out.println(t2.print());
System.out.println(t2.print(3, 4));
System.out.println(t2.print(5, 1.23234));
}
}