本博文將介紹面向過程和麪向對象設計的不同指出,將會學習面向對象程序設計的優點,並掌握如何高效的使用它。先來了解幾個概念:不可變類、變量的作用域、this引用、類的抽象和封裝、面向對象的思考、類的組合及其設計原則。
一:不可變類
一個對象一旦創建其內容就不能改變,這種對象叫做不可變對象。而它的類叫不可變類。必須滿足以下三點:
1)所有數據域都是私有的;
2)沒有修改器方法;
3)沒有一個訪問器方法,它會返回一個指向可變數據域的引用。
二:變量的作用域
一個類的實例變量和靜態變量稱爲類變量或數據域。在方法內部定義的變量稱爲局部變量。類變量作用域是整個類,只能聲明一次,但在一個方法不同的非嵌套塊中,可以多次聲明相同的變量。
如果一個局部變量和一個類變量具有相同的名字,那麼局部變量有效,而同名的類變量將被隱藏。舉例如下:
注意:爲了避免混淆和錯誤,除了方法中的參數,不要將實例變量或靜態變量的名字作爲局部變量的名字。
三:this引用
關鍵字this是指向調用對象本身的引用名。常見的有兩種應用方法。
1)引用類的隱藏數據域。隱藏的靜態變量可以簡單地通過“類名.靜態變量”的方式引用;隱藏的實例變量就需要使用關鍵字this引用。關鍵字this給出一種指代對象的方法,這樣,可以在實例方法中調用實例方法。
2)讓構造方法調用同一個類的另一個構造方法。如下所示:
注意:如果一個類有多個構造方法,最好儘可能使用this(參數列表)實現它們。通常,無參數或參數少的構造方法可以用this(參數表)調用參數多的構造方法。這樣做通常可以簡化代碼,使類易於閱讀和維護。
Java要求在構造方法中,語句this(參數列表)應在任何其他語句之前出現 。
四:類的抽象和封裝
類的抽象也是將類的實現和使用分離。如下圖所示:
類的合約:類的創建者提供類的描述,讓使用者明白如何才能實用類。從類外可以訪問的全部方法和數據域,以及期望這些成員如何行動的描述,合稱爲類的合約。
類的封裝:類的實現細節經過封裝,對用戶隱藏起來。
注意:在類的實現之前,可以先編寫這個類的測試程序,即使不知道這個類是如何執行的也可以。這樣做有以下幾點好處:
1)演示開發類和使用類是兩個不同的任務;
2)能使你跳過某個類的複雜實現、而不干擾整體思路;
3)通過使用熟悉類,更容易學會如何去實現一個類。
從現在開始的所有例子,都可以參照這個思路,先從這個類創建一個對象,然後嘗試使用它的方法,之後就將注意力放在它的實現上。
五:面向對象的思考面向過程範式重在設計方法,面向對象範式將數據和方法組合在對象中。面向對象方法結合了面向過程範式的功能以及將數據的操作和操作集成在對象中的特性。
面向過程程序設計中,數據和數據上的操作是分離的,而且這裏的方法論是要求給方法發送數據。面向對象程序設計將數據和它們的操作都放在一個對象中,它按照鏡像到真實世界的方式組織程序,在真實世界中,所有的對象都是和屬性及動作相關聯的。
使用對象提高了軟件的可重用性,並且使程序更易於開發和維護。Java程序設計涉及的是對對象的思考,一個Java程序可以看做是一個相互操作的對象集合。
六:對象的組合
一個對象可以包含另一個對象,這兩個對象之間的關係稱爲組合。組合實際上是聚集關係的一種特殊形式。聚集模擬了具有關係,表示兩個對象之間的歸屬關係。
歸屬關係中的所有者稱爲聚集對象,而它的類稱爲聚集類;從屬對象稱爲被聚集對象,它的類稱爲被聚集類。
一個對象可以被幾個其他聚集對象所擁有。如果一個對象只歸屬一個聚集對象,那麼它和聚集對象之間的關係稱爲組合關係。
UML類圖中,用實心的菱形表示組合關係,空心表示聚集關係。
七:對象的設計原則
1)內聚性
類應該描述一個單一的實體,而所有的類操作應該在邏輯上相互配合,支持一個連貫性的目標。如果一個實體擔負太多的職責,就應該按各自的職責分成幾個類。例如,String類、StringBuilder類、StringBuffer類都用於處理字符串,但是它們的職責不同。String類處理不可變字符串,StringBuilder類創建可變字符串,StringBuffer與StringBuilder類似,只是它還包含更新字符串的同步方法。
2)一致性
遵循標準Java程序設計風格和命名習慣。給類、數據域和方法選擇有信息量的名字。流行的風格是將數據聲明置於構造方法之前,並且將構造方法置於方法之前。
選擇名字要保持一致,給類似的操作選擇不同的名字並非好習慣。
一般來說,應該一律提供一個默認實例的公共無參構造方法。如果一個類不支持無參構造方法,要用文檔寫明原因。如果沒有顯示定義構造方法,就假定有一個空方法體的公共默認無參構造方法。
如果不像讓用戶創建類的對象,可以在類中聲明一個私有的構造方法,如Math類和GuessDate類一樣。
3)封裝性
一個類應該使用private修飾符隱藏其數據,以免用戶直接訪問它。這使得類更易於維護。
如果想讓數據可讀,只需提供get方法;想讓數據可更新,只需提供set方法。
4)清晰性
爲使設計清晰,內聚性、一致性、封裝性都是很好的設計原則。除此之外,類應該有一個很清晰的合約,易於解釋和理解。
類應該沒有對用戶使用目的和使用時間進行限制,設計屬性以容許用戶按值的任何順序和任何組合來設置,所設計的方法實現的功能應該與他們出現的順序無關。
方法應該在不產生混淆的情況下憑直覺來定義。
不應該聲明一個來自其他數據域的數據域。
5)完整性
類經常是爲許多不同用戶的使用而設計的。爲了能夠在一個廣泛的應用中使用,一個類應該通過屬性和方法提供多種方案以適應用戶的不同需求。如爲滿足不用應用需求,String類包含了40多種很實用的方法。
瞭解了以上幾個概念,讓我們來看幾個範例的練習,以增進理解。
範例一:貸款類的實現,並編寫測試程序。程序要求用戶輸入年利率、貸款年限、貸款總額;程序輸出每月還款額以及總共需要還款數目。
運行效果如右圖所示:
實現的源代碼如下所示:
package Blog;
import java.util.Date;
import java.util.Scanner;
import javax.swing.*;
public class blogTryProject{
public static void main(String[]args){
TestLoanClass tlc = new TestLoanClass();
tlc.main(args);
}
}
//貸款類測試類
class TestLoanClass{
public static void main(String[]args){
Scanner input = new Scanner(System.in);
System.out.print("Enter yearly interest rate,for example,8.25: ");
double annualInterestRate = input.nextDouble();
System.out.print("Enter number of years as an integer: ");
int numberOfYears = input.nextInt();
System.out.print("Enter loan amount,for example,120000.95 : ");
double loanAmount = input.nextDouble();
Loan loan = new Loan(annualInterestRate,numberOfYears,loanAmount);
System.out.printf("The loan was created on %s\n"+"The monthly payment is %.2f\n"
+ "The total payment is %.2f\n",loan.getLoanDate().toString(),
loan.getMonthlyPayment(),loan.getTotalPayment());
}
}
//貸款類
class Loan{
private double annualInterestRate;
private int numberOfYears;
private double loanAmount;
private java.util.Date loanDate;
public Loan(){
this(2.5,1,1000);
}
public Loan(double annualInterestRate,int numberOfYears,double loanAmount){
this.annualInterestRate = annualInterestRate;
this.numberOfYears = numberOfYears;
this.loanAmount = loanAmount;
loanDate = new java.util.Date();
}
public double getAnnualInterestRate(){
return annualInterestRate;
}
public void setAnnualInterestRate(double annualInterestRate){
this.annualInterestRate = annualInterestRate;
}
public int getNumberOfYears(){
return numberOfYears;
}
public void setNumberOfYears(int numberOfYears){
this.numberOfYears = numberOfYears;
}
public double getLoanAmount(){
return loanAmount;
}
public void setLoanAmount(double loanAmount){
this.loanAmount = loanAmount;
}
public double getMonthlyPayment(){
double monthlyInterestRate = annualInterestRate / 1200;
double monthlyPayment = loanAmount * monthlyInterestRate /
(1 - (Math.pow(1/(1+monthlyInterestRate), numberOfYears*12)));
return monthlyPayment;
}
public double getTotalPayment(){
double totalPayment = getMonthlyPayment() * numberOfYears * 12;
return totalPayment;
}
public java.util.Date getLoanDate(){
return loanDate;
}
}
範例二:堆棧類和選課類的實現。選課類主要實現某門課程有哪些人選課,並可以對某門課的學生進行刪除、統計人數、或清空該門課的學生統計,以模擬選課系統查看和操作學生名單的功能。
運行效果如圖所示:
實現的源代碼如下所示:
package Blog;
import java.util.Date;
import java.util.Scanner;
public class blogTryProject{
public static void main(String[]args){
TestStackOfIntegers tsoi = new TestStackOfIntegers();
tsoi.main(args);
TestCourse tc = new TestCourse();
tc.main(args);
}
}
//堆棧類測試
class TestStackOfIntegers{
public void main(String[]args){
StackOfIntegers stack = new StackOfIntegers();
for(int i = 0;i < 10;i++)
stack.push(i);
System.out.println("堆棧的出棧順序爲:");
while(!stack.empty())
System.out.print(stack.pop()+" ");
System.out.println();
}
}
//堆棧類
class StackOfIntegers{
private int[] elements;
private int size;
public static final int DEFUALT_CAPACITY = 16;
public StackOfIntegers(){
this(DEFUALT_CAPACITY);
}
public StackOfIntegers(int capacity){
elements = new int[capacity];
}
public void push(int value){
if(size >= elements.length){
int[] temp = new int[elements.length * 2];
System.arraycopy(elements, 0, temp, 0, elements.length);
elements = temp;
}
elements[size++] = value;
}
public int pop(){
return elements[--size];
}
public int peek(){
return elements[size - 1];
}
public boolean empty(){
return size == 0;
}
public int getSize(){
return size;
}
}
//選課類測試程序
class TestCourse{
public static void main(String[]args){
Course course1 = new Course("Data Structures");
Course course2 = new Course("Database Systems");
course1.addStudent("Peter Jones");
course1.addStudent("Brain Smith");
course1.addStudent("Anne Kennedy");
course2.addStudent("Peter Jones");
course2.addStudent("Steve Smith");
System.out.println("Number fo students in course1: "+course1.getNumberOfStudents());
String[] students = course1.getStudents();
for(int i = 0;i < course1.getNumberOfStudents();i++)
System.out.print(students[i]+", ");
System.out.println();
System.out.println("Number of students in course2: "+course2.getNumberOfStudents());
String[] studentsOfCourse2 = course2.getStudents();
for(int i = 0;i < course2.getNumberOfStudents();i++)
System.out.print(studentsOfCourse2[i]+", ");
System.out.println();
course1.dropStudent("Peter Jones");
System.out.println("刪除Peter Jones之後的學生爲:");
System.out.println("Number fo students in course1: "+course1.getNumberOfStudents());
students = course1.getStudents();
for(int i = 0;i < course1.getNumberOfStudents();i++)
System.out.print(students[i]+", ");
System.out.println();
System.out.println("將某門課程清空之後,學生爲:");
course2.clear("DataBase Systems");
System.out.print("Number of students in course2: "+course2.getNumberOfStudents());
studentsOfCourse2 = course2.getStudents();
for(int i = 0;i < course2.getNumberOfStudents();i++)
System.out.print(studentsOfCourse2[i]+", ");
}
}
//選課類
class Course{
private String courseName;
private String[] students = new String[10];
private int numberOfStudents;
public Course(String courseName){
this.courseName = courseName;
}
public void addStudent(String student){
if(numberOfStudents > students.length){
String[] newStudents = new String[students.length * 2];
System.arraycopy(students, 0, newStudents, 0, students.length);
students = newStudents;
}
students[numberOfStudents++] = student;
}
public String[] getStudents(){
return students;
}
public int getNumberOfStudents(){
return numberOfStudents;
}
public String getCourseName(){
return courseName;
}
public void dropStudent(String student){
for(int i = 0;i < numberOfStudents;i++){
if(students[i] == student){
if(i == numberOfStudents - 1){
students[i] = "";
numberOfStudents--;
}
else{
for(int j = i;j < numberOfStudents - 1;j++){
students[j] = students[j+1];
}
students[numberOfStudents - 1] = "";
numberOfStudents--;
}
}
}
}
public void clear(String courseName){
for(int i = 0;i < numberOfStudents;i++){
students[i] = "";
numberOfStudents--;
}
numberOfStudents--;//由於numberOfStudents是自加統計的,所以要多減一次
}
}
範例三:ATM機功能的模擬。實現指定賬戶的查詢、存款、取款、退出;以及不同賬戶之間的轉賬功能。利用對象數組存儲不同的賬戶。
運行效果如圖所示:
實現的源代碼如下所示:
package Blog;
import java.util.Date;
import java.util.Scanner;
public class blogTryProject{
public static void main(String[]args){
TestATM tatm = new TestATM();
tatm.main(args);
}
}
//ATM機模擬
class TestATM{
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
Acount[] atm = new Acount[10];
//初始化對象數組的值爲100
for(int i = 0;i < atm.length;i++){
atm[i] = new Acount();
atm[i].setBalance(100);
}
//ATM操作
while(true){
System.out.print("請輸入一個賬戶ID, (eg:0~9) : ");
int numberOfId = input.nextInt();
int numberOfChoice = 0;
//操作選擇
while(true){
System.out.println("Main menu");
System.out.println("1:查詢餘額");
System.out.println("2:取款");
System.out.println("3:存款");
System.out.println("4:退出此次ID的卡");
System.out.println("5:退出ATM");
System.out.println("6:轉賬");
System.out.print("選擇一個操作,(1~6): ");
numberOfChoice = input.nextInt();
//輸入合法性判斷
while(true){
if(numberOfChoice >= 1 && numberOfChoice <= 6)
break;
else{
System.out.print("輸入錯誤,請重新輸入操作類型: ");
numberOfChoice = input.nextInt();
}
}
switch (numberOfChoice) {
case 1: {
System.out.println("餘額爲: "
+ atm[numberOfId].getBalance());
break;
}
case 2: {
System.out.print("輸入取款數目: ");
double withDrawNumber = input.nextDouble();
atm[numberOfId].withDraw(withDrawNumber);
break;
}
case 3: {
System.out.print("輸入存款數目: ");
double depositNumber = input.nextDouble();
atm[numberOfId].deposit(depositNumber);
break;
}
case 4: {
break;
}
case 6:{
System.out.print("輸入要轉入的賬戶,(0~9): ");
int numOfIdIn = input.nextInt();
System.out.print("輸入要轉入的金額: ");
double numOfIn = input.nextDouble();
atm[numberOfId].withDraw(numOfIn);
atm[numOfIdIn].deposit(numOfIn);
}
}
//結束本個對象ATM或全部ATM操作判斷
if(numberOfChoice == 4 || numberOfChoice == 5)
break;
}
//結束ATM判斷
if(numberOfChoice == 5){
System.out.println("結束ATM操作");
break;
}
}
}
}
//存取款類
class Acount{
private int id;
private double balance;
private double annualInterestRate;
private Date dateCreated;
Acount(){
id = 0;
balance = 0;
dateCreated = new Date();
}
Acount(int newID,double newBalance){
id = newID;
balance = newBalance;
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(double outBalance){
balance -= outBalance;
}
public void deposit(double inBalance){
balance += inBalance;
}
}