本文中主要介紹Java中的運算操作。 如何正確的使用運算符,防止溢出是每個程序員的責任。
Java支持以下的算術運算:
對於下面的表達式:
轉化為編碼(1+2*a)/3 + (4*(b+c)*(5-d-e))/f - 6*(7/g+h),需要注意的不能省略乘號(*)。
算術運算僅適用於基本類型:byte, short, int, long, float, double和char,其中不包括boolean。
如果兩個操作數的類型是int/long/float/double, 運算操作會直接使用此類型進行計算,例如int 5 + int 6 → int 11;double 2.1 + double 1.2 → double 3.3。
值得注意的是對於int的除法運算,計算結果會被截斷,例如1/2 → 0而不是0.5。
如果兩個操作數的類型是byte,short或者char,運算操作會使用int類型進行計算,char會被轉化為16位無符號整數,例如byte 127 + byte 1 → int 127 + int 1 → int 128。
如果兩個操作數屬於不同的類型,較小的類型會被隱式的轉換成較大的類型,運算操作會使用較大的類型進行計算。
二元運算操作對於類型的轉換概括如下:
一元運算操(正號、負號)對於類型的轉換概括如下:
byte b1 = 1;
byte b2 = -b1; // 編譯會出錯, 因為-b1會返回int,不能轉換成byte
為了計算余數會重復的運行減法運算,直到差值的絕對值小於右操作數的絕對值,舉例說明:
在Java中是沒有指數運算符的,你看到的'^'運算符是異或,不過你可以使用Math.exp(X,Y)進行指數的運算。
研究下面的代碼並解釋輸出輸出:
/*
* "int"溢出說明
*/
public class OverflowTest {
public static void main(String[] args) {
// int取值范圍[-2147483648, 2147483647]
int i1 = 2147483647; // int最大值
System.out.println(i1 + 1); // -2147483648 (溢出)
System.out.println(i1 + 2); // -2147483647
System.out.println(i1 * i1); // 1
int i2 = -2147483648; // int最小值
System.out.println(i2 - 1); // 2147483647 (溢出)
System.out.println(i2 - 2); // 2147483646
System.out.println(i2 * i2); // 0
}
}
對於運算過程中的溢出,Java不會發出錯誤或者警告信息,但會產生不正確的結果。
另一方面整數除法會產生截斷的整數,我們稱之為向下溢出,例如1/2→ 0,而不是0.5。
做為程序員你有責任去檢查編程中的溢出。
這時候我們也許會問,為什麼計算機不去標記溢出?由於歷史的原因, 當時處理器很慢,檢查溢出會消耗性能。
在Java中,如果將double或float數據賦值給int變量會產生編譯錯誤。
double d = 3.5;
int i;
i = d; // 編譯錯誤
int sum = 55.66f; // 編譯錯誤
double賦值給int變量,你需要使用顯示類型轉換,形式為(int)value,返回的結果是被截斷的int數據,舉例說明:
double d = 3.5;
int i;
i = (int) d; // 將double類型的3.5轉換成int類型的3,之後賦值給i
類型轉換只需要一個操作數,Java中有兩種類型轉換:
int i = 3;
double d;
d = i; // 正確, 不需要進行類型轉換,d=3.0
d = (double) i; // 也可以使用顯示類型轉換
double aDouble = 55; // 編譯器會自動的將int 55轉換成double 55.0
double nought = 0; // 編譯器會自動的將int 0轉換成double 0.0
// 值得注意的是int 0和double 0.0是不同的
下面的這幅圖展示了編譯器隱示類型轉換的順序,轉換規則是將小類型晉升為大類型,這樣做可以防止精度缺失。降級類型需要顯示類型轉換,精度會缺失,值得注意的是char會被視作16位無符號整數,取值范圍[0, 65535],boolean類型不支持轉換。
例子,計算從1到100的平均值,仔細研究下面的代碼
public class Sum1To100 {
public static void main(String[] args) {
int sum = 0;
double average;
int number = 1;
while (number <= 100) {
sum += number; // sum最後的結果為int 5050
++number;
}
average = sum / 100; // average = 50.0而不是50.5
System.out.println("Average is " + average); // 平均值為50.0
}
}
這是因為sum與100都是int類型,二者相除返回的是被截斷的int,如果想得到正確的結果,你可以采用下面的方式:
average = (double)sum / 100; // 進行除法運算前顯示的將sum轉成double類型
average = sum / (double)100; // 進行除法運算前顯示的將100轉成double類型
average = sum / 100.0;
average = (double)(sum / 100); // 這種做法是錯誤的,你知道是什麼原因嗎?
除了前面介紹的常用的賦值運算=,Java還提供了其它的復合賦值運算:
復合加法運算
var+=expr對於一元運算自增(++)和自減(--),適用於除boolean類型以外的其它 基本類型byte, short, char, int, long, float和double。
自增和自減都是基於自身的操作,例如x++自增後重新返回給x。
自增/自減操作符號可以放置於操作數之前,也可以放在操作數之後,但是兩者有著不同的意義。
如果這些運算符基於自身操作,運算符前置和後置具有同樣的效果,例如++x和x++,因為表達式的值會被忽略。
如果用於其它的操作,例如y=x++或y=++x,對於y值來說運算符前置和後置有不同的值。
很多時候,你需要對兩個值進行比較之後,才會進行某些操作,舉例如果mark值大於等於50,輸出"PASS!"。
Java提供了6種比較運算符,經過比較運算後返回布爾值即true或false。
每個比較運算符需要兩個操作數,正確的寫法:x > 1 && x < 100,錯誤的寫法 1 < x < 100, 這裡面&&表示與操作。
Java提供了4種基於boolean的邏輯運算,按照優先級順序如下:
真值表如下:
舉例說明:
// 如果x取值范圍在[0,100],返回true
(x >= 0) && (x <= 100)
// 如果x取值范圍不在[0,100],返回true
(x < 0) || (x > 100)
!((x >= 0) && (x <= 100))
// 計算是否為閏年:某年被4但不能被100整除,或者被400整除
((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)
練習:研究下面的程序並解釋輸出。
public class RelationalLogicalOpTest {
public static void main(String[] args) {
int age = 18;
double weight = 71.23;
int height = 191;
boolean married = false;
boolean attached = false;
char gender = 'm';
System.out.println(!married && !attached && (gender == 'm'));
System.out.println(married && (gender == 'f'));
System.out.println((height >= 180) && (weight >= 65) && (weight <= 80));
System.out.println((height >= 180) || (weight >= 90));
}
}
根據提供的日期:年、月(1-12)和日(1-31),計算該日期是否早於1582年10月15日。
優先級由高到低:'!','^','&&','||',編程中如果不確定,請使用括號()。
System.out.println(true || true && false); // true (和下面的一樣)
System.out.println(true || (true && false)); // true
System.out.println((true || true) && false); // false
System.out.println(false && true ^ true); // false (和下面的一樣)
System.out.println(false && (true ^ true)); // false
System.out.println((false && true) ^ true); // true
邏輯與(&&)和邏輯或(||)被稱為短路操作符,這意味計算結果如果可以通過左操作數來確定,那麼右操作數會被忽略,例如false && ...會返回false,true || ...會返回true。