本博文將講述類的繼承和多態的相關情況。
在面向對象程序設計中,可以從已有的類派生出新類,這稱爲繼承。繼承是Java在軟件重用方面一個重要且功能強大的特徵。假設要定義一個類,對圓、矩形和三角形建模。這些類有很多特性。設計這些類來避免冗餘並使系統更易於理解和維護的最好辦法就是繼承。
在面嚮對象語言中,接口的多種不同的實現方式即爲多態。多態性是允許將父對象設置成爲和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。歸結爲一句話:允許將子類類型賦值給父類類型,這有別與C++中的多態(通過虛函數實現)。
範例一:繼承多態基本概念用法練習。
運行效果如圖:
其實現的源代碼如下所示:(其中包括自定義一個堆棧類MyStack的實現)
package Blog;
import java.util.Date;
public class blogTryProject{
public static void main(String[] args) {
//父類與子類
TestCircleRectangle tcr = new TestCircleRectangle();
tcr.main(args);
//動態綁定
System.out.println("\n動態綁定實例演示");
m(new GraduateStudent());
m(new Student());
m(new Person());
m(new Object());
//繼承與多態
System.out.println("\n多態和類型轉換演示");
CastingDemo cd = new CastingDemo();
cd.main(args);
//ArrayList數組線性表
System.out.println("\nArrayList數組線性表");
TestArrayList tal = new TestArrayList();
tal.main(args);
}
public static void m(Object x){
System.out.println(x.toString());
}
}
//MyStack自定義棧類
class MyStack{
private java.util.ArrayList list = new java.util.ArrayList();
public boolean isEmpty(){
return list.isEmpty();
}
public int getSize(){
return list.size();
}
public Object peek(){
return list.get(getSize() - 1);
}
public Object pop(){
Object o = list.get(getSize() - 1);
list.remove(getSize() - 1);
return o;
}
public void push(Object o){
list.add(o);
}
public int search(Object o){
return list.lastIndexOf(o);
}
public String toString(){
return "Stack: "+list.toString();
}
}
//ArrayList數組線性表
class TestArrayList{
public static void main(String[]args){
java.util.ArrayList cityList = new java.util.ArrayList();
cityList.add("London");
cityList.add("Denver");
cityList.add("Paris");
cityList.add("Miami");
cityList.add("Seoul");
cityList.add("Tokyo");
System.out.println("List size? "+cityList.size());
System.out.println("Is Miami in the list ? "+cityList.contains("Miami"));
System.out.println("The location fo Denver in the list ?"+cityList.indexOf("Denver"));
System.out.println("Is the list empty? "+cityList.isEmpty());
cityList.add(2, "Xian");
cityList.remove("Miami");
cityList.remove(1);
System.out.println(cityList.toString());
for(int i = cityList.size() - 1;i >= 0;i--)
System.out.print(cityList.get(i)+" ");
System.out.println();
java.util.ArrayList list = new java.util.ArrayList();
list.add(new Circle1(2));
list.add(new Circle1(3));
System.out.println("The area of the circle? "+((Circle1)list.get(0)).getArea());
}
}
//多態和類型轉換
class CastingDemo{
public static void main(String[]args){
Object object1 = new Circle1(1);
Object object2 = new Rectangle1(1,1);
displayObject(object1);
displayObject(object2);
}
public static void displayObject(Object object){
if(object instanceof Circle1){
System.out.println("The circle diameter is "+((Circle1)object).getDiameter());
System.out.println("The circle area is "+((Circle1)object).getArea());
}
else if(object instanceof Rectangle1){
System.out.println("The rectangle area is "+((Rectangle1)object).getArea());
}
}
}
//動態綁定實例
class DynamicBindingDemo{
public static void main(String[]args){
}
}
class GraduateStudent extends Student{
}
class Student extends Person{
public String toString(){
return "Student";
}
}
class Person extends Object{
public String toString(){
return "Person";
}
}
//父類子類繼承及其使用
class TestCircleRectangle{
public static void main(String[]args){
System.out.println("父類子類繼承及其使用");
Circle1 circle = new Circle1(5);
System.out.println("A circle "+circle.toString());
System.out.println("The radius is "+circle.getRadius());
System.out.println("The area is "+circle.getArea());
System.out.println("The diameter is "+circle.getDiameter());
Rectangle1 rectangle = new Rectangle1(2,4,"Yellow",true);
System.out.println("\nA rectangle "+rectangle.toString());
System.out.println("The area is "+rectangle.getArea());
System.out.println("The perimeter is "+rectangle.getPerimeter());
}
}
class GeometricObject1{
private String color = "White";
private boolean filled;
private java.util.Date dateCreated;
public GeometricObject1(){
dateCreated = new java.util.Date();
}
public GeometricObject1(String color,boolean filled){
dateCreated = new java.util.Date();
this.color = color;
this.filled = filled;
}
public String getColor(){
return color;
}
public void setColor(String color){
this.color = color;
}
public void setFilled(boolean filled){
this.filled = filled;
}
public java.util.Date getDateCreated(){
return dateCreated;
}
public String toString(){
return "created on "+dateCreated+"\ncolor: "+color+"\t\tfilled: "+filled;
}
}
class Circle1 extends GeometricObject1{
private double radius;
public Circle1(){
}
public Circle1(double radius){
this.radius = radius;
}
public Circle1(double radius,String color,boolean filled){
this.radius = radius;
setColor(color);//這裏不能用this.color = color;因爲它是父類的私有數據域,在GeometricObject
//類外只能通過訪問器和修改器對它進行操作
setFilled(filled);
}
public double getRadius(){
return radius;
}
public void setRadius(double radius){
this.radius = radius;
}
public double getArea(){
return radius * radius * Math.PI;
}
public double getDiameter(){
return 2 * radius;
}
public void printCircle(){
System.out.println("The circle is created "+getDateCreated()+
" and the radius is "+radius);
}
}
class Rectangle1 extends GeometricObject1{
private double width;
private double height;
public Rectangle1(){
}
public Rectangle1(double width,double height){
this.width = width;
this.height = height;
}
public Rectangle1(double width,double height,String color,boolean filled){
this.width = width;
this.height = height;
setColor(color);
setFilled(filled);
}
public double getWidth(){
return width;
}
public void setWidth(double width){
this.width = width;
}
public double getHeight(){
return height;
}
public void setHeight(double height){
this.height = height;
}
public double getArea(){
return width * height;
}
public double getPerimeter(){
return 2 * (width + height);
}
}
範例二:多重繼承的實現。Person類,有Student類和Employee類繼承Person,而Employee類又被教員類Faculty類和職員類Staff類繼承。每個人都有姓名、地址、電話號碼和電子郵件地址。學生有年級狀態(大一到大四)。一個僱員有辦公室、工資和受聘日期。教員有辦公時間和級別。職員有職務稱號。覆蓋每個類的toString()方法,顯示相應的類名和人名。編寫一個測試程序,創建Person、Student、Employee、Faculty和Staff,並且調用它們的toString()方法。
運行效果如圖所示:
package Blog;
import java.util.Date;
public class blogTryProject{
public static void main(String[] args) {
TestPerson person = new TestPerson();
person.main(args);
}
}
//多重繼承的練習
class TestPerson{
public static void main(String[]args){
Person1 person = new Person1();
System.out.println(person.toString());
Student1 st = new Student1();
System.out.println(st.toString());
Employee employee = new Employee();
System.out.println(employee.toString());
Faculty faculty = new Faculty();
System.out.println(faculty.toString());
}
}
class Person1{
private String name;
private String address;
private String phone;
private String addressOfMessage;
public Person1(){
this("公安局警官","上海市長寧路","110","[email protected]");
}
public Person1(String name,String address,String phone,String addressOfMessage){
this.name = name;
this.address = address;
this.phone = phone;
this.addressOfMessage = addressOfMessage;
}
public String getName(){
return this.name;
}
public String getAddress(){
return this.address;
}
public String getPhone(){
return this.phone;
}
public String getAddressOfMessage(){
return this.addressOfMessage;
}
public String toString(){
return "\n此人的基本信息如下\n姓名:"+this.getName()+"\n地址: "+this.getAddress()+
"\n電話:"+this.getPhone()+"\n郵箱:"+this.getAddressOfMessage()+"\n";
}
}
class Student1 extends Person1{
private String status;
public Student1(){
this("大一","鏈條哥","上海市浦東新區海科路","13977998876","[email protected]");
}
public Student1(String status,String name,String address,
String phone,String addressOfMessage){
super(name,address,phone,addressOfMessage);
this.status = status;
}
public String getStatus(){
return this.status;
}
public String toString(){
return super.toString()+"學生\n年紀爲:"+this.getStatus()+"\n";
}
}
class Employee extends Person1{
private String office;
private String salary;
private java.util.Date dateOfEmployeed;
public Employee(){
this("浩哥","上海市浦東新區99號","13944533343","[email protected]",
"小衛星502室","工資8000rmb",new java.util.Date());
}
public Employee(String name,String address,String phone,String addressOfMessage,
String office,String salary,java.util.Date dateOfEmployeed){
super(name,address,phone,addressOfMessage);
this.office = office;
this.salary = salary;
this.dateOfEmployeed = dateOfEmployeed;
}
public String getOffice(){
return this.office;
}
public String getSalary(){
return this.salary;
}
public java.util.Date getDateOfEmployeed(){
return this.dateOfEmployeed;
}
public String toString(){
return super.toString()+"辦公室:"+this.getOffice()+
"\n薪水:"+this.getSalary()+"\n受聘日期:"+this.getDateOfEmployeed();
}
}
class Faculty extends Employee{
private String time;
private String status;
public Faculty(){
this("老三","上海市浦東新區100號","13387654563","[email protected]",
"小衛星502室","工資8000rmb",new java.util.Date(),
"週一到週五上午9點到下午5點","導航室工程師");
}
public Faculty(String name,String address,String phone,String addressOfMessage,
String office,String salary,java.util.Date dateOfEmployeed,
String time,String status){
super(name,address,phone,addressOfMessage,office,salary,dateOfEmployeed);
this.time = time;
this.status = status;
}
public String getTime(){
return this.time;
}
public String getStatus(){
return this.status;
}
public String toString(){
return super.toString()+"\n職員\n工作時間:"+this.getTime()+
"\n級別:"+this.getStatus();
}
}
範例三:數組線性表ArrayList的是要用及實現。
其UML類圖如下所示:
運行效果如圖所示:第一張爲使用效果圖;第二張爲實現效果圖。
其實現的源代碼如下所示:
ArrayList的使用
package Blog;
import java.util.Date;
import java.util.ArrayList;
public class blogTryProject{
public static void main(String[] args) {
MyArrayList mal = new MyArrayList();
mal.main(args);
}
}
//數組線性表ArrayList範例
class MyArrayList{
public static void main(String[]args){
System.out.println("數組線性表ArrayList使用範例");
ArrayList list = new ArrayList();
MyArrayList tal1 = new MyArrayList();
MyArrayList tal2 = new MyArrayList();
MyArrayList tal3 = new MyArrayList();
MyArrayList tal4 = new MyArrayList();
MyArrayList tal5 = new MyArrayList();
list.add(tal1);
list.add(tal2);
list.add(tal3);
list.add("I Love Java");
System.out.println("\n線性表的長度爲:"+list.size());
System.out.println("線性表所包含的元素爲:");
for(int i = 0;i < list.size();i++){
System.out.println(list.get(i));
}
list.remove(0);
System.out.println("\n刪除第一個元素後新的list爲:");
for(int i = 0;i < list.size();i++){
System.out.println(list.get(i));
}
list.add(0,tal4);
System.out.println("\n在第一個位置添加新元素後新的list爲:");
for(int i = 0;i < list.size();i++){
System.out.println(list.get(i));
}
list.set(0, tal4);
System.out.println("\n在第一個位置設置新元素後新的list爲:");
for(int i = 0;i < list.size();i++){
System.out.println(list.get(i));
}
System.out.println("\nlist中是否包含d? "+list.contains(tal2));
System.out.println("\nlist中是否包含d? "+list.contains(tal5));
}
}
ArrayList的實現
package Blog;
import java.util.Date;
import java.util.ArrayList;
public class blogTryProject{
public static void main(String[] args) {
TestArrayListOfDesign tal = new TestArrayListOfDesign();
tal.main(args);
}
}
class TestArrayListOfDesign{
public static void main(String[]args){
System.out.println("ArrayList的實現範例");
NewCourse course = new NewCourse("English");
java.util.Date date = new java.util.Date();
String s = "I Love Java";
javax.swing.JFrame frame = new javax.swing.JFrame();
Person person = new Person();
ArrayListOfDesign list = new ArrayListOfDesign();
list.add(course);
list.add(date);
list.add(s);
list.add(frame);
list.add(person);
System.out.println("元素對象個數爲:"+list.size());
for(int i = 0;i < list.size();i++)
System.out.println(list.get(i).toString());
list.remove(0);
System.out.println("移除掉第一個元素後,list中的元素爲:");
for(int i = 0;i < list.size();i++)
System.out.println(list.get(i).toString());
System.out.println("\"I Love Java\"在第"+list.indexOf("I Love Java")+"個位置上");
list.set(list.indexOf("I Love Java"), "I Love Java and C++");
System.out.println("將I Love Java 設置爲 I Love Java and C++後list中的元素爲:");
for(int i = 0;i < list.size();i++)
System.out.println(list.get(i).toString());
list.clear();
System.out.print("清除之後,元素個數爲:");
for(int i = 0;i < list.size();i++)
System.out.println(list.get(i).toString());
System.out.println(list.size());
}
}
//ArrayList的實現
class ArrayListOfDesign{
private int numberOfCount;
private Object[] list;
ArrayListOfDesign(){
this.list = new Object[10];
}
public void add(Object o){
if(numberOfCount > numberOfCount){
Object[] newList = new Object[numberOfCount * 2];
System.arraycopy(list, 0, newList, 0, numberOfCount);
list = newList;
}
list[numberOfCount++] = o;
}
public void add(int index,Object o){
Object temp = new Object();
numberOfCount++;
for(int i = numberOfCount - 1;i >= index + 1;i--){
list[i] = list[i - 1];
}
list[index] = temp;
}
public void clear(){
Object obj = new Object();
for(int i = numberOfCount - 1;i >= 0;i--){
list[i] = obj;
numberOfCount--;
}
}
public boolean contains(Object o){
boolean flag = false;
for(int i = 0;i < numberOfCount;i++){
if(list[i] == o){
flag = true;
break;
}
}
return flag;
}
public Object get(int index){
return list[index];
}
public int indexOf(Object o){
int index = -1;
for(int i = 0;i < numberOfCount;i++){
if(list[i] == o){
index = i;
break;
}
}
return index;
}
public boolean isEmpty(){
if(numberOfCount != 0)
return true;
else
return false;
}
public int lastIndexOf(Object o){
int index = -1;
for(int i = numberOfCount - 1;i >= 0;i--){
if(list[i] == o){
index = i;
break;
}
}
return index;
}
public void remove(Object o){
Object obj = new Object();
for(int i = 0;i < numberOfCount;i++){
if(list[i] == o){
if(i == numberOfCount - 1){
list[i] = obj;
numberOfCount--;
}
else{
for(int j = i;j < numberOfCount - 1;j++){
list[j] = list[j+1];
}
list[numberOfCount - 1] = obj;
numberOfCount--;
}
}
}
}
public int size(){
return numberOfCount;
}
public void remove(int index){
Object obj = new Object();
for(int i = index;i < numberOfCount;i++)
list[i] = list[i+1];
list[numberOfCount - 1] = obj;
numberOfCount--;
}
public void set(int index,Object o){
list[index] = o;
}
}
//選課系統類的實現
class NewCourse{
private String courseName;
private ArrayList studentList = new ArrayList();
public NewCourse(String courseName){
this.courseName = courseName;
}
public void addStudent(String student){
this.studentList.add(student);
}
public ArrayList getStudents(){
return this.studentList;
}
public int getNumberOfStudents(){
return this.studentList.size();
}
public String getCourseName(){
return this.courseName;
}
public void dropStudent(String student){
int index = this.studentList.indexOf(student);
this.studentList.remove(index);
}
public void clear(String courseName){
this.studentList.clear();
}
}
//Person類的實現
class Person{
private String name;
private String address;
private String phone;
private String addressOfMessage;
public Person(){
this("公安局警官","上海市長寧路","110","[email protected]");
}
public Person(String name,String address,String phone,String addressOfMessage){
this.name = name;
this.address = address;
this.phone = phone;
this.addressOfMessage = addressOfMessage;
}
public String getName(){
return this.name;
}
public String getAddress(){
return this.address;
}
public String getPhone(){
return this.phone;
}
public String getAddressOfMessage(){
return this.addressOfMessage;
}
public String toString(){
return "\n此人的基本信息如下\n姓名:"+this.getName()+"\n地址: "+this.getAddress()+
"\n電話:"+this.getPhone()+"\n郵箱:"+this.getAddressOfMessage()+"\n";
}
}
範例四:升級銀行賬戶類Acount(《Java基本功練習十五》中的ATM機模擬中的賬戶類Acount)。
升級的要求如下:
1)添加一個String類型的name存儲客戶的名字;
2)添加一個新的構造方法,該方法創建一個帶指定名字、id和收支額的賬戶;
3)添加一個名爲transaction的新數據域,它的類型爲ArrayList,可以爲賬戶存儲交易。每筆交易就是一個 Transaction類的實例。Transaction類的定義如下圖所示。
4)修改withdraw和deposit方法,向transactions數組線性表添加一筆交易。
編寫一個測試程序,創建一個年利率爲1.5%,收支額爲1000,id爲1122,而名字爲George的Acount。向該賬戶存入30,40,50,並從賬戶取出5,4,3圓。打印賬戶清單,顯示賬戶持有者名字、收支額和所有的交易。
Transaction的UML類圖:
運行效果如圖所示:
實現的源代碼如下所示:
package Blog;
import java.util.Date;
import java.util.ArrayList;
public class blogTryProject{
public static void main(String[] args) {
TestAcount ta = new TestAcount();
ta.main(args);
}
}
//TestAcount的測試程序
class TestAcount{
public static void main(String[]args){
Acount a = new Acount();
long time = System.currentTimeMillis();
a.deposit('D', 30,time);//模擬存取款時的時間
a.deposit('D', 40,time + 100000000);
a.deposit('D', 50,time + 200000000);
a.withDraw('W', 5,time + 400000000);
a.withDraw('W', 4,time + 800000000);
a.withDraw('W', 3,time + 1600000000);
a.printJiLu();
System.out.println("賬戶創建日期爲:"+a.getDateCreated());
}
}
//Acount類的升級
class Acount{
private String name;
private int id;
protected double balance;
private double annualInterestRate;
private Date dateCreated;
private ArrayList jiaoyijilu = new ArrayList();
Acount(){
this("George",1122,1000,1.5);
}
Acount(String name,int id,double balance,double annualInterestRate){
this.name = name;
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
dateCreated = new Date();
}
public int getId(){
return id;
}
public void setId(int newID){
id = newID;
}
public double getBalance(){
return balance;
}
public void setBalance(double newBalance){
balance = newBalance;
}
public double getAnnualInterestRate(){
return annualInterestRate;
}
public void setAnnualInterestRate(double newRate){
annualInterestRate = newRate;
}
public String getDateCreated(){
return dateCreated.toString();
}
public double getMonthlyInterestRate(){
return annualInterestRate / 12.0;
}
public void withDraw(char type,double amount,long elapstime){
Date date = new Date(elapstime);
Transaction actions = new Transaction(type,amount,balance,date);
jiaoyijilu.add(actions);
balance = actions.getNewBalance();
}
public void deposit(char type,double amount,long elapstime){
Date date = new Date(elapstime);
Transaction actions = new Transaction(type,amount,balance,date);
jiaoyijilu.add(actions);
balance = actions.getNewBalance();
}
public void printJiLu(){
System.out.println("名爲:"+name+" ID: "+id+" 的賬戶交易記錄如下:");
for(int i = 0;i < jiaoyijilu.size();i++){
System.out.println(jiaoyijilu.get(i).toString());
}
}
}
//交易記錄類
class Transaction extends Acount{
private char type;
private double amount;
private double newBalance;
private String description;
public Transaction(char type,double amount,double balance,Date date){
if(type == 'W'){
newBalance = balance;
newBalance -= amount;
description = date.toString()+" 取款 "+amount+" 可用餘額爲:"+newBalance;
}
else if(type == 'D'){
newBalance = balance;
newBalance += amount;
description = date.toString()+" 存款 "+amount+" 可用餘額爲:"+newBalance;
}
}
public double getNewBalance(){
return newBalance;
}
public String toString(){
return description;
}
}
總結:
a.繼承的注意點
1)和傳統理解上相反,子類並不是父類的一個子集。實際上一個子類通常比它的父類包含更多的信息 和方法;
2)父類中的私有數據域在該類之外是不可訪問的。因此,不能在子類中直接使用它們。但是,如果父類中 定義 了公共的訪問/修改器,那麼可以通過set和get方法修改和訪問它們;
3)不是所有的是關係(is-a)都該用繼承來建模的。如果要用B類去擴展類A,那麼A應該要比B包含更多的信息;
4)繼承是用來爲是(is-a)關係建模的。不要僅僅爲了重用方法這個原因而盲目的擴展一個類。一個父類和它的子 類之間必須存在是關係;
5)某些程序設計語言允許從幾個類派生出一個子類的。這種能力稱爲多重繼承。但在Java中,不允許多重 繼承。一個Java類只可能直接繼承一個父類。這中限制稱爲單一繼承。如果使用extends關鍵字來定義一個子 類,它只允許有一個父類。然後,多重繼承是可以通過接口來實現的,b.super關鍵字
子類繼承它的父類所有可訪問的數據域和方法,同樣可以用super關鍵字繼承其構造方法和方法。
c.覆蓋和重載的區別
重載方法意味着可以定義多個同名的方法,但這些方法具有不同的簽名。覆蓋方法意味着爲子類中的方法提供一個全新的實現。該方法已經在父類中定義。
爲了覆蓋方法,這個方法必須使用相同的簽名以及相同的返回值類型在子類中進行定義。d.可見性修飾符的使用
關鍵字private和public表示是否可以從類外訪問這些數據域。私有成員只能在類內訪問,而共有成員可以被其他類訪問。
如果允許子類訪問定義在父類中的數據域和方法,但不允許非子類訪問這些數據域和方法。可以使用關鍵字protected完成該功能。父類中被保護的數據域或方法可以再它的子類中訪問。
它們的可見性可有下表做一個簡潔的歸納顯示。
1)如果不像從類外使用類的成員,就把成員聲明爲private;
2)如果想讓該類的用戶都能使用類的成員,就把成員聲明爲public;
3)如果想讓該類的擴展者使用數據域和方法,而不想讓該類的用戶使用,則把成員聲明爲protected。
4)private和protected只能用於類的成員,而public和默認修飾符即可用於類成員,也可用於類。一個沒有修飾符的類(即非公共類)是不能被其他包中的類訪問的。
e.防止擴展和覆蓋的辦法
有時候,可能希望防止類擴展。在這種情況下,使用final修飾符表明一個類是終極的,是不能作爲父類的。Math類就是一個終極類。String、StringBuilder和StringBuffer類也可以是終極類。最後,繼承和多態在Java中應用靈活多變,所以必須是重點學習的內容。