运算符
优先级 | 运算符 | 类 | 结合性 |
---|---|---|---|
1 | () | 括号运算符 | 由左至右 |
2 | !、+(正号)、-(负号) | 一元运算符 | 由左至右 |
2 | ~ | 位逻辑运算符 | 由右至左 |
2 | ++、-- | 递增与递减运算符 | 由右至左 |
3 | *、/、% | 算术运算符 | 由左至右 |
4 | +、- | 算术运算符 | 由左至右 |
5 | <<、>> | 位左移、右移运算符 | 由左至右 |
6 | >、>=、<、<= | 关系运算符 | 由左至右 |
7 | ==、!= | 关系运算符 | 由左至右 |
8 | & | 位运算符、逻辑运算符 | 由左至右 |
9 | ^ | 位运算符、逻辑运算符 | 由左至右 |
10 | | | 位运算符、逻辑运算符 | 由左至右 |
11 | && | 逻辑运算符 | 由左至右 |
12 | || | 逻辑运算符 | 由左至右 |
13 | ?: | 条件运算符 | 由右至左 |
14 | =、+=、-=、*=、/=、%= | 赋值运算符、扩展运算符 | 由右至左 |
前日回顾:
算术运算符
加+、减-、乘*、除/、取余%
整型和整型运算,得到的结果是整型(int),除非有long参与得到的结果是long类型
取余%的结果的正负: A%B得到结果的正负号和A一样
比较运算符
大于>、小于<、大于等于>=、小于等于<=、等于==、不等于!=
==在基本数据类型当中比较的是值是否一样
比较运算符得到的结果永远是布尔型(true/false)
一元自增自减运算符
a++ a-- ++a --a
a++ 后加加,先将a的值赋给表达式,然后a的值再+1
++a 前加加,先a+1,再将a的值赋给表达式
逻辑运算符
逻辑与&& 两个都为true结果才为true
逻辑或|| 有一个为true结果就为true
逻辑非! 取结果的反值
短路现象
多个逻辑表达式运算,前面的表达式已经可以明确整个表达式的结果,后面的就不需要再运行了
位运算符
二进制位操作后,再将二进制转为十进制即为结果
按位与运算符
& A&B
将A和B转成二进制,右侧对齐上下比较,两者都为1结果才为1,否则为0
按位或运算符
| A|B
将A和B转成二进制,右侧对齐上下比较,两者有一个为1结果即为1,两个都为0结果才为0
按位异或
^ A^B
将A和B转成二进制,右侧对齐上下比较,两者不同则为1,相同则为0
反码运算
~ ~A
将二进制的0换成1,1换成0
//按位与运算符 & A&B
//将A和B转成二进制,右侧对齐上下比较,两者都为1结果才为1,否则为0
//再将二进制转为十进制即为结果
int result = 12&11;
//1 1 0 0
//1 0 1 1
System.out.println(result);
//按位或运算
result = 12|11;
//将A和B转成二进制,右侧对齐上下比较,两者有一个为1结果即为1,两个都为0结果才为0
System.out.println(result);
//按位异或运算
result = 12^11;
//将A和B转成二进制,右侧对齐上下比较,两者不同则为1,相同则为0
System.out.println(result);
//反码运算
result = ~11; // 1 0 1 1
//反码 ~ 将二进制的0换成1,1换成0 ...1 1 0 1 0 0
System.out.println(result);//负数取反加1 1...0 0 1 0 1 1 +1 = -12
移位运算
移位运算符在程序设计中,是位操作运算符的一种,用于在二进制的基础上对数字进行平移。根据平移的方向和填充数字的规则,移位运算符主要分为三种:<<(左移)、>>(带符号右移)和>>>(无符号右移)。以下是这三种移位运算符的区别:
1. 左移运算符 <<
- 定义:左移运算符将数的各二进制位全部左移若干位,由<<右边的数指定移动的位数,高位丢弃,低位补0。
- 特点:左移n位相当于乘以2的n次方。
- 示例:
- 正数:
r = 20 << 2
。20的二进制补码是0001 0100
,向左移动两位后变为0101 0000
,结果为80
。 - 负数(以-20为例):
-20
的二进制补码是1110 1100
,左移两位后的补码为1011 0000
,反码为1010 1111
,原码为1101 0000
,结果为-80
。
- 正数:
2. 带符号右移 >>
- 定义:带符号右移运算符将数的各二进制位全部右移若干位,由>>右边的数指定移动的位数。对于正数,高位补0;对于负数,则高位补1。(补符号位)
- 特点:右移n位相当于除以2的n次方(向下取整)。
- 示例:
- 正数:
r = 20 >> 2
。20的二进制补码是0001 0100
,向右移动两位后变为0000 0101
,结果为5
。 - 负数(以-20为例):
-20
的二进制补码是1110 1100
,右移两位后的补码为1111 1011
,反码为1111 1010
,原码为1000 0101
,结果为-5
。
- 正数:
3. 无符号右移 >>>
- 定义:无符号右移运算符将数的各二进制位全部右移若干位,由>>>右边的数指定移动的位数。无论是正数还是负数,高位都补0。
- 特点:对于正数,无符号右移与带符号右移结果相同;对于负数,无符号右移会将其视为正数进行处理,结果通常远大于其原始值。
- 示例:
- 正数:
r = 20 >>> 2
的结果与r = 20 >> 2
相同,都是5
。 - 负数(以-20为例,假设数据类型为int 32位):
-20
的二进制补码是11111111 11111111 11111111 11101100
,右移两位后的结果为00111111 11111111 11111111 11111011
,转换为十进制是1073741819
。
- 正数:
总结
- 左移(<<):低位补0,左移n位相当于乘以2的n次方。
- 带符号右移(>>):正数高位补0,负数高位补1,右移n位相当于除以2的n次方(向下取整)。
- 无符号右移(>>>):无论正负,高位都补0,对于负数结果可能远大于其原始值。
- 补充:移位运算符是最基本的运算符之一,几乎每种编程语言都包含这一运算符。移位操作中,被操作的数据被视为二进制数,移位就是将其向左或向右移动若干位的运算。移位运算符只作用于整形变量,分为两类,第一类是 long 类型,long 类型长度为 8 字节 64 位;第二类为 int 类,int 长度为 4 字节 32 位,short、byte、char 在做移位之前会自动转换为 int 型,因此与 int 规则一致, 在做完移位运算后,short、byte、char类型都将变成 int 型数据(转换过程中需要考虑高位丢失的问题)
注:位与或运算符&和 |也可以对boolean值进行运算,得到boolean值,&和&&运算结果是一样的
boolean bool = true & false; //&和&&运算结果是一样的
int a = 12;
int b = 12;
bool = a++<12 & b++>12;
bool = a++<12 && b++>12;
问:&和&& 有什么不同?
答:&是按位与运算符,需要都转成二进制,然后按位比较,没有短路现象
&&是逻辑与运算符,有短路现象
补充:3*16最快的计算方式就是移位 3<<4相当于乘以2的4次方
赋值运算符
= += -= *= /= %=
int a = 12;
//整数和整数算术运算的结果是整数(int)
a += 2; //和a = a+2;不完全相同,byte需要强转
a -= 2;
a *= 2;
a /= 2;
a %= 2; //把a%2的值赋值给a
三目运算符
A?B:C
其中,A是条件(boolean类型),B和C是同一类型或者可以自动转换,A为true则返回B,否则C
int result = true?11:33;
System.out.println(result);
result = 12.2!=33.99?'a':33;
// 12.2+3? 有double参与结果为double
System.out.println(true?3:4.0);//3.0
流程控制
条件选择分支
if...else if...else...
条件从大到小写可以减少比较提高效率
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int score = scanner.nextInt();
if(score>=85){
System.out.println("优秀");
}else if (score>=70){
System.out.println("良好");
}else if (score>=60){
System.out.println("及格");
}else{
System.out.println("不及格");
}
}
switch...case...default...
switch 匹配某一个变量的值,如果匹配到某一个case项,就从这个case项开始运行
运行到break或者代码块结束
所有的case项都没有匹配成功才会执行default
注:1.case项和default的顺序可以是错乱的,执行顺序不会变
(先判断case不匹配再执行default)但是会穿透略过case判定直接向下执行语句到break
2.switch可以匹配的类型:只能匹配byte、short、int、char、String、Enum(枚举)
int num = 8;
switch (num){
default://5.进入default
System.out.println("解析不了");//6.输出
//如果没有break;
case 1://1.匹配失败
System.out.println("第一季");//7.绕过case 1直接输出
break;//9.破坏,退出
case 2://2.匹配失败
System.out.println("第二季");
break;
case 3://3.匹配失败
System.out.println("第三季");
break;
case 4://4.匹配失败
System.out.println("第四季");
break;
}
穿透现象:
在switch语句中,当没有在某个case分支中使用break语句时,会出现穿透现象。穿透现象指的是程序不会在匹配到的case分支结束后跳出switch语句,而是会继续执行后续的case分支。
具体来说,当程序执行匹配的case分支之后,会继续执行下一个case分支中的代码,直到遇到break语句或者switch语句的结尾。这样可能会导致意外的行为和错误的结果。
int num = scanner.nextInt();
switch (num){
case 1: //case并列往下执行,穿透现象
case 2:
case 3:
System.out.println("春季");
break; //1、2、3都会到break前输出"春季"
case 4:
case 5:
case 6:
System.out.println("夏季");
break;
case 7:
case 8:
case 9:
System.out.println("秋季");
break;
case 10:
case 11:
case 12:
System.out.println("冬季");
break;
default:
System.out.println("输入错误");
}
循环
while(继续循环的条件){
循环体
}
int i = 'a';//计数器
while ( i <= 'd'){
System.out.println((char)i);//输出a,b,c,d
i++;//计数器自增
}
do{
先至少执行一次的循环体(即使循环条件不成立)
}while(继续循环的条件)
//至少执行一次
i = 1;
do{
System.out.println("条件不成立");
}while (i>20);
for(计数器声明;循环继续的条件;计数器自增){
如果括号里只有分号,其他什么都没写,就是无限循环
}
for(int n=0;n<10;n++){
System.out.println(n+1);//输出1~10
}
几种循环可以互相转换
流程控制语句
break 破坏、结束break所在的循环体
continue 跳过,继续执行下一次循环
for(int a=1;a<100;a++){
if(a%7==0){
//break;//结束break所在的循环体
continue;//跳过,执行下一次循环
}
System.out.println(a);
}
break如何跳出多重循环
1.给需要的那一层循环的代码块起一个标签label,直接break label跳出对应循环
a:for(int i=0;i<30;i++){
b:for(int j=0;j<20;j++){
c:for(int k=0;k<10;k++){
if(k==5){
break a;
}
}
}
}
2.也可以不使用break,而是用一个额外的变量给循环加个锁来控制所有的循环
boolean bool=true;//定义一个变量作为锁
a:for(int i=0;bool&&i<30;i++){
b:for(int j=0;bool&&j<20;j++){
c:for(int k=0;bool&&k<10;k++){
if(k==5){
//break a;
bool=false;
}
}
}
}
死循环:没有结束条件的循环,之后不能写其他代码(永远没机会执行)
无限循环:结束条件永远达不到,后面可以继续编写其他代码
int a = 0;
while (a<1000){}//无限循环
for(;;){
}
while(true){}//死循环
数组
数组:一组有序的元素序列
数组的定义
//定义
int[] arr={31,52,13,84};//静态的
int[] arrA=new int[]{23,45};
//动态的
int[] arrB=new int[4];//长度定义数组 [4]代表可以存放4个元素
数组的限定
1.只能存放指定类型的数据
2.数组的长度是不可变的
使用数组元素
下标从0开始,依次增加
//使用数组中的元素 下标 从0开始依次增加
System.out.println(arr[3]);
System.out.println(arr);//直接输出首个元素的地址
System.out.println(Arrays.toString(arr));//输出完整数组元素
设置元素的数据
//设置元素的数据
arr = new int[4];
System.out.println(Arrays.toString(arr));
arr[1]=34;
arr[3]=99;
//arr[4]=8; //ArrayIndexOutOfBoundsException数组下标越界异常
//打印出arr数组中的所有元素
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
获取数组的长度
//获取数组的长度
System.out.println(arr.length);
二维数组
二维数组的限定:
二维数组中的一维数组个数不可变,但每个一维数组的长度可变
//二维数组
int[][] arrs=new int[4][2];
arrs[0][1]=12;
System.out.println(Arrays.deepToString(arrs));
//二维数组的限定 一维数组的个数不可变
arrs[2]=new int[]{1,2,3,4};//一维数组的长度可变
System.out.println(Arrays.deepToString(arrs));
//声明一个二维数组 使用随机数为每个元素赋值
int[][] arrC = new int[5][4];
for(int i=0;i< arrC.length;i++){
System.out.println(Arrays.toString(arrC[i]));
for(int j=0;j< arrC[i].length;j++){
arrC[i][j]=(int)(Math.random()*80);
}
System.out.println(Arrays.toString(arrC[i]));
}
System.out.println(Arrays.deepToString(arrC));