這是我參與8月更文挑戰的第3天,活動詳情查看:8月更文挑戰java
封裝是面向對象的三大特徵之一,什麼是封裝?封裝有什麼好處?怎麼封裝,代碼怎麼寫?這是這篇文章學習的內容。安全
封裝從字面上來理解就是包裝的意思,專業點就是信息隱藏,是指利用抽象數據類型將數據和基於數據的操做封裝在一塊兒,使其構成一個不可分割的獨立實體,數據被保護在抽象數據類型的內部,儘量地隱藏內部的細節,只保留一些對外接口使之與外部發生聯繫。系統的其餘對象只能經過包裹在數據外面的已經受權的操做來與這個封裝的對象進行交流和交互。也就是說用戶是無需知道對象內部的細節,但能夠經過該對象對外提供的接口來訪問該對象。markdown
在現實世界當中咱們能夠看到不少事物都是封裝好的,好比「鼠標」,外部有一個殼,將內部的原件封裝起來,至於鼠標內部的細節是什麼,咱們不須要關心,只須要知道鼠標對外提供了左鍵、右鍵、滾動滑輪這三個簡單的操做。對於用戶來講只要知道左鍵、右鍵、滾動滑輪都能完成什麼功能就好了。爲何鼠標內部的原件要在外部包裝一個「殼」呢,起碼內部的原件是安全的,不是嗎。app
再如「數碼相機」,外部也有一個殼,將內部複雜的結構包裝起來,對外提供簡單的按鍵,這樣每一個人均可以很快的學會照相了,由於它的按鍵很簡單,另外照相機內部精密的原件也受到了殼兒的保護,不容易壞掉。ide
封裝以後就造成了獨立實體,獨立實體能夠在不一樣的環境中重複使用,顯然封裝能夠下降程序的耦合度,提升程序的擴展性,以及重用性或複用性,例如「鼠標」能夠在A電腦上使用,也能夠在B電腦上使用。另外封裝能夠隱藏內部實現細節,站在對象外部是看不到內部複雜結構的,對外只提供了簡單的安全的操做入口,因此封裝以後,實體更安全了。post
第一步:屬性私有化測試
第二步:1個屬性對外提供兩個set和get方法。外部程序只能經過set方法修改,只能經過get方法讀取,能夠在set方法中設立關卡來保證數據的安全性。ui
在強調一下: set和get方法都是實例方法,不能帶static。
不帶static的方法稱爲實例方法,實例方法的調用必須先new對象。
set和get方法寫的時候有嚴格的規範要求:(你們要按照規矩來)
public void set+屬性名首字母大寫(1個參數){
xxx = 1個參數;
}
get方法長這個樣子:
public 返回值類型 get+屬性名首字母大寫(無參){
return xxx;
}
複製代碼
public class MobilePhone {
//電壓:手機正常電壓在3~5V
double voltage;
}
複製代碼
public class MobilePhoneTest {
public static void main(String[] args) {
MobilePhone phone = new MobilePhone();
phone.voltage = 3.7;
System.out.println("手機電壓 = " + phone.voltage);
phone.voltage = 100;
System.out.println("手機電壓 = " + phone.voltage);
}
}
複製代碼
運行結果以下圖所示:
圖1:未進行封裝的程序測試
以上程序MobilePhone類未進行封裝,其中的電壓屬性voltage對外暴露,在外部程序當中能夠對MobilePhone對象的電壓voltage屬性進行隨意訪問,致使了它的不安全,例如手機的正常電壓是3~5V,可是以上程序已經將手機電壓設置爲100V,這個時候顯然是要出問題的,但這個程序編譯以及運行仍然是正常的,沒有出現任何問題,這是不對的。
爲了保證內部數據的安全,這個時候就須要進行封裝了,封裝的第一步就是將應該隱藏的數據隱藏起來,起碼在外部是沒法隨意訪問這些數據的,怎麼隱藏呢?咱們可使用java語言中的private修飾符,private修飾的數據表示私有的,私有的數據只能在本類當中訪問。請看程序:
public class MobilePhone {
//電壓:手機正常電壓在3~5V
private double voltage;
}
複製代碼
public class MobilePhoneTest {
public static void main(String[] args) {
MobilePhone phone = new MobilePhone();
phone.voltage = 3.7;
System.out.println("手機電壓 = " + phone.voltage);
phone.voltage = 100;
System.out.println("手機電壓 = " + phone.voltage);
}
}
複製代碼
以上程序編譯報錯了,請看下圖:
圖2:private修飾的數據沒法在外部程序中直接訪問
經過以上的測試,手機對象的電壓屬性確實受到了保護,在外部程序中沒法訪問了。但從當前狀況來看,voltage屬性有點兒太安全了,一個對象的屬性沒法被外部程序訪問,天然這個數據就沒有存在的價值了。因此這個時候就須要進入封裝的第二步了:對外提供公開的訪問入口,讓外部程序統一經過這個入口去訪問數據,咱們能夠在這個入口處設立關卡,進行安全控制,這樣對象內部的數據就安全了。
對於「一個」屬性來講,咱們對外應該提供幾個訪問入口呢?一般狀況下咱們訪問對象的某個屬性,不外乎讀取(get)和修改(set),因此對外提供的訪問入口應該有兩個,這兩個方法一般被稱爲set方法和get方法(請注意:set和get方法訪問的都是某個具體對象的屬性,不一樣的對象調用get方法獲取的屬性值不一樣,因此set和get方法必須有對象的存在才能調用,這樣的方法定義的時候不能使用static關鍵字修飾,被稱爲實例方法。實例方法必須使用「引用」的方式調用。還記得以前咱們接觸的方法都是被static修飾的,這些方法直接採用「類名」的方式調用,而不須要建立對象,在這裏顯然是不行的)。請看如下代碼:
public class MobilePhone {
//電壓:手機正常電壓在3~5V
private double voltage;
public MobilePhone(){
}
public void setVoltage(double _voltage){
if(_voltage < 3 || _voltage > 5){
//當電壓低於3V或者高於5V時拋出異常,程序則終止
throw new RuntimeException("電壓非法,請愛護手機!");
}
//程序若是能執行到此處說明以上並無發生異常,電壓值合法
voltage = _voltage;
}
public double getVoltage(){
return voltage;
}
}
複製代碼
public class MobilePhoneTest {
public static void main(String[] args) {
MobilePhone phone = new MobilePhone();
phone.setVoltage(3.7);
System.out.println("手機電壓 :" + phone.getVoltage());
phone.setVoltage(100);
System.out.println("手機電壓 :" + phone.getVoltage());
}
}
複製代碼
運行結果以下圖所示:
圖3:對封裝以後的測試
經過以上程序,能夠看出MobilePhone的voltage屬性不能在外部程序中隨意訪問了,只能調用MobilePhone
的setVoltage()
方法來修改電壓,調用getVoltage()
方法來讀取電壓,在setVoltage()
方法中編寫了安全控制代碼,當電壓低於3V,或者高於5V的時候,程序拋出了異常,不容許修改電壓值,程序結束了。只有合法的時候,才容許程序修改電壓值。(異常機制在後續的內容中會學到,不要着急。)
總之,在java語言中封裝的步驟應該是這樣的:
須要被保護的屬性使用private進行修飾,給這個私有的屬性對外提供公開的set和get方法,其中set方法用來修改屬性的值,get方法用來讀取屬性的值。而且set和get方法在命名上也是有規範的,規範中要求set方法名是set + 屬性名(屬性名首字母大寫),get方法名是get + 屬性名(屬性名首字母大寫)。
其中set方法有一個參數,用來給屬性賦值,set方法沒有返回值,通常在set方法內部編寫安全控制程序,由於畢竟set方法是修改內部數據的,而get方法不須要參數,返回值類型是該屬性所屬類型(先記住,之後講:另外set方法和get方法都不帶static關鍵字,不帶static關鍵字的方法稱爲實例方法,這些方法調用的時候須要先建立對象,而後經過「引用」去調用這些方法,實例方法不能直接採用「類名」的方式調用。)
例如如下代碼:
public class Product {
private int no;
private String name;
private double price;
public Product(){
}
public Product(int _no , String _name , double _price){
no = _no;
name = _name;
price = _price;
}
public int getNo() {
return no;
}
public void setNo(int _no) {
no = _no;
}
public String getName() {
return name;
}
public void setName(String _name) {
name = _name;
}
public double getPrice() {
return price;
}
public void setPrice(double _price) {
price = _price;
}
}
複製代碼
public class ProductTest {
public static void main(String[] args) {
Product p1 = new Product(10000 , "小米5S" , 2000.0);
System.out.println("商品編號:" + p1.getNo());
System.out.println("商品名稱:" + p1.getName());
System.out.println("商品單價:" + p1.getPrice());
p1.setNo(70000);
p1.setName("小米6");
p1.setPrice(2100.0);
System.out.println("商品編號:" + p1.getNo());
System.out.println("商品名稱:" + p1.getName());
System.out.println("商品單價:" + p1.getPrice());
}
}
複製代碼
運行結果以下圖所示:
圖4:set和get方法測試
有的小夥伴可能會有這樣的疑問:構造方法中已經給屬性賦值了,爲何還要提供set方法呢?
由於,這是兩個徹底不一樣的時刻,構造方法中給屬性賦值是在建立對象的時候完成的,當對象建立完畢以後,屬性可能仍是會被修改的,後期要想修改屬性的值,這個時候就必須調用set方法了。