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如何封裝的問題進行探討,希望各位給我捧場,並加以指正,共同提高。
本文系原創,轉載需聲明出處