JAVA运算符
算数运算符
运算符和表达式
-
运算符
对字面量或者变量进行操作的符号
-
表达式
用运算符把字面量或者变量连接起来符合JAVA语法的式子就称为表达式
-
举例说明
int a = 10;
int b = 20;
int c = a + b;+: 是运算符,并且是算数运算符
a + b:是表达式,由于+是算术运算符,所以这个表达式叫算数表达式
符号 | 作用 | 说明 |
---|---|---|
+ | 加 | 数学加法运算 |
- | 减 | 数学减法运算 |
* | 乘 | 数学乘法运算 |
/ | 数学除法运算 | 数学除法运算 |
% | 取余 | 获取的是两个数据做除法的余数 |
注意事项
/ 和 % 的区别:两个数做除法, / 取结果的商, % 取结果的余数
整数操作只能得到整数,要想得到小数,必须有浮点数参与运算案例
需求: 键盘录入一个三位数,将其拆分为个位 十位 百位后,打印在控制台
代码如下:import java.util.Scanner; public class NoteDemo { public static void main(String[] args){ Scanner sc = new Scanner(System.in); System.out.println("请输入三个数字:例如123"); int age = sc.nextInt(); int a = age %10; int b = age /10%10; int c = age /10/10%10; System.out.println(a);//结果余数为3 System.out.println(b);//结果余数为2 System.out.println(c);//结果余数为1 } }
分析
- 使用Scanner键盘录入一个三位数123
- 个位的计算通过数值 % 10
123除以10 (商12,余数为3)
- 十位的计算通过数值 / 10 % 10
123 除以 10(商12,余数为3,整数相除只能得到整数)
12 除以 10(商1,余数2)
- 百位计算通过数值 / 10 / 10 / %10
123 / 10 / 10 % 10(123 / 10 得到12,12 / 10 得到1,1 % 10得到1)
结果打印出来是个位为: 3 , 十位为: 2 , 百位为: 1
规律:
如果想继续拆分千位,万位,十万位,百万位等,代码如下:import java.util.Scanner; public class NoteDemo { public static void main(String[] args){ Scanner sc = new Scanner(System.in); System.out.println("请输入七个数字:例如5969876"); int age = sc.nextInt(); int a = age %10;//余数为6 int b = age /10%10;//余数为7 int c = age /10/10%10;//余数为8 int d = age /10/10/10%10;//余数为9 int e = age /10/10/10/10%10;//余数为6 int f = age /10/10/10/10/10%10;//余数为9 int g = age /10/10/10/10/10/10%10;//余数为5 System.out.println(a); System.out.println(b); System.out.println(c); System.out.println(d); System.out.println(e); System.out.println(f); System.out.println(g); } }
优化代码:
import java.util.Scanner; public class NoteDemo { public static void main(String[] args){ Scanner sc = new Scanner(System.in); System.out.println("请输入七个数字"); int age = sc.nextInt(); int a = age %10; int b = age /10%10; int c = age /10/10%10; int d = age /1000%10; int e = age /10000%10; int f = age /100000%10; int g = age /1000000%10; System.out.println(a); System.out.println(b); System.out.println(c); System.out.println(d); System.out.println(e); System.out.println(f); System.out.println(g); } }
字符串拼接操作
当 + 操作中,遇到了字符串,这时 + 就是字符串连接符,而不是算术运算
代码如下:public class NoteDemo { public static void main(String[] args){ // + 字符只有遇到字符串才会进行字串连接操作,没有遇到字符串前他会进行算术运算 System.out.println(1+23);//24 //先遇到字符串时他会如贪吃蛇一样连接右边的字符成为自身 System.out.println("年龄为:" + 23);//年龄为:23 //由于代码运行是有顺序的,他先遇到1 + 99,还没有遇到后面的字符串前会先进行加法运算,再进行字符串连接 System.out.println(1 + 99 + "年帅哥");//100年帅哥 //先遇到字符串后会连接字符串,变成了字符串"年龄为23"后,继续连接后面的值成为自身 System.out.println("年龄为:" + 23+1);//年龄为:231 } }
如果在连接字符串中需要算术运算时,可以通过()将要运算的数据括起来,代码如下:
>public class NoteDemo { public static void main(String[] args){ //使用()提升算数优先级,会将括号数据先进行算术运算,得到结果24,再进行字符串连接操作 System.out.println("年龄为:" + (23+1));//年龄为:24 } }
通过字符串拼接操作将上面的拆分个位 十位 百位 的案例所打印的结果进行优化
import java.util.Scanner; public class NoteDemo { public static void main(String[] args){ Scanner sc = new Scanner(System.in); System.out.println("请输入七个数字:例如5969876"); System.out.println("请输入七个数字"); int age = sc.nextInt(); int a = age % 10; int b = age / 10 % 10; int c = age / 10 / 10 % 10; int d = age / 1000 % 10; int e = age / 10000 % 10; int f = age / 100000 % 10; int g = age / 1000000 % 10; System.out.println("整数:" + age + "的个位为:" + a); System.out.println("整数:" + age + "的十位为:" + b); System.out.println("整数:" + age + "的百位为:" + c); System.out.println("整数:" + age + "的千位为:" + d); System.out.println("整数:" + age + "的万位为:" + e); System.out.println("整数:" + age + "的十万位为:" + f); System.out.println("整数:" + age + "的百万位为:" + g); } }
自增自减运算符
符号 | 作用 | 说明 |
---|---|---|
++ | 自增 | 变量自身的值加1 |
- - | 自减 | 变量自身的值减1 |
如何使用
单独使用
一句代码中,只做 ++ 或者 - - , ++ 和 - - 无论是放在变量前面还是变量后面,结果都是一样的
实例:
public class NoteDemo { public static void main(String[] args) { int a = 10, b = 20;//定义一个变量a,将10赋值给它 System.out.println("操作前a的值为:"+ a);//10 System.out.println("操作前b的值为:"+ b);//20 a++;//让变量a在原来的值10上,自身加1 b--; System.out.println("操作后a的值为:"+ a);//结果为11 System.out.println("操作后b的值为:"+ b);//结果为19 System.out.println("======================"); int c = 10, d = 20;//定义一个变量a,将10赋值给它 System.out.println("操作前c的值为:"+ c);//10 System.out.println("操作前d的值为:"+ d);//20 ++c;//让变量c在原来的值10上,自身加1 --d;//让变量d在原来的值20上,自身加1 System.out.println("操作后c的值为:"+ c);//结果为11 System.out.println("操作后d的值为:"+ d);//结果为19 } }
结论:无论是**++或者- -**,放在变量的前面还是后面,在单独使用的情况下,结果都是一样的
参与操作运算
如果放在变量的后面,会先拿变量的值进行运算,再对变量的值进行+1或者-1
public class NoteDemo { public static void main(String[] args) { int a = 10; System.out.println("a原来是:" +a);//结果为10 //变量a首先将其原来的值10,先赋值给变量b,此时b=10,变量a再自增1,此时a=11 int b = a++; System.out.println("b赋值后变成:" + b);//结果为10 System.out.println("a++后值变成:" +a);//结果为11 } }
结论: ++ 或者 - - 参与运算操作时,放在变量的前面和后面,会有两种不同的结果
- 放在后面,例如: int a=10; int b=a++; 参与运算操作这种情况下,变量a 首先将其原来的值,先赋值给 变量b , 此时 b 拿到值并存储后, 变量a 才会操作自身的值 自增1 或 自减1
- 放在前面,例如 int c=20; int d=++a; 参与运算操作这种情况下, 变量c 自身的值首先会先 自增1 或 自减1 后,又将操作后的值赋值给 变量d ,此时变量a 和 变量d 的值相同
尝试练习并分析结果:
案例一:public class NoteDemo { public static void main(String[] args) { //首先x原来的值是10 int x = 10; //变量y=x ++,++在后面,先操作变量x=10的值给变量y,此时y=10,变量x自增1后变成了x=11 int y = x++; //z= ++y, ++在前面,变量y先操作自增1后,变成y=11,再将变量y的值赋值给变量z,变成z=11 int z = ++y; System.out.println(x);//结果为11 System.out.println(y);//结果为11 System.out.println(z);//结果为11 } }
案例二
public static void main(String[] args) { int a = 8; //第一个(- -a)为先自减1再操作变成a=7;第二个(++a)先自增1变成a=8;第三个(a++),先操作a=8,再自增1;第四个(a*10),通过前三个的自增自减操作变量a=9,通过计算9*10=90,所以最后的结果为b=7+8+8+90 int b = (--a) + (++a) + (a++) + (a*10); System.out.println(a);//9 System.out.println(b);//结果为113 } }
注意事项:++ - - 只能操作变量,不能操作常量
类型转换
隐式类型转换
把一个取值范围小的数值或者变量,赋值给另一个取值范围大的变量
代码如下:public class NoteDemo { public static void main(String[] args) { //变量a为int类型,4个字节大小 int a = 10; //变量b为double类型,8个字节大小 //int整数变量a=10赋值给double浮点数类型的变量b时,由于数据类型不同,结果变成了10.0,也就是说类型悄然发生了变化,这种变化并非人为对其进行手动转换的,而是内部自动进行转换的,所以这种就叫做隐式转换 double b=a; System.out.println(b);//结果为10.0 } }
简单记: 小的给大的,可以直接给,这就叫做隐式转换
取值范围从小到大
byte(1字节) -> short(2字节) -> int(4字节) -> long(8字节) -> float(4字节) -> double(8字节)
char(1个字节)
疑问:float占用内存4个字节,long占用内存8个字节,为什么long放在了float的后面?
解释:虽然float占用内存之后4个字节,但是float[取值范围]要比占用内存8个字节的long[取值范围]大
原因: 小数的二进制存储形式更加节省内存
运算过程中的隐式转换
取值范围小的数据和取值范围大的数据进行数据运算时,小的会先提升为大的之后,再进行转换
public class NoteDemo { public static void main(String[] args) { double a = 10.0; int b=18; //在java中,如果数据类型不统一是不能直接做运算的,取值范围小的数据类型,会将自己提升为大的数据类型,与其进行运算操作,同时数据类型也会进行转换 double c = a + b; System.out.println(c); }
上面代码中,double类型的变量a与int类型的变量b进行运算操作时,变量b作为取值范围小的数据类型,会将自己提升为和double类型的变量a一样的数据类型后,再与之进行运算,也就变成了 double a = 10.0; 和 double b = 18.0;两个double数据类型进行加法运算,结果还是double数据类型的结果:28.0.
byte short char 这三种数据在运算的时候,都会提升为int,然后再进行运算
public class NoteDemo { public static void main(String[] args) { byte a = 10; byte b = 20; //两个内存占用1个字节的byte类型的数据在进行运算时,会将自己提升为内存占用4个字节的两个int后,再进行运算 int c = a+b; System.out.println(c); } }
强制转换
把一个取值范围大的数值或者变量,赋值给另一个取值范围小的变量
首先不可这样赋值,错误代码如下:
public class NoteDemo { public static void main(String[] args) { double a = 10.0; //这里编辑器会报错,变量a取值范围大,不能直接赋值给取值范围小的变量b int b = a; } }
不允许直接复制,需要加入强制转换
格式:目标数据类型 变量名 = (目标数据类型) 被强转的数据public class NoteDemo { public static void main(String[] args) { double a = 10.0; //double类型的变量a,通过强制转换后,会 int b = (int)a; System.out.println(a); System.out.println(b) } }
注意:通过上面的代码可以看出,强制转换有可能会出现精度损失的情况,并不是一定的
代码如下:
public class NoteDemo { public static void main(String[] args) { int a = 130; byte b = (byte)a; System.out.println(a);//130 System.out.println(b);//结果为 -126 为什么? } }
通过上面的代码,变量a =30 , 为 int类型,变量b为 byte类型,变量a通过强制转换成byte,赋值给变量b,大的给小的通过强制转换可以给,但是为什么变量a原来的值为30,结果在强转赋值给变量b后是**-126**呢?
知识拓展
通过二进制的知识来解释:
先了解什么是进制?由于计算机中存储数据的单位为:字节,而每一个字节在计算机底层都是以二进制形式进行体现的
计算机中进制的分类和书写格式
进制 说明 前缀 十六进制 用数字0到9和字母A到F(或a ~ f)表示,其中:a ~ f 表示10~15,这些称作十六进制 [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [a] [b] [c] [d] [e] [f] 0x 十进制 由0,1,2,3,4,5,6,7,8,9进行表示,逢十进一,借一当十 八进制 由0,1,2,3,4,5,6,7八个数表示,逢八 进一 0 二进制 二进制数据是用0和1两个数码来表示的,二进制规则是"逢二进一",借位规则是"借一当二" 0b 二进制转换十进制方法一
公式:系数 * 基数的权次幂,然后相加
系数: 就是每一位上的数
基数: 当前的进制数
权 : 从右到左,一次为0 1 2 3 4 5 6 7 8 …
例如:
1 0 1 系数*基数的权次幂 系数*基数的权次幂 系数*基数的权次幂 1*2^2 0*2^1 1*2^0 4 + 0 + 1 ------------------------------------------------------------------------------------------------------------- 结果为 5 二进制转十进制方法二
二进制中,每一位的1都是代表着一个固定的数值,把每一位的1代表的十进制数加起来得到的结果就是它所代表的十进制
8421转换法:
128 64 32 16 8 4 2 1 以二进制0b1101为例
1 1 0 1 8 4 1 通过8421转换法,计算上面表格中得到的值8+4+1=13如下:
8 4 1 1+4+8 结果为:13
也就是说二进制0b1101转换为十进制后结果为:13
二进制的原码 反码 补码
数据的二进制形式,每一个字节由8个二进制位组成
+ 二进制高位 其余二进制位 所表示二进制的数据 0 0 0 0 1 0 1 0 10 1 0 0 0 1 0 1 0 -10 ---------------------------------------------------------------------------- 1 0 0 1 0 1 0 0
1 0 0 1 0 1 0 0 通过下面的8 4 2 1快速转换(遇到二进制的0则不取) 128 64 32 16 8 4 2 1 转换后的结果为: 1(高位为1代表负数) 16 4 4+16= -20 高位:二进制数据中,最左侧的数据,通过高位代表的符号位
原码
原码 1 0 0 0 1 0 1 0
反码
正数的反码与其原码相同
负数的反码是以其原码逐位取反,但符号位除外
原码 1 0 0 0 1 0 1 0 反码 1 1 1 1 0 1 0 1
补码
什么是补码: 补码是通过反码推出来的
正数的补码与其原码相同
负数的补码是在其反码的末位加1
+ 原码 1 0 0 0 1 0 1 0 反码 1 1 1 1 0 1 0 1 补码是在其反码的末位加1进行运算如下: 1 补码 1 1 1 1 0 1 1 0 上面二进制的补码对应的十进制数据为 -10
**正确运算:**通过上面得到的-10(负数)的补码与10(正数)的补码(正数的原 反 补 相同)来进行下面正确运算操作
+ 下面是十进制负数 -10 的补码 1 1 1 1 0 1 1 0 -10 下面是十进制正数 10 的补码 0 0 0 0 1 0 1 0 10 ---------------------------------------------------------------------------- 10 0 0 0 0 0 0 0
通过上面的正确运算操作,就得到了(-10 + 10=0)的正确结果
由于一个字节是由8个二进制位组成的,所以第9个进的1被挤出去不要了原 反 补总结:计算机在运算的时候,都是以二进制补码的形式在运算,也就是只有以补码的形式进行运算操作,得到的结果才是正确的
言归正传回到JAVA
**JAVA强转中的精度损失的问题**public class NoteDemo { public static void main(String[] args) { int a = 130; byte b = (byte)a; System.out.println(a);//130 System.out.println(b);//结果为 -126 为什么? } }
第一步: 首先将上面代码中int变量a的值130的十进制数据转换成二进制,再得到其补码状态的二进制
>由于我们对int类型的变量a做了byte类型强制转换操作,然而,byte类型占用只用1个字节,一个字节由4个二进制位组成,也就是说,一个字节强制转为一个字节,会暴力砍掉后面所有的,只保留一个字节由于变量a的数据类型为int类型,而int类型占用4个字节,1个字节由8个二进制位组成,那么也就是说130的完整二进制位为4组8个二进制位,不够的会做补零操作,如下:
这里需要注意,上面强转之前的高位是0 (0表示正数,1表示负数)
强转之后高位变成了1 (0表示正数,1表示负数)
int类型的十进制数据130完整的的二进制表示如下 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0
int类型十进制130数据强转为byte类型后二进制表示如下 1 0 0 0 0 0 1 0
这里我们又发现一个问题,这组二进制数据"10000010"还是补码的状态,配合上面提到过的[8421转换法]得到如下结果如下:
1 1 0 0 0 0 1 0 通过下面的8 4 2 1快速转换(遇到二进制的0则不取) 128 64 32 16 8 4 2 1 转换后的结果为: 1(高位为1代表负数) 2 结果为: -2
上面表格中的结果,我们发现二进制10000010 通过[8421转换法]转换出来的结果不是130,而是 -2 (高位为1所以是负数)
第二步:尝试通过补码反向推出原码
原码需要通过将补码状态的二进制 10000010 取反得到反码(负数的反码是以其原码逐位取反,但符号位除外),再反码的末位加1求得原码
+ 补码 1 0 0 0 0 0 1 0 反码 1 1 1 1 1 1 0 1 求原码需要在反码末位加1进行运算如下: 1 原码 1 1 1 1 1 1 1 0 下面再通过[8421转换法](遇到二进制的0则不取) 求出该原码的十进制数据 128 64 32 16 8 4 2 1 转换后的结果为: 1(高位为1代表负数) 64 32 16 8 4 2 2+4+8+16+32+64= 结果为:-126
通过上面的[知识拓展]部分的阐述,至此,代码:
public class NoteDemo { public static void main(String[] args) { int a = 130; byte b = (byte)a; System.out.println(a);//130 System.out.println(b);//结果为 -126 为什么? } }
中为什么int类型变量a=130;强制转换为byte=(byte)a;输出结果为什么是-126而不是原来存储的130,其计算机内部转换的过程就大概了解清楚了
类型转换面试题测试1.下面代码是否存在错误,如果有,请指出说明,并改正错误;
public class NoteDemo{ public static void main(String[] args){ byte a =3; byte b = 4; byte c= a + b; } }
错误的原因:
1.变量a和变量b两个数据类型都为byte,然而我们知道,byte short char三种数据类型在进行运算操作的时候都会将自己提升为int类型进行数据运算操作,byte类型的变量a和变量b,在提升为int之后,所运算的结果还是int类型.因此,将提升为int类型的变量a与变量b所相加的结果赋值给byte类型的变量c,属于大的给小的赋值,不能直接给
解决错误:
public class NoteDemo{ public static void main(String[] args){ byte a = 3; byte b = 4; int c = a + b;//变量a,b提升为int类型运算后给int类型的变量c可以直接给 Stytem.out.println(c);//运行结果为7 } }
↑由上面的面试题引出拓展知识:↑
先看代码:public class NoteDemo{ public static void main(String[] args{ byte a = 3 + 4;//思考这样的定义变量为什么不会报错? System.out.println(a); } }
按照目前所了解的知识:
上面的3和4是两个字面量,之前了解过,所有整数都默认是int类型,所以这里按理来说应该是两个int数据类型在做运算.运算后的结果还是int类型,.如果int类型的结果给byte类型的变量赋值,属于大的给小的赋值,是不能直接给的,会出现编译错误的
然而,思考一下为什么上面这样的定义变量为什么不会报错,而且会正常输出结果为7?
解答
Java是存在常量优化机制的,在编译的时候(也就是在执行javac过程)就会将3和4两个字面量进行运算操作.所产生的字节码文件(也就是.class文件)中的代码会直接编译为:public class NoteDemo{ public static void main(String[] args){ byte a = 7; System.out.println(a)//运行结果为7 } }
这里需要注意:如果上面的字面量运算操作的结果超出byte类型的取值范围时,是会直接报错的