在面向?qū)ο蟪淌皆O(shè)計方法中,封裝(Encapsulation)是指一種將抽象性函式接口的實(shí)現(xiàn)細(xì)節(jié)部分包裝捅彻、隱藏起來的方法。
封裝可以被認(rèn)為是一個保護(hù)屏障、防止該類的代碼和數(shù)據(jù)被外部類定義的代碼隨機(jī)訪問凛剥。
要訪問該類的代碼和數(shù)據(jù),必須通過嚴(yán)格的接口控制轻姿。
封裝的主要的功能在于我們能修改自己的實(shí)現(xiàn)代碼犁珠,而不用修改那些調(diào)用我們代碼的程序片段。
適當(dāng)?shù)姆庋b可以讓程式碼更容易理解與維護(hù)互亮,也將強(qiáng)了程式碼的安全性犁享。
封裝的優(yōu)點(diǎn)
- 良好的封裝能夠減少耦合
- 類內(nèi)部的結(jié)構(gòu)可以自由修改
- 可以對成員變量進(jìn)行更精確的控制
- 隱藏信息,實(shí)現(xiàn)細(xì)節(jié)
實(shí)現(xiàn)Java的封裝
- 修改屬性的可見性來限制對屬性的訪問(一般限制為private)豹休,例如:
public class Person {
private String name;
private int age;
}
這段代碼中炊昆,將name和age屬性設(shè)置為私有的,只能本類才能訪問威根,其它類都訪問不了凤巨,如此就對信息進(jìn)行了隱藏。
- 對每個值屬性提供對外的公共方法訪問洛搀,也就是創(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)鍵字是為了解決實(shí)例變量(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;
}
}
以上實(shí)例中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());
}
}
以上代碼編譯運(yùn)行結(jié)果如下:
Name : James Age : 20
包
有時候在封裝的時候會遇到這樣的問題,就是我們的類名可能是重復(fù)的瓶堕。為了更好地組織類隘道,Java提供了包機(jī)制,用于區(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)入實(shí)驗樓下People
這個類裁着。import com.shiyanlou.People
,同時如果import com.shiyanlou.*
這是將包下的所有文件都導(dǎo)入進(jìn)來暮刃,*
是通配符跨算。
這里要注意一點(diǎn),包的命名規(guī)則是全小寫字母拼寫椭懊。
訪問修飾符
我們在前面的代碼中經(jīng)常用到private
和public
修飾符,這些修飾符的作用和意義是什么呢步势?接下來我們就來學(xué)習(xí)Java中的訪問修飾符氧猬。
訪問修飾符可以用來修飾屬性和方法的訪問范圍。
訪問修飾符 | 本類 | 同包 | 子類 | 其它 |
---|---|---|---|---|
private | √ | |||
默認(rèn) | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
如圖所示坏瘩,代表了不同的訪問能修飾符的訪問范圍盅抚,比如 private
修飾符的屬性或者方法,只能在當(dāng)前類中中訪問或者使用倔矾。默認(rèn)
是什么修飾符都不加妄均,默認(rèn)在當(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)部類所實(shí)現(xiàn)的功能使用外部類同樣可以實(shí)現(xiàn),只是有時使用內(nèi)部類更方便
- 內(nèi)部類允許繼承多個非接口類型
注意:內(nèi)部類是編譯時的概念渗鬼,一旦編譯成功览露,就會稱為完全不同的兩類。對于一個名為outer的外部類和其內(nèi)部定義的名為inner的內(nèi)部類譬胎。編譯完成后出現(xiàn)outer.class和outer$inner.class兩類差牛。所以內(nèi)部類的成員變量/方法名可以和外部類的相同。
我們通過代碼來詳細(xì)學(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)部類默認(rèn)訪問自己的成員變量或方法诅炉,如果要訪問外部類的成員變量蜡歹,可以使用 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)成員可以使用類名直接訪問溯香,也可以使用對象名進(jìn)行訪問。
*/
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方法
}
}
以上代碼編譯運(yùn)行結(jié)果如下:
訪問內(nèi)外部類中的name:LiLei
訪問外部類中的ID:510xxx199X0724XXXX
訪問內(nèi)部類中的ID:20151234
靜態(tài)內(nèi)部類是static修飾的內(nèi)部類玫坛,這種內(nèi)部類的特點(diǎn)是:
- 靜態(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);
}
}
以上編譯運(yùn)行結(jié)果如下:
定義在方法內(nèi):===========
訪問外部類的方法中的常量sex:man
訪問內(nèi)部類中的變量ID:20151234
定義在作用域內(nèi):===========
訪問外部類的方法中的常量sex:man
訪問內(nèi)部類中的變量ID:20151234
局部內(nèi)部類也像別的類一樣進(jìn)行編譯蒸矛,但只是作用域不同而已,只在該方法或條件的作用域內(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();
}
以上代碼編譯運(yùn)行結(jié)果如下:
Inner
匿名內(nèi)部類是不能加訪問修飾符
的吮铭。要注意的是时迫,new 匿名類,這個類是要先定義的
谓晌,如果不限定義,編譯時會報錯該類找不到癞揉。
同時纸肉,在上面的例子中溺欧,當(dāng)所在的方法的形參需要在內(nèi)部類里面使用時,該形參必須為final
柏肪。這里可以看到形參 name 已經(jīng)定義為 final 了姐刁,而形參 city 沒有被使用則不用定義為 final。
然而烦味,因為匿名內(nèi)部類沒名字聂使,是用默認(rèn)的構(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。