前言
计算机最基本的执行操作之一就是进行数学运算了。众所周知的是计算机一开始诞生的目的就是进行数学运算,解放人类的手工算力,又快有准,只是后来才有了其他方向的用途。java作为一门高级程序语言,提供了一套非常丰富的运算符来操作变量,具体可划分分为以下几组:
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 条件运算符
- instanceof运算符
- 其他运算符
- [] 数组访问运算符
- . 成员访问运算符
- :: 方法引用运算符(Java 8引入)
问:你为什么要写这篇比较基础的文章?
答:因为我在做题的时候由于基础知识掌握的不牢固导致做错了,所以我必须系统的再整理一遍运算符知识点。
下面,笔者将会对这些运算符给出实际的代码用例,逐个进行分析,以便我们了解其概念和基本用法。
一、算术运算符
算数运算符就是数学运算中的操作符,比如最基本的加、减、乘、除。现在假设有两个整数变量A=20,B=10
操作符 | 描述 | 例子 |
---|---|---|
+ | 加法-把运算符两侧的数值相加 (特殊如果是字符串则加号充当连接符) | A + B 等于 30 |
- | 减法-左操作数减去右操作数 | A-B等于10 |
* | 乘法-相乘操作符两侧的值 | A*B等于200 |
/ | 除法-左操作数除以右操作数 | A/B等于2 |
% | 取余数-左操作数除以右操作数的余数 | A%B余0,B%A余10(这里是取余数,不要算成分数1/2了,不是一个概念) |
++ | 自增-操作数的数值增加1 | ++A和A++后,A的值都变为21 |
- - | 自减-操作数的数值减少1 | - -A和A- -后,A的值都变为19 |
以下是算数运算符的注意事项
- 加法操作符 "+"
以下代码,a和b都是数值类型,打印结果为11
java
int a = 5;
int b = 6;
System.out.println(a + b);
再看下面的代码,运行结果分别是字符串567和字符串513了
java
String a = "5";
int b = 6;
int c = 7;
System.out.println(a + b + c);
System.out.println(a + (b + c));
在Java中,字符串和数字进行加法运算时,数字会先被转换为字符串,然后进行字符串拼接。第二个输出是b和c先进行加法运算得到结果13,然后将结果转换为字符串并与a进行拼接。
- 取余操作符"%"
代码示例如下
java
int a = 25;
int b = 10;
System.out.println(a % b);
System.out.println(b % a);
当被除数小于除数时,比如10%20,余数就是被除数本身,这里要区别于算术运算本身,不能算成分数或者小数形式。
- 自增+ +和自减- -操作符
以++为例说明
java
a++;
++a;
System.out.println(a);
输出结果为22,可以看到++a和a++都是对a进行自增加一的操作
那么二者的区别是什么呢,++符号在前和++符号在后有什么不同?请看下面的代码
++符号在前
java
int a = 20;
int b = ++a;
System.out.println(a);
System.out.println(b);
输出结果如下
可以看到a和b的最终结果都是21,b是a的值先加一操作后再赋值给b,也是21
++符号在后
java
int a = 20;
int b = a++;
System.out.println(a);
System.out.println(b);
输出结果如下
发现a的值变成21,而b的值仍然是20,说明++符号如果在变量的后面,则是先进行赋值或者其他条件的执行,比如判断大小等,然后再对变量进行加一的操作,int b = a++
是先把a的值赋值给b,再把a值进行加1操作,再看下面更直观的例子
总结一句话就是:++符号在前,先进行自增加一操作,然后在执行赋值、比较等其他代码表达式逻辑操作。++符号再后,先进行代码表达式逻辑操作赋值、比较等,然后再进行自增加一。
实际开发中,我们应该依据自身需要来选择自增或自减符号在前面还是在后面。如果只是单纯的++a或a++,则二者相同,用哪个都可以。如果在自增的同时,需要进行其他操作比如赋值、比较,如 int b = a++
,那么就需要根据场景自行决定是前置++还是后置++。
操作符 - - 是同样的道理,这里不再叙述。
二、关系运算符
符 | 描述 | 例子 |
---|---|---|
== | 检查如果两个操作数的值是否相等,如果相等则条件为真。 | (A == B)为假。 |
!= | 检查如果两个操作数的值是否相等,如果值不相等则条件为真。 | (A != B) 为真。 |
> | 检查左操作数的值是否大于右操作数的值,如果是那么条件为真。 | (A> B)为假。 |
< | 检查左操作数的值是否小于右操作数的值,如果是那么条件为真。 | (A <B)为真。 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真。 | (A> = B)为假。 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真。 | (A <= B)为真。 |
结果如下
关系运算符非常简单,通过以上示例可以说明两个操作数关系的比较。
三、逻辑运算符
操作 | 描述 | 例子 |
---|---|---|
&& | 称为逻辑与运算符。当且仅当两个操作数都为真,条件才为真。 | (A && B)为假。 |
|| | 称为逻辑或操作符。如果任何两个操作数任何一个为真,条件为真。 | (A | | B)为真。 |
! | 称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。 | !(A && B)为真。 |
简单代码示例如下
java
public class Test {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
System.out.println("a && b = " + (a&&b));
System.out.println("a || b = " + (a||b) );
System.out.println("!(a && b) = " + !(a && b));
}
}
结果如下
java
a && b = false
a || b = true
!(a && b) = true
逻辑与运算符 &&,这是一个短路运算符。a && b,只要a为false,那么就不会去判断b了,整个表达式直接返回false。
四、位运算符
Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。位运算符作用在所有的位上,并且按位运算。现假设 A = 60,B = 13,分别把它们的二进制表示出来
初始值60
步骤1: 60 ÷ 2 = 30...余数 0
步骤2: 30 ÷ 2 = 15...余数 0
步骤3: 15 ÷ 2 = 7...余数 1
步骤4: 7 ÷ 2 = 3...余数 1
步骤5: 3 ÷ 2 = 1...余数 1
步骤6: 1 ÷ 2 = 0...余数 1
从下到上 读取余数: 111100,
从右往左按照四位划分,不足四位的补零成四位,A的二进制可表示为0011 1100
初始值: 13
步骤1: 13 ÷ 2 = 6...余数 1
步骤2: 6 ÷ 2 = 3...余数 0
步骤3: 3 ÷ 2 = 1...余数 1
步骤4: 1 ÷ 2 = 0...余数 1
从下到上 读取余数: 1101,B =13的二进制表示为1101,因为需要和A做位运算,所以需要补齐到8位二进制数,B的二进制比表示为 0000 1101
注意:前导0是人为补的,做位运算移位操作可能会需要,实际二进制表示并不一定必须补零。
操作符 | 描述 | 例子 |
---|---|---|
按位与(&) | 如果相对应位都是1,则结果为1,否则为0 | (A&B),得到12,即0000 1100 |
按位或(|) | 如果相对应位都是 0,则结果为 0,否则为 1 | (A | B)得到61,即 0011 1101 |
按位异或(^) | 如果相对应位值相同,则结果为0,否则为1 | (A ^ B)得到49,即 0011 0001 |
按位取反(〜) | 按位取反运算符翻转操作数的每一位,即0变成1,1变成0。 | (〜A)得到-61,即1100 0011 |
左移(<<) | 按位左移运算符。左操作数按位左移右操作数指定的位数。 | A << 2得到240,即 1111 0000 |
有符号右移(>>) | 按位右移运算符。左操作数按位右移右操作数指定的位数。 | A >> 2得到15即 1111 |
无符号右移(>>>) | 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。 | A>>>2得到15即0000 1111 |
通过你这个表格来看,位运算的几种类型概念比较清晰了,但下面有两个注意点
-
为什么左移不区分有符号和无符号?
在编程语言中,左移操作(通常表示为 <<)是针对整数类型进行的位操作**。无论是有符号还是无符号整数,左移操作都是逻辑左移,这意味着最左边移出的位会被丢弃,而在最右边添加零来填充空位**。这是因为计算机在执行位移操作时,不区分有符号和无符号,它只是简单地移动位。
在大多数编程语言中,包括Java和C/C++,左移操作对于有符号和无符号整数的行为是一致的,即都是逻辑左移。这是因为对于有符号整数,左移操作不会保留符号位,而是按照逻辑位移的方式进行。换句话说,无论是正数还是负数,左移操作都是无符号的,因为它不影响数值的正负性,只是简单地将所有位向左移动指定的位数。
-
为什么右移区分有符号和无符号 ?
右移操作在有符号整数中区分符号的原因在于它涉及到如何处理被移出的最高位,也就是符号位。在二进制表示中,最高位(最左边的位)决定了数值的正负性。对于有符号整数,右移操作有两种不同的行为:
- 有符号右移(>>):
当执行有符号右移时,保留最高位的符号位。如果原始数值是正数,符号位是0,右移后左侧用0填充;如果原始数值是负数,符号位是1,右移后左侧用1填充。这保持了数值的符号不变。 - 无符号右移(>>>):
无符号右移不考虑数值的符号,无论原始数值是正还是负,右移后左侧总是用0填充。这意味着,即使是负数,右移后也会变成一个较大的正数,因为符号位被0替换。
这种区分是必要的,因为在处理有符号整数时,我们希望保持数值的正负性不变。对于无符号整数,由于没有正负之分,右移后用0填充是合理的,因为它总是表示一个非负的数值。
- 有符号右移(>>):
在实际编程中,这种区分对于处理有符号整数时的溢出和负数特别重要,特别是在进行位运算和计算时。例如,当处理负数时,有符号右移可以用来快速地将负数转换为更大的负数,而无符号右移则用于处理无符号数据或进行位操作时保持数值的非负性。
示例代码
java
package com.execute.batch.executebatch;
public class Test {
public static void main(String[] args) {
int a = 60; /* 60 = 0011 1100 */
int b = 13; /* 13 = 0000 1101 */
int c;
c = a & b; /* 12 = 0000 1100 */
System.out.println("a & b = " + c );
c = a | b; /* 61 = 0011 1101 */
System.out.println("a | b = " + c );
c = a ^ b; /* 49 = 0011 0001 */
System.out.println("a ^ b = " + c );
c = ~a; /*-61 = 1100 0011 */
System.out.println("~a = " + c );
c = a << 2; /* 240 = 1111 0000 */
System.out.println("a << 2 = " + c );
c = a >> 2; /* 15 = 1111 */
System.out.println("a >> 2 = " + c );
c = a >>> 2; /* 15 = 0000 1111 */
System.out.println("a >>> 2 = " + c );
}
}
运行结果如下
java
a & b = 12
a | b = 61
a ^ b = 49
~a = -61
a << 2 = 240
a >> 2 = 15
a >>> 2 = 15
五、赋值运算符
这类运算符比较容易理解了,实际开发中使用的很多,下面仅使用一个表格来说明
操作符 | 描述 | 例子 |
---|---|---|
= | 简单的赋值运算符,将右操作数的值赋给左侧操作数 | C = A + B将把A + B得到的值赋给C |
+ = | 加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数 | C + = A等价于C = C + A |
- = | 减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数 | C - = A等价于C = C - A |
* = | 乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数 | C * = A等价于C = C * A |
/ = | 除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数 | C / = A,C 与 A 同类型时等价于 C = C / A |
(%)= | 取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数 | C%= A等价于C = C%A |
<< = | 左移位赋值运算符 | C << = 2等价于C = C << 2 |
>> = | 右移位赋值运算符 | C >> = 2等价于C = C >> 2 |
&= | 按位与赋值运算符 | C&= 2等价于C = C&2 |
^ = | 按位异或赋值操作符 | C ^ = 2等价于C = C ^ 2 |
| = | 按位或赋值操作符 | C\ |
示例代码
java
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 0;
c = a + b;
System.out.println("c = a + b = " + c );
c += a ;
System.out.println("c += a = " + c );
c -= a ;
System.out.println("c -= a = " + c );
c *= a ;
System.out.println("c *= a = " + c );
a = 10;
c = 15;
c /= a ;
System.out.println("c /= a = " + c );
a = 10;
c = 15;
c %= a ;
System.out.println("c %= a = " + c );
c <<= 2 ;
System.out.println("c <<= 2 = " + c );
c >>= 2 ;
System.out.println("c >>= 2 = " + c );
c >>= 2 ;
System.out.println("c >>= 2 = " + c );
c &= a ;
System.out.println("c &= a = " + c );
c ^= a ;
System.out.println("c ^= a = " + c );
c |= a ;
System.out.println("c |= a = " + c );
}
}
运行结果
java
c = a + b = 30
c += a = 40
c -= a = 30
c *= a = 300
c /= a = 1
c %= a = 5
c <<= 2 = 20
c >>= 2 = 5
c >>= 2 = 1
c &= a = 0
c ^= a = 10
c |= a = 10
六、条件运算符
条件运算符又叫三目运算符,该运算符有3个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。比如:
int c = a>b ? a :b
意思是 a>b为true,那么c = a,否则c = b
示例代码如下
java
public class Test {
public static void main(String[] args){
int a , b;
a = 10;
// 如果 a 等于 1 成立,则设置 b 为 20,否则为 30
b = (a == 1) ? 20 : 30;
System.out.println( "Value of b is : " + b );
// 如果 a 等于 10 成立,则设置 b 为 20,否则为 30
b = (a == 10) ? 20 : 30;
System.out.println( "Value of b is : " + b );
}
}
运行结果
java
Value of b is : 30
Value of b is : 20
七、instanceof 运算符
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。
instanceof运算符使用格式如下:
java
( Object reference variable ) instanceof (class/interface type)
如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,那么结果为真。
下面是一个例子:
java
String name = "James";
boolean result = name instanceof String; // 由于 name 是 String 类型,所以返回真
如果被比较的对象兼容于右侧类型,该运算符仍然返回 true。
java
class Vehicle {}
public class Car extends Vehicle {
public static void main(String[] args){
Vehicle a = new Car();
boolean result = a instanceof Car;
System.out.println( result);
}
}
运行结果
java
true
八、其他运算符
- 数组访问运算符
这个很简单,就是访问数组某个位置的元素
java
int[] intArray = new int[10]; // 创建一个包含10个元素的整数数组
int firstElement = intArray[0]; // 访问数组的第一个元素(索引从0开始)
- 成员访问运算符
比如当MyClass 中的某个字段myField是静态变量时,可以使用如下方式
java
MyClass obj = new MyClass();
obj.myField; // 访问字段
obj.myMethod(); // 调用方法
- 方法引用运算符
这个是java引入的一种写法,也就是语法糖的一种了
java
List<String> list = List.of("1", "2", "3", "4", "5");
list.forEach(System.out::println);
关于java运算符的介绍就到这里了,实际工作中经常遇到,也可能还是记不住,比如位运算符相关。不过只需要多练习,次数多了就记住了。我们要打牢基础,所谓基础不牢,地动山摇!