Java作为一门高级语言,有其优点也有其缺点,当前孰优孰劣现在还不敢轻易下结论,但是我们可以通过操作系统来琢磨一下高级语言的一些优劣:首先作为操作系统中的组件,向上提供接口和服务,向下提供操作和管理硬件,具有这点特点的操作系统才能在技术层面上迅速普及和有更多的人应对开发,记得在绝影的书中有句话,我加以会意后改为这样的版本,底层程序员才是真正的挑战,因为底层相对于高层语言改动周期更长,改动影响更大,难度也相对的比较大,所以我们通常使用高级语言作为开发,比如Java在封装好了的虚拟机和API我们就可以进行我们的高层开发,真正底层是具体怎么操作的,一般程序员是不明白的,记得绝影大二的时候开发了相应的一个大数DLL组件,让性能提高了20倍,这个例子足以说明了底层语言上的性能优化更加强大,但是身为高级语言编程人员的我们,也要琢磨点节约系统开销和运行效率的方法。
首先我认为在节约系统开销和运行效率的方法可以用这样一个公式来表示: P = V * T 或者(V + T ) P 为总效率 V 为空间 T为时间
学过数据库的都应该知道我们总是在时间和空间上面寻求更好的平衡点,鱼和熊掌是不可兼得的,这点尤为重要,下面我来演示几个小代码,看怎么才能在Java中寻求 代码可读性和代码运行效率之间的平衡,当然运行效率之间的平衡也会有所演示,让你的Java代码不再拖沓.
1.>> 和 << 的灵活运用
在Java中是支持左移右移的运算的,我们在学汇编的时候不是有反码,补码的机制么,为什么这样,其实在我们的CPU中的运算机制和指令集是很少的,CPU的主要工作还是执行很多次的加法运算,运用Binary可以清楚的讲硬件的开关合和二进制联合起来,而反码和补码的机制的出现分别是将 减法变为加法 将乘法变为加法,除法变为乘法然后变为加法,这样就使得CPU主要负责加法的运算,可以说成是加法器,我们来看这段代码:
public class Test1 {
public static void main(String[] args) {
int num = 8 ;
int ans_1 = 0;
int ans_2 = 0;
long start = System.currentTimeMillis();
for(int i = 0 ; i < 10000000 ; i ++){
ans_1 = num/2;
}
System.out.println("常规1000000次除法计算消耗:" + (System.currentTimeMillis() - start)+ "ms");
System.out.println("常规计算结果" + ans_1);
start = System.currentTimeMillis() ;
for(int i = 0 ; i < 10000000 ; i ++){
ans_2 = num>>1;
}
System.out.println("优化1000000次除法计算消耗:" + (System.currentTimeMillis() - start)+ "ms");
System.out.println("优化计算结果" + ans_2);
}
}
我们可以清楚的看到在1000000次相同计算中的时间节约和比较,对于手持设备和服务器的高并发处理的时候的计算这点时间的节约是起很大的性能瓶颈解决的.
2.局部变量和全局变量
我们在学Java基础的时候就明确了局部变量和全局变量的不同的好处,全局变量在维护引用指向的开销的时候是很大的,特别是在维护的数据量比较大的时候是很容易出现高能耗,使用Hibernate这样的高层次语言的时候我们就会发现在很多的情况下我们都需要对代码进行相对的优化工作,而局部变量在维护引用相对的开销的时候会比较小,生命周期有限,所以开销小,从而性能更加高,缺点就是代码的可阅读性不强,这点在我们的日常编程中最不容易注意到,下面我们来看看这段代码:
package com.cs.test;
public class Test2 {
static int num = 8 ;
public static void main(String[] args) {
long start = System.currentTimeMillis() ;
add1();
System.out.println("直接操作全局变量消耗:" + (System.currentTimeMillis() - start)+ "ms");
start = System.currentTimeMillis() ;
add2();
System.out.println("局部变量来操作全局变量消耗:" + (System.currentTimeMillis() - start)+ "ms");
}
private static void add1(){
//直接操作全局变量
for(int i = 0 ; i < 10000000 ; i ++){
num += i ;
}
}
private static void add2(){
//运用局部变量来操作全局变量
int temp = num ;
for(int i = 0 ; i < 10000000 ; i ++){
temp += i ;
}
num = temp ;
}
}
结果很详细,我也不废话了.在add2中的temp变量使用的时候如果业务代码更长或者更加复杂的情况下,很容易造成阅读不方便,这样的做法是个典型的用空间换时间的做法,有兴趣的话可以自己用Javac自带的命令监控下内存.
3.switch...case...语句比if...else if...嵌套或者叠加更合适
我们通常在处理很多选择的时候通常很容易想到if...else 而不是switch...case,就算是switch...case的时候很容易将default漏掉,从而使得程序拥有一些处理起来比较棘手的漏洞.这个我就不发代码,另外在JDK1.5的新特性中可以使用ENUM来进行选择的功能的强化,使之不局限于int和Char...的判断.
package com.cs.test;
public class Test3 {
private static enum Choose{
first , second
}
public static void main(String[] args) {
Choose choose = Choose.first ;
switch(choose){
case first :
System.out.println("FIRST");
break;
case second:
System.out.println("SECOND");
break;
default:
System.out.println("DEFAULT");
}
}
}
4.注意循环控制中容易出现的性能损耗
通常我们在迭代一个Collection的时候,容易采取下面这样的编程方式,我下面来进行对比代码的编写和测试,看看哪个更加的运行效率低:
import java.util.ArrayList;
public class Test4 {
public static void main(String[] args) {
ArrayList<String> v = new ArrayList<String>();
for(int i = 0 ; i < 1000000 ; i ++){
v.add("1234" + i);
}
//常规代码编写
long start = System.currentTimeMillis();
for(int i = 0 ; i < v.size() ; i ++ ){
v.get(i);
}
System.out.println("常规1000000次读取计算消耗:" + (System.currentTimeMillis() - start)+ "ms");
//优化代码编写
start = System.currentTimeMillis() ;
int size = v.size();
for(int i = 0 ; i < size ; i ++ ){
v.get(i);
}
System.out.println("优化代码编写 :优化1000000次读取计算消耗:" + (System.currentTimeMillis() - start)+ "ms");
//另外一种写法
start = System.currentTimeMillis() ;
for(int i = v.size() - 1 ; i >= 0 ; --i ){
v.get(i);
}
System.out.println("另外一种写法 :优化1000000次读取计算消耗:" + (System.currentTimeMillis() - start)+ "ms");
}
}
5 . StringBuffer 和 String 的习惯用法
public class Test5 {
public static void main(String[] args) {
String str1 = "temp";
StringBuffer str2= new StringBuffer("temp");
//常规代码编写
long start = System.currentTimeMillis();
for(int i = 0 ; i < 10000 ; i ++ ){
str1 += (i);
}
System.out.println("String 常规1000000次增加计算消耗:" + (System.currentTimeMillis() - start)+ "ms");
//优化代码编写
start = System.currentTimeMillis() ;
for(int i = 0 ; i < 10000 ; i ++ ){
str2.append(i);
}
System.out.println("StringBuffer优化1000000次增加计算消耗:" + (System.currentTimeMillis() - start)+ "ms");
}
}
6.try{}catch{}块某些时候有优化的余地
有的时候我们可以强暴的在try{}catch{}强制的调用某些可能出现的语句,当发生异常的时候强制的向上一级代码抛出,这种情况我们可以再某些情况联合if...else...进行使用,可以避免系统捕获异常时候的额外开销,使得程序的正常容错性更加强悍,下面我们来看一种可以改进的一场方法:
public class Test6 {
public static void main(String[] args) {
Mytest x = null;
//强暴抛出
try{
x.show();
}catch(NullPointerException e){
e.printStackTrace();
}
//礼貌抛出
if(x == null){
System.out.println("出现问题");
}else{
x.show();
}
}
private static class Mytest{
public void show(){
System.out.println("展示信息");
}
}
}
今天由于时间关系,我就写到这里,下次有时间我会将在IO操作中的一些性能优化和缓存使用事项进行剖析和代码展示,将会以缓冲视图机制和随机文件多线程访问下载和内存Mapping机制和IO如何封装的问题进行探讨,希望各位给我捧场,并加以指正,共同提高。
本文系原创,转载需声明出处
怎么从Java中挖掘性能
最新推荐文章于 2024-07-03 18:12:06 发布