我們知道,封裝將數據和處理數據的代碼連接起來。同時,封裝也提供另一個重要屬性:訪問控制(aclearcase/" target="_blank" >ccess control )。通過封裝你可以控制程序的哪一部分可以訪問類的成員。通過控制訪問,可以阻止對象的濫用。例如,通過只允許適當定義的一套方法來訪問數據,你能阻止該數據的誤用。因此,如果使用得當,可以把類創建一個“黑盒子”,雖然可以使用該類,但是它的內部機制是不公開的,不能修改。但是,本書前面創建的類可能不會完全適合這個目標。例如,考慮在第6章末尾示例的Stack類。方法push( ) 和pop() 確實為堆棧提供一個可控制的接口,這是事實,但這個接口并沒被強制執行。也就是說,程序的其他部分可以繞過這些方法而直接存取堆棧,這是可能的。當然,如果使用不當,這可能導致麻煩。本節將介紹能精確控制一個類各種各樣成員的訪問的機制。
一個成員如何被訪問取決于修改它的聲明的訪問指示符(access specifier )。Java 提供一套豐富的訪問指示符。存取控制的某些方面主要和繼承或包聯系在一起(包,package,本質上是一組類)。Java 的這些訪問控制機制將在以后討論?,F在,讓我們從訪問控制一個簡單的類開始。一旦你理解了訪問控制的基本原理,其他部分就比較容易了。
Java 的訪問指示符有public (公共的,全局的)、private (私有的,局部的)、和protected (受保護的)。Java 也定義了一個默認訪問級別。指示符protected僅用于繼承情況中。下面我們描述其他兩個訪問指示符。
讓我們從定義public 和private 開始。當一個類成員被public 指示符修飾時,該成員可以被你的程序中的任何其他代碼訪問。當一個類成員被指定為private 時,該成員只能被它的類中的其他成員訪問?,F在你能理解為什么main( ) 總是被public 指示符修飾。它被在程序外面的代碼調用,也就是由Java 運行系統調用。如果不使用訪問指示符,該類成員的默認訪問設置為在它自己的包內為public ,但是在它的包以外不能被存?。ò鼘⒃谝院蟮恼鹿澲杏懻摚?。
到目前為止,我們開發的類的所有成員都使用了默認訪問模式,它實質上是public 。然而,這并不是你想要的典型的方式。通常,你想要對類數據成員的訪問加以限制,只允許通過方法來訪問它。另外,有時你想把一個方法定義為類的一個私有的方法。
訪問指示符位于成員類型的其他說明的前面。也就是說,成員聲明語句必須以訪問指示符開頭。下面是一個例子:
public int i;
private double j;
private int myMethod(int a,char b) { // ...
要理解public 和private 對訪問的作用,看下面的程序:
/* This program demonstrates the difference between
public and private.
*/
class Test {
int a; // default access
public int b; // public access
private int c; // private access
// methods to access c
void setc(int i) { // set c's value
c = i; }
int getc() { // get c's value
return c;
}
}
class AccessTest {
public static void main(String args[]) {
Test ob = new Test();
// These are OK,a and b may be accessed directlyob.a = 10;ob.b = 20;
// This is not OK and will cause an error
// ob.c = 100; // Error!
// You must access c through its methodsob.setc(100); // OKSystem.out.println("a,b,and c: " + ob.a + " " +
ob.b + " " + ob.getc());
}
}
可以看出,在Test 類中,a使用默認訪問指示符,在本例中與public 相同。b被顯式地指定為public 。成員c被指定為private ,因此它不能被它的類之外的代碼訪問。所以,在AccessTest 類中不能直接使用c。對它的訪問只能通過它的public 方法:setc()和getc() 。如果你將下面語句開頭的注釋符號去掉,
// ob.c = 100; // Error!
則由于違規,你不能編譯這個程序。為了理解訪問控制在實際中的應用,我們來看在第6章末尾所示的Stack 類的改進版本。
// This class defines an integer stack that can hold 10 values.
class Stack {
/* Now,both stck and tos are private. This means that they cannot be accidentally or maliciouslyaltered in a way that would be harmful to the stack.
*/private int stck[] = new int[10]; private int tos;
// Initialize top-of-stack
Stack() {
tos = -1;
}
// Push an item onto the stack void push(int item) {if(tos==9) System.out.println("Stack is full."); else
stck[++tos] = item; }// Pop an item from the stack
int pop() {
if(tos < 0) { System.out.println("Stack underflow."); return 0;
}
else
return stck[tos--];
}
}
在本例中,現在存儲堆棧的stck和指向堆棧頂部的下標tos ,都被指定為private 。這意味著除了通過push() 或pop(),它們不能夠被訪問或改變。例如,將tos 指定為private ,阻止你程序的其他部分無意中將它的值設置為超過stck 數組下標界的值。
下面的程序表明了改進的Stack 類。試著刪去注釋前面的線條來證明stck和tos 成員確實是不能訪問的。
class TestStack {
public static void main(String args[]) {
Stack mystack1 = new Stack();
Stack mystack2 = new Stack();
// push some numbers onto the stack
for(int i=0; i<10; i++) mystack1.push(i);
for(int i=10; i<20; i++) mystack2.push(i);
// pop those numbers off the stack
System.out.println("Stack in mystack1:");
for(int i=0; i<10; i++)
System.out.println(mystack1.pop());
System.out.println("Stack in mystack2:");
for(int i=0; i<10; i++)
System.out.println(mystack2.pop());
// these statements are not legal
// mystack1.tos = -2;
// mystack2.stck[3] = 100;
}
}
盡管由類定義的方法通常提供對數據的訪問,但情況并不總是這樣。當需要時允許一個實例變量為public 是完全合適的。例如,為簡單起見,本書中大多數的簡單類在創建時不關心實例變量的存取。然而,在大多數實際應用的類中,你將有必要僅僅允許通過方法來對數據操作。下一章將回到訪問控制的話題。你將看到,在繼承中訪問控制是至關重要的。
原文轉自:http://www.anti-gravitydesign.com