运算符是一些特殊的符号,主要用于数学函数、一些类型的赋值语句和逻辑比较方面。
Java语言中提供了丰富的运算符,如赋值运算符、算数运算符、比较运算符等等。Java中的运算符按操作数的个数总体可以分为三类:单目运算符、双目运算符和三目运算符。
单目运算符
Java中单目运算符主要有自增和自减运算符。
自增、自减运算符可以放在操作数前(如:++i,- -i),也可以放在操作数后(如i++,i- -),操作数必须是一个整型或者浮点型变量。自增、自减运算符的作用是使变量的值加1或减1。
注意:因为自增、自减运算符改变了变量的值,所以操作数不能是数值,例如4++是非法的。
例如:
int i = 5;
System.out.println(i);//5
int j = i++;
System.out.println(j);//5
int k = ++i;
System.out.println(k);//7
上面的代码中先声明了i = 5,再声明了j,并且把表达式i++赋值给j,再声明了k并把表达式++i的值赋值给k。其中j = i++是先把i = 5的值拷贝了一份(i’ = 5),在做i+1的运算,把计算结果6赋值给i,i = 6,然后将原先的拷贝i’ = 5的值赋给j,即j = 5;k = ++i是先把计算i + 1 = 7,再把计算结果拷贝一份复制给k,即k = 7,计算结果赋给i,即i = 7。如下图:左为j = i++,右为k = ++j
下面我们再来看一个例子:
public static void main(String[] args) {
int count1 =0;
int count2 =0;
for(int i=0; i<100; i++) {
count1 = count1++;
count2 = ++count2;
}
System.out.println("count1 = " + count1);
System.out.println("count2 = " + count2);
}
}
上面的代码打印什么呢?是count1 = 100和count2 = 100吗?下面我们看下实际运行的结果:
很明显打印的结果并不是我们预料的那样,count2 = 100比较容易理解,但是想要彻底明白count1++的运行原理我们需要了解JVM底层是怎么执行count1++运算的:
先把count1的值(注意是值,不是引用)拷贝到一个临时变量区,然后对count1变量加1,最后返回临时变量区的值。程序第一次循环时的详细处理步骤如下:
步骤1:JVM把count1的值拷贝到临时变量区
步骤2:count1值加1,这时候count1的值是1
步骤3:返回临时变量区的值,0
步骤4:返回值赋值给count1,此时count1值被重置成0.
双目运算符
Java中的双目运算符比较多,例如赋值运算符(=)、算数运算符(+,-,*,/,%)比较运算符(<,>,<=,>=,==,!=),逻辑运算符(&&,&,||,|,!)和位运算符(&,|,~,>>,<<,>>>)。
算数运算符:
算数运算符主要有加(+)减(-)乘(*)除(/)和取余(%),它们的运算原理与我们平时计算的方式一样,取余运算时取除法运算中的余数。例如:5%2=1;
注意:
1、在进行除法运算时,0不能作为除数。例如,对于int a = 5/0;编译时不会报错但运行时会报ArithmeticException异常;对于“1.0/0.0”的值将是Infinity(无穷大)。
2、在不用数据类型的数值进行算数运算时需要注意隐式类型转换。例如:1+1.0f+1.0d=3.0
。
赋值运算符:
赋值运算符以符号“=”表示,其作用是将右方操作数的值赋给左方的操作数,例如:int num = 9
。
由于赋值运算符“=”处理时会先取得右边表达式处理的结果,因此一个表达式中如果含有两个以上的“=”运算符,会从最右边的“=”开始处理。
比较运算符:
比较运算符属于双目运算符,用于变量之间或其他类型的信息之间的比较,比较运算符的运算结果是boolean型的。当运算符对应的关系成立时,运算结果为true,否则为false,经常用于循环语句和if条件判断语句中。比较运算符共有6个:大于(>),小于(<),等于(==),大于等于(>=),小于等于(<=)和不等于(!=)。
逻辑运算符:
逻辑运算符主要有:逻辑与(&&)、逻辑或(||)和逻辑非(!),以及布尔逻辑与(&)、布尔逻辑或(|)和布尔逻辑亦或(^)。
逻辑运算符的操作数必须是boolean型数据,在逻辑运算符中除了逻辑非(!)是单目运算符外,其余的都是双目运算符。
逻辑运算符&&和||是短路运算,Java编译器对这两个运算符进行了优化,如果&&前面是false,那么&&后面的表达式就不会进行运算了。如果||前面是true,那么后面的表达式也不会进行运算了。
布尔逻辑运算符&和|是非短路运算,无论第一个表达式的值是true还是false,后面的表达式都会进行运算,并且布尔逻辑运算符的优先级要高于逻辑运算符。
例如下面这段代码:
package com.demo;
public class Test {
public static void main(String[] args) {
int m = 5,n = 5;
if ((m != 5) && (n++ == 5)) {}
System.out.println("a." + n);//m != 5为false,后面的n++ == 5不执行,所以打印“a.5”
m = n = 5;
if((m != 5) & (n++ == 5)) {}
System.out.println("b." + n);//布尔逻辑运算符“&”两边的表达式都会执行,所以打印“b.6”
m = n = 5;
if((m == 5) || (n++ == 5)) {}
System.out.println("c." + n);//m == 5为true,后面的n++ == 5不执行,所以打印“a.5”
m = n = 5;
if((m == 5) | (n++ == 5)) {}
System.out.println("d." + n);//布尔逻辑运算符“|”两边的表达式都会执行,所以打印“b.6”
}
}
位运算符:
位运算符除“按位与”和“按位或”运算符外,其他的只能处理整型操作数。位运算是完全针对位方面的操作,整型数据在内存中以二进制的形式存在,如int型变量7的二进制表示是00000000 00000000 00000000 00000111。
左边最高位是符号位,最高位是0表示正数,若为1则表示负数。负数用补码表示,如-8的二进制表示为11111111 11111111 11111111 11111000。
“按位与”运算的运算符为“&”。“按位与”的运算法则是:如果两个整型数据a和b对应位都是1,则结果位才为1,否则为0。
“按位或”运算的运算符为“|”。“按位或”的运算法则是:如果两个整型数据a和b对应位都是0,则结果位才为0,否则为1。
“按位取反”运算的运算符为“~”,是单目运算符,将操作数二进制中的1改为0,0改为1 。
“按位异或”运算的运算符为“^”。“按位异或”的运算法则是:如果两个整型数据a和b对应位相同,则结果位为0,否则为1。
注意:若A,B均为整数,则A = A^B;B = B^A;A = A^B;可以实现A,B互换。
移位操作主要有左移(<<),右移(>>)和无符号右移(>>>)。左移就是将运算符左边的操作数的二进制数据按照运算符右边操作数指定的位数向左移动,右边移空的部分补0;右移时,如果最高位是0,右移的空位就补0,如果最高位是1,右移空的位就填入1;无符号右移则不管最高位是0还是1,左侧被移空的高位都补0。
注意:
1、移位可以实现整数除以或乘以2n的效果。例如y<<2与y*4结果相同;y>>1与y/2的结果相同。
2、移位操作符右边的参数要先进行模32运算,并且移位是对二进制进行操作,而二进制中8位是一个循环。所以,n>>32等于n>>0,而且n >> 33等于n>>1。例如:
public class Test {
public static void main(String[] args) {
int n = 32;
System.out.println(n << 32);//打印32
}
}
三目运算符
三目运算符的使用格式为:条件表达式?值1:值2。
三目运算符的运算规则是:若条件式的值为true,则整个表达式取值1,否则取值2。例如:
public class Test {
public static void main(String[] args) {
int a = 5;
System.out.println((a<5) ? 10.9:9);
}
}
(a < 5)为false,上面的程序将打印9.0,在进行运算的时候要注意操作数直接的隐式转换。
运算符的优先级
自上而下优先级递减。