一、安卓上的類(lèi)型安全枚舉
public class Machine {
public static final int STOPPED = 10;
public static final int INITIALIZING = 20;
public static final int STARTING = 30;
public static final int RUNNING = 40;
public static final int STOPPING = 50;
public static final int CRASHED = 60;
private int mState;
public Machine() {
mState = STOPPED;
}
public int getState() {
return mState;
}
public void setState(int state) {
mState = state;
}
}
問(wèn)題:雖然這些常量是期望的,但是沒(méi)有方法保證setState()方法接收其他的值。如果
要在設(shè)置方法中添加檢查,那么一旦得到的是非預(yù)期值,開(kāi)發(fā)者就需要處理錯(cuò)誤。開(kāi)發(fā)者所需要的是在編譯時(shí)檢查非法賦值。類(lèi)型安全的枚舉解決了這個(gè)問(wèn)題,如下所示:
public class Machine {
public enum State {
STOPPED, INITIALIZING, STARTING, RUNNING, STOPPING, CRASHED
}
private State mState;
public Machine() {
mState = State.STOPPED;
}
public State getState() {
return mState;
}
public void setState(State state) {
mState = state;
}
}
注意在聲明不同類(lèi)型安全值的地方新加的內(nèi)部枚舉類(lèi)。這在編譯時(shí)就會(huì)解決非法賦值的問(wèn)題,所以代碼更不容易出錯(cuò)。
Android早期的版本中不建議使用枚舉類(lèi)型,因?yàn)楹褪褂谜统A肯啾?,這種設(shè)計(jì)帶來(lái)的內(nèi)存和性能損失更大。如今有了更強(qiáng)的JIT編譯器以及一個(gè)不斷改進(jìn)的Dalvik虛擬機(jī),開(kāi)發(fā)者不必再擔(dān)心這個(gè)問(wèn)題,放心大膽地使用類(lèi)型安全枚舉即可。
二、增強(qiáng)for循環(huán)
void loopOne(String[] names){
int size = names.length;
for(int i=0;i<size;i++){
printName(names[i]);
}
}
void loopTwo(String[] names){
for(String name:names){
printName(name);
}
}
void loopThree(Collection<String> names){
for(String name:names){
printName(name);
}
}
void loopFour(Collection<String> names){
Iterator<String> iterator = names.iterator();
while(iterator.hasNext()){
printName(iterator.next());
}
}
// 不要在ArrayList上使用增強(qiáng)版的for循環(huán)
void loopFive(ArrayList<String> names){
int size = names.size();
for(int i=0;i<size;i++){
printName(names[i]);
}
}
如果只是讀取元素的話(huà),可以放心地對(duì)數(shù)組使用增強(qiáng)版 for 循環(huán)。對(duì) Collection 對(duì)象來(lái)說(shuō),增強(qiáng)版 for 循環(huán)和使用迭代器遍歷元素有著相同的性能。 ArrayList 對(duì)象應(yīng)避免使用增強(qiáng)版 for 循環(huán)。
如果不僅需要遍歷元素,而且需要元素的位置,就一定要使用數(shù)組或者 ArrayList ,因?yàn)樗衅渌?Collection 類(lèi)在這些情況下會(huì)更慢。
一般情況下,如果在讀取元素幾乎不變的數(shù)據(jù)集時(shí)對(duì)性能要求很高,建議使用常規(guī)數(shù)組。
三、隊(duì)列、同步和鎖
1. 更智能的隊(duì)列
LinkedBlockingQueue<String> blockingQueue = new LinkedBlockingQueue<String>();
上面的一行代碼能提供阻塞隊(duì)列,甚至能提供額外的線(xiàn)程安全操作。java.util.concurrent包含許多可選的隊(duì)列以及并發(fā)映射類(lèi),所以,一般情況下,建議使用它們。
2.更智能的鎖
Java提供的 synchronized 關(guān)鍵字允許開(kāi)發(fā)者創(chuàng)建線(xiàn)程安全的方法和代碼塊。synchronized關(guān)鍵字易于使用,也很容易濫用,對(duì)性能造成負(fù)面影響。當(dāng)需要區(qū)分讀數(shù)據(jù)和寫(xiě)數(shù)據(jù)時(shí),synchronized 關(guān)鍵字并不是最有效的。幸好,java.util.concurrent.locks包中的工具類(lèi)對(duì)這種情況提供了很好的支持。
public class ReadWriteLockDemo(){
private final ReentrantReadWriteLock mLock;
private String mName;
private int mAge;
private String mAddress;
public ReadWriteLockDemo(){
mLock = new ReentrantReadWriteLock();
}
public void setPersonData(String name, int age, String address){
ReentrantReadWriteLock.WriteLock writeLock = mLock.writeLock();
try{
writeLock.lock();
mName = name;
mAge = age;
mAddress = address;
}finally{
writeLock.unlock();
}
}
public String getName() {
ReentrantReadWriteLock.ReadLock readLock = mLock.readLock();
try {
readLock.lock();
return mName;
} finally {
readLock.unlock();
}
}
// 重復(fù)代碼不再贅述
}
上面的代碼展示了在什么地方使用 ReentrantReadWriteLock ,它允許多個(gè)并發(fā)線(xiàn)程對(duì)數(shù)據(jù)進(jìn)行只讀訪(fǎng)問(wèn),并確保同一時(shí)間只有一個(gè)線(xiàn)程寫(xiě)入相同的數(shù)據(jù)。
在代碼中使用 synchronized 關(guān)鍵字仍然是處理鎖問(wèn)題的有效方法,但無(wú)論何種情況下,都要考慮 ReentrantReadWriteLock 是否是更有效的解決方案。
四、管理和分配內(nèi)存
1. 應(yīng)盡可能避免在循環(huán)中分配對(duì)象
2. 有時(shí)候無(wú)法避免在循環(huán)中創(chuàng)建對(duì)象,所以還需要采用某種方法處理這種情況。本書(shū)的解決方案是使用一個(gè)靜態(tài)工廠(chǎng)方法按需分配對(duì)象,Joshua Bloch在《Effective Java中文版》一書(shū)的第一條中詳細(xì)地描述了該方法。
public final class Pair{
public int firstValue;
public int secondValue;
//引用對(duì)象池中的另一個(gè)對(duì)象
private Pair next;
//同步鎖
private static final Object sPoolSync = new Object();
//對(duì)象池中第一個(gè)可用對(duì)象
private static Pair sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
/**
* 只能用obtain()方法獲取對(duì)象
*/
private Pair() { }
/**
* 返回回收的對(duì)象或者當(dāng)對(duì)象池為空時(shí)創(chuàng)建一個(gè)新對(duì)象
*/
public static Pair obtain(){
synchronized(sPoolSync){
if(sPool != null){
Pair m = sPool;
sPool.next = m.next;
m.next = null;
sPoolSize --;
return m;
}
}
return new Pair();
}
/**
* 回收該對(duì)象。調(diào)用該方法后需要釋放所有對(duì)該實(shí)例的引用
*/
public void recycle(){
synchronized(sPoolSync){
if(sPoolSize < MAX_POOL_SIZE){
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
注意,本例增加了多個(gè)字段,有靜態(tài)的也有非靜態(tài)的??墒褂眠@些字段實(shí)現(xiàn)傳統(tǒng)的 Pair 對(duì)象鏈表。通過(guò)使用私有構(gòu)造函數(shù)來(lái)防止在類(lèi)外面創(chuàng)建對(duì)象,只能通過(guò) obtain 方法創(chuàng)建該類(lèi)的對(duì)象。 obtain 方法首先會(huì)檢查對(duì)象池中是否包含任何存在的對(duì)象,并刪除列表中的第一個(gè)元素然后返回它。如果對(duì)象池為空, obtain 方法會(huì)創(chuàng)建一個(gè)新的對(duì)象。要把對(duì)象重新放回池中,需要在使用完該對(duì)象時(shí),對(duì)它調(diào)用 recycle 方法。這時(shí),不能再有對(duì)該對(duì)象的引用。
另外,由于 obtain 和 recycle 是線(xiàn)程安全的,可以在多個(gè)并發(fā)線(xiàn)程中安全地使用這兩個(gè)方法。唯一的缺點(diǎn)是,必須記住要手動(dòng)調(diào)用 recycle 方法,不過(guò)這是一個(gè)很小的代價(jià)。