運算
第一節我們談了通過變量定義數據,上節我們介紹了給數據賦值,有了初始值之後,可以對數據進行運算。計算機之所以稱為"計算"機,是因為發明它的主要目的就是運算。運算有不同的類型,不同的數據類型支持的運算也不一樣,本文介紹Java中基本類型數據的主要運算。
算術運算
算術運算符有加減乘除,符號分別是+-*/,另外還有取模運算符%,以及自增(++)和自減(–)運算符。取模運算適用於整數和字符類型,其他算術運算適用於所有數值類型和字符類型,其他都符合常識,但字符類型看上去比較奇怪,後續文章解釋。
減號(-)通常用於兩個數相減, 但也可以放在一個數前面,例如 -a, 這表示改變a的符號,原來的正數會變為負數,原來的負數會變為正數,這也是符合我們常識的。
取模(%)就是數學中的求余數,例如,5%3是2,10%5是0。
自增(++)和自減(--),是一種快捷方式,是對自己進行加一或減一操作。
加減乘除大部分情況和直觀感覺是一樣的,都很容易理解,但有一些需要注意的地方,而自增自減稍微復雜一些,下面我們解釋下。
加減乘除注意事項
運算時要注意結果的范圍,使用恰當的數據類型。兩個正數都可以用int表示,但相乘的結果可能就會超,超出後結果會令人困惑,例如:
int a = 2147483647*2; //2147483647是int能表示的最大值
a的結果是-2。為什麼是-2我們暫不解釋,要避免這種情況,我們的結果類型應使用long,但只改為long也是不夠的,因為運算還是默認按照int類型進行,需要將至少一個數據表示為long形式,即在後面加L或l,下面這樣才會出現期望的結果:
long a = 2147483647*2L;
另外,需要注意的是,整數相除不是四捨五入,而是直接捨去小數位,例如:
double d = 10/4;
結果是2而不是2.5,如果要按小數進行運算,需要將至少一個數表示為小數形式,或者使用強制類型轉化,即在數字前面加(double),表示將數字看做double類型,如下所示任意一種形式都可以:
double d = 10/4.0; double d = 10/(double)4;
以上一些注意事項,我想也沒什麼特別的理由,大概是方便語言設計者實現語言吧。
小數計算結果不精確
無論是使用float還是double,進行運算時都會出現一些非常令人困惑的現象,比如:
float f = 0.1f*0.1f; System.out.println(f);
這個結果看上去,不言而喻,應該是0.01,但實際上,屏幕輸出卻是0.010000001,後面多了個1。換用double看看:
double d = 0.1*0.1; System.out.println(d);
屏幕輸出0.010000000000000002,一連串的0之後多了個2,結果也不精確。
這是怎麼回事?看上去這麼簡單的運算,計算機怎麼能計算不精確呢?但事實就是這樣,究其原因,我們需要理解float和double的二進制表示,後續文章進行分析。
自增(++)/自減(--)
自增/自減是對自己做加一和減一操作,但每個都有兩種形式,一種是放在變量後,例如a++, a--,另一種是放在變量前,例如++a, --a。
如果只是對自己操作,這兩種形式也沒什麼差別,區別在於還有其他操作的時候。放在變量後(a++),是先用原來的值進行其他操作,然後再對自己做修改,而放在變量前(++a),是先對自己做修改,再用修改後的值進行其他操作。例如,快捷運算和其等同的運算分別是:
快捷運算 等同運算 b=a++-1b=a-1
a=a+1
c = ++a-1a=a+1
c=a-1
arrA[i++]=arrB[++j]j=j+1
arrA[i]=arrB[j]
i=i+1
自增/自減是"快捷"操作,是讓程序員少寫代碼的,但遺憾的是,由於比較奇怪的語法和詭異的行為,帶給了初學者一些困惑。
比較運算
比較運算就是計算兩個值之間的關系,結果是一個布爾類型(boolean)的值。比較運算適用於所有數值類型和字符類型。數值類型容易理解,但字符怎麼比呢?後續文章解釋。
比較操作符有:大於(>),大於等於(>=),小於(<),小於等於(<=),等於(==),不等於(!=)。
大部分也都是比較直觀的,需要注意的是等於。
首先,它使用兩個等號==,而不是一個等號(=),為什麼不用一個等號呢?因為一個等號(=)已經被占了,表示賦值操作。
另外,對於數組,==判斷的是兩個數組是不是同一個數組,而不是兩個數組的元素內容是否一樣,即使兩個數組的內容是一樣的,但如果是兩個不同的數組,==依然會返回false,如下所示:
int[] a = new int[] {1,2,3}; int[] b = new int[] {1,2,3}; // a==b的結果是false
如果需要比較數組的內容是否一樣,需要逐個比較裡面存儲的每個元素。
邏輯運算
邏輯運算根據數據的邏輯關系,生成一個布爾值true或者false。邏輯運算只可應用於boolean類型的數據,但比較運算的結果是布爾值,所以其他類型數據的比較結果可進行邏輯運算。
邏輯運算符具體有:
邏輯運算的大部分都是比較直觀的,需要注意的是&和&&,以及|和||的區別。如果只是進行邏輯運算,它們也都是相同的,區別在於同時有其他操作的情況下,例如:
boolean a = true; int b = 0; boolean flag = a | b++>0;
因為a為true,所以flag也為true,但b的結果為1,因為|後面的式子也會進行運算,即使只看a已經知道flag的結果,還是會進行後面的運算。而||則不同,如果最後一句的代碼是:
boolean flag = a || b++>0;
則b的值還是0,因為||會"短路",即在看到||前面部分就可以判定結果的情況下,忽略||後面的運算。
這個例子我們還可以看出,自增/自減操作帶給我們的困擾,別的操作都干干脆脆,賦值就賦值,加法就加法,比較就比較,它非混在一起,可能會少寫些代碼,但如果使用不當,會使理解困難很多。
運算符優先級
一個稍微復雜的運算可能會涉及多個變量,和多種運算,那哪個先算,哪個後算呢?程序語言規定了不同運算符的優先級,有的會先算,有的會後算,大部分情況下,這個優先級與我們的常識理解是相符的。
但在一些復雜情況下,我們可能會搞不明白其運算順序。但這個我們不用太操心,可以使用括號()來表達我們想要的順序,括號裡的會先進行運算,簡單的說,不確定順序的時候,就使用括號。
小結
本節我們介紹了算術運算,比較運算和邏輯運算,但我們遺留了一些問題,比如:
這是怎麼回事呢?
------------------
未完待續,查看最新文章,敬請關注微信公眾號“老馬說編程”,深入淺出,探索Java編程及計算機技術的本質。原創文章,保留所有版權。