JAVA基础之三:运算操作符

来源:互联网 发布:中国乒乓球 知乎 编辑:程序博客网 时间:2024/06/07 19:36

原文链接: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
+expr1 + 2 → 3
1.1 + 2.2 → 3.3-减法(负号)expr1 - expr2
-expr1 - 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.0double 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 + exprx += 5;
等价于x = x + 5-=复合减法运算var -= expr
等价于var = var - exprx -= 5;
等价于x = x - 5*=复合乘法运算var *= expr
等价于var = var * exprx *= 5;
等价于x = x * 5/=复合除法运算var /= expr
等价于var = var / exprx /= 5;
等价于x = x / 5%=复合取余运算var %= expr
等价于var = var % exprx %= 5;
等价于x = x % 5

7. 自增/自减

对于一元运算自增(++)和自减(--),适用于除boolean类型以外的其它 基本类型byte,  short,  char,  int,  long,  float和double。

操作符解释例子++原数值加1
x++或++x等价于x += 1或x = x + 1int x = 5;
x++; 
++x;--原数值减1
x--或--x等价于x -= 1或x = x - 1int y = 6;
y--;
--y;

自增和自减都是基于自身的操作,例如x++自增后重新返回给x。
自增/自减操作符号可以放置于操作数之前,也可以放在操作数之后,但是两者有着不同的意义。

如果这些运算符基于自身操作,运算符前置和后置具有同样的效果,例如++x和x++,因为表达式的值会被忽略。 
如果用于其它的操作,例如y=x++或y=++x,对于y值来说运算符前置和后置有不同的值。

操作符解释例子++var自增
首先var加1,计算结果使用vary = ++x;
等价于x=x+1; y=x;var++自增
首先计算结果使用var,接着var加1y = 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

真值表如下:

与 (&&)truefalsetruetruefalsefalsefalsefalse   或 (||)truefalsetruetruetruefalsetruefalse   非 (!)truefalseResultfalsetrue   异或 (^)truefalsetruefalsetruefalsetruefalse

举例说明:

// 如果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));  // trueSystem.out.println((true || true) && false);  // falseSystem.out.println(false && true ^ true);     // false (和下面的一样)System.out.println(false && (true ^ true));   // falseSystem.out.println((false && true) ^ true);   // true
短路操作符

逻辑与(&&)和逻辑或(||)被称为短路操作符,这意味计算结果如果可以通过左操作数来确定,那么右操作数会被忽略,例如false && ...会返回false,true || ...会返回true。


0 0