一、安卓上的类型安全枚举
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;
}
}
问题:虽然这些常量是期望的,但是没有方法保证setState()方法接收其他的值。如果
要在设置方法中添加检查,那么一旦得到的是非预期值,开发者就需要处理错误。开发者所需要的是在编译时检查非法赋值。类型安全的枚举解决了这个问题,如下所示:
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;
}
}
注意在声明不同类型安全值的地方新加的内部枚举类。这在编译时就会解决非法赋值的问题,所以代码更不容易出错。
Android早期的版本中不建议使用枚举类型,因为和使用整型常量相比,这种设计带来的内存和性能损失更大。如今有了更强的JIT编译器以及一个不断改进的Dalvik虚拟机,开发者不必再担心这个问题,放心大胆地使用类型安全枚举即可。
二、增强for循环
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上使用增强版的for循环
void loopFive(ArrayList<String> names){
int size = names.size();
for(int i=0;i<size;i++){
printName(names[i]);
}
}
如果只是读取元素的话,可以放心地对数组使用增强版 for 循环。对 Collection 对象来说,增强版 for 循环和使用迭代器遍历元素有着相同的性能。 ArrayList 对象应避免使用增强版 for 循环。
如果不仅需要遍历元素,而且需要元素的位置,就一定要使用数组或者 ArrayList ,因为所有其他 Collection 类在这些情况下会更慢。
一般情况下,如果在读取元素几乎不变的数据集时对性能要求很高,建议使用常规数组。
三、队列、同步和锁
1. 更智能的队列
LinkedBlockingQueue<String> blockingQueue = new LinkedBlockingQueue<String>();
上面的一行代码能提供阻塞队列,甚至能提供额外的线程安全操作。java.util.concurrent包含许多可选的队列以及并发映射类,所以,一般情况下,建议使用它们。
2.更智能的锁
Java提供的 synchronized 关键字允许开发者创建线程安全的方法和代码块。synchronized关键字易于使用,也很容易滥用,对性能造成负面影响。当需要区分读数据和写数据时,synchronized 关键字并不是最有效的。幸好,java.util.concurrent.locks包中的工具类对这种情况提供了很好的支持。
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();
}
}
// 重复代码不再赘述
}
上面的代码展示了在什么地方使用 ReentrantReadWriteLock ,它允许多个并发线程对数据进行只读访问,并确保同一时间只有一个线程写入相同的数据。
在代码中使用 synchronized 关键字仍然是处理锁问题的有效方法,但无论何种情况下,都要考虑 ReentrantReadWriteLock 是否是更有效的解决方案。
四、管理和分配内存
1. 应尽可能避免在循环中分配对象
2. 有时候无法避免在循环中创建对象,所以还需要采用某种方法处理这种情况。本书的解决方案是使用一个静态工厂方法按需分配对象,Joshua Bloch在《Effective Java中文版》一书的第一条中详细地描述了该方法。
public final class Pair{
public int firstValue;
public int secondValue;
//引用对象池中的另一个对象
private Pair next;
//同步锁
private static final Object sPoolSync = new Object();
//对象池中第一个可用对象
private static Pair sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
/**
* 只能用obtain()方法获取对象
*/
private Pair() { }
/**
* 返回回收的对象或者当对象池为空时创建一个新对象
*/
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();
}
/**
* 回收该对象。调用该方法后需要释放所有对该实例的引用
*/
public void recycle(){
synchronized(sPoolSync){
if(sPoolSize < MAX_POOL_SIZE){
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
注意,本例增加了多个字段,有静态的也有非静态的。可使用这些字段实现传统的 Pair 对象链表。通过使用私有构造函数来防止在类外面创建对象,只能通过 obtain 方法创建该类的对象。 obtain 方法首先会检查对象池中是否包含任何存在的对象,并删除列表中的第一个元素然后返回它。如果对象池为空, obtain 方法会创建一个新的对象。要把对象重新放回池中,需要在使用完该对象时,对它调用 recycle 方法。这时,不能再有对该对象的引用。
另外,由于 obtain 和 recycle 是线程安全的,可以在多个并发线程中安全地使用这两个方法。唯一的缺点是,必须记住要手动调用 recycle 方法,不过这是一个很小的代价。