1. 程式人生 > >Java開發筆記(四十五)成員屬性與成員方法

Java開發筆記(四十五)成員屬性與成員方法

前面介紹了許多資料型別,除了基本型別如整型int、雙精度型double、布林型boolean之外,還有高階一些的如包裝整型Integer、字串型別String、本地日期型別LocalDate等等,那麼這些資料型別為何會分成基本和高階兩種呢?這與程式語言的發展歷程息息相關,像中文、英文這些是人類社會的自然語言,而計算機能夠識別的是機器語言,但是機器語言全為以0和1表達的二進位制串,看起來彷彿天書一般,讀都讀不懂,更別說寫出來了。為了方便程式設計師也能操縱計算機,科學家把機器語言所表達的一些常見操作歸納起來,分別使用特定的英語單詞來代替它們,例如MOV表示移動指令,ADD表示加法指令,SUB表示減法指令,MUL表示乘法指令,DIV表示除法指令等等。那時的數字則放在一個個暫存器中,所謂暫存器指的是某個儲存單元,每個儲存單位可儲存8位的二進位制數,計算機中有八個通用的8位暫存器,分別是AH、AL、BH、BL、CH、CL、DH、DL。對於組合語言來說,指令相當於操作符,暫存器相當於變數,此時沒有明確的資料型別之分,因為所有資料都是8位二進位制數。雖然組合語言比起機器語言來說,總算能被程式設計師看懂了,但是它的表達手段無疑很原始,所以組合語言屬於計算機的低階語言。
然而組合語言實在是太低階了,每次操作只能處理8位的二進位制數,如果需要對很大的數字進行運算,組合語言就得把大數運算拆分成若干條指令處理。比如300與400相加,由於300和400各需16位二進位制數存放,因此它們的加法運算至少要分解為三條加法指令;第一條是兩個數字的前面8位相加,第二條是兩個數字的後面8位相加,倘若第二條加法之和超出255,則第三條還得給第一條的運算結果加一。可見大數相加已然如此繁瑣,大數相乘或者相除只會更加麻煩,為了進一步簡化組合語言,科學家又發明了以C語言為代表的中級語言。C語言相對組合語言的一個顯著進步,是把基本資料型別分門別類,推出了整型int、長整型long、浮點型float、雙精度型double、布林型boolean等型別,每種基本型別都有明確的數字表達範圍,若在精度要求範圍之內開展加減乘除四則運算,只要書寫一次帶有運算子+、-、*、/的操作語句即可。正因為C語言遮蔽了不同數字型別在機器儲存上的差異,使得程式設計師能夠專注於業務邏輯層面的編碼,從而促進了軟體行業的飛躍發展。
可是C語言僅僅劃分了基本的資料型別,若想處理更復雜的資料,就得定義新的結構體struct來存放複雜資料。這個結構體只是若干基本型別變數的堆砌,對結構體的操作等同於修改它的某個變數值,導致程式程式碼嚴重依賴於最初的編碼人員,因為一個變數的含義往往只有他才知道,要是換了別人接手,得費老大的勁才能搞清楚某變數的涵義。於是科學家又設計了基於C的C++語言,C++提供了全新的類class意圖替代結構體struct,在class這個類裡面,變數被稱作類的屬性,函式被稱作類的方法,外部通過類的方法來讀寫類的屬性。這樣做的好處是顯而易見的,首先方法名可以起個有意義的名稱;其次方法擁有輸入引數和輸出引數,能夠處理更多的資訊量;再次方法內部允許編寫複雜的業務邏輯,而不僅限於單純讀寫某個屬性。如此一來,C++通過類便能定義和處理接近於自然界的事物概念,好比你媽催你找物件,物件可不是幾塊布縫製而成的洋娃娃,而是有血有肉能說會唱的伊人。C語言的結構體猶如不會動的洋娃娃,C++的類才與活生生的伊人相仿,因此工業界將C++稱作面向物件的程式語言,並歸入高階語言的行列。
至於Java語言,則是承繼了C++的衣鉢,一方面保留了面向物件的精髓,另一方面去掉了繁瑣的指標操作,即所謂的取其精華、去其糟粕。Java中的類同樣使用關鍵字class來表達,類內部的各種要素被稱為類的成員,例如類的屬性也叫做成員屬性,類的方法也叫做成員方法。先前介紹的那些高階型別諸如Integer、String、LocalDate,它們的原始碼都是通過class定義的,包含的成員屬性與成員方法各不相同。譬如Integer擁有一個整型的成員屬性,還有包括equals在內的幾個成員方法;String擁有一個字元陣列的成員屬性,還有包括indexOf、replace在內的若干個成員方法;LocalDate擁有整型的年、月、日這三個成員屬性,還包括getDayOfMonth、plusMonths、isLeapYear等等成員方法。不過Java開發包提供的高階型別畢竟是有限的,要想滿足更加具體的業務需求,則需由程式設計師自己定義新的資料型別,也就是從頭編寫新的class。
早在本系列文章的一開始,便給出了一個如下所示的簡單類定義:

package com.donghan.nanjun.dangyang; // 東漢帝國南郡當陽縣

public class Maicheng {
}

以上的類定義程式碼,開頭的public表示這個類對外開放,class是類的識別符號,Maicheng則是類的名稱,其後的左右花括號內部用來填寫類的各種成員。現在往類內部新增幾個成員屬性,把它變成一個有實際意義的事物,就像下面的橘子類程式碼,定義了橘子的名稱、重量、是否成熟、產地等物品特徵:

