您的位置:首页 > 编程语言 > Java开发

JAVA基础之三:运算操作符

2016-04-14 19:19 423 查看
原文链接:http://happyshome.cn/blog/java/basics/operations.html





本文中主要介绍Java中的运算操作。 如何正确的使用运算符,防止溢出是每个程序员的责任。


1. 算术运算

Java支持以下的算术运算:
运算符描述使用例子
*乘法expr1 * expr22 * 3 → 6

3.3 * 1.0 → 3.3
/除法expr1 / expr21 / 2 → 0

1.0 / 2.0 → 0.5
%取余expr1 % expr25 % 2 → 1

-5 % 2 → -1

5.5 % 2.2 → 1.1
+加法(正号)expr1 + expr2

+expr
1 + 2 → 3

1.1 + 2.2 → 3.3
-减法(负号)expr1 - expr2

-expr
1 - 2 → -1

1.1 - 2.2 → -1.1


2. 算术表达式

对于下面的表达式:



转化为编码(1+2*a)/3 + (4*(b+c)*(5-d-e))/f - 6*(7/g+h),需要注意的不能省略乘号(*)。

优先级:


乘法(*),除法(/)和取余(%)优先于加法(+)和减法(-)运算,例如1+2*3-4/2可以表示为1+(2*3)-(4/2)。
正号(+)和负号(-)拥有更高级别。
括号()拥有最高级别,常用来调整运算顺序。
对于同级别运算符号,表达式结果从左到右计算,例如1+2-3+4等价于((1+2)-3)+4,1*2%3/4等价于((1*2)%3)/4。


3. 混合类型运算

算术运算仅适用于基本类型: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。

如果两个操作数属于不同的类型,较小的类型会被隐式的转换成较大的类型,运算操作会使用较大的类型进行计算。

举例说明:


int/double → double/double → double,1/2 → 0, 1.0/2.0 → 0.5, 1.0/2 → 0.5, 1/2.0 → 0.5。
char + float → int + float → float + float → float。
9 / 5 * 20.1 → (9 / 5) * 20.1 → 1 * 20.1 → 1.0 * 20.1 → 20.1 (你可能不会想到这个答案)。
byte 1 + byte 2 → int 1 + int 2 → int 3 (结果是int,不是byte)。

二元运算操作对于类型的转换概括如下:

如果其中一个操作数是double,另一个操作数默认转为double。
如果其中一个操作数是float,另一个操作数默认转为float。
如果其中一个操作数是long,另一个操作数默认转为long。
其余的操作数默认的转为int。

一元运算操(正号、负号)对于类型的转换概括如下:

如果操作数是double,float,long或者int,不需要转换。
其余的如果是byte,short或char,会默认转换为int。


举例说明:

byte b1 = 1;
byte b2 = -b1;  // 编译会出错, 因为-b1会返回int,不能转换成byte


取余运算符

为了计算余数会重复的运行减法运算,直到差值的绝对值小于右操作数的绝对值,举例说明:

-5 % 2 ⇒ -3 % 2 ⇒ -1
5.5 % 2.2 ⇒ 3.3 % 2.2 ⇒ 1.1


指数

在Java中是没有指数运算符的,你看到的'^'运算符是异或,不过你可以使用Math.exp(X,Y)进行指数的运算。


4. 向上溢出/向下溢出

研究下面的代码并解释输出输出:
/*
* "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。

做为程序员你有责任去检查编程中的溢出。

这时候我们也许会问,为什么计算机不去标记溢出?由于历史的原因, 当时处理器很慢,检查溢出会消耗性能。


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中有两种类型转换:

以(new-type)操作数的形式进行显示类型转换。
如果没有精度缺失,编译器自动的会进行隐示类型转换。

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);   // 这种做法是错误的,你知道是什么原因吗?


6. 复合赋值运算

除了前面介绍的常用的赋值运算=,Java还提供了其它的复合赋值运算:
操作符解释使用例子
=赋值

将右操作数赋值给左操作数
var = exprx = 5;
+=复合加法运算
var += expr

等价于var = var + expr
x += 5;

等价于x = x + 5
-=复合减法运算var -= expr

等价于var = var - expr
x -= 5;

等价于x = x - 5
*=复合乘法运算var *= expr

等价于var = var * expr
x *= 5;

等价于x = x * 5
/=复合除法运算var /= expr

等价于var = var / expr
x /= 5;

等价于x = x / 5
%=复合取余运算var %= expr

等价于var = var % expr
x %= 5;

等价于x = x % 5


7. 自增/自减

对于一元运算自增(++)和自减(--),适用于除boolean类型以外的其它 基本类型byte, short, char, int, long, float和double。
操作符解释例子
++原数值加1

x++或++x等价于x += 1或x = x + 1
int x = 5;

x++;

++x;
--原数值减1

x--或--x等价于x -= 1或x = x - 1
int y = 6;

y--;

--y;
自增和自减都是基于自身的操作,例如x++自增后重新返回给x。

自增/自减操作符号可以放置于操作数之前,也可以放在操作数之后,但是两者有着不同的意义。
如果这些运算符基于自身操作,运算符前置和后置具有同样的效果,例如++x和x++,因为表达式的值会被忽略。

如果用于其它的操作,例如y=x++或y=++x,对于y值来说运算符前置和后置有不同的值。
操作符解释例子
++var自增

首先var加1,计算结果使用var
y = ++x;

等价于x=x+1; y=x;
var++自增

首先计算结果使用var,接着var加1
y = x++;

等价于oldX=x; x=x+1; y=oldX;
--var自减y = --x;

等价于x=x-1; y=x;
var--自减y = x--;

等价于oldX=x; x=x-1; y=oldX;


8. 关系和逻辑运算符

很多时候,你需要对两个值进行比较之后,才会进行某些操作,举例如果mark值大于等于50,输出"PASS!"。

Java提供了6种比较运算符,经过比较运算后返回布尔值即true或false。
操作符解释使用例子(x=5, y=8)
==相等expr1 == expr2(x == y) → false
!=不相等expr1 != expr2(x != y) → true
>大于expr1 > expr2(x > y) → false
>=大于等于expr1 >= expr2(x >= 5) → true
<小于expr1 < expr2(y < 8) → false
<=小于等于expr1 >= expr2(y <= 8) → true
每个比较运算符需要两个操作数,正确的写法:x > 1 && x < 100,错误的写法 1 < x < 100, 这里面&&表示与操作。

Java提供了4种基于boolean的逻辑运算,按照优先级顺序如下:
操作符解释使用
!逻辑非!booleanExpr
^逻辑异或booleanExpr1 ^ booleanExpr2
&&逻辑与booleanExpr1 && booleanExpr2
||逻辑或booleanExpr1 || booleanExpr2
真值表如下:
与 (&&)truefalse
truetruefalse
falsefalsefalse
或 (||)truefalse
truetruetrue
falsetruefalse
非 (!)truefalse
Resultfalsetrue
异或 (^)truefalse
truefalsetrue
falsetruefalse
举例说明:
// 如果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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: