【java基礎(二十六)】類、超類、子類(四)

抽象類

如果自上而下在類的繼承層級結構中上移,位於上冊的類更具有通用性,甚至可能更加抽象。從某種角度看,祖先類更加通用,人們只將它作爲派生類的基類,而不作爲想使用的特定的實例類。例如,考慮一下對Employee類層次的擴展。一名僱員是一個人,一名學生也是一個人。下面將類Persion和類Student添加到類的層次結構中。
在這裏插入圖片描述
爲什麼要花費精力進行這樣高層次的抽象呢?每個人都有一些諸如姓名這樣的屬性。學生與僱員都有姓名屬性,因此可以將getName方法放置在位於繼承關係較高層次的通用超類中。
現在,再增加一個getDescription方法,它可以返回對一個人的簡單描述。如:
對於Employee: an employee with a salary of $50,000,00
對於Student: a student majoring in computer science
在Employee類和Student類中實現這個方法很容易。但是在Person類中應該提供什麼內容呢?除了姓名之外,Person類一無所知。當然,可以讓Person.getDescription()返回一個空字符串。然而,還有一個更好的方法,就是使用abstract關鍵字,這樣就完全不需要實現這個方法了。

public abstract String getDescription();
// 不需要方法的實現

爲了提高程序的清晰度,包含一個或多個抽象方法的類本身必須被聲明爲抽象的。

public abstract class Persion {
	public abstract String getDescription();
}

除了抽象方法之外,抽象類還可以包含具體數據和具體方法。例如,Persion類還保存着姓名和一個返回姓名的具體方法。

public abstract class Person() {
	private String name;
	
	public Person(String name) {
		this.name = name;
	}
	
	public abstract String getDescription();
	
	public String getName() {
		return name;
	}
}

抽象方法充當着佔位的角色,它們的具體實現在子類中。擴展抽象類可以有兩種選擇。一種是在抽象類中定義部分抽象類方法或不定義抽象類方法,這樣就必須將子類也標記爲抽象類;另一種是定義全部的抽象方法,這樣一來,子類就不是抽象的了。
例如,通過擴展抽象Person類,並實現getDescription方法來定義Student類。由於在Student類中不再含有抽象方法,所以不必將這個類聲明爲抽象的。
類即使不含抽象方法,也可以將類聲明爲抽象類。
抽象類不能被實例化。也就是說,如果將一個類聲明爲abstract,就不能創建這個類的對象。如:

new Person("Vince Vu");

是錯誤的,但可以創建一個具體子類的對象。
需要注意,可以定義一個抽象類的對象變量,但是它之鞥呢引用非抽象子類的對象。如:

Person p = new Student("Vince Vu", "Economics");

這裏的p是一個抽象類Peron的變量,Person引用了一個非抽象子類Student的實例。
下面定義一個擴展抽象類Person的具體子類Student:

public class Student extends Person {
	private String major;
	
	public Student(String name, String major) {
		super(name);
		this.major = major;
	}
	
	public String getDescription() {
		return "a student majoring in " + major;
	}
}

在Student類中定義了getDescription方法。因此,在Student類中的全部方法都是非抽象的,這個類不再是抽象類。
定義Person數組,將僱員和學生填充到Person引用數組:

Person[] people = new Person[2];
people[0] = new Employee(...);
people[1] = new Student(...);

然後,輸出這些對象的姓名和信息描述:

for (Person p : people)
	System.out.println(p.getName + ", " + p.getDescription());

有些人可能對下面這個調用感到困惑:
p.getDescription();
這不是調用了一個沒有定義的方法嗎?請牢記,由於不能構造抽象類Person的對象,所以變量p永遠不會引用Person對象,而是引用諸如Employee或Student這樣的具體子類對象,而這些對象都定義了getDescription方法。

實例

Person.java

package cn.freedompc.abstractclasses;

public abstract class Person {

	public abstract String getDescription();
	
	private String name;
	
	public Person(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
	
}

Employee.java

package cn.freedompc.abstractclasses;

import java.time.LocalDate;

public class Employee extends Person {

	private double salary;
	private LocalDate hireDay;
	
	public Employee(String name, double salary, int year, int month, int day) {
		super(name);
		this.salary = salary;
		hireDay = LocalDate.of(year, month, day);
	}
	
	public double getSalary() {
		return salary;
	}
	
	public LocalDate getHireDay() {
		return hireDay;
	}	
	
	public String getDescription() {
		return String.format("an employee with a salary of $%.2f", salary);
	}
	
	public void raiseSalary(double byPercent) {
		double raise = salary * byPercent / 100;
		salary += raise;
	}
}

Student.java

package cn.freedompc.abstractclasses;

public class Student extends Person {

	private String major;
	
	public Student(String name, String major) {
		super(name);
		this.major = major;
	}
	
	public String getDescription() {
		return "a student majoring in " + major;
	}
	
}

PersonTest.java

package cn.freedompc.abstractclasses;

public class PersonTest {

	public static void main(String[] args) {
		Person[] people = new Person[2];
		
		people[0] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
		people[1] = new Student("Maria Morris", "computer science");
		
		for (Person p : people)
			System.out.println(p.getName() + ", " + p.getDescription());
	}
	
}

運行結果

在這裏插入圖片描述
是否可以省略Person超類中的抽象方法,而僅在Employee和Student子類中定義getDescription方法呢?如果這樣的話,就不能通過變量p調用getDescription方法了。編譯器只允許調用在類中聲明的方法。
在Java程序設計語言中,抽象方法是一個重要的概念。在接口(interface)中會看到更多的抽象方法。

受保護訪問

大家都知道,最好將類中的域標記爲private,而方法標記爲public。任何聲明爲private的內容對其他類都是不可見的。前面已經看到,這對於子類來說也完全適用,即子類也不能訪問超類的私有域。
然而,在有些時候,人們希望超類中的某些方法允許被子類調用,或允許子類的方法訪問超類的某個域。爲此,需要將這些方法域聲明爲protected。例如,如果將超類Employee中的hireDay聲明爲protected,而不是私有的,Manager中的方法就可以直接地訪問它。
不過,Manager類中的方法只能夠訪問Manager對象中的hireDay域,而不能訪問其他Employee對象中的這個域。這種限制有助於避免濫用受保護機制,使得子類只能獲得訪問受保護域的權利。
在實際應用中,要謹慎使用protected屬性。假設需要將設計的類提供給其他程序猿使用,而這個類中設置了一些受保護域,由於其他程序猿可以由這個類再派生出新類,並訪問其中的受保護域。在這種情況下,如果需要這個類的實現進行修改,就必須通知所有使用這個類的程序猿。這違背了OOP提倡的數據封裝原則。
受保護的方法更具有實際意義。如果需要限制某個方法的使用,就可以將它聲明爲protected。這表明子類得到信任,可以正確地使用這個方法,而其他類則不行。
下面歸納一下Java用於控制可見性的4個訪問修飾符:

  1. 僅對本類可見 – private
  2. 對所有類可見 – public
  3. 對本包和所有子類可見 – protected
  4. 對本包可見 – 默認,不需要修飾符。

捐贈

若你感覺讀到這篇文章對你有啓發,能引起你的思考。請不要吝嗇你的錢包,你的任何打賞或者捐贈都是對我莫大的鼓勵。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章