//演示如何定義類的屬性
public class OrangeSimple {

	// 定義了橘子的名稱
	public String name;
	// 定義了橘子的重量
	public double weight;
	// 定義了橘子是否成熟。true表示成熟,false表示未成熟
	public boolean isRipe;
	// 定義了橘子的產地
	public String place;
}

上面的類程式碼合計定義了橘子的四種屬性,接下來外部先利用關鍵字new建立橘子類的一個例項,再通過形如“例項名.屬性名”的格式訪問該例項的各個屬性,具體的操作程式碼如下所示:

	// 演示OrangeSimple類的呼叫
	private static void testSimple() {
		// 建立OrangeSimple的一個例項
		OrangeSimple orange = new OrangeSimple();
		orange.name = "橘子"; // 設定名稱屬性
		orange.place = "淮南"; // 設定產地屬性
		orange.isRipe = true; // 設定是否成熟的屬性
		orange.weight = 200; // 設定重量屬性
	}

 

可是這個OrangeSimple類只有成員屬性,沒有成員方法,充其量是C語言時代的孑遺,還得補充幾個成員方法,才配得上高階語言的身份。並且使用成員方法至少有下列幾項好處:


1、把屬性的讀寫操作分開。
比如通過get***方法獲取某個屬性的值,通過set***方法修改某個屬性的值,此時屬性定義的字首需要把public改為private,表示該屬性不對外開放。以名稱欄位name為例,新增getName方法用於讀取橘子的名稱,新增setName方法用於變更橘子的名稱,另外把name屬性的開放性改為private,修改之後的程式碼片段如下:

	// 定義了橘子的名稱
	private String name;

	// 設定橘子的名稱
	public void setName(String inputName) {
		name = inputName;
	}

	// 獲取橘子的名稱
	public String getName() {
		return name;
	}

  

2、一個方法可以同時修改多個屬性的值。
古人云“橘生淮南則為橘,生於淮北則為枳”,可見橘子的名稱與其產地是有關聯的,一旦產地欄位發生變更,則橘子名稱也可能跟著變化。那麼依照“橘生淮北則為枳”的規則,可將產地設定方法setPlace更改為以下邏輯:

	// 設定橘子的產地
	public void setPlace(String inputPlace) {
		place = inputPlace;
		if (place.equals("淮北")) {
			name = "枳子";
		} else {
			name = "橘子";
		}
	}

  


3、一個方法可以同時輸出多個屬性的值。
當某個型別擁有多個屬性的時候,最好能夠一次性輸出所有屬性值。譬如本地日期型別LocalDate,其內部包含年、月、日三種屬性,呼叫日期例項toString方法,即可返回完整的年月日字串。對於橘子型別來說,也可在該類的內部定義一個toString方法,把該類的所有屬性值拼接成字串並返回,就像下面程式碼示範的那樣:

	// 輸出各屬性欄位的取值
	public String toString() {
		String desc = String.format("這個%s的重量是%f克,%s成熟,產地是%s。", 
				name, weight, isRipe?"已":"未", place);
		return desc;
	}

  


綜合上述的幾點修改,得到添加了成員方法的OrangeMember類,它的完整定義程式碼如下所示:

//演示類的封裝,對成員屬性和成員方法的定義
public class OrangeMember {
	
	// 定義了橘子的名稱
	private String name;
	// 定義了橘子的重量
	private double weight;
	// 定義了橘子是否成熟。true表示成熟,false表示未成熟
	private boolean isRipe;
	// 定義了橘子的產地
	private String place;
	
	// 設定橘子的產地
	public void setPlace(String inputPlace) {
		place = inputPlace;
		if (place.equals("淮北")) {
			name = "枳子";
		} else {
			name = "橘子";
		}
	}

	// 獲取橘子的產地
	public String getPlace() {
		return place;
	}
	
	// 設定橘子的名稱
	public void setName(String inputName) {
		name = inputName;
	}

	// 獲取橘子的名稱
	public String getName() {
		return name;
	}

	// 設定橘子的重量
	public void setWeight(double inputWeight) {
		weight = inputWeight;
	}

	// 獲取橘子的重量
	public double getWeight() {
		return weight;
	}

	// 設定橘子是否成熟
	public void setRipe(boolean inputRipe) {
		isRipe = inputRipe;
	}

	// 獲取橘子是否成熟
	public boolean getRipe() {
		return isRipe;
	}

	// 輸出各屬性欄位的取值
	public String toString() {
		String desc = String.format("這個%s的重量是%f克,%s成熟,產地是%s。", 
				name, weight, isRipe?"已":"未", place);
		return desc;
	}
}

然後外部在建立該類的例項之後,便能呼叫例項的成員方法進行相應操作了。下面是外部使用OrangeMember型別的程式碼例子:

	// 演示OrangeMember類的呼叫
	private static void testMember() {
		// 建立OrangeMember的一個例項
		OrangeMember orange = new OrangeMember();
		orange.setName("橘子"); // 呼叫名稱設定方法
		orange.setPlace("淮南"); // 呼叫產地設定方法
		orange.setRipe(true); // 呼叫是否成熟的設定方法
		orange.setWeight(200); // 呼叫重量設定方法
		// 列印該例項的詳細資訊
		System.out.println(orange.toString());
	}

 

執行上面的例子程式碼,得到如下的日誌資訊,可見OrangeMember類的幾個成員方法正常工作:

這個橘子的重量是200.000000克,已成熟,產地是淮南。

  

更多Java技術文章參見《Java開發筆記(序)章節目錄