掌握常见的Java运算符的使用,包括算术运算符、关系运算符、逻辑运算符、赋值运算符、条件运算符、字符串连接运算符。
运算符是指明对操作数的运算方式。组成表达式的Java操作符有很多种(什么是操作数和操作符,例如1+2,其中1和2都是操作数,+是操作符,操作符和操作数联合起来构成表达式)。运算符按照其要求的操作数数目来分,可以有单目运算符(1个操作数)、双目运算符(2个操作数)和三目运算符(3个操作数)。运算符按其功能来分,有算术运算符、赋值运算符、关系运算符、逻辑运算符、位运算符、条件运算符、字符串连接运算符和其他运算符。常见的运算符如下所示:
算术运算符 |
+、-、*、/、%(取模)、++(自加1【单目】)、--(自减1【单目】) |
关系运算符 |
>、>=、<、<=、==、!= |
逻辑运算符 |
&(逻辑与)、|(逻辑或)、!(逻辑非)、&&(短路与)、||(短路或) |
赋值运算符 |
=、+=、-=、*=、/=、%=、^=、&=、|=、<<=、>>= |
位运算符 |
&(按位与)、|(按位或)、^(按位异或)、~(按位取反【单目】)、<<(左移)、>>(带符号右移)、>>>(无符号右移) |
条件运算符 |
布尔表达式?表达式1:表达式2 (三目) |
字符串连接运算符 |
+ |
其它运算符 |
instanceof、new |
每个编程语言当中都有运算符,基本上都是通用的,这么多的运算符,它们同时出现的时候有优先级吗?答案是有的。那么如果不确定它们的优先级怎么办,其实很简单,直接加小括号就可以了,添加有小括号优先级一定是高的,所以优先级不需要死记硬背,不确定就加小括号,例如:1 + 2 * 3,想确保先求和,你就需要这样写:(1+2)*3。
Java算术运算符包括:+(两个数字求和)、-(两个数字相减)、*(两个数字乘积)、/(两个数字相除)、%(两个数字取模或者求余)、++(单目运算符,对数字自加1)、--(单目运算符,对数字自减1)。对于初学者来说,可能%、++、--比较生疏一些,我们重点来学习一下,请看以下代码:
public class ArithmeticOperatorTest01 {
public static void main(String[] args) {
int a = 10;
int b = 3;
System.out.println(a + b); //13
System.out.println(a - b); //7
System.out.println(a * b); //30
/*
* 在java语言中10除以3结果不是3.3333..
* 因为java中有一个语法规则:int类型和int类型
* 做运算,最终的结果还是int类型,所以10除以3
* 的结果就是3,这个过程会取整数位。
*/
System.out.println(a / b); //3
/*
* 10对3求余数,3乘3得9,余1。
*/
System.out.println(a % b); //1
/*
* ++和--都是单目运算符,++负责自加1,--负责自减1
* 以下程序重点研究++运算符,--运算符可以依照++运算
* 符进行类推。
*/
int i = 10;
i++;
System.out.println("i = " + i); //11
int j = 10;
++j;
System.out.println("j = " + j); //11
/*
* 根据以上的程序可以得出结论:++可以出现在变量前,也
* 可以出现在变量后,无论是变量前还是变量后,只要执行完
* ++,最终的结果都会让变量自加1。那么++出现在变量前
* 和变量后到底有什么区别呢?请看以下程序
*/
int k = 10;
int m = k++;
/*
* 根据以上结论,++无论是出现在变量前还是变量后,只要++执行
* 变量一定会自加1,那么k变量一定会自加1,所以以下输出k=11
*/
System.out.println("k = " + k); //11
/*
* 以上int m = k++;代码中++和=两个运算符同时存在,
* 那么此时这两个运算符哪个优先级高呢?java语法中是这样
* 规定的,当++运算符出现在变量后,会先做赋值运算,再自
* 加1,所以最后m = 10
*/
System.out.println("m = " + m); //10
int x = 10;
int y = ++x;
//上行代码执行结束后,我们可以确定x变量一定自加1了,所以x=11
System.out.println("x = " + x); //11
/*
* java语法中又有这样的规定,++出现在变量前,会先进行自加1
* 的操作,然后再做赋值运算,所以y = 11
*/
System.out.println("y = " + y); //11
}
}
运行结果如下图所示:
图5-1:运行结果
通过以上代码的学习,我们得知:
那么,大家思考一下以下代码的执行结果是什么?
int c = 100;
System.out.println(c++);
int d = 100;
System.out.println(++d);
关系运算符主要是完成数据和数据之间的比较,比如:5>3,结果是true(真),5>10,结果是false(假),那么关系运算符都有哪些呢?>、>=、<、<=、==、!=。关系运算符是比较简单容易理解的,我们来看一段代码:
public class RelationOperatorTest01 {
public static void main(String[] args) {
int a = 10;
int b = 10;
System.out.println(a > b);//false
System.out.println(a >= b);//true
System.out.println(a < b);//false
System.out.println(a <= b);//true
System.out.println(a == b);//true
System.out.println(a != b);//false
//比较两个字符串是否相等,不能使用“==”,
//必须调用equals方法
//equals方法以后会讲解的,现在先记住吧
System.out.println(“abc”.equals(“abc”));//true
}
}
通过以上代码可以看出,任何一个关系运算符的运算结果都是布尔类型,最后的结果不是true就是false,没有其他值,并且我们也看到a变量和b变量在比较的时候是拿着变量当中保存的值进行比较。也就是说a == b实际上是拿着a变量中保存的10和b变量中保存的10进行比较,看它们是否相等。
逻辑运算符主要包括逻辑与(&),逻辑或(|),逻辑异或(^),短路与(&&),短路或(||)。所有逻辑运算符的特点是操作数都是布尔类型,并且最终的运算结果也是布尔类型。逻辑运算符的基本运算规则如下表所示
操作符 | 例子 | 结果 | 描述 |
& |
true&true |
true |
(5>3) & (5>4):5大于3并且5大于4,有道理 |
| |
true|false |
true |
(5>3) | (5>10):5大于3或者5大于10,没毛病 |
! |
!false |
true |
false取反是true,true取反就是false |
^ |
true^false |
true |
异或运算符,只要两边的操作数不同,结果就是true |
&& |
true&&true |
true |
短路与(&&)和逻辑与(&)实际上最终的运行结果是完全相同的,只不过短路与(&&)会存在短路现象。 |
|| |
true||false |
true |
短路或(||)和逻辑或(|)实际上最终的运行结果是完全相同的,只不过短路或(||)会存在短路现象。 |
接下来我们来研究一段代码,重点看看短路是如何发生的:
public class LogicalOperatorTest01 {
public static void main(String[] args) {
System.out.println(5 > 3 & 5 > 4); //true
System.out.println(5 > 100 | 5 > 4); //true
System.out.println(!(5 > 3)); //false
System.out.println(!(5 < 3)); //true
System.out.println(true & true); //true
System.out.println(true & false); //false
System.out.println(true | false); //true
System.out.println(!false); //true
System.out.println(!true); //false
System.out.println(true ^ false); //true
System.out.println(true ^ true); //false
System.out.println(false ^ false); //false
//重点研究逻辑与(&)和短路与(&&)的区别
int x = 100;
int y = 98;
int z = 99;
System.out.println(x > y & x > z); //true
System.out.println(x > y && x > z); //true
//通过以上测试得出,逻辑与(&)和短路与(&&)最终运行结果相同
//那么它们有什么区别呢?请看以下代码。
int m = 99;
int n = 100;
System.out.println(m > n & m > n++); //false
//逻辑与(&)运算符,只有当两边的操作数都是true的时候,结果才是
//true,只要有一个是false,结果必然是false。以上程序逻辑与
//左边的表达式(m > n)显然结果是false,通过左边的表达式已经可
//以得出结果是false了,右边的表达式没有执行的必要,那我们来看一下
//右边的表达式有没有执行,只要输出n就可以了断定。
System.out.println("n = " + n); //101
//以上程序的运行结果是101,足以说明n++已经执行了。也说明了逻辑与
//运算符无论左边表达式结果是true还是false,右边表达式一定会执行。
//那我们来看一下短路与(&&)是怎样的?
int k = 99;
int f = 100;
System.out.println(k > f && k > f++); //false
System.out.println("f = " + f); //100
//通过以上测试得出f++并没有执行,因为左边表达式(k > f)为false
//右边的表达式就不再执行了,这种现象被称为短路现象。也就是说对于短路与
//来说,左边的表达式只要为false,就会发生短路,右边表达式不再执行了。
}
}
通过以上的测试,可以得出短路与(&&)在左边的表达式结果为false的时候,右边的表达式则不再执行,这种现象被称为短路现象,这种机制也显得短路与比较智能一些,效率更高一些,所以在实际开发中短路与(&&)的使用率要比逻辑与高一些。但这并不是绝对的,有的时候也可能会选择使用逻辑与(&),这取决于你是否期望右边的表达式一定执行。
大家思考一个问题,短路或(||)在什么情况下会发生短路现象呢?
位运算符是直接对数据的二进制进行操作,目前我们还用不到这块内容,等后面课程中涉及到的时候,我们再进行详细学习。
赋值运算符目前也是只需要掌握=、+=、-=、*=、/=、%=,其它和二进制相关的内容也是到后面遇到的时候再详细学习。赋值类的运算符包括基本赋值运算符(=)和扩展的赋值运算符(+=、-=、*=、/=、%=)。我们来看一段代码:
public class AssignmentOperatorTest01 {
public static void main(String[] args) {
//基本的赋值运算符
int i;
i = 10;
System.out.println("i = " + i);
i = 100;
System.out.println("i = " + i);
//扩展的赋值运算符
int x = 10;
x += 1; //等同于x = x + 1
System.out.println("x = " + x); //11
int y = 10;
y -= 1; //等同于y = y - 1
System.out.println("y = " + y); //9
int z = 10;
z *= 2; //等同于z = z * 2
System.out.println("z = " + z); //20
int m = 10;
m /= 3; //等同于m = m / 3
System.out.println("m = " + m); //3
int n = 10;
n %= 3; //等同于 n = n % 3
System.out.println("n = " + n); //1
}
}
x += 1和x = x + 1真的是完全相同吗?我们来看下面的代码:
public class AssignmentOperatorTest02 {
public static void main(String[] args) {
byte b = 10;
//以下程序编译报错,编译器提示错误信息为:
//Type mismatch: cannot convert from int to byte
/*
* 编译没有通过的原因:b是byte类型,1是int类型,根据之前讲解的类型
* 转换规则得知,byte和int混合运算最后结果是int类型,int类型的值
* 无法直接赋值给byte类型的变量b,所以编译报错。
*/
//b = b + 1;
b += 1; //编译通过并可以正常运行
System.out.println("b = " + b); //11
//通过以上的测试得出:b = b + 1和b += 1是不一样的
//那么b += 1等同于什么呢?
/*
* 实际上java对于扩展类的赋值运算符进行了特殊的处理,所有
* 的扩展赋值运算符,最终都不会改变运算的结果类型,假设前面
* 的变量是byte类型,那么后面的表达式运算之后的结果还是byte
* 类型。所以实际上b += 1等同于:
*/
b = (byte)(b + 1);
System.out.println("b = " + b); //12
b += 1000; //编译通过,并且可以正常运行
/*
* 以上代码实际上等同于:b = (byte)(b + 1000);
* 分析得出,显然结果已经超出了byte类型取值范围,所以精度一定
* 会损失,最终的结果需要对计算机二进制的原码反码补码进行研究。
*/
System.out.println("b = " + b); //-12
}
}
根据以上代码测试得出,对于扩展类的赋值运算符在运算的过程中不会改变运算的结果类型,也就是说byte b = 100; b += 1000;b变量最初是byte类型,最后的运算结果还是一个byte类型。这是一条固定的语法规则,大家记住就行了,以后在使用扩展类赋值运算符的时候要谨慎,不小心就会精度损失的。
条件运算符属于三目运算符,它的语法结构是:布尔表达式?表达式1:表达式2。它的运行原理是这样的,先判断布尔表达式的结果是true还是false,如果是true,则选择表达式1的结果作为整个表达式的结果,反之则选择表达式2的结果作为整个表达式的结果。来看一段代码:
public class ConditionalOperatorTest01 {
public static void main(String[] args) {
//编译报错:这不是一个语句
//10;
boolean flag = true;
//编译报错:这不是一个语句
//flag ? 1 : 0;
//以上如果是一条完整的语句应该这样写
int k = flag ? 1 : 0;
System.out.println("k = " + k); //1
//三目运算符最经典的用法
boolean sex = true;
//当布尔变量sex为true则结果是'男',反之'女'
char gender = sex ? '男' : '女';
System.out.println("性别:" + gender); //男
sex = false;
gender = sex ? '男' : '女';
System.out.println("性别:" + gender); //女
//又如
int x = 100;
int y = 100;
System.out.println(x == y ? "x和y相等" : "x和y不相等");
}
}
实际上条件运算符和后期要学习的控制语句if可以达到同等效果。在实际开发中灵活运用条件运算符会让你的代码看起来更加简洁清凉,达到意想不到的效果。
在java语言中所有的字符串都使用半角双引号括起来的,字符串属于引用数据类型,不属于基本数据类型的范畴,怎么定义一个字符串的变量呢?例如:String name = “jack”;,这就类似于int i = 10;是一样的,int是一种整数类型,i是变量,10是整数型字面量。那么String则是一种字符串类型,name是变量,”jack”是字符串型字面量。在java编程中对字符串的操作是非常频繁的,例如字符串的连接操作,此时就需要使用“+”字符串连接运算符了。
实际上“+”运算符在java语言中有两个作用,作用一是对数字进行求和运算,作用二就是字符串连接运算,那么它在什么时候是进行求和,什么时候又进行字符串连接呢?大家可以这样进行区分,当“+”运算的时候,两边的操作数都是数字的话,一定会进行求和运算,只要其中有一个操作数是字符串类型,那么一定会进行字符串拼接运算,字符串拼接之后的结果还是字符串类型。需要注意的是,当一个表达式当中有多个“+”,并且在没有小括号的前提下,遵循自左向右的顺序依次执行。我们来看一段程序:
public class PlusTest01 {
public static void main(String[] args) {
int a = 10;
int b = 20;
//加号两边都是int类型,执行求和运算
int c = a + b;
/*
* 以下代码虽然有两个加号,第一个加号在双引号当中,
* 不起任何作用,只是一个普通字符串,第二个加号由于
* 左边操作数是一个字符串,所以这个加号一定会进行
* 字符串连接,连接之后的结果还是一个字符串。
*/
System.out.println("a + b = " + c); //a + b = 30
//分析以下程序的输出结果?哪些加号进行求和,哪些加号进行字符串连接
System.out.println(a + " + " + b + " = " + a + b);
//分析以下程序的输出结果?哪些加号进行求和,哪些加号进行字符串连接
System.out.println(a + " + " + b + " = " + (a + b));
//定义一个字符串类型变量
String name = "jack";
/*
* 字符串拼接在实际开发中很常用,拼接的时候是有口诀,大家记住,
* 将变量放到字符串当中进行拼接,可以这样做:1、在拼接的位置添加
* 一个英文双引号;2、在双引号中间添加两个加号;3、把字符串变量放到
* 两个加号中间。
*/
System.out.println("登录成功,欢迎" + name + "回来!");
}
}
对于程序System.out.println(a + " + " + b + " = " + a + b);的分析见下图:
图5-2:字符串连接运算
对于程序System.out.println(a + " + " + b + " = " + (a + b));的分析见下图:
图5-3:字符串连接运算
总之,使用“+”进行字符串拼接在开发中使用太频繁了,大家一定要将其掌握,尤其是怎么将一个变量放到字符串当中,你还记得以上程序的口诀吗?
其它运算符instanceof、new等在讲Java面向对象内容的时候一定会用到,到那时候再详细学习。