Think In Java讀書筆記-1(第5——7章)- -
第5章 隱藏實(shí)現(xiàn)細(xì)節(jié)
一.Java訪問權(quán)限飾詞(access specifiers)
Java有public、protect、friendly、private四種訪問權(quán)限,并且這四訪問權(quán)限的訪問范圍越來越小。
1. friendly
1) 果一個(gè)class內(nèi)的數(shù)據(jù)成員或方法沒有任何權(quán)限飾詞,那么它的缺省訪問權(quán)限就是friendly。同一個(gè)package內(nèi)的其它所有classes都可以訪問friendly成員,但對(duì)package以外的classes則形同private。
2) 對(duì)于同一個(gè)文件夾下的、沒有用package的classes,Java會(huì)自動(dòng)將這些classes初見為隸屬于該目錄的default package,可以相互調(diào)用class中的friendly成員。如以下兩個(gè)class分別在同一個(gè)文件夾的兩個(gè)文件中,雖然沒有引入package,但隸屬于相同的default package。
class Sundae{
//以下兩個(gè)方法缺省為friendly
Sundae(){}
Void f() {System.out.println("Sundae.f()");
}
public class IceCream{
public static void main(String[] args){
Sundae x = new Sundae();
x.f();
}
}
2. public:可以被任何class調(diào)用
3. private:private成員只能在成員所屬的class內(nèi)被調(diào)用,如:
class Sundae{
private Sundae(){}//只能在Sundae class中被調(diào)用
Sundae(int i) {}
static Sundae makASundae() {
return new Sundae();
}
}
public class IceCream{
public static void main(String[] args){
// Sundae class中構(gòu)造函數(shù)Sundae()是private,
// 所以不能用它進(jìn)行初始化
//Sundae x = new Sundae();
Sundae y = new Sundae(1);//Sundae(int)是friendly,可以在此調(diào)用
Sundae z = Sundae.makASundae();
}
}
4. protected:具有friendly訪問權(quán)限的同時(shí),又能被subclass(當(dāng)然包括子孫類,即子類的子類)所訪問。即,既能被同一package中的classes訪問,又能被protected成員所在class的subclass訪問。
二.Class的訪問權(quán)限
1.Class同樣具有public、protect、friendly、private四種訪問訪問權(quán)限:
1)public:在任何地方都可被使用
2)protect、private:除了它自己,沒有任何class可以使用,所以class不能是
protected或private(inner class除外)
3) friendly:同一個(gè)package中的classes能用
2. 如何調(diào)用構(gòu)造函數(shù)被聲明為private的class
1) 用static函數(shù)
2) 用Singteton模式
class Soup{
private Soup(){}
//(1)靜態(tài)函數(shù)方法
public static Soup makeSout(){
return new Soup();
}
//(2)The "Singleton" pattern:
private static Soup ps1 = new Soup();
public static Soup access(){
return ps1;
}
public void f(String msg){
System.out.println("f(" + msg + ")");
}
}
public class Lunch{
public static void main(String[] args){
//Soup priv1 = new Soup();編譯錯(cuò)誤
Soup priv2 = Soup.makeSout();
Soup priv3 = Soup.access();
priv2.f("priv2");
priv3.f("priv3");
}
第6章 重復(fù)運(yùn)用classes
一.繼承(inheritance)
1. 在derived class中overriding某個(gè)函數(shù)時(shí),只能覆寫base class中的接口,即base class中的public或protected或friendly函數(shù)。如果試圖overriding一個(gè)private函數(shù),雖然編譯通過,但實(shí)際上你只是在derived class中添加了一個(gè)函數(shù)。如
class Cleanser{
private void prt(){//(b)
System.out.println("Cleanser.prt()");
}
}
public class ExplicitStatic extends Cleanser{
public void prt(){
System.out.println("ExplicitStatic.prt()");
}
public static void main(String[] args){
Cleanser x = new ExplicitStatic();
x.prt();//(a)
}
}
因?yàn)镃leanser中的prt()是private,所以不能在其derived class中被覆寫。ExplicitStatic中的prt()只是ExplicitStatic中的一個(gè)函數(shù),所以當(dāng)試圖在(a)處通過多態(tài)來調(diào)用prt()時(shí),會(huì)發(fā)生錯(cuò)誤。如果把(b)處的private去掉,則結(jié)果為
ExplicitStatic.prt()
2. Super的使用
1)通過關(guān)鍵字super可以調(diào)用當(dāng)前class的superclass(父類)。
例6.1.1.1
class Base{
Base(){System.out.println("Base()");}
public void scrub() { System.out.println(" Base.scrub()"); }
}
class Cleanser extends Base{
private String s = new String("Cleanser");
public void append(String a) { s+=a; }
public void dilute() { append(" dilute()"); }
public void apply() { append(" apply()"); }
public void scrub() { append(" scrub()"); }
public void print() { System.out.println(s); }
Cleanser(){
System.out.println("Cleanser(): " + s);
}
public static void testStatic(){
System.out.println("testStatic()");
}
public static void main(String[] args){
Cleanser x = new Cleanser();
x.dilute(); x.apply(); x.scrub(); x.print();
}
}
public class ExplicitStatic extends Cleanser{
ExplicitStatic(){
System.out.println("ExplicitStatic()");
}
public void scrub(){
append(" Detergen.scrub()");
super.testStatic();
super.scrub();//調(diào)用的是Cleanser.scrub()
}
public void foam() { append(" foam()"); }
public static void main(String[] args){
ExplicitStatic x = new ExplicitStatic();
x.dilute(); x.apply(); x.scrub(); x.foam();
x.print(); System.out.println("Test base class:");
Cleanser.main(args);
testStatic();
}
}
運(yùn)行結(jié)果:
Base()
Cleanser(): Cleanser
ExplicitStatic()
testStatic()
Cleanser dilute() apply() Detergen.scrub() scrub() foam()
Test base class:
Base()
Cleanser(): Cleanser
Cleanser dilute() apply() scrub()
testStatic()
2)通過super來調(diào)用superclass中的成員時(shí),調(diào)用的是最近成員。
例6.1.1.2
class Base{
protected String baseS = "Base";//(a)
//private String baseS = "Base";
Base(){System.out.println("Base()");}
}
class Cleanser extends Base{
protected String baseS = "Cleanser";//(b)
public String s = new String("Cleanser");
Cleanser(){
System.out.println("Cleanser(): " + s);
}
Cleanser(String a){
System.out.println("Cleanser(" + a + "): s = " + s );
}
}
public class ExplicitStatic extends Cleanser{
String s2 = s;
String baseS = super.baseS; //(c)
ExplicitStatic(){
super("ExplicitStatic");
System.out.println("ExplicitStatic():s2 = " + s2 + ", baseS = "+ baseS + "super.baseS = " + super.baseS);
baseS = "ExplicitStatic";
System.out.println("baseS = " + baseS + " , super.baseS = " + super.baseS);
}
public static void main(String[] args){
ExplicitStatic x = new ExplicitStatic();
}
}
結(jié)果1:
Base()
Cleanser(ExplicitStatic): s = Cleanser
ExplicitStatic():s2 = Cleanser, baseS = Cleanser,super.baseS = Cleanser
baseS = ExplicitStatic , super.baseS = Cleanser
在上面例子中,在三個(gè)class中都存在String bases實(shí)例。在ExplicitStatic中如果直接調(diào)用baseS,則實(shí)際調(diào)用的是當(dāng)前類ExplicitStatic中的baseS(即(c)處的成員);如果通過super.bases來調(diào)用baseS,則調(diào)用的是離當(dāng)前類ExplicitStatic最近的baseS成員,即Cleanser class中的baseS實(shí)例(即(b)處),產(chǎn)生的結(jié)果如結(jié)果1所示。如果把(b)處語句注釋掉,則將調(diào)用Base class中的baseS,結(jié)果如結(jié)果2所示。
結(jié)果2:
Base()
Cleanser(ExplicitStatic): s = Cleanser
ExplicitStatic():s2 = Cleanser, baseS = Base,super.baseS = Base
baseS = ExplicitStatic , super.baseS = Base
3. Base class的初始化
2.1 當(dāng)你產(chǎn)生derived class對(duì)象時(shí),其中會(huì)包含base class子對(duì)象(subobject)。這個(gè)子對(duì)象就和你另外產(chǎn)生的base class對(duì)象一模一樣。
2.2 通過super()可調(diào)用base class的構(gòu)造函數(shù),但必須放在構(gòu)造函數(shù)的第一行,并且只能在構(gòu)造函數(shù)中運(yùn)用。
2.3 初始化順序?yàn)椋?BR>1) 加載代碼(.class文件)
2) 初始化class的靜態(tài)成員,初始化順序了"從里到外",即從base class開始。
3) 在derived class的構(gòu)造函數(shù)中調(diào)用base class的構(gòu)造函數(shù)。
如果在derived class的構(gòu)造函數(shù)中沒有通過super()顯式調(diào)用調(diào)用base class的構(gòu)造函數(shù),編譯器會(huì)調(diào)用bass class的default構(gòu)造函數(shù)并自動(dòng)生成相應(yīng)的調(diào)用語句,從而產(chǎn)生一個(gè)base class實(shí)例。如果在derived class的構(gòu)造函數(shù)中通過super()顯示調(diào)用了父類的構(gòu)造函數(shù),則調(diào)用所指定的構(gòu)造函數(shù)。調(diào)用構(gòu)造函數(shù)的調(diào)用順序是"從里到外"。
4) 調(diào)用derived class的構(gòu)造函數(shù)。
**:當(dāng)base class沒有default構(gòu)造函數(shù)時(shí),必須在derived class的構(gòu)造函數(shù)中通過super顯示調(diào)用base class的構(gòu)造函數(shù)。
例:下面代碼的初始化過程為:
1) 裝載ExplicitStatic的代碼(裝載ExplicitStatic.class文件)。
2) 發(fā)現(xiàn)ExplicitStatic有關(guān)鍵字extends,裝載ExplicitStatic的base class的代碼(裝載Cleanser.class文件)。
3) 發(fā)現(xiàn)Cleanser有關(guān)鍵字extends,裝載Cleanser的base class的代碼(裝載Base.class文件)。
4) 初始化Base class中的靜態(tài)成員。
5) 初始化Cleanser class中的靜態(tài)成員。
6) 初始化ExplicitStatic class中的靜態(tài)成員。
如果把(c)處的代碼注釋掉,那么初始化工作到此就結(jié)束了。
7) 為ExplicitStatic對(duì)象分配存儲(chǔ)空間,并把存儲(chǔ)空間初始化為0。
8) 在ExplicitStatic class的構(gòu)造中調(diào)用super("ExplicitStatic")(在ExplicitStatic class的構(gòu)造函數(shù)中顯式調(diào)用父類的構(gòu)造函數(shù)),試圖產(chǎn)生一個(gè)Cleanser class實(shí)例。
9) 為Cleanser對(duì)象分配存儲(chǔ)空間,并把存儲(chǔ)空間初始化為0。
10) 由于Cleanser class又是繼承自Base class,會(huì)在Cleanser class的構(gòu)造函數(shù)中通過super()(由于沒有顯式調(diào)用父類的構(gòu)造函數(shù),所以自動(dòng)調(diào)用父類的default構(gòu)造函數(shù))調(diào)用父類的構(gòu)造函數(shù),試圖產(chǎn)生一個(gè)Cleanser class實(shí)例。
11) 產(chǎn)生一個(gè)Base class實(shí)例。先初始化成員變量,再調(diào)用構(gòu)造函數(shù)。
12) 回到Cleanser class,產(chǎn)生一個(gè)實(shí)例。首先初始化Cleanser class中的成員數(shù)據(jù),再執(zhí)行構(gòu)造函數(shù)Cleanser(String a)中的其余部分。
13) 回到ExplicitStatic class,產(chǎn)生一個(gè)實(shí)例。首先初始化ExplicitStatic class中的成員數(shù)據(jù),再執(zhí)行構(gòu)造函數(shù)ExplicitStatic ()中的其余部分(System.out.println("ExplicitStatic()"))。class Base{
static int s1 = prt("s1 initialized.", 11);
int i1 = prt("i1 initialized.", 12);
Base(){
System.out.println("Base()");
System.out.println("s1 = " + s1 + " ,i1 = " + i1);
draw();//(d)
}
void draw(){
System.out.println("base.draw:s1 = " + s1 + " ,i1 = " + i1);
}
static int prt(String s, int num) {
System.out.println(s);
return num;
}
}
class Cleanser extends Base{
static int s2 = prt("s2 initialized.", 21);
int i2 = prt("i2 initialized.", 22);
Cleanser(){
System.out.println("Cleanser()");
System.out.println("s2 = " + s2 + " ,i2 = " + i2);
}
Cleanser(String a){
//super();(b)
System.out.println("Cleanser(" + a + ")");
System.out.println("s2 = " + s2 + " ,i2 = " + i2);
}
void draw(){
System.out.println("Cleanser.draw:s2 = " + s2 + " ,i2 = " + i2);
}
}
public class ExplicitStatic extends Cleanser{
static int s3 = prt("s3 initialized.", 31);
int i3 = prt("i3 initialized", 31);
ExplicitStatic(){
super("ExplicitStatic");//(a)
System.out.println("ExplicitStatic()");
System.out.println("s3 = " + s3 + " ,i3 = " + i3);
}
public static void main(String[] args){
ExplicitStatic x = new ExplicitStatic();//(c)
}
}
結(jié)果:
s1 initialized.
s2 initialized.
s3 initialized.
//如果把(c)處的代碼注釋掉,輸出結(jié)果到此為止,不會(huì)輸出下面結(jié)果
i1 initialized.
Base()
s1 = 11 ,i1 = 12
Cleanser.draw:s2 = 21 ,i2 = 0//(d)處結(jié)果
i2 initialized.
Cleanser(ExplicitStatic)//(a)處結(jié)果
s2 = 21 ,i2 = 22
i3 initialized
ExplicitStatic()
s3 = 31 ,i3 = 31
由于在Base()中調(diào)用draw()時(shí),Cleanser中的i2還未進(jìn)行初始化,而在為Cleanser對(duì)象分配存儲(chǔ)空間時(shí),把存儲(chǔ)空間初始化為0,所以此時(shí)i2為0。
2.4 代碼及結(jié)果中的(a)說明了是先產(chǎn)生當(dāng)前class的base class的實(shí)例,否則在derived class中無法調(diào)用base class的成員。在調(diào)用Cleanser class的構(gòu)造函數(shù)Cleanser(String a)時(shí),在Cleanser(String a)中沒有用super顯式調(diào)用Base class的構(gòu)造函數(shù),所以系統(tǒng)會(huì)自動(dòng)生成調(diào)用Base class的default構(gòu)造函數(shù)的語句,如(b)。
4. 組合與繼承之間的快擇
. 1)繼承表示的是一種"is-a(是一個(gè))"的關(guān)系,如貨車是汽車中的一種;組合表示的是一種"has-a(有一個(gè))"的關(guān)系,如汽車有四個(gè)輪子。
2)是否需要將新的class向上轉(zhuǎn)型為base class。
5. 在繼承中的訪問權(quán)限
protect變量能被子孫類所調(diào)用。如Base class中的baseS能被Cleanser class和ExplicitStatic class調(diào)用。class Base{
protected String baseS = "Base";
//private String baseS = "Base";
Base(){System.out.println("Base()");}
}
class Cleanser extends Base{
protected String baseS = "Cleanser";
public String s = new String("Cleanser");
Cleanser(){
System.out.println("Cleanser(): " + s);
}
Cleanser(String a){
System.out.println("Cleanser(" + a + "): s = " + s );
}
}
public class ExplicitStatic extends Cleanser{
String s2 = s;
String baseS = super.baseS;
ExplicitStatic(){
super("ExplicitStatic");
System.out.println("ExplicitStatic():s2 = " + s2 + ", baseS = "
+ baseS + "super.baseS = " + super.baseS);
baseS = "ExplicitStatic";
System.out.println("baseS = " + baseS + " , super.baseS = " + super.baseS);
}
public static void main(String[] args){
ExplicitStatic x = new ExplicitStatic();
}
}
結(jié)果:
Base()
Cleanser(ExplicitStatic): s = Cleanser
ExplicitStatic():s2 = Cleanser, baseS = Cleanser, super.baseS = Cleanser
baseS = ExplicitStatic , super.baseS = Cleanser
二.關(guān)鍵字final
1.Final data
1.1 final data
1)當(dāng)基本型別被定義為final,表示它的數(shù)據(jù)值不能被改變。如
final int i = 9;
i++;//編譯錯(cuò)誤,不能改變I的值
2) 當(dāng)object reference被定義為final時(shí),不能改變的只是reference而不是對(duì)象本身。如
class Value{int i = 1;
}
public class ExplicitStatic extends Cleanser{
public static void main(String[] args){
final Value v = new Value();//v.i = 1
v.i++;//v.i = 2
//v = new Value();
}
}
由于v為final,所以不能通過new Value()使v重新指向一個(gè)對(duì)象;但是v所指向的對(duì)象的值是可以改變的(v.i++)。
1.2 blank finals
我們可以將數(shù)據(jù)成員聲明為final但不給予初值,這就是blank finals。但blank finals必須且只能在構(gòu)造函數(shù)中進(jìn)行初始化。public class ExplicitStatic {
final int ib;
final int i = 1;
ExplicitStatic()
{
ib = 2;//(a)
//i = 3; (b)
System.out.println("i = " + i + ", ib = " + ib);
}
public static void main(String[] args){
ExplicitStatic ex = new ExplicitStatic();
}
}
ib為blank finals,所以可以在構(gòu)造函數(shù)中進(jìn)行初始化。如果把(a)處的代碼注釋掉,則ib沒有初值,編譯出錯(cuò)。而i在定義處已進(jìn)行了初始化,則不能改變i的值,(b)處的代碼編譯錯(cuò)誤。
**:非blank finals成員即使在構(gòu)造函數(shù)中也不能更改其值
2.Final methods
1)被聲明為final的函數(shù)不能被覆寫
2)class中所有private函數(shù)自然而然會(huì)是final。
3. Final classes
1)當(dāng)一個(gè)class被聲明為final時(shí),表示它不能被繼承,但class的數(shù)據(jù)成員不是final,可以被改變。如class SmallBrain{}
final class Dinosaur{
int i = 7;
int j = i;
SmallBrain x = new SmallBrain();
void f(){};
}
//不能繼承final函數(shù)
//class Further extends Dinosaur{}
public class ExplicitStatic{
public static void main(String[] args){
Dinosaur n = new Dinosaur();
n.f();
n.i = 40;//final class中的non-final數(shù)據(jù)成員可以被改變
n.j++;
}
}
2)final class中的所有函數(shù)也都自然是final,因?yàn)闆]有人能夠加以覆寫。第7章 多態(tài)
一.再探向上轉(zhuǎn)型(upcasting)
將某個(gè)object reference視為一個(gè)"reference to base type"的動(dòng)作,稱為向上轉(zhuǎn)型。
1. Upcasting后調(diào)用某個(gè)函數(shù)時(shí),如果derived class中覆寫了該函數(shù),則會(huì)調(diào)用derived class中的函數(shù);否則,會(huì)調(diào)用base class中的函數(shù)。如class First{
public void prt(){
System.out.println("First");
}
}
class Second extends First{
//(a)
public void prt(){
System.out.println("Second");
}
}
public class ExplicitStatic{
public static void main(String[] args){
First n = new Second();
n.prt();;
}
}
結(jié)果為Second。如果當(dāng)Second class中的prt()函數(shù)注釋掉,將輸出First。
2. 向上轉(zhuǎn)型后只能調(diào)用base class中被derived class覆寫的函數(shù)。/*
abstract class First{
int i = 122;
public void prt(){
System.out.println("First.i = " + i);
}
public abstract void prt(First f);
}
class Second extends First{
public void prt(){
System.out.println("Second.i = " + i);
}
public void prt(First i)
{
}
public void prt(int i)
{
}
}
public class ExplicitStatic{
public static void main(String[] args){
First n = new Second();
n.prt(2);;
}
}
*/
class First{
public void prt(){
System.out.println("First");
}
}
class Second extends First{
//(a)
public void prt(){
System.out.println("Second");
}
public void prt(int i){//(a)
System.out.println("Second.i = " + i);
}
}
public class ExplicitStatic{
public static void main(String[] args){
First n = new Second();
n.prt(3);
}
}
(a)處的函數(shù)只是Second class中的函數(shù),所以不能通過n.prt(3)進(jìn)行調(diào)用。
二.Abstract class和Abstract methods
1. 如果一個(gè)class中存在abstract class,則class也必須被聲明為abstract class。
2. abstract class不能被實(shí)例化。
3. 如果base class是一個(gè)abstract class,那么derived class必須實(shí)現(xiàn)base class中所有的abstract methods;否則,derived class也必須被聲明為abstract class。
三.其它要點(diǎn)
1. 純粹繼承與擴(kuò)充
純粹繼承:只有base class所建議的函數(shù),才被derived class加以覆寫。
擴(kuò)充:除了覆寫base class的函數(shù),還實(shí)現(xiàn)了自己的函數(shù)abstract class First{
public abstract void f();
public abstract void g();
}
//純粹繼承
class Second extends First{
public void f(){}
public void g(){}
}
//擴(kuò)充
class Third extends First{
public void f(){}
public void g(){}
public void u(){}//base class不存在的函數(shù)
}
2. 向下轉(zhuǎn)型
1) 向下轉(zhuǎn)型時(shí)只能調(diào)用base class中被覆寫過的函數(shù)
2) 只有本來就為derived class對(duì)象時(shí)才能正確向下轉(zhuǎn)弄。class First{
public void f(){}
public void g(){}
}
class Second extends First{
public void f(){}
public void g(){}
public void u(){}
public void v(){}
}
public class ExplicitStatic{
public static void main(String[] args){
First[] x = {new First(), new Second()};
x[0].f();
x[1].g();
//!x[1].u();class First中不存在函數(shù)u()
//((Second)x[0]).f();(a)
((Second)x[1]).u();
}
}