在描述對象時候,例如描述對象為狗,可以設置Dog.age= 100;看上去語法沒有任何毛病,但是顯然這么寫是不合理的。因此,java提供了封裝的機制,將類和對象的成員變量進行封裝。
案例1:
class FengZhuang{ publicstatic void main(String[] args){ Dogjinmao = new Dog(); jinmao.setAge(180); //jinmao.age= 100; }}class Dog{ PRivate int age; public void setAge(intage){ if(age > 20){ System.out.println("滾犢子!"); return; } System.out.println("設置成功"); this.age = age; }} 概念:
封裝是面向對象三大特征之一,它指的是將對象的狀態信息隱藏在對象內部,不允許外部程序直接訪問對象內部信息,而是通過該類提供的方法來實現對內部信息的訪問。這樣,當我們設置屬性值的時候,可以判斷設置的屬性值是否合理。
//TODOprivate int age; publicvoid setAge(int age){ if(age> 20){ System.out.println("滾犢子!"); return; } System.out.println("設置成功"); this.age= age; } 封裝是面向對象編程語言對客觀世界的模擬,在客觀世界里,對象的信息都是被隱藏在對象內部,外界無法直接操作訪問和修改。就如對Dog.age設置屬性那樣,外界不能直接修改age屬性,只能隨著歲月的流逝(條件),age才會增加。
封裝的目的:
1、 隱藏類的實現細節。
隱藏和封裝的區別:
隱藏就是將屬性私有化(private),不能被外界直接訪問。
封裝比隱藏多一步,另外提供公有的方法讓外界去操作該隱藏屬性。
如下:temp是內部實現功能的細節,只需要隱藏。而age是描述對象屬性,需要隱藏起來后提供公有方法對其進行訪問和設置。
class Dog{ //這個變量的業務邏輯只是在該對象方法中有一個記錄 //臨時數據功能 privateString temp; privateint age; publicvoid setAge(int age){ temp= "這個狗狗的年紀是"+age; if(age> 20){ System.out.println("滾犢子!"); return; } System.out.println("設置成功"); this.age= age; } publicint getAge(){ System.out.println(temp); returnage; }}2、讓使用者只能通過預定義的方法來訪問數據,從而可以在該方法中添加控制邏輯,限制對成員變量的不合理的訪問。
3、可進行數據檢查,從而有利于保證對象完整性。
4、提高代碼的可維護性。
為了實現良好的封裝,需要從兩個方面考慮:
1、將對象的成員變量和實現細節隱藏起來,不允許外部直接訪問。
2、將方法暴露出來,讓方法來控制對這些成員變量進行安全訪問和操作。
因此封裝實際上就是:將該隱藏的隱藏,將該暴露的暴露。而實現這一目的則是通過java提供的訪問控制符來實現。
訪問控制符(權限修飾符)
java提供3個訪問控制符關鍵字,private、protected、public分別代表三個訪問控制級別,另外還有一個默認訪問控制級別,一共4個訪問控制級別。
默認訪問控制即不寫權限修飾符(default)。
控制級別由小到大排序為:
private->default->protected->public
使用4種權限類型控制的效果:
private:修飾的成員(變量、方法、構造器)只能在當前類內部訪問,外界是無法獲取該成員。顯然,使用這個修飾符可以達到隱藏成員變量的目的。
class FengZhuang{ public static void main(String[] args){ Dog jinmao = new Dog(); jinmao.age = 100; }}class Dog{ private int age; }
default:默認修飾符,不適用任何修飾符關鍵字修飾。修飾的成員或類可以被相同包下的其他類訪問,不能被其他包下的類訪問。
案例:定義bao1. Bao1Class, bao1. Bao1Class2, bao2. Bao2Class,在bao2下的Bao2Class類無法訪問在bao1下的Bao1Class的name屬性。但是,bao1下的Bao1Class2類可以訪問在bao1下的Bao1Class的name屬性。
package bao1;public class Bao1Class{ staticString name;} package bao2;import bao1.*;public class Bao2Class{ publicstatic void main(String[] args){ Bao1Class.name= "小明"; }} package bao1;public class Bao1Class2{ publicstatic void main(String[] args){ Bao1Class.name= "小明"; }}protected:子類訪問權限修飾符,修飾的成員可以被同包、不同包下的子類訪問。通常使用該修飾符修飾的方法,希望其子類能重寫這個方法。
package bao1;public class Bao1Class{ staticprotected String name;}Bao2Class不可以訪問Bao1Class的name屬性package bao2;import bao1.*;class Bao2Class { publicstatic void main(String[] args){ Bao1Class.name= "小明"; }}package bao1;public class Bao1Class{ staticprotected String name;}Bao2Class可以訪問Bao1Class的name屬性,因為有繼承關系package bao2;import bao1.*;class Bao2Class extends Bao1Class { publicstatic void main(String[] args){ Bao1Class.name= "小明"; }}public:公開訪問權限,這是最寬松的訪問權限級別,修飾的成員、類,那么這個成員或者外部類可以被所有包下所有類訪問。
權限最大,在任意位置,都可以訪問(注意:導包)

修飾符修飾類:
注意:外部類只能用public 或者默認權限修飾,不能使用private、protected來修飾。
因為被這兩個修飾的類不能被訪問,也就沒有任何意義。
//TODO代碼驗證權限修飾符
測試public修飾的類:
package bao1;public class Bao1Class{ staticpublic String name;} package bao2;import bao1.*;class Bao2Class{ publicstatic void main(String[] args){ Bao1Class.name= "小明"; }} 默認修飾符(default):package bao1;class Bao1Class{ staticpublic String name;}這樣是無法訪問的,因為Bao1Class權限僅限bao1包下,bao2中的類無法訪問。
package bao2;import bao1.*;class Bao2Class extends Bao1Class{ publicstatic void main(String[] args){ Bao1Class.name= "小明"; }}這樣是可以訪問的,因為Bao1Class2和Bao1Class在一個包下。
package bao1;class Bao1Class2{ publicstatic void main(String[] args){ Bao1Class.name= "小明"; }}不能使用private和protected修飾類


總結已學的修飾符。
static:靜態修飾符;
private:私有權限修飾符;
default:默認權限修飾符;
protected:子類權限修飾符;
public:公開的權限修飾符。
當使用private修飾構造器的時候,會出現什么情況?
Private修飾的方法,作用范圍是:類本身,其他類不能調用。

那么將對象的構造器方法都私有后,就無法創建對象了,那么在實際應用中,還可以通過暴露一個公開的方法,讓外界獲取一個對象。
classDuoTai { publicstatic void main(String[] args){ Personperson = Person.getPerson(); person.method(); } } classPerson { privatePerson(){ System.out.println("構造器方法執行"); } publicstatic Person getPerson(){ returnnew Person(); } public void method(){ System.out.println("執行person中的方法"); } } 單例設計模式
這種案例在實際開發中,我們以單例模式舉例。
當我們上網時候,發現顧客是多個,但是服務生是一個,并不是一個顧客有一個服務生。
那在這個時候,網管就是唯一的,那網管就是單例對象。
單例模式(Singleton Pattern)是 Java中最簡單的設計模式之一。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。
這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
注意:
· 1、單例類只能有一個實例。
· 2、單例類必須自己創建自己的唯一實例。
· 3、單例類必須給所有其他對象提供這一實例。
意圖:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
主要解決:一個全局使用的對象頻繁地創建與銷毀。
何時使用:當您想控制實例數目,節省系統資源的時候。
如何解決:判斷系統是否已經有這個單例,如果有則返回,如果沒有則創建。
關鍵代碼:構造函數是私有的。
更多相關資料請查閱:http://www.runoob.com/design-pattern/singleton-pattern.html
代碼實現:
class Singleton{ publicstatic void main(String[] args){ /*顯然,這種情況不符合常理 實際中,一個網管,服務多個顧客, 網管就是唯一的,我們把網管對象變為唯一 對象,就符合常理了。 */ Customerc1 = new Customer(); c1.surfing(); Customerc2 = new Customer(); c2.surfing(); Customerc3 = new Customer(); c2.surfing(); }}class Customer{ publicvoid surfing(){ Waiterwaiter = Waiter.getWaiter(); waiter.kaiji(); }}class Waiter{ privateWaiter(){ } privatestatic Waiter waiter; publicstatic Waiter getWaiter(){ //這個時候,我要保證每次獲取網管對象都是唯一的 if(waiter== null){ waiter= new Waiter(); returnwaiter; } returnwaiter; } int temp =0; publicvoid kaiji(){ System.out.println("開機" + temp +"號電腦"); temp++; }} 接下來介紹單例模式的兩種實現方式:
1、 懶漢式,顧名思義,獲取單例對象的操作采用偷懶的方式。
在代碼中,那就是說:你什么時候需要用到該對象,那么什么時候再創建。
如下代碼:當調用getWaiter方法獲取對象時,才會創建對象。而不調用getWaiter方法的話,是不會創建對象的。
class Waiter{ private Waiter(){ } privatestatic Waiter waiter; publicstatic Waiter getWaiter(){ //這個時候,我要保證每次獲取網管對象都是唯一的 if(waiter== null){ waiter= new Waiter(); returnwaiter; } returnwaiter; } int temp =0; publicvoid kaiji(){ System.out.println("開機" + temp +"號電腦"); temp++; }} 2、 餓漢式
和懶漢式相反,在類存在的時候,該類型對象就存在了!即便你沒有調用getWaiter方法,該對象也存在!
class Waiter{ privateWaiter(){ } privatestatic Waiter waiter = new Waiter(); publicstatic Waiter getWaiter(){ /*這個時候,不管有沒有調用該方法, 返回的對象就已經存在了!而且該對象 屬于類,所以每次調用該方法,返回 的就是唯一的Waiter*/ returnwaiter; } int temp =0; publicvoid kaiji(){ System.out.println("開機" + temp +"號電腦"); temp++; }} 其實這兩種單例模式在開發中也并不是非常完美,但是作為現階段,我們只需熟練編寫上面兩個案例即可,如果還有興趣,可以參考下面鏈接的文章,還有其他創建單例對象的方式(按住ctrl鍵單擊)。
http://www.runoob.com/design-pattern/singleton-pattern.html
思考案例:
情況1:
定義Person類,定義Man類,兩個類不在同一個包下
package bao1;public class Person{ protectedint age;} import bao1.Person;public class Man{ Person p = new Person(); publicvoid setAge(){ p.age= 12; }}編譯Man類會報錯,因為age屬性是protected修飾,必須是Person本包下或它的子類才可以訪問,這里Man即不在同一個包下,也不是Person子類!
情況2:
定義Person類,定義Man類,兩個類不在同一個包下
package bao1;public class Person{ protectedint age;}
import bao1.Person;public class Man extends Person{ Person p = new Person(); publicvoid setAge(){ p.age= 12; }}這種情況還是會報錯,因為雖然Man繼承了Person,但是,實際上訪問age屬性的還是Person對象,這里并不滿足Man類和Person類在同一包下,也不滿足調用age屬性的對象是Person對象的子類。這里的p指的是Person類型對象,不是他的子類對象。
情況3:
正確的情況:
定義Person類,定義Man類,兩個類不在同一個包下
package bao1;public class Person{ protectedint age;}
import bao1.Person;public class Man extends Person{ Man p =new Man(); publicvoid setAge(){ p.age= 12; }}
這里,p.age是可以的,雖然Man不和Person在同一個包下,但是,創建出來訪問age屬性的對象是Person類型對象的子類對象,符合protected權限修飾符的訪問范圍,所以不報錯!
情況4:
編寫測試類TestPro,測試并思考:
package bao1;public class Person{ protectedint age;}
import bao1.Person;public class Man extends Person{ public intgetAge(){ returnthis.age; } publicvoid setAge(int age){ this.age= age; }}
class TestPro{ publicstatic void main(String[] args){ Manman = new Man(); man.setAge(123); intage = man.getAge(); System.out.println(age); }}
這里通過測試類創建man對象,然后調用Man對象的age屬性。
在man對象中,this代表的是當前對象,這個當前對象又是Person類的子類對象,所以是可以訪問protected修飾的age屬性,所以不會報錯!!
新聞熱點
疑難解答