訪問權限控制
1.Java文件組織
.java文件: 一個包中有多個.java文件,每個java文件都是一個編譯單元。編譯一個.java文件時,.java文件中的每個類都會有一個輸出文件,輸出文件名稱和.java文件中每個類的名稱相同,只是多一個後綴名.class。每個編譯單元內只能有一個public類,該類的名稱必須與文件的名稱相同(包括大小寫)。非public類對包外不可見,主要爲public class服務。
可運行程序:是一組可以打包並壓縮爲一個Java文檔文件的.class文件。
package: 類庫是一組類文件,每個文件都有一個public類,以及任意數量的非public類。因此每個文件都有一個構件。若將這些構件(每一個都有自己獨立的.java和.class文件)從屬於同一個羣組,就可以使用關鍵字package。除註釋外的第一行代碼,在文件起始處寫。
注意Java包的命名全部選用小寫字母。
2.類庫的使用
使用import關鍵字:import java.lang.* import java.util.ArrayList
條件編譯:不改變程序代碼,可以切換開關產生不同的行爲,如調試功能。
3.包權限訪問修飾詞
pubic、protected、friendly、private
包訪問權限:
- 如果包中成員爲public,則包及包之外都可以訪問該成員。
- 如果包中成員爲friendly,則只有包之內的才能對其進行訪問。
- 如果包中成員爲private,則只有包含該成員的類才能對其進行訪問。——但是可以通過訪問器(accessor)和變異器(mutator)方法(get/set)讀取或者改變原來的值。
- 繼承的類可以訪問protected和public成員
//get、set方法
public class User{
private String username;
private int age;
public void setUsername(String username){
this.username = username;
}
public char getUsername(String username){
return username;
}
}
類的訪問權限修飾符:public friendly
成員變量和成員方法訪問權限修飾符:private public protected friendly
4.重點程序 lunch.java:
class Soup1{
private Soup1() {}//構造器私有化
public static Soup1 makesoup(){//用static方法不創建對象,調用構造器
return new Soup1();
}
}
class Soup2{
private Soup2() {}
private static Soup2 ps1 = new Soup2();
//私有的靜態對象,容易忘記ps1是靜態的
public static Soup2 access(){
return ps1;//只能訪問唯一的Soup2對象
}
public void f() {}
}
public class Lunch{
void testStatic() {
Soup1 soup = Soup1.makesoup();//靜態方法創建對象,可創建多個
}
void testSingleton() {
Soup2.acess().f();//只可以訪問唯一的一個對象
}
}
5.包名的編碼和解析
包名的第一部分是類的創建者的反順序的域名。
第二部分是將package名稱分解爲機器上的一個目錄。當Java程序運行需要加載class文件時,可以確定文件在目錄上所處的位置。首先,Java解釋器將找出環境變量CLASSPATH,CLASSPATH包含一個或多個目錄,用作查找.class文件的根目錄。從根目錄開始,解釋器獲取包的名稱並將每個句點替換成反斜槓,以從CLASSPATH根中產生一個路徑名稱。
6.封裝
封裝:封裝是指把數據成員和相關方法都放進一個類中,並使用訪問權限來控制其可見性。
第六章 複用類
1.組合
在新類中使用現有類的對象作爲成員變量。由於新的類是有現有類的對象組成,所以這種方法稱爲組合。
組合複用的是功能。
對象引用的三種初始化方法:
- 指定初始化:
在定義對象的地方進行初始化,這意味着在構造器調用之前他們完成了初始化。
- 構造器初始化:
在構造器中初始化對象。
- 懶惰初始化
就在正要使用對象之前,進行初始化。對象引用初始化爲NULL。
2.繼承
按照現有類的類型創造新類。不改變類的形式,增加新的成員變量和方法。
繼承複用的是接口。
爲便於繼承,成員變量定義爲private,方法定義爲public。
2.1 extends 和 super 關鍵字用法:
class Cleanser{
public void scrub() {}
}
public class Detergent extends Cleanser{//用extends
public void scrub(){
append("Detergent.scrub()");
super.scrub();//調用基類的方法
}
}
super用於調用基類的方法,和在基類構造器帶參數的情況下,在派生類構造器中顯式調用基類構造器。
class Game{
Game(int i) {
}
}
class BoardGame extends Game{
BoardGame(int i){
super(i);
}
}
//或者也可以
class ComputerGame extends Game{
Computer(){
super(11);
}
}
2.2 名稱屏蔽
如果Java中基類已經擁有某個已被多次重載的方法名稱,那麼在導出類中重新定義該方法並不會屏蔽在基類中的任何版本(這一點與C++不同)。
重寫是重新覆蓋父類的方法,在一些子類要實現的方法中,方法名、參數列表和返回值類型都和父類的方法一樣,但具體實現與父類不同,這時候就需要重寫父類的方法。
如果沒有重寫,那麼子類調用一個子類沒有的方法時,實際上是在調用父類。
以下是方法重載的一個例子。
class Homer {
char doh(char 'c') {
print("doh(char)");
}
float doh(float f) {
print("doh(float)");
}
}
class Milhouse {}
class Bart extends Homer {
void doh(Milhouse m) {
print("doh(Milhouse)");
}
}
public class Hide{
public static void main(String[] args) {
Bart b = new Bart();
b.doh(1);//doh(float)
b.doh('x');//doh(char)
b.doh(new Milhouse());//doh(Milhouse)
} //不能構造b.doh(0.2324);可以構造b.doh(0.2324f);
}
2.3 基類中的protected成員和方法可以被派生類訪問
3.final關鍵字
3.1 final數據
對於基本類型,final使基本類型的數值不發生改變。
對於對象引用,final使引用恆定不變。但是對象的數值是可以修改的。
必須在域的定義處或者每個構造器中用表達式對final進行賦值。
注意:是每個構造器!
final與static:
對於常量,兩者差別不大。最好使用public static final
來聲明常量。
帶有恆定初始值的final static常量全部用大寫字母命名,單詞之間用下劃線隔開。
private final int valueone = 9;
private static final int VAULE_TWO = 99;
對於特殊變量:也不能認爲某數據是final的,所以一定可以在編譯前知道它的值。
private final int i4 = rand.nextInt(20);
//每次new新對象時值會改變,對於同一個對象值不變
private static final int INT_5 = rand.nextInt(20);
//對於所有創建的對象,經過第一次創建對象初始化後,值永遠不會發生改變。
final參數:
Java允許在參數列表中,以聲明的方式將參數指明爲final。這意味着將無法在方法中更改引用所指向的對象。
3.2 final方法
目的:鎖定方法,防止派生類修改;考慮效率,如果一個方法是final的,所有對該方法的調用都轉爲內嵌調用。
3.3 final類
不可以被繼承
4.基類的初始化
(1)在創建派生類對象時,該對象包含一個基類的子對象。基類的子對象的初始化是通過調用基類構造器實現,自動在派生類的構造器中插入對基類構造器的調用。
(2)基類的構造器在派生類構造器之前被調用。
(3)如果沒有默認的構造器或構造器帶參數必須顯式的調用。
class Insect {
int i = 9;
int j;
int m = prt("Insect.m initialized");
Insect() {
prt("i = " + i + ", j = " + j);
j = 39;
}
static int x1 =
prt("static Insect.x1 initialized");
static int prt(String s) {
System.out.println(s);
return 47;
}
}
public class Beetle extends Insect {
int k = prt("Beetle.k initialized");
int l = prt("Beetle.l initialized");
Beetle() {
prt("k = " + k);
prt("j = " + j);
}
static int x2 =
prt("static Beetle.x2 initialized");
public static void main(String[] args) {
prt("Beetle constructor");
Beetle b = new Beetle();
}
}
第一步:訪問Beetle.main()函數
第二步:加載器啓動並找出Beetle類的編譯代碼(在Beetle.class中)
第三步:繼續加載基類Insect.class
第四步:對基類中的static進行初始化
第五步:對派生類中的static進行初始化
第六步:創建對象
第七步:將對象的內存設爲二進制零值,所有的基本類型設爲默認值,所有的引用設爲NULL
第八步:基類實例變量初始化
第九步:調用基類構造器
第十步:派生類實例變量初始化
十一步:調用派生類構造器
執行結果:
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
Insect.m initialized
i = 9, j = 0
Beetle.k initialized
Beetle.l initialized
k = 47
j = 39