在描述對象時候,例如描述對象爲狗,可以設置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;
}
}
概念:
封裝是面向對象三大特徵之一,它指的是將對象的狀態信息隱藏在對象內部,不允許外部程序直接訪問對象內部信息,而是通過該類提供的方法來實現對內部信息的訪問。這樣,當我們設置屬性值的時候,可以判斷設置的屬性值是否合理。
//TODO
private 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屬性,所以不會報錯!!