数据的运算
既然是“计算机”,那么其主要功能就是“计算”了。我们平时使用计算机打游戏、看电影、听音乐、上网、处理文档、科学研究等行为,本质上都是将各种问题转换成了计算的问题。
常用的计算包括算术运算、赋值运算、比较运算、逻辑运算、位运算等。表示运算的符号就是运算符。
下面我们来一一学习各种运算和对应的运算符。
算术运算
Java中的算术运算,是以加减乘除为基础的一些运算方法。包括:
运算符 | 说明 | 示例 | 结果 |
---|---|---|---|
+ | 正号 | i=10; m=+i; | m为10 |
- | 负号 | i=10; m=-i; | m为-10 |
+ | 加 | 8+4 | 12 |
- | 减 | 8-4 | 4 |
* | 乘 | 8*4 | 32 |
\ | 除 | 8\2 | 4 |
% | 取模(求余) | 9 % 4 | 1 |
++ | 自增(变量前) | i=4;m=++i; | i为5,m为5 |
++ | 自增(变量后) | i=4;m=i++; | i为5,m为4 |
– | 自减(变量前) | i=4;m=–i; | i为3,m为3 |
– | 自减(变量后) | i=4;m=i–; | i为3,m为4 |
大多数运算都和数学所学相同,不再赘述。着重介绍除法和自增自减运算。
注意:在Java中,实数(float和double类型)是可以进行取模操作的。
除法和求余
我们先来看一个程序(只显示主方法内的代码):
public class Arithmetic01 {
public static void main(String[] args) {
System.out.println("8/4="+8/4);//数学计算中结果为2,结果为2
System.out.println("9/4="+9/4);//数学计算中结果为2.25,结果为2
System.out.println("11/4="+11/4);//数学计算中结果为2.75,结果为2
System.out.println("11.0/4="+11.0/4);//数学计算中结果为2.75,结果为2.75
System.out.println("11%4="+11%4);//余数为3
System.out.println("11.5%3.5="+11.5%3.5);//余数为1.0
}
}
运行结果为:
8/4=2
9/4=2
11/4=2
11.0/4=2.75
11%4=3
11.5%3.2=1.0
通过这个程序,我们可以看到,8除以4结果为2,这符合我们的预期;但9除以4和11除以4,结果均只有整数部分,这就是整数除法的特点:整数除以整数,结果还是整数。
如果我们希望得到期望中的小数,将除法运算中任何一个数改为实数类型即可。
这个特性是由计算机中整数的存储和处理方法决定的,因此我们在使用除法时一定要特别注意。
此外,在Java中,实数(float和double类型)是可以进行取模操作的。
自增和自减
自增、自减,顾名思义,就是为使用这个运算的变量自身增加1或者减少1。例如变量x原来的值是40,使用自增运算后,x的值为41;如果变量x原来的值是40,使用自减运算后,x的值为39。让上面的表中,最令人迷惑的就是:当自增|自减运算是其它运算的一部分时,变量和自增|自减运算的位置不同,会导致最终结果的不同。由于自增和自减的规则相同,所以下面这个例子中只使用了自增运算:
public class Arithmetic02 {
public static void main(String[] args) {
int x = 10;
int y;
System.out.println("x=" + x);//x的初始值为10
x++;
System.out.println("进行x++运算后,x=" + x);//自增运算一次,x值为11
++x;
System.out.println("进行++x运算后,x=" + x);//再自增运算一次,x值为12
//先提取x的值进行赋值运算,再进行自增运算
System.out.println("x+++y=" + (x+++y)+",x="+x+",y="+y);
//先进行自增运算,再提取x的值进行赋值运算
System.out.println("++x+y=" + (++x+y)+",x="+x+",y="+y);
}
}
运行结果是:
x=10,y=20
进行x++运算后,x=11
进行++x运算后,x=12
x+++y=32,x=13,y=20
++x+y=34,x=14,y=20
通过这个例子,不难总结:如果自增|自减运算是单独进行的,那么变量和运算符的位置关系不影响最终结果;如果自增|自减运算是其它运算中的一部分,那么变量在前面时,就先把变量的值代入到整个运算中,运算完毕后再进行自增|自减运算,运算符在前面时,就先进行自增|自减运算,然后把结果代入到整个运算中。
赋值运算
赋值运算我们一直在用,前面的程序中:int x=10;
。这可不能读作“整型变量x等于10”,而是“把10赋给整型变量x”。这个=
在数学上是“等号”,但在Java中是“赋值运算符”,它的作用是把运算符右边的值赋给左边的变量。所以:
- 赋值运算是从右向左进行的。
a=a+1
这样的代码在执行时,就会先把右边的变量a的值提取出来,加1,再将和存入到变量a中。同时,代码int a,b,c;a=b=c=10;
是正确的,因为从右向左赋值;但int a=b=c=10;
是错误的,因为10先赋给了c,但c还没有定义。 - 右边必须是一个确定的值或能求出确定值的运算式,左边则必须、只能是个变量
为了简化代码,还有以下赋值运算符:
运算符 | 说明 | 示例 | 相当于 |
---|---|---|---|
+= | 加等于 | a+=3 | a=a+3 |
-= | 减等于 | a-=3 | a=a-3 |
*= | 乘等于 | a*=3 | a=a*3 |
\= | 除等于 | a\=3 | a=a\3 |
%= | 模等于 | a%=3 | a=a |
02
比较运算
在Java中可以对两个数值进行大小比较,其结果为一个布尔值,true表示比较成立,false表示不成立。比较运算包括:
运算符 | 说明 | 示例 | 结果 |
---|---|---|---|
== | 等于 | 50==23 | false |
!= | 不等于 | 50!=23 | true |
> | 大于 | 50>23 | true |
< | 小于 | 50<23 | true |
>= | 大于等于 | 50>=23 | true |
<= | 小于等于 | 50<=23 | true |
大家可以编写并运行以下程序(只显示主要部分):
public class Compare {
public static void main(String[] args) {
System.out.println("50==23?"+(50==23));
System.out.println("50!=23?"+(50!=23));
System.out.println("50>23?"+(50>23));
System.out.println("50<23?"+(50<23));
System.out.println("50>=23?"+(50>=23));
System.out.println("50<=23?"+(50<=23));
}
}
结果为:
50==23?false
50!=23?true
50>23?true
50<23?false
50>=23?true
50<=23?false
逻辑运算
逻辑运算是针对逻辑值(布尔类型)的运算,其结果还是一个布尔类型的值。逻辑运算包括:
运算符 | 说明 | 示例 | 结果 |
---|---|---|---|
&和&& | 与 | a&b(a&&b) | 当a和b均为true时,结果为true,其他情况均为false |
| 和 || | 或 | a|b(a||b) | 当a和b均为false时,结果为false,其他情况均为true |
! | 非 | !a | 取相反值,a为true时,!a为false;a为false时,!a为true |
^ | 异或 | a^b | a、b值相同时为false,不相同时为true |
与和或运算均出现了两个符号。其中,&和|中包含其它运算时,所有的运算都会被执行;&&和||中包含其它运算时,如果在不完成所有运算的情况下就能得出结论,那么不再执行剩下的运算。
由于短路与和短路或情况类似,我们这里只看短路或的例子。对于短路与,大家可以编写下面的程序:
public class LogicDemo {
public static void main(String[] args) {
int a=100;
int b=200;
boolean c=true;
boolean d=false;
System.out.println("逻辑或:"+(c|d));
System.out.println("逻辑与:"+(c&d));
System.out.println("逻辑异或:"+(c^d));
System.out.println("逻辑非:"+(!c));
System.out.println("使用普通或运算:");
System.out.println((a>20)|(++b>500));//a>20值为true,++b>500值为false,同时b自增1,结果为true
System.out.println("b="+b);//输出b的值,为201
System.out.println("使用短路或运算:");
System.out.println("计算(a>20)||(++b>500):"+((a>20)||(++b>500)));//a>20值为true,此时已经可以确定最终结果,++b>500没有执行
System.out.println("b="+b);//输出b的值,为201
System.out.println("计算(a<20)||(++b>500):"+((a<20)||(++b>500)));//a<20值为false,此时不能确定最终结果,++b>500需要执行
System.out.println("b="+b);//输出b的值,为202
}
}
运行结果为:
逻辑或:true
逻辑与:false
逻辑异或:true
逻辑非:false
使用普通或运算:
true
b=201
使用短路或运算:
计算(a>20)||(++b>500):true
b=201
计算(a<20)||(++b>500):false
b=202
其它运算大家可以自己写程序进行验证和练习。
位运算
位运算只能用于整型数据,而且只能在二进制层面进行运算。整型数据在计算机中是以二进制形式存储,位运算也是针对二进制数据进行的运算。主要包括:
运算符 | 说明 | 示例 | 结果 |
---|---|---|---|
& | 按位与 | a&b | 参与运算的两位都是1,结果为1,其余为0 |
| | 按位或 | a|b | 参与运算的两位都是0,结果为0,其余为1 |
~ | 取反 | ~a | 1变为0,0变为1 |
^ | 异或 | a^b | 参与运算的两位相同为0,不同为1 |
<< | 左移 | a<<2 | 二进制数据向左移动2位,右端补0 |
>> | 右移 | a>>2 | 二进制数据向右移动2位,正数左端补0,负数补1 |
>>> | 无符号右移 | a>>>2 | 二进制数据向右移动2位,无论正负均补0 |
在与、或、异或的位运算中,参与运算的两个数必须是二进制形式,右对齐,左边不足补0,从右向左进行计算。例如:
10011001
& 00001101
----------
00001001
取反时,将二进制的1转换为0,0转换为1。需要注意的是,由于正整数和负整数在内存中的表示方法不同,对十进制的整数1
进行取反操作,结果是-2
,而不是0。
左移时,将最左边的数据移出变量的存储空间,右侧补0。如果左移时,没有1被移出存储空间,每左移一位,就相当于乘以一次2。
右移时,将最右边的数据移出变量的存储空间,但由于最左边那一位是符号位,左边补0还是1,产生两种处理方式:一是正数补0,负数补1,即>>
;二是无论正负,一律补0,即>>>
。
大家可以编写并运行以下程序,思考一下具体执行过程:
public class BitOperate {
public static void main(String[] args) {
int a=1;//二进制形式为01,省略了前面30字位的0
int b=3;//二进制形式为11,省略了前面30字位的0
System.out.println("a&b="+(a&b));//01&11=01,十进制的1
System.out.println("a|b="+(a|b));//01|11=11,十进制的3
System.out.println("a^b="+(a^b));//01^11=10,十进制的2
System.out.println("~a="+(~a));//~01=10,前面30字位的0也要取反为1,所以实际答案是11111111111111111111111111111110,-2
System.out.println("a<<2="+(a<<2));//a左移两位,为4
System.out.println("-a<<2="+((-a)<<2));//-a,即-1左移两位,为-4
System.out.println("b>>1="+(b>>1));//b右移1位,为1
System.out.println("-b>>1="+((-b)>>1));//-b右移1位,为-2
System.out.println("b>>>1="+(b>>>1));//b无符号右移1位,为1
System.out.println("-b>>>1="+((-b)>>>1));//-b无符号右移1位,为2147483646
}
}
条件运算
Java中还有一个条件运算,也是Java唯一一个三目运算符(这个运算涉及到三个参数)。它的格式是:表达式1?表达式2:表达式3
。
表达式1必须是逻辑表达式——其自身或者运算结果必须是个逻辑值,true或者false。如果表达式1的值是true,整个运算的结果就是表达式2的值;如果表达式1的值是false,整个运算的结果就是表达式3的值。也就是说,条件运算可以根据条件的不同(表达式1的值),来产生不同的结果(表达式2和表达式3的值)。
我们来看这个程序:
public class SelectiveOperation {
public static void main(String[] args) {
int a=300;
int b=500;
byte score=90;
System.out.println("a和b中:"+(a>b?"a比较大":"b比较大"));
System.out.println("你的考试成绩:"+(score>=60?"及格":"不及格"));
}
}
02
数据类型转换
之前我们提到,9/4=2
,这是由于计算机中不同数据类型的保存和处理方法是不同的。这就带来一个问题,当不同类型的数据放到一起进行运算时,该如何处理?
答案是通过数据类型转换,把不同类型的数据转换为相同的数据类型,再进行运算。
具体转换方法这里不进行深入探讨,我们主要学习在什么情况下使用哪种转换方式。
在Java中,有两种数据类型转换方式:自动类型转换和强制类型转换。
自动类型转换
顾名思义,不需要我们手工干预就可以完成。一般来说,当两个不同类型的数据在一起运算时,表示范围小、精度低的数据类型会转换为表示范围大、精度高的类型。
转换规律为:
(byte,short,char)–>int–>long–>float–>double
byte,short和char三种类型都会转换为int类型——即使运算中不包含int类型时也是如此,例如两个short类型数据在一起运算。
强制类型转换
我们有时需要手工指定转换目标,例如把表示范围大、精度高的类型转换为范围小、精度低的类型。转换方法:(目标数据类型)转换对象
,例如:(int)3.14
;这会把3.14(double类型)强制转换为int类型,结果为3。
不过有时也需要强制把表示范围小、精度低的类型会转换为表示范围大、精度高的类型,例如在计算平均分时,我们需要用总分除以人数。人数必然是整数,分数很多时候也是分数,很容易在无意间写成两个整数相除,结果必然还是整数。我们可以把计算平均分的方法写成:
//avg方法用于计算平均分,sum表示总分,num表示人数
double avg(int sum,int num){
return (double)sum/num;
}
在这个方法中,sum会被强制转换为double类型,那么结果自然也是double类型了。
需要注意的是:除了
=
之外的所有赋值运算符,都会在需要时自动进行强制类型转换。如:short s=50; int i=100; s+=i;
我们看这个对类型转换进行总结的程序:
public class DTConversion {
public static void main(String[] args) {
//整数的自动类型转换
byte b=34;
short s=23;
s=(short)(s+b);//自动转换时,short数据和byte数据相加,会转换为int数据,所以不能直接赋值给short变量
System.out.println("自动进行强制转换:"+(s+=b));//这种形式的赋值运算会自动进行强制转换
System.out.println("不转换:"+(12/5));
System.out.println("自动类型转换:"+(12.0/5));
System.out.println("强制类型转换:"+((double)12/5));
System.out.println("强制转换可能会导致精度损失:"+(int)12.5);
}
}
运算符的优先级
Java中包含了很多运算符,当这些运算符在同一个运算式中时,必须规定执行顺序,这就是优先级。Java中运算符的优先级是这样规定的:
优先级 | 运算符 | 结合性 |
---|---|---|
1 | .、()、[]、{} | 从左向右 |
2 | !、+、-、~、++、– | 从右向左 |
3 | *、/、% | 从左向右 |
4 | +、-(正负号) | 从左向右 |
5 | <<、 >>、 >>> | 从左向右 |
6 | <、 <=、 >、>=、instanceof | 从左向右 |
7 | ==、!= | 从左向右 |
8 | & | 从左向右 |
9 | ^ | 从左向右 |
10 | | | 从左向右 |
11 | && | 从左向右 |
12 | || | 从左向右 |
13 | ?: | 从右向左 |
14 | =、+=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、~=、^=、|= | 从右向左 |
由于小括号的优先级非常高,因此,如果暂时记不住这个表,那么可以用小括号把需要先计算的部分括起来。但仍需要通过记忆和练习,记住他们的优先级的。
02