《Android编程实战》学习笔记二——在Android上编写高效的Java(1)
一、安卓上的类型安全枚举
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 names){ for(String name:names){ printName(name); } }void loopFour(Collection names){ Iterator iterator = names.iterator(); while(iterator.hasNext()){ printName(iterator.next()); }}// 不要在ArrayList上使用增强版的for循环void loopFive(ArrayList names){ int size = names.size(); for(int i=0;i
如果只是读取元素的话,可以放心地对数组使用增强版 for 循环。对 Collection 对象来说,增强版 for 循环和使用迭代器遍历元素有着相同的性能。 ArrayList 对象应避免使用增强版 for 循环。
如果不仅需要遍历元素,而且需要元素的位置,就一定要使用数组或者 ArrayList ,因为所有其他 Collection 类在这些情况下会更慢。
一般情况下,如果在读取元素几乎不变的数据集时对性能要求很高,建议使用常规数组。
三、队列、同步和锁
1. 更智能的队列
LinkedBlockingQueue blockingQueue = new LinkedBlockingQueue();
上面的一行代码能提供阻塞队列,甚至能提供额外的线程安全操作。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 方法,不过这是一个很小的代价。
更多相关文章
- Android httpClient Get&Post方法
- Android 给textview添加下划线的一种方法(可复用)
- Android中两种实现倒计时的方法
- Android获取系统内核版本的方法
- Android Studio执行Java类的main方法及解决方法
- Android 延迟执行方法
- Android ExecutorService线程池