1. 在合适的场合使用单例
使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:
1.1控制资源的使用,通过线程同步来控制资源的并发访问;
1.2控制实例的产生,以达到节约资源的目的;
1.3控制数据共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信。
2. 避免随意使用静态变量
当某个对象被定义为static变量所引用,那么GC通常是不会回收这个对象所占有的内存,如:
public class A{
private static B b = new B();
}
此时静态变量b的生命周期与A类同步,如果A类不会卸载,那么b对象会常驻内存,直到程序终止。
3. 尽量使用final修饰符
带有final修饰符的类是不可派生的。在JAVA核心API中,有许多应用final的例子,例如java、lang、String,为String类指定final防止了使用者覆盖length()方法。另外,如果一个类是final的,则该类所有方法都是final的。java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关),此举能够使性能平均提高50%。
如:让访问实例内变量的getter/setter方法变成”final:
简单的getter/setter方法应该被置成final,这会告诉编译器,这个方法不会被重载,所以,可以变成”inlined”,例子:
class MAF {
public void setSize (int size) {
_size = size;
}
private int _size;
}
更正:
class DAF_fixed {
final public void setSize (int size) {
_size = size;
}
private int _size;
}
4. 静态类、单例类、工厂类将它们的构造函数置为 private
这是因为静态类、单例类、工厂类这种类本来我们就不需要外部将它们 new 出来,将构造函数置为 private 之后,保证了这些类不会产生实例对象。
5. 尽量使用局部变量
调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快;其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。
6. 处理好包装类型和基本类型两者的使用场所,尽量使用基本数据类型代替对象
虽然包装类型和基本类型在使用过程中是可以相互转换,但它们两者所产生的内存区域是完全不同的,基本类型数据产生和处理都在栈中处理,包装类型是对象,是在堆中产生实例。什么时候该用包装类,什么时候该用基本类型,看基本的业务来定:这个字段允不允许null值,如果允许,则必然要用封装类;否则,基本类型就可以了。如果用到比如集合类对象,泛型和反射调用函数,就需要用包装类!
String str = "hello";
上面这种方式会创建一个“hello”字符串,而且JVM的字符缓存池还会缓存这个字符串;
String str = new String("hello");
此时程序除创建字符串外,str所引用的String对象底层还包含一个char[]数组,这个char[]数组依次存放了h,e,l,l,o
7. 慎用synchronized
实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。synchronize方法被调用时,直接会把当前对象锁了,在方法执行完之前其他线程无法调用当前对象的其他方法。所以在保证线程安全的前提下,同步的范围应该尽可能的小,除非能确定一整个方法都是需要进行同步的,否则尽量使用同步代码块,避免对那些不需要进行同步的代码也进行了同步,影响了代码执行效率。
8. 尽量不要使用finalize方法
实际上,将资源清理放在finalize方法中完成是非常不好的选择,由于GC的工作量很大,选择使用finalize方法进行资源清理,会导致GC负担更大,程序运行效率更差。
finalize方法是Object提供的的实例方法,使用规则如下:
9. 尽量避免使用二维数组
二维数据占用的内存空间比一维数组多得多,大概10倍以上。
10. 在不必要考虑线程安全前提下应尽量使用HashMap、ArrayList。HashTable、Vector等使用了同步机制,降低了性能。
11. 合理的初始化集合或者工具类的大小
比如ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet、Vector等等。当你要创建一个比较大的hashMap时,充分利用这个构造函数
public HashMap(int initialCapacity, float loadFactor);
避免HashMap多次进行了hash重构,扩容是一件很耗费性能的事,在默认中initialCapacity只有16,而loadFactor是 0.75,需要多大的容量,你最好能准确的估计你所需要的最佳大小。
StringBuffer 的构造器会创建一个默认大小(通常是16)的字符数组。在使用中,如果超出这个大小,它就会重新分配内存,会将自身容量增加到当前的2倍+2,也就是2*n+2;并将原先的数组复制过来,再丢弃旧的数组,这会浪费很多时间。在大多数情况下,你可以在创建 StringBuffer的时候指定大小,这样就避免了在容量不够的时候自动扩容,以提高性能。
它会将自身容量增加到当前的2倍+2,也就是2*n+2。无论何时,只要StringBuffer到达它的最大容量,它就不得不创建一个新的对象数组,然后复制旧的对象数组,这会浪费很多时间。所以给StringBuffer设置一个合理的初始化容量值,是很有必要的!如:StringBuffer buffer = new StringBuffer(1000);
Vector与StringBuffer类似,每次扩展容量时,所有现有元素都要赋值到新的存储空间中。Vector的默认存储能力为10个元素,扩容加倍。
vector.add(index,obj) 这个方法可以将元素obj插入到index位置,但index以及之后的元素依次都要向下移动一个位置(将其索引加 1)。除非必要,否则对性能不利。同样规则适用于remove(int index)方法,移除此向量中指定位置的元素。将所有后续元素左移(将其索引减 1)。返回此向量中移除的元素。所以删除vector最后一个元素要比删除第1个元素开销低很多。删除所有元素最好用removeAllElements()方法。
如果要删除vector里的一个元素可以使用 vector.remove(obj);而不必自己检索元素位置,再删除,如int index = indexOf(obj);vector.remove(index)。
12. 避免在循环条件中使用复杂表达式
如:for(int i=0;i 并且在循环中应该避免使用复杂的表达式,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快。 13. 就近创建所需对象,尽量避免不必要的创建 A a = new A(); if(i==1){ list.add(a); } 应该改为: if(i==1){ A a = new A(); list.add(a); } 14. 尽量在finally块中释放资源 程序中使用到的资源应当及时被释放,以避免资源泄漏,特别是对 I/O流,数据库连接等大对象的操作会造成系统大的开销,并且这些最好在finally块中去做。因为不管程序执行的结果如何,finally块总是会执行的,以确保资源的正确关闭。 15. 考虑不用new关键字创建对象的实例 用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。 下面是Factory模式的一个典型实现: public static Credit getNewCredit(){ return new Credit(); } 改进后的代码使用clone()方法: private static Credit BaseCredit = new Credit(); public static Credit getNewCredit(){ return (Credit)BaseCredit.clone(); } 16. 尽早释放无用或者过期对象的引用 很多时候,方法局部引用变量所引用的对象会随着方法结束而变成垃圾,因此,大部分时候程序无需将局部引用变量显式设为null。例如: public void test(){ Object obj = new Object(); …… Obj=null; } 上面这个就没必要了,随着方法test()的执行完成,程序中obj引用变量的作用域就结束了。但是如果是改成下面: public void test(){ Object obj = new Object(); …… Obj=null; //执行耗时,耗内存操作;或调用耗时,耗内存的方法 …… } 这时候就有必要将obj赋值为null,可以尽早的释放对Object对象的引用。 17. 使用最有效率的方式去遍历Map 遍历Map的方式有很多,通常场景下我们需要的是遍历Map中的Key和Value,那么推荐使用的、效率最高的方式是: public static void main(String[] args) { HashMap hm.put("111", "222"); Set Iterator while (iter.hasNext()) { Map.Entry System.out.println(entry.getKey() + "\t" + entry.getValue()); } } 如果你只是想遍历一下这个Map的key值,那用”Set 18. 循环内尽量不要创建对象引用 for (int i = 1; i <= count; i++) { Object obj = new Object(); } 这种做法会导致内存中有count份Object对象引用存在,count很大的话,就耗费内存了,建议为改为: Object obj = null; for (int i = 0; i <= count; i++) { obj = new Object(); } 这样的话,内存中只有一份Object对象引用,每次new Object()的时候,Object对象引用指向不同的Object罢了,但是内存中只有一份,这样就大大节省了内存空间了。 19. ArrayList & LinkedList 一个是线性表,一个是链表,随机查询ArrayList优于LinkedList,LinkedList还要移动指针,添加删除的操作LinkedList优于ArrayList,ArrayList还要移动数据,不过这是理论性分析,事实未必如此,重要的是理解好两者的数据结构。 20. 缓存经常使用的对象 尽可能将经常使用的对象进行缓存,可以使用数组,或HashMap的容器来进行缓存,但这种方式可能导致系统占用过多的缓存,性能下降,推荐可以使用一些第三方的开源工具,如EhCache,Oscache,redis进行缓存,他们基本都实现了FIFO/FLU等缓存算法。 21. 慎用异常,规避继承自 RuntimeException 的运行时异常 当创建一个异常时,需要收集一个栈跟踪(stack track),这个栈跟踪用于描述异常是在何处创建的。构建这些栈跟踪时需要为运行时栈做一份快照,正是这一部分开销很大。当需要创建一个 Exception 时,JVM 不得不说:先别动,我想就您现在的样子存一份快照,所以暂时停止入栈和出栈操作。栈跟踪不只包含运行时栈中的一两个元素,而是包含这个栈中的每一个元素。 如果您创建一个 Exception ,就得付出代价,好在捕获异常开销不大,因此可以使用 try-catch 将核心内容包起来。从技术上讲,你甚至可以随意地抛出异常,而不用花费很大的代价。招致性能损失的并不是 throw 操作——尽管在没有预先创建异常的情况下就抛出异常是有点不寻常。真正要花代价的是创建异常。 异常处理效率低,RuntimeException 的运行时异常中绝大多数完全可以由程序员来规避,比如 ArithmeticException 可以通过判断除数是否为空来规避,NullPointerException 可以通过判断对象是否为空来规避,IndexOutOfBoundsException 可以通过判断数组/字符串长度来规避,ClassCastException 可以通过 instanceof 关键字来规避,ConcurrentModificationException 可以使用迭代器来规避。 22. 尽量使用System.arraycopy ()代替通过来循环复制数组 System.arraycopy() 要比通过循环来复制数组快的多。 public void test15() { Integer[] strArray = new Integer[1000]; for (int i = 0; i < 1000; i++) { strArray[i] = i; } Integer[] strArray2 = new Integer[100]; System.arraycopy(strArray, 50, strArray2, 50, 50); for (int i = 0, len = strArray2.length; i < len; i++) { System.out.print(" " + strArray2[i] + " "); } } 23. 在字符串相加的时候,如果字符串只有一个字符的话,使用 ' ' 代替 " "。 public void method(String s) { String string = s + "d" string = "abc" + "d" } 更正: 将一个字符的字符串替换成' ' public void method(String s) { String string = s + 'd' string = "abc" + 'd' } 24. 把一个基本数据类型转为字符串,基本数据类型.toString()是最快的方式、String.valueOf(数据)次之、数据+"" 最慢 1、String.valueOf()方法底层调用了Integer.toString()方法,但是会在调用前做空判断. 2、Integer.toString()方法就不说了,直接调用了. 3、i + “”底层使用了StringBuilder实现,先用append方法拼接,再用toString()方法获取字符串.