一、爲什麼要封裝?
封裝是面向對象的三大特徵之一(另外兩個是繼承和多態),它指的是將對象的狀態信息隱藏在對象內部,不允許外部程序直接訪問對象內部的信息,而是通過該類提供的方法來實現對內部信息的操作和訪問。
比如Person類的屬性中可能包含age,age屬性只能隨着時間流逝才能變大,通常不能隨意修改Person對象的age屬性。對一個類或對象實現封裝,好處很多:
- 隱藏類的實現細節
- 讓使用者只能通過事先預定的方法來訪問數據,從而可以在該方法里加入控制邏輯,限制對屬性的不合理訪問
- 便於修改,提高代碼的可維護性
爲了實現良好的封裝,通常從以下兩個方面着手:
- 將對象的屬性和實現細節隱藏起來,不允許外部直接訪問
- 把方法暴露出來,讓方法來操作或訪問這些屬性
二、訪問控制符
Java提供3個訪問控制符:private, protected, public
,分別代表了3個訪問控制級別。另外還有一個不加任何訪問控制符的訪問控制級別,提供了4個訪問控制級別。訪問控制級別從小到大如下圖所示:
4個訪問控制級別中的defalut
並沒有對應的訪問控制符,當不使用任何訪問控制符來修飾類或者類成員時,系統默認使用該訪問控制級別。4個訪問控制級別詳解如下:
private
:若類中某個成員使用private
修飾時,該成員只能在該類的內部被訪問。顯然,private
用於修飾屬性最合適,使用它來修飾屬性就可以把屬性隱藏在類的內部。defalut
:若類中某個成員不被任何訪問控制符修飾,則稱其爲默認訪問控制。defalut
訪問控制的成員或頂級類可以被相同包下的其他類訪問。protected
:若類中某個成員使用protected
修飾,那麼該成員既可以被同一個包的其他類訪問,也可以被不同包的子類訪問。通常情況下,若使用protected
來修飾一個方法,則是希望其子類來重寫這個方法。public
:若類中某個成員使用public
修飾,那麼該成員或頂級類可以被所有類訪問,不管訪問類和被訪問類是否在同一包中,是否具有父子繼承關係。
private | default | protected | public | |
---|---|---|---|---|
同一個類中 | ||||
同一個包中 | ||||
子類中 | ||||
全局範圍內 |
對於頂級類而言,只能有兩種訪問控制級別:public
和默認。頂級類不能使用private, protected
修飾,因爲頂級類既不處於任何類的內部,也就沒有其外部類的子類了,因此private, protected
訪問對於頂級類沒有意義。使用public
修飾的頂級類可被所有類訪問;不使用的話只能被同一包中的所有類訪問。
拿Person類舉個例子。Person類如下:
public class Person {
private int age;
private String name;
public void setName(String name)
{
if(name.length() > 10 || name.length() < 2)
{
System.out.println("您設置的名字不符合要求");
return;
}
this.name = name;
}
public String getName() { return this.name; }
public void setAge(int age)
{
if(age >= 150 || age < 0)
{
System.out.println("您設置的年齡不合法");
return;
}
this.age = age;
}
public int getAge() { return this.age; }
}
測試Person類如下:
public class TestPerson {
public static void main(String[] args)
{
Person p = new Person();
p.setName("程勇");
p.setAge(23);
System.out.println(p.getName());
System.out.println(p.getAge());
}
}
輸出結果如下:
程勇
23
如上述程序所示,TestPerson類中main方法不可直接修改Person對象的name和age屬性,只能通過各自的set方法來操作這兩個屬性值。這樣就能在set方法中增加自己的控制邏輯,從而保證Person對象的 name 和age屬性合法。
總結下訪問控制符使用的幾條基本原則
- 類中絕大部分屬性都應該使用
private
修飾,除了一些static
修飾的全局變量屬性。除此之外,有些方法只是用於輔助實現該類的其他方法,這些方法稱爲工具方法,也應使用private
修飾。 - 若某個類主要用於其他類的父類,這個類包含的大部分方法可能僅希望被其子類重寫,而不想被外界直接調用,應使用
protected
修飾。 - 類的構造器使用
public
修飾,暴露給其他類中創建該類的對象。頂級類通常的都希望被其他類自由調用,所以大部分頂級類都是public
修飾。
三、package和import
1、package
有成千上萬的類,類名難免會有重複,如何解決類的命名衝突問題呢?Java引入了package
機制,即在類名前加一個前綴來限定這個類。
Java允許將一組功能相關的類放在同一個package
下,從而組成邏輯上的類庫單元。若希望將一個類放在指定的包結構下,在該類所在源文件第一行加入一句
package packageName;
一旦在Java源文件中使用了package
語句,則意味着該源文件裏定義的所有類都屬於這個包。位於包中的每個類的完整類名都應該是包名和類名的組合,如果其他人需要使用該包下的類,也應該使用包名+類名的組合。
package lee;
public class Hello {
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
用javac -d . Hello.java
編譯上述代碼,則會在該路徑上生成一個lee
文件,並將Hello.java
放入這個文件中去。此時再執行java Hello.java
則能得到正確結果。
此時如果進入文件夾lee
,再使用java Hello.java
會報錯,表明此時Hello
類已經與lee
包綁定在一起,只能通過包名.類名
,即lee.Hello
訪問。上述命令操作如下圖所示:
值得注意的是:不要認爲只要把生成的class文件放在某個目錄下,這個目錄名就成了這個類的包名。包名必須在Java源文件中通過package
語句指定,而不是靠目錄名來指定。Java的包機制需要2個方面保證:
- 源文件裏使用
package
語句指定包名 - class文件必須放在對應的路徑下
package
語句一個源文件中只能有一條,即一個源文件只能指定一個包。該源文件中可以定義多個類,則這些類將全部位於該包下。
如果沒有顯示指定package
語句則處於默認包下。實際開發中不會這樣做。
2、import
正如下面看到的,如果需要使用不同包中的其他類時,總是需要使用該類的全名,這是一件很繁瑣的事情。爲簡化編程,Java引入了import
關鍵字,import
可以向某個Java文件中導入指定包層下某個類或全部類。import
語句應出現在package
語句(如果有的話)之後,可以有多條import
語句用於導入多個包層次下的類。舉個例子:
import java.util.HashMap; // 導入某個類
import java.util.*; // 導入java.util包層次下所有類
一旦在Java源文件中使用import
語句來導入指定類,在該源文件中使用這些類時可以省略包前綴。Java默認所有的源文件都導入java.lang
包下的所有類,包括System, String, Math, Integer, Float, Double, Byte
等。