我簡單整理一下覺得不動要的就自動跳過了
case 59:運用insterface支持多重繼承
java其實是可以用多重繼承的,只要使用interface就可以達到類似效果
這點在重構(Refacorting)跟設計模式(Design Patterns)都有相關手法
interface A{...}
class B implements A{}
implements這個關鍵字可以看作extends的一種變體,他會讓B成為A的子類別
而interface則可以看作class一種變體
A a=new B();//這是可以的
case 60:避免interface中的函式衝突
interface A{
int f();
}
interface B{
void f();
}
class C implements A,B{
}
上述例子是編譯不過的
但是可以用回傳一致化或是改名子來解決
interface A{
int f();
}
interface B{
int f();
}
class C implements A,B{
}
如果無法修改原碼的時候,可以用繼承interface或是用adapter這個Desing Patterns解決
case 61:如需提供部分實作(partial implementation)請使用abstract classes
interface強迫必須實作所有他放出來的函式,但是有時候不希望每個介面都實作
就可以使用abstract class
abstract class Foo{
abstract void f1();//有abstract修飾 一定得實作複寫
void f2(){...}//可以不用複寫
}
case 62:區分interface,abstract class和concrete class
特性 | |||
---|---|---|---|
interface | abstract class | concrete class | |
描述 | 一種契約(contract)表示,不代實作一種契約(contract)表示,帶有部分實作 | 一種具象實作,經常是某個interface或abstract class的實作 | |
使用時機 | 噹想使用單一或多個interface繼承,或為了確定maker interface當打算提供部分實作 | 當打算提供完整的具象實作 | |
內容限制 | public函式和public static final常數無任何限制 | 無任何限制 | |
實作 | 不允許實作允許部分實作 | 要求完全實作 |
總結 | |||||
---|---|---|---|---|---|
支援多重繼承 | 支援抽象函式 | 允許實作 | 允許創劍實體 | 允許部分實作 | |
interface | Y | Ya | N | N | N |
abstract class | N | Y | Y | N | Y |
concrete class | N | N | Y | Y | N |
a:interface所有函式都暗自宣告成abstract
case 63:審慎定義和實作immutable classes
有些類別希望生存期間不得修改其值,這種類別一旦件夠好就不再變化
他有以下規範
- 將class中所有資料宣告成private
- 只提供取值(getter)函式而不提供設值(setter)函式
- 宣告class為final
- 從獲取器回傳reference to mutable object之前先clone那些物件
- 將傳遞給建構式之reference to mutable object先clone一份
- 在建構式之中就設定好所有資料
舉個例子
final class PinNumber{//使用final防止繼承
private StringBuffer owner;
orivate int acc;
PinNumber(StringBuffer owe,int ac){
owner=owe;
acc=ac;
//物件所有值在建構時就設定好
}
//只提供取值函式
public StringBuffer getOwner{//getter
return owner
}
....
}
case 64:欲傳遞或接收mutable object(可變物件)之object reference時請實施clone()
這是延續上一個case,為了避免因為getter傳出個物件讓外部有機會修其物件,應該替他實施clone
PinNumber p=new PinNumber(new StringBuffer("Tom"),10000);
StringBuffer owe=p.getOwner();
owe.append(" cat");//不可變被破壞
修改
public StringBuffer getOwner{//getter
return owner.clone();
}
clone又分成shallow cloning(淺層clone 只copy reference,依然共享實體)跟deep cloning(深層clone ,連reference的實體也copy一份)
case 65:使用ingeritance或delegation來定義immutable object
這個case探討三個定義immutable class的方法
- immutable inteface(不可變界面)
- 公共界面或基礎類別(common interface or base class)
- immutable delegation class
immutable inteface
提供一個只能取值的界面
interface Circle{
public double radius();
}
class MyCircle implements Circle{
double radius;
private MyCircle();
MyCircle createCircle(){
return new MyCircle();
}
void setRadius(double r){radius=r;}
}
類似這樣的設計,把建構式藏起來,用一個factor取代
Circle c=MyCircle.createCircle();
c.setRadius(100);
像這樣的程式就會編譯不通過,因為Circle沒有提供設值函式,這樣一來user就不能修改c
但是他是有破綻的
Circle c=MyCircle.createCircle();
((MyCircle)c).setRadius(100);
諸如此類,透過轉型就能破解
common interface or base class
因此有了進階的common interface or base class出現補救
他有三個要素
- 一個interface或abstract的父類別,宣告可被子類別(derived class)共用的immutable函式
- 一個子類別提供mutable函式實作
- 另一個子類別提供immutable函式實作
interface Circle{
public double radius();
}
class IMCircle implements Circle{
double radius;
private IMCircle();
IMCircle createCircle(){
return new IMCircle();
}
}
class MCircle implements Circle{
double radius;
private MCircle();
MCircle createCircle(){
return new MCircle();
}
void setRadius(double r){}
}
immutable delegation class
利用委託的方式讓物建不可變
class Circle{
double radius;
public Circle();
void setRadius(double r){}
double getRadius(){};
}
class ImmutableCircle{
private Circle c;
ImmutableCircle(){
c=new Circle();
}
double getRadius(){
return c.getRadius();
}
}
以下提出總結
技術 | 優點 | 缺點 |
immutable inteface | 實作簡單,無效率代價 | 有破綻 可破壞 |
common interface or base class | 沒破綻,將immutable和mutable清楚分開 | 需實作更多class跟繼承體 |
immutable delegation class | 無破綻,當無法修改源碼時可用 | 效率較低 |
case 66:實作clone()記得呼叫super.clone()
要支援clone的class必須呼叫java.lang.Object的clone()
呼叫super.clone()可以確保java.lang.Object的clone()會被呼叫
java.lang.Object的clone()會創建一個正確型別的新物件並執行shallow clone
即使要實作deep clone也該呼叫
考慮下面一段程式
class House implements Cloneable
{
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return new House();
}
}
class MyHouse extends House{
}
...
public static void main(String[] args) {
// TODO Auto-generated method stub
MyHouse t1=new MyHouse();
try {
MyHouse t2=(MyHouse)t1.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
他沒有呼叫Object的clone
會出現如下錯誤訊息
Exception in thread "main" java.lang.ClassCastException: House cannot be cast to MyHouse
因為此時clone回傳的是House物件而非MyHouse
但是我只需要將程式碼小小修改
class House implements Cloneable
{
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
class MyHouse extends House{
}
...
public static void main(String[] args) {
// TODO Auto-generated method stub
MyHouse t1=new MyHouse();
try {
MyHouse t2=(MyHouse)t1.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
就不會有這問題了
case 67:別只依賴finalize()清理non-memory(記憶體之外)的資源
簡單來說像是socket或File等東西都應該手動去清除他,JVM不會替我們回收
class MySocket{
Socket ss;
....
void close(){//如果class有物件是會暫用non-memory資源,就該提供清除的函式
ss.close();
}
}
....
MySocket ss=new MySocket();
...
ss.close();
case 68:在建構室內呼叫non-final函式的時候要小心
當使用函式去初始化資料的時候,要小心多型帶來的例外
class Base{
private int val;
Base(){
val=lookup();
}
int lookup(){return 5;}
int values(){return val;}
}
class Dev extends Base{
private int num=10;
int lookup(){return num;}
}
...
Dev d=new Dev();
System.out.println(d.values());
//印出結果:0
為什麼是0呢,原來Dev呼叫的是int lookup(){return num;}而非int lookup(){return 5;}
但此時Dev的instance都還沒初始化完成,num此時是0而不是10
------------------------------------
沒有留言:
張貼留言