JAVA基础之三:运算操作符

原文链接:http://happyshome.cn/blog/java/basics/operations.html


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

1. 算术运算

Java支持以下的算术运算:

运算符 描述 使用 例子
* 乘法 expr1 * expr2 2 * 3 → 6
3.3 * 1.0 → 3.3
/ 除法 expr1 / expr2 1 / 2 → 0
1.0 / 2.0 → 0.5
% 取余 expr1 % expr2 5 % 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 = expr 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
%= 复合取余运算 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

真值表如下:

与 (&&) true false
true true false
false false false
     
或 (||) true false
true true true
false true false
     
非 (!) true false
Result false true
     
异或 (^) true false
true false true
false true false

举例说明:

// 如果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。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值