现在java程序已经够快的了,不过有时写出了的程序效率就不怎么样,很多细节值得我们注意,比如使用StringBuffer或者StringBuilder来拼接或者操作字符串就比直接使用String效率高,想请大家谈谈还有哪些细节需要注意的,以及有哪些方法可以优化JAVA程序的执行效率和性能?
-
0可以看看问题:JAVA中优化代码性能都有哪些技巧? – 王辉 2012-03-05
-
0effective java 这本书就是你所有需要的东东 – sarowlwp 2012-09-20
-
5 票
-
-
4 票
-
可以参考下这个JAVA中优化代码性能都有哪些技巧?,我再补充点:
一、避免在循环条件中使用复杂表达式
在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快。例子:
- import java.util.Vector;
- class CEL {
- void method (Vector vector){
- for(int i =0; i < vector.size (); i++) // Violation
- ;// ...
- }
- }
更正:
- class CEL_fixed {
- void method (Vector vector){
- int size = vector.size ()
- for(int i =0; i < size; i++)
- ;// ...
- }
- }
二、为'Vectors' 和 'Hashtables'定义初始大小
JVM为Vector扩充大小的时候需要重新创建一个更大的数组,将原原先数组中的内容复制过来,最后,原先的数组再被回收。可见Vector容量的扩大是一个颇费时间的事。
通常,默认的10个元素大小是不够的。你最好能准确的估计你所需要的最佳大小。例子:
- import java.util.Vector;
- publicclass DIC {
- publicvoid addObjects (Object[] o){
- // if length > 10, Vector needs to expand
- for(int i =0; i< o.length;i++){
- v.add(o); // capacity before it can add more elements.
- }
- }
- publicVector v =newVector(); // no initialCapacity.
- }
更正:
自己设定初始大小。
三、在finally块中关闭Stream
程序中使用到的资源应当被释放,以避免资源泄漏。这最好在finally块中去做。不管程序执行的结果如何,finally块总是会执行的,以确保资源的正确关闭。
四、使用'System.arraycopy ()'代替通过来循环复制数组,例子:
- publicclass IRB
- {
- void method (){
- int[] array1 =newint[100];
- for(int i =0; i < array1.length; i++){
- array1 [i]= i;
- }
- int[] array2 =newint[100];
- for(int i =0; i < array2.length; i++){
- array2 [i]= array1 [i]; // Violation
- }
- }
- }
更正:
- publicclass IRB
- {
- void method (){
- int[] array1 =newint[100];
- for(int i =0; i < array1.length; i++){
- array1 [i]= i;
- }
- int[] array2 =newint[100];
- System.arraycopy(array1,0, array2,0,100);
- }
- }
五、让访问实例内变量的getter/setter方法变成”final”
简单的getter/setter方法应该被置成final,这会告诉编译器,这个方法不会被重载,所以,可以变成”inlined”,例子:
更正:
六、避免不需要的instanceof操作
如果左边的对象的静态类型等于右边的,instanceof表达式返回永远为true。
例子:
- publicclass UISO {
- public UISO (){}
- }
- classDogextends UISO {
- void method (Dog dog, UISO u){
- Dog d = dog;
- if(d instanceof UISO)// always true.
- System.out.println("Dog is a UISO");
- UISO uiso = u;
- if(uiso instanceofObject)// always true.
- System.out.println("uiso is an Object");
- }
- }
更正:
删掉不需要的instanceof操作。
- <coding-7lang="as">
- class Dog extends UISO {
- void method () {
- Dog d;
- System.out.println ("Dog is an UISO");
- System.out.println ("UISO is an UISO");
- }
- }
七、避免不需要的造型操作
所有的类都是直接或者间接继承自Object。同样,所有的子类也都隐含的“等于”其父类。那么,由子类造型至父类的操作就是不必要的了。例子:
- class UNC {
- String _id ="UNC";
- }
- classDogextends UNC {
- void method (){
- Dog dog =newDog();
- UNC animal =(UNC)dog; // not necessary.
- Object o =(Object)dog; // not necessary.
- }
- }
更正:
八、对于常量字符串,用'String' 代替 'StringBuffer'
常量字符串并不需要动态改变长度。
例子:
- publicclass USC {
- String method (){
- StringBuffer s =newStringBuffer("Hello");
- String t = s +"World!";
- return t;
- }
- }
更正:
把StringBuffer换成String,如果确定这个String不会再变的话,这将会减少运行开销提高性能。
九、在字符串相加的时候,使用 ' ' 代替 " ",如果该字符串只有一个字符的话
例子:
- publicclass STR {
- publicvoid method(String s){
- Stringstring= s +"d" // violation.
- string="abc"+"d" // violation.
- }
- }
更正:
将一个字符的字符串替换成' '
-
5 票
-
尽量别用异常
异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。 异常只能用于错误处理,不应该用来控制程序流程。
Try {} catch()要使用得当。
不要在循环中使用:
Try {
} catch() {
}
应把其放置在最外层。
尽量使用HashMap 和ArrayList ,除非必要,否则不推荐使用HashTable和Vector ,后者由于使用同步机制,而导致了性能的开销。
-
0"尽量别用异常": 实在不敢苟同啊, 至少说法太不严谨了. 用不用异常, 得看具体的程序逻辑吧, 该用的时候还是得用. 顶多说 不可以 滥用.
"循环中不要使用try{}catch(){}": 如果我的逻辑需要处理错误并 继续下一轮循环呢?
HashTable早就不推荐使用了, Java5 后同步map就应该用ConcurrentHashMap. – brayden 2013-05-06
-
3 票
-
网站优化一般包含两方面的内容:“减小代码的体积”和“提高代码的运行效率”
一、不用new关键词创建类的实例用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。
在使用设计模式(DesignPattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单。
二、使用非阻塞I/O版本较低的JDK不支持非阻塞I/OAPI。为避免I/O阻塞,一些应用采用了创建大量线程的办法(在较好的情况下,会使用一个缓冲池)。这种技术可以在许多必须支持并发I/O流的应用中见到,如Web服务器、报价和拍卖应用等。然而,创建Java线程需要相当可观的开销。JDK1.4引入了非阻塞的I/O库(java.nio)。如果应用要求使用版本较早的JDK,需要支持非阻塞I/O的软件包。
三、慎用异常异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。
四、不要重复初始化变量默认情况下,调用类的构造函数时,Java会把变量初始化成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)设置成0,float和double变量设置成0.0,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键词创建一个对象时,构造函数链中的所有构造函数都会被自动调用。
五、尽量指定类的final修饰符带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String。为String类指定final防止了人们覆盖length()方法。另外,如果指定一个类为final,则该类所有的方法都是final。Java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50%。
六、尽量使用局部变量调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。另外,依赖于具体的编译器/JVM,局部变量还可能得到进一步优化。请参见《尽可能使用堆栈变量》。
七、乘法和除法修改后的代码不再做乘以的操作,而是改用等价的左移的操作,每左移1位相当于乘以2。相应地,右移1位操作相当于除以2。值得一提的是,虽然移位操作速度快,但可能使代码比较难于理解,所以最好加上一些注释。
-
0 票
-