在面向?qū)ο蟪淌皆O(shè)計方法中,封裝(Encapsulation)是指一種將抽象性函式接口的實現(xiàn)細節(jié)部分包裝、隱藏起來的方法。
封裝可以被認為是一個保護屏障、防止該類的代碼和數(shù)據(jù)被外部類定義的代碼隨機訪問。
要訪問該類的代碼和數(shù)據(jù),必須通過嚴格的接口控制。
封裝的主要的功能在于我們能修改自己的實現(xiàn)代碼,而不用修改那些調(diào)用我們代碼的程序片段。
適當(dāng)?shù)姆庋b可以讓程式碼更容易理解與維護,也將強了程式碼的安全性。
封裝的優(yōu)點
- 良好的封裝能夠減少耦合
- 類內(nèi)部的結(jié)構(gòu)可以自由修改
- 可以對成員變量進行更精確的控制
- 隱藏信息,實現(xiàn)細節(jié)
實現(xiàn)Java的封裝
- 修改屬性的可見性來限制對屬性的訪問(一般限制為private),例如:
public class Person {
private String name;
private int age;
}
這段代碼中,將name和age屬性設(shè)置為私有的,只能本類才能訪問,其它類都訪問不了,如此就對信息進行了隱藏。
- 對每個值屬性提供對外的公共方法訪問,也就是創(chuàng)建一對賦值方法,用于對私有屬性的訪問,例如:
public class Person{
private String name;
private int age;
?
public int getAge(){
return age;
}
?
public String getName(){
return name;
}
?
public void setAge(int age){
this.age = age;
}
?
public void setName(String name){
this.name = name;
}
}
采用this關(guān)鍵字是為了解決實例變量(private String name)和局部變量(setName(String name)中的name變量)之間發(fā)生的同名的沖突。
讓我們來看一個Java封裝類的例子:
/* 文件名: EncapTest.java */
public class EncapTest{
private String name;
private String idNum;
private int age;
public int getAge(){
return age;
}
public String getName(){
return name;
}
public String getIdNum(){
return idNum;
}
public void setAge( int newAge){
age = newAge;
}
public void setName(String newName){
name = newName;
}
public void setIdNum( String newId){
idNum = newId;
}
}
以上實例中public方法是外部類訪問該類成員變量的入口。
通常情況下,這些方法被稱為getter和setter方法。
因此,任何要訪問類中私有成員變量的類都要通過這些getter和setter方法。
通過如下的例子說明EncapTest類的變量怎樣被訪問:
/* F文件名 : RunEncap.java */
public class RunEncap{
public static void main(String args[]){
EncapTest encap = new EncapTest();
encap.setName("James");
encap.setAge(20);
encap.setIdNum("12343ms");
System.out.print("Name : " + encap.getName()+
" Age : "+ encap.getAge());
}
}
以上代碼編譯運行結(jié)果如下:
Name : James Age : 20
包
有時候在封裝的時候會遇到這樣的問題,就是我們的類名可能是重復(fù)的。為了更好地組織類,Java提供了包機制,用于區(qū)別類名的命名空間。
包的作用
- 把功能相似或相關(guān)的類或接口組織在同一個包中,方便類的查找和使用。
- 包采用了樹形目錄的存儲方式。同一個包中的類名字是不同,不同的包中的類的名字是可以相同的,當(dāng)同時調(diào)用兩個不同包中相同類名的類時,應(yīng)該加上包名加以區(qū)別。
- 包也限定了訪問權(quán)限,擁有包訪問權(quán)限的類才能訪問某個包中的類。
定義包語法
package 包名
//注意:必須放在源程序的第一行,包名可用"."號隔開
例如:
//我們在定義文件夾的時候利用"/"來區(qū)分層次
//包中我們用"."來分層
package com.shiyanlou.Java
不僅是我們這樣利用包名來區(qū)分類,系統(tǒng)也是這樣做的。
系統(tǒng)中的包
java.(功能).(類)
java.lang.(類) 包含java語言基礎(chǔ)的類
java.util.(類) 包含java語言中各種工具類
java.io.(類) 包含輸入、輸出相關(guān)功能的類
那我們怎么在不同包中使用另一個文件中的類呢?這時候就需要用到import關(guān)鍵字。比如我們要導(dǎo)入實驗樓下People這個類。import com.shiyanlou.People,同時如果import com.shiyanlou.*這是將包下的所有文件都導(dǎo)入進來,*是通配符。
這里要注意一點,包的命名規(guī)則是全小寫字母拼寫。
訪問修飾符
我們在前面的代碼中經(jīng)常用到private和public修飾符,這些修飾符的作用和意義是什么呢?接下來我們就來學(xué)習(xí)Java中的訪問修飾符。
訪問修飾符可以用來修飾屬性和方法的訪問范圍。
| 訪問修飾符 | 本類 | 同包 | 子類 | 其它 |
|---|---|---|---|---|
| private | √ | |||
| 默認 | √ | √ | ||
| protected | √ | √ | √ | |
| public | √ | √ | √ | √ |
如圖所示,代表了不同的訪問能修飾符的訪問范圍,比如 private 修飾符的屬性或者方法,只能在當(dāng)前類中中訪問或者使用。默認是什么修飾符都不加,默認在當(dāng)前類中和同一包下都可以訪問和使用。protected修飾的屬性或者方法,對同一包內(nèi)的類和所有子類可見。public修飾的屬性或者方法,對所有類可見。
我們可以舉一個例子,比如 money,如果我們用private修飾代表著這是私有的,只能我自己可以使用。如果是protected代表著我可以使用,和我有關(guān)系的人,比如兒子也可以用。如果是public就代表了所有人都可以使用。
內(nèi)部類
可以將一個類的定義放在另一個類的定義內(nèi)部,這就是內(nèi)部類。而包含內(nèi)部類的類被稱為外部類。
內(nèi)部類的主要作用如下:
- 內(nèi)部類提供了更好的封裝,可以把內(nèi)部類隱藏在外部類之內(nèi),不允許同一個包中的其它類訪問該類
- 內(nèi)部類的方法可以直接訪問外部類的所有數(shù)據(jù),包括私有的數(shù)據(jù)
- 內(nèi)部類所實現(xiàn)的功能使用外部類同樣可以實現(xiàn),只是有時使用內(nèi)部類更方便
- 內(nèi)部類允許繼承多個非接口類型
注意:內(nèi)部類是編譯時的概念,一旦編譯成功,就會稱為完全不同的兩類。對于一個名為outer的外部類和其內(nèi)部定義的名為inner的內(nèi)部類。編譯完成后出現(xiàn)outer.class和outer$inner.class兩類。所以內(nèi)部類的成員變量/方法名可以和外部類的相同。
我們通過代碼來詳細學(xué)習(xí)以下內(nèi)部類把!
成員內(nèi)部類
ackage com.shiyanlou;
//外部類People
public class People {
private String name = "LiLei"; //外部類的私有屬性
//內(nèi)部類Student
public class Student {
String ID = "20151234"; //內(nèi)部類的成員屬性
//內(nèi)部類的方法
public void stuInfo(){
System.out.println("訪問外部類中的name:" + name);
System.out.println("訪問內(nèi)部類中的ID:" + ID);
}
}
//測試成員內(nèi)部類
public static void main(String[] args) {
People a = new People(); //創(chuàng)建外部類對象,對象名為a
Student b = a.new Student(); //使用外部類對象創(chuàng)建內(nèi)部類對象,對象名為b
// 或者為 People.Student b = a.new Student();
b.stuInfo(); //調(diào)用內(nèi)部對象的suInfo方法
}
}
由此,我們可以直到,成員內(nèi)部類的使用方法:
- Student 類相當(dāng)于 People 類的一個成員變量,所以 Student 類可以使用任意訪問修飾符。
- Student 類在 People 類里,所以訪問范圍在類里的所有方法均可以訪問 People 的屬性(即內(nèi)部類里可以直接訪問外部類的方法和屬性,反之不行)
- 定義成員內(nèi)部類后,必須使用外部類對象來創(chuàng)建內(nèi)部類對象,即
內(nèi)部類 對象名 = 外部類對象.new 內(nèi)部類(); - 如果外部類和內(nèi)部類具有相同的成員變量或方法,內(nèi)部類默認訪問自己的成員變量或方法,如果要訪問外部類的成員變量,可以使用 this 關(guān)鍵字 加上述代碼中:a.this
注:成員內(nèi)部類不能含有static的變量和方法,因為成員內(nèi)部類需要先創(chuàng)建了外部類,才能創(chuàng)建它自己的。
靜態(tài)內(nèi)部類
靜態(tài)內(nèi)部類通常被稱為嵌套類。
package com.shiyanlou;
//外部類People
public class People {
private String name = "LiLei"; //外部類的私有屬性
/*外部類的靜態(tài)變量。
Java 中被 static 修飾的成員稱為靜態(tài)成員或類成員。它屬于整個類所有,而不是某個對象所有,即被類的所有對象所共享。靜態(tài)成員可以使用類名直接訪問,也可以使用對象名進行訪問。
*/
static String ID = "510xxx199X0724XXXX";
//靜態(tài)內(nèi)部類Student
public static class Student {
String ID = "20151234"; //內(nèi)部類的成員屬性
//內(nèi)部類的方法
public void stuInfo(){
System.out.println("訪問外部類中的name:" + (new People().name));
System.out.println("訪問外部類中的ID:" + People.ID);
System.out.println("訪問內(nèi)部類中的ID:" + ID);
}
}
//測試成員內(nèi)部類
public static void main(String[] args) {
Student b = new Student(); //直接創(chuàng)建內(nèi)部類對象,對象名為b
b.stuInfo(); //調(diào)用內(nèi)部對象的suInfo方法
}
}
以上代碼編譯運行結(jié)果如下:
訪問內(nèi)外部類中的name:LiLei
訪問外部類中的ID:510xxx199X0724XXXX
訪問內(nèi)部類中的ID:20151234
靜態(tài)內(nèi)部類是static修飾的內(nèi)部類,這種內(nèi)部類的特點是:
- 靜態(tài)內(nèi)部類不能直接訪問外部類的非靜態(tài)成員,但可以通過
new 外部類().成員的方式訪問 - 如果外部類的靜態(tài)成員與內(nèi)部類的成員名稱相同,可通過
類名.靜態(tài)成員訪問外部類的靜態(tài)成員;如果外部類的靜態(tài)成員與內(nèi)部類的成員名稱不相同,則可通過成員名直接調(diào)用外部類的靜態(tài)成員 - 創(chuàng)建靜態(tài)內(nèi)部類的對象時,不需要外部類的對象,可以直接創(chuàng)建
內(nèi)部類 對象名=new 內(nèi)部類();
局部內(nèi)部類
局部內(nèi)部類,是指內(nèi)部類定義在方法和作用域內(nèi)。
例如:
package com.shiyanlou;
//外部類People
public class People {
//定義在外部類中的方法內(nèi):
public void peopleInfo() {
final String sex = "man"; //外部類方法中的常量
class Student {
String ID = "20151234"; //內(nèi)部類中的常量
public void print() {
System.out.println("訪問外部類的方法中的常量sex:" + sex);
System.out.println("訪問內(nèi)部類中的變量ID:" + ID);
}
}
Student a = new Student(); //創(chuàng)建方法內(nèi)部類的對象
a.print();//調(diào)用內(nèi)部類的方法
}
//定義在外部類中的作用域內(nèi)
public void peopleInfo2(boolean b) {
if(b){
final String sex = "man"; //外部類方法中的常量
class Student {
String ID = "20151234"; //內(nèi)部類中的常量
public void print() {
System.out.println("訪問外部類的方法中的常量sex:" + sex);
System.out.println("訪問內(nèi)部類中的變量ID:" + ID);
}
}
Student a = new Student(); //創(chuàng)建方法內(nèi)部類的對象
a.print();//調(diào)用內(nèi)部類的方法
}
}
//測試方法內(nèi)部類
public static void main(String[] args) {
People b = new People(); //創(chuàng)建外部類的對象
System.out.println("定義在方法內(nèi):===========");
b.peopleInfo(); //調(diào)用外部類的方法
System.out.println("定義在作用域內(nèi):===========");
b.peopleInfo2(true);
}
}
以上編譯運行結(jié)果如下:
定義在方法內(nèi):===========
訪問外部類的方法中的常量sex:man
訪問內(nèi)部類中的變量ID:20151234
定義在作用域內(nèi):===========
訪問外部類的方法中的常量sex:man
訪問內(nèi)部類中的變量ID:20151234
局部內(nèi)部類也像別的類一樣進行編譯,但只是作用域不同而已,只在該方法或條件的作用域內(nèi)才能使用,退出這些作用域后無法引用的。
匿名內(nèi)部類
匿名內(nèi)部類,顧名思義,就是沒有名字的內(nèi)部類。正因為沒有名字,所有匿名內(nèi)部類只能使用一次,它通常用來簡化代碼編寫。但使用匿名內(nèi)部類還有個前提條件:必須繼承一個父類或?qū)崿F(xiàn)一個接口。
例如:
public class Outer {
public Inner getInner(final String name, String city) {
return new Inner() {
private String nameStr = name;
public String getName() {
return nameStr;
}
};
}
public static void main(String[] args) {
Outer outer = new Outer();
Inner inner = outer.getInner("Inner", "NewYork");
System.out.println(inner.getName());
}
}
interface Inner {
String getName();
}
以上代碼編譯運行結(jié)果如下:
Inner
匿名內(nèi)部類是不能加訪問修飾符的。要注意的是,new 匿名類,這個類是要先定義的,如果不限定義,編譯時會報錯該類找不到。
同時,在上面的例子中,當(dāng)所在的方法的形參需要在內(nèi)部類里面使用時,該形參必須為final。這里可以看到形參 name 已經(jīng)定義為 final 了,而形參 city 沒有被使用則不用定義為 final。
然而,因為匿名內(nèi)部類沒名字,是用默認的構(gòu)造函數(shù)的,無參數(shù)的,如果需要該類有帶參數(shù)的構(gòu)造函數(shù),示例如下:
public Inner getInner(final String name, String city) {
return new Inner(name, city) {
private String nameStr = name;
public String getName() {
return nameStr;
}
};
}
注意這里的形參 city,由于它沒有被匿名內(nèi)部類直接使用,而是被抽象類 Inner 的構(gòu)造函數(shù)所使用,所以不必定義為 final。