跟光磊学Java应用开发与系统架构-Java运算符
Java运算符概述
计算机最核心的任务就是完成数据的计算,Java最常用的运算符有算术,关系,逻辑以及比较和位运算符。
在介绍具体的运算符之前先要明确关于运算符的一些概念:操作数、运算符、表达式、优先级和结合性。
运算符:表示数据参与的运算类型,例如这里的"+"表示"+"左右两边的操作数执行加法运算。
操作数就是参与运算的数据,例如这里的5和8就是操作数
int result =5 + 8;
优先级:计算的先后顺序,以算术运算符为例,其默认的顺序是先乘除,后加减。但是可以通过()来改变了运算的优先级。
Java中的运算符优先级顺序记忆比较困难,我们在使用运算符的过程中,切记表达式不要太复杂,可以分解成多行实现,如果需要使用混合运算,那么明确优先计算的使用()括起来。
int result = (45 + 5) / 5 * 10;
package net.ittimeline.java.core.jdk.foundational.operator;
/**
* 运算符的优先级
* 运算符的优先级决定了存在多个运算符时一个表达式各部分的运算顺序。Java 对运算顺序作出了特别的规定。
* 其中,最简单的规则就是乘法和除法在加法和减法之前完成。程序员经常都会忘记其他优先级规则,所以应该用括号明确规定运算顺序。
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 12:13 下午
* @since JDK11
*/
public class Precedence {
public static void main(String[] args) {
int x=1,y=2,z=3;
int a=x+y-2/2+z;
int b=x+(y-2)/(2+z);
// a = 5
System.out.println("a = "+a);
// b = 1
System.out.println("b = "+b);
}
}
结合性:表达式计算的方向,从右向左或者从左向右
例如赋值运算的结合性就是从右向左,
int left, middle, right;
left = middle = right =10;
算术运算符的结合性是从左向右,结合性决定了优先级相等的情况下的计算顺序
int result = (45 + 5) / 5 * 10;
运算符不能相邻,此处编译会发生错误
1 + *3;
但是加(+)减(-)号例外 因为加减号作用于整数的时候,用来区分正负数
1 + +3;
1 - -2;
运算符和操作数组成了表达式,()可以嵌套使用
int result = ((4 * 5) + (20 - 10) - 12 * 3) + 12 - 2;
算术运算符
算术运算符就是数学意义上的加减乘除(+ - * /)以及取模(%)运算(即求余数)。
其优先级是按照先乘除,后加减的计算顺序。结合性是从左到右的计算方向。
两个整数相除结果是整数,小数部分被舍去。
两个整数执行求余运算时,运算结果的符号和被除数相同。
如果除法任意一边有浮点数,那么运算的结果一定是浮点数
package net.ittimeline.java.core.jdk.foundational.operator.arithmetic;
/**
* 算术运算符
* 加法(+)
* 减法(-)
* 乘法(*)
* 除法()
* 取模(%)
* 正号(+)
* 符号(-)
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 1:48 下午
* @since JDK11
*/
public class ArithmeticTest {
public static void main(String[] args) {
int x=10;
int y=3;
//因为第二个加号左边是字符串,因此这里是字符串拼接,运算的结果是x + y = 103
System.out.println("x + y = "+x+y);
//正确的写法应该是将表达式()起来,提高优先级
System.out.println("x + y = "+(x+y));
System.out.println("x - y = "+(x-y));
//乘除法优先级高于加减法 因此这里的()可以省略
System.out.println("x * y = "+ x*y);
//整数相除的结果一定是整数,小数部分会被舍弃
System.out.println("x / y = "+ x/y);
//除法一边有浮点数,运算结果一定是浮点数
System.out.println("5.0 /3 = "+(5.0/3));
System.out.println("x % y = "+(x%y));
// 另外一种更直观的写法,其中%d表示占位符,会被对应的整数参数替换掉 \n表示转义字符,实现换行
System.out.printf("%d + %d = %d \n",x,y,(x+y));
System.out.printf("%d - %d = %d \n",x,y,(x-y));
System.out.printf("%d * %d = %d \n",x,y,(x*y));
System.out.printf("%d / %d = %d \n",x,y,(x/y));
System.out.printf("%d %% %d = %d \n",x,y,(x%y));
//只有整数才能执行求模运算,运算结果的符号(即正负)和被除数相同
System.out.printf("%d %% %d = %d \n",5,3,(5%3));
System.out.printf("%d %% %d = %d \n",-5,3,(-5%3));
System.out.printf("%d %% %d = %d \n",5,-3,(5%-3));
System.out.printf("%d %% %d = %d \n",-5,-3,(5%-3));
//正负号的使用
int number=-4;
//负负得正
System.out.println("-number = "+ (-number));
}
}
基本数据类型中只有数值型的数据类型能参与算术运算,而且只有整数才能够进行取余(取模)运算
package net.ittimeline.java.core.jdk.foundational.operator.arithmetic;
import java.util.Random;
/**
*
* 数值型数据类型的算术运算符
*
* 只有数值类型才能使用算术运算
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 12:19 下午
* @since JDK11
*/
public class MathOps {
public static void main(String[] args) {
Random random=new Random(88);
//同时声明多个变量
int i,j,k;
//赋随机生成的1-100
j=random.nextInt(100)+1;
System.out.println("j = "+j);
k=random.nextInt(100)+1;
System.out.println("k = "+j);
i=j+k;
System.out.println("j + k = "+i);
i=j-k;
System.out.println("j - k = "+i);
i=j*k;
System.out.println("j * k = "+i);
i=j/k;
System.out.println("j / k = "+i);
i=j%k;
System.out.println("j % k = "+i);
double u,v,w;
v=random.nextDouble();
System.out.println("v = "+v);
w=random.nextDouble();
System.out.println("w = "+w);
u=v+w;
System.out.println("v + w = "+u);
u=v-w;
System.out.println("v - w = "+u);
u=v*w;
System.out.println("v * w = "+u);
u=v/w;
System.out.println("v / w = "+u);
u+=v;
System.out.println("u+=v = "+u);
u-=v;
System.out.println("u-=v = "+u);
u*=v;
System.out.println("u*=v = "+u);
u/=v;
System.out.println("u/=v = "+u);
}
}
算术运算符案例:已知一个三位数:例如987,使用取模运算实现求这个数值的百位、十位和个位数
package net.ittimeline.java.core.jdk.foundational.operator.arithmetic;
/**
* 已知一个三位数:例如987,使用取模运算实现求这个数值的百位、十位和个位数
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 2:48 下午
* @since JDK11
*/
public class ArithmeticExample1 {
public static void main(String[] args) {
int number = 987;
int hundred = number / 100;
// int decade=number/10%10;
//另外一种获取十位的方法
int decade = number % 100 / 10;
int unit = number % 10;
System.out.printf("%d的百位数是%d,十位数是%d,个位数是%d", number, hundred, decade, unit);
}
}
算术运算符和基本数据类型的运算
package net.ittimeline.java.core.jdk.foundational.operator.arithmetic;
/**
* 8种数据类型、字符串和算术运算符的综合应用
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 2:58 下午
* @since JDK11
*/
public class ArithmeticExample2 {
public static void main(String[] args) {
int number=10;
String str1="abc";
//str2=abcxyz10
String str2=str1+"xyz"+number;
//str2=abcxyz123
str2=str2+"123";
char c='中';
double PI=3.1415;
// str2=abcxyz1233.1415
str2=str2+PI;
boolean b=false;
//str2=abcxyz1233.1415false
str2=str2+b;
//str2=abcxyz1233.1415false中
str2=str2+c;
System.out.println("str2 = "+str2);
}
}
字符串和数值类型的加法运算
package net.ittimeline.java.core.jdk.foundational.operator.arithmetic;
/**
* 字符串和数值类型的加法运算
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 3:03 下午
* @since JDK11
*/
public class ArithmeticExample3 {
public static void main(String[] args) {
//编译错误,整数常量没办法直接赋值给字符串变量
//String str1=4;
String str1="4";
//3.5+"" 编译器会将这个表达式当成字符串处理
String str2=3.5f+"";
//3.5
System.out.println(str2);
//7Hello!
System.out.println(3+4+"Hello!");
//Hello!34
System.out.println("Hello!"+3+4);
//98Hello!
System.out.println('a'+1+"Hello!");
// Hello!a1
System.out.println("Hello!"+'a'+1);
}
}
类型转换与算术运算
package net.ittimeline.java.core.jdk.foundational.operator.arithmetic;
/**
* 类型转换与算术运算
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 3:08 下午
* @since JDK11
*/
public class ArithmeticExample4 {
public static void main(String[] args) {
short st = 5;
// 这里会发生编译错误,因为short与int运算的结果类型是int 因此编译错误
// st = st - 2;
byte bt = 3;
//这里会发生编译错误,因为short与int运算的结果类型是int 因此编译错误
//bt = bt + 4;
//强制类型将int转换为byte
bt=(byte)(bt+1);
char ch='a';
int i=5;
float flt=.314F;
double result=ch+i+flt;
System.out.println("result = "+result);
bt=5;
st=3;
// 编译错误 因为short和byte运算的结果也是int类型
// short shortResult=bt+st;
}
}
使用算术运算符实现一个两位整数的反转
```java
package net.ittimeline.java.core.jdk.foundational.operator.arithmetic;
/**
* 求模运算的案例:输入一个整数,输出颠倒后的结果。例如输入95,输出59。
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 6:25 下午
* @since JDK11
*/
public class ArithmeticExample5 {
public static void main(String[] args) {
int number=95;
int decade=number/10;
int unit=number%10;
int reversal=unit*10+decade;
System.out.printf("%d反转后的结果是%d",number,reversal);
}
}
在使用算术运算时注意不要越界,否则会出现错误的运算结果
package net.ittimeline.java.core.jdk.foundational.operator.arithmetic;
/**
* 在使用算术运算时注意不要越界,否则会出现错误的运算结果
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 6:11 下午
* @since JDK11
*/
public class Overflow {
public static void main(String[] args) {
int big = Integer.MAX_VALUE;
System.out.println("big = " + big);
int bigger = big * 4;
System.out.println("bigger = " + bigger);
}
}
自增自减运算符
自增、自减运算符的作用是将变量的值增加1或者减少1。自增运算符是++。自减运算符是--。在使用时需要注意自增、自减运算符只能作用于变量,不能作用在常量或者表达式。
前置++、--和后置++、--的区别是当自增、自减运算符放置在变量前面时会先自增、自减1,再参与运算,当自增、自减运算符放在变量后面时会先参与运算,再自增、自减1。
自增、自减运算符前置或者后置对于操作变量来说都会自增、自减1,而自增、自减运算符,但是自增、自减运算符前置或者后置对于操作表达式来来说计算结果是不一样的。
自增、自减运算符和算术运算符同时参与运算时,自增、自减运算符优先级高于算术运算符。
自增自减运算符在一元运算的使用
package net.ittimeline.java.core.jdk.foundational.operator.autoinc;
/**
* 自增自减运算符
* 一元运算的自增自减运算符
* 一元表示只有一个操作数
* @author liuguanglei aaa@qq.com
* @version 2020/11/8 8:40 下午
* @since JDK11
*/
public class AutoIncrementUnaryTest {
public static void main(String[] args) {
//自增运算符的使用
int i = 1;
System.out.println("i : " + i);
//先自增1再打印i的值 i=2
System.out.println("++i : " + ++i);
//先打印i的值 i=2 再自增1 i=3
System.out.println("i++ : " + i++);
System.out.println("i : " + i);
//自减运算的使用
//先自减1,再打印i的值 i=2
System.out.println("--i:" + --i);
//先打印i的值 i=2 再减1 i=1
System.out.println("i--: " + i--);
// i = 1
System.out.println("i :" + i);
}
}
自增运算符在多元运算的使用
package net.ittimeline.java.core.jdk.foundational.operator.autoinc;
/**
* 自增自减运算符
* 多元运算的自增自减运算符
* 二元运算的自增自减运算符,二元表示有两个操作数,依次类推
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 2:14 下午
* @since JDK11
*/
public class AutoIncrementMultiTest {
public static void main(String[] args) {
int i = 1;
//先赋值,再自增1
int j = i++;
//因此
// i = 2
System.out.println("i = " + i);
//j = 1
System.out.println("j = " + j);
int m = 1;
// ① 先取出m的值1放到操作数栈 ② 然后完成m的自增1 ③把之前操作数栈的值赋值给m
m = m++;
//输出m =1
System.out.println("m = " + m);
int k = 1;
// k++=1 k=2
//++k=3
//1+3=4
//k=3
System.out.println("k++ + ++k = " + (k++ + ++k));
System.out.println("k = " + k);
int q = 1;
int p = 2;
// 1*2+3=5
System.out.println("q++ * p+ ++p = " + (q++ * p + ++p));
i=1;
j=2;
//2+2*3=8
System.out.println("++i +j * ++i = "+(++i +j * ++i));
int x = 2;
int y = 3;
/*
*
* z的结果计算过程
* x=2,x++=2 ,x=3
* x=3,++x=4,x=4
* x=4,++x=5 ,x=5
* y=3,y++=3,y=4
* z=2+4+5*3=21
*/
int z = x++ + ++x + ++x * y++;
System.out.println("z = " + z);
// x =5
System.out.println("x = " + x);
// y =4
System.out.println("y = " + y);
}
}
赋值运算符
在学习变量赋值时用到的等号(=)实际上就是赋值运算符,它的作用就是用来将一个常量或者表达式赋值给一个变量,其赋值的格式为 数据类型 变量标识符=常量值或者表达式
。等号左边永远是变量,如果等号右边是表达式,赋值前把右边的整个表达式先计算完,才会做赋值的操作。
优先级:所有运算符中仅高于逗号运算符,
结合性:当表达式中有多个变量同时赋值时按照从右往左的结合性进行赋值
赋值运算符还可以结合算术运算符来使用,例如+=
,-=
,*=
, /=
,%=
。
package net.ittimeline.java.core.jdk.foundational.operator.assignment;
/**
* 赋值运算符和扩展运算符
* = 赋值运算符
* 扩展运算符 += -= *= /= %=
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 3:16 下午
* @since JDK11
*/
public class AssignmentTest {
public static void main(String[] args) {
int x=10;
System.out.println("x = "+x);
int y=20;
//等价于x = x+y
//因此 x=30
x+=y;
System.out.println("x = "+x);
x=1;
y=5;
// 1+5
//y=5*6
// y=30
y*=x++ +y;
System.out.println("y = "+y);
}
}
赋值运算符与算术运算符的组合使用
package net.ittimeline.java.core.jdk.foundational.operator.assignment;
/**
* 赋值运算符和算术运算符的组合使用
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 6:29 下午
* @since JDK11
*/
public class AssignmentArithmeticTest {
public static void main(String[] args) {
int number=10;
System.out.println("number = "+number);
//等价于number=number+10
number+=10;
System.out.println("number = "+number);
//等价于number=number-5
number-=5;
System.out.println("number = "+number);
//等价于number=number*5
number*=5;
System.out.println("number = "+number);
//等价于number=number/5
number/=5;
System.out.println("number = "+number);
}
}
关系运算符
关系运算符用于计算数值之间的关系,其运算的结果是boolean类型,如果表达式成立则返回true,否则返回为fasle。
日常常用的关系运算符有大于(>)、小于(<)、等于()、不等于(!=),大于等于(>=),小于等于(<=)。
关系运算符用于基本数据类型的比较,而和!=可以用于引用数据类型的比较。
关系运算符和操作数组成的表达式可以作为判断条件和逻辑运算符的操作数。
在使用关系运算符的==进行相等性判断时需要和赋值(=)运算符进行区分开来,否者程序会出现逻辑错误
package net.ittimeline.java.core.jdk.foundational.operator.relation;
/**
* 关系运算符
* 大于(>)
* 小于(<)
* 等于(==)
* 不等于(!=)
* 大于等于(>=)
* 小于等于(<=)
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 3:24 下午
* @since JDK11
*/
public class RelationTest {
public static void main(String[] args) {
int age=16;
//关系运算符的结果是true或者false
System.out.println("是否成年?"+(age>=18));
//关系运算符通常作为判断条件
//基本数据类型的关系运算
if(age>=18){
System.out.println("已经成年,祝购物愉快");
}else{
System.out.println("未成年人不得进入");
}
boolean flag=false;
if(flag==true){
System.out.println("条件成立");
}
//在使用 == 进行相等性判断时,切勿使用成了=,因为=表示赋值,这里把true赋值给flag, 否则程序会出现错误
if(flag=true){
System.out.println("flag = "+flag);
System.out.println("条件成立");
}
//等价于
if(flag){
System.out.println("flag = "+flag);
System.out.println("条件成立");
}
}
}
使用关系运算符实现判断是否能够结婚
package net.ittimeline.java.core.jdk.foundational.operator.relation;
/**
* 使用关系运算符模拟是否能够结婚
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 8:00 下午
* @since JDK11
*/
public class RelationMarryTest {
public static void main(String[] args) {
//判断要么满足年龄大于等于22周岁并且性别为男性,或则年龄大于等于20周岁并且性别为女性的才能结婚
String gender = "男";
int age = 18;
if (gender.equals("男") && age >= 22 || (gender.equals("女") && age >= 20)) {
System.out.println("你符合条件,可以结婚了");
} else {
System.out.println("你还没有到达结婚的条件");
}
}
}
逻辑运算符
逻辑运算符用于判断两个表达式的逻辑关系,逻辑运算符运算的结果是boolean类型。
常用的逻辑运算符
-
逻辑与(&): &左右两边的结果为true,逻辑与表达式的结果为true
- true & true=true
- true & false=false
- fasle & true=false
- false & false =false
-
逻辑或(|): |左右两边只要有一边的结果为true,那么逻辑或的结果就是true
- true| true=true
- true | false = true
- false | true = true
- false | false = false
-
逻辑非(!):表达式的结果为true,逻辑非为false,即取反
- !true=false
-
!false=true
除此以外还有短路与(&&)与短路或(||),它们与逻辑与(&)和逻辑或(|)的不同之处在于存在短路特性,即一旦明确整个表达式的值,就不再计算剩下的部分。
关系运算符与逻辑运算符实现两个随机整数的关系判断
package net.ittimeline.java.core.jdk.foundational.operator.logic;
import java.util.Random;
/**
* 关系运算符与逻辑运算符实现两个随机整数的关系判断
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 5:51 下午
* @since JDK11
*/
public class Bool {
public static void main(String[] args) {
Random random=new Random(88);
int i=random.nextInt(100);
int j=random.nextInt(100);
System.out.println("i = "+i);
System.out.println("j = "+j);
System.out.println("i > j is "+(i>j));
System.out.println("i < j is "+(i<j));
System.out.println("i >= j is "+(i>=j));
System.out.println("i <= j is "+(i<=j));
System.out.println("i == j is "+(i==j));
System.out.println("i != j is "+(i!=j));
System.out.println("(i<10) && (j<10) is "+ ((i<10) && (j<10)));
System.out.println("(i<10) || (j<10) is "+ ((i<10) || (j<10)));
}
}
逻辑运算符与if语句结合实现条件判断
package net.ittimeline.java.core.jdk.foundational.operator.logic;
/**
* 逻辑运算符 与if语句结合使用实现条件判断
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 3:40 下午
* @since JDK11
*/
public class LogicTest {
public static void main(String[] args) {
int score=80;
//逻辑与
if(score>=70 & score<=80){
System.out.println("成绩在70-80之间");
}
score=101;
//假设成绩的正常区间是[0-100],否则输出成绩有误
if(score>=100| score<=0){
System.out.println("成绩有误");
}
//逻辑非
if(!(score>=100| score<=0)){
System.out.println("成绩正常");
}
//逻辑亦或 ^左右两边的结果不同,运算结果为true
System.out.println(true^false);
System.out.println(true^true);
System.out.println(false^false);
System.out.println(false^true);
}
}
理解逻辑运算符的短路特性
package net.ittimeline.java.core.jdk.foundational.operator.logic;
/**
* 短路与、短路或的短路特性
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 3:48 下午
* @since JDK11
*/
public class LogicShortCircuitTest {
public static void main(String[] args) {
int i = 1;
int j;
//逻辑与的短路特性
// ++i==1 结果为false 短路与运算的结果就是false 由于短路特性不会再计算i++ == 2
// 而if/else是二选一的关系
if (++i == 1 && i++ == 2) {
j = 1;
} else {
j = 2;
}
//因此 i=2 j=2
System.out.println("短路与:i = " + i + " j = " + j);
//清空之前短路与的计算结果
i=1;
j=0;
//i++ ==1 结果为1 短路或的运算结果是true,因此不会再计算i++ == 2
if (i++ == 1 || i++ == 2) {
j=1;
}else{
j=2;
}
// 因此 i=2 j=1
System.out.println("短路或:i = " + i + " j = " + j);
}
}
逻辑与和自增运算符的混合计算
package net.ittimeline.java.core.jdk.foundational.operator.logic;
/**
* 逻辑与、自增运算符的混合计算
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 4:02 下午
* @since JDK11
*/
public class LogicExample1 {
public static void main(String[] args) {
int x=1;
int y=1;
// x++ ==2 结果为false 但是 逻辑与(&)不具备短路特性,因此会计算++y ==2 但是整体表达式结果为false,不会执行x = 7的赋值操作
if(x++ ==2 & ++y ==2){
x=7;
}
// x=2 y=2
System.out.println("x = "+x+" y = "+y);
}
}
短路与和自增运算符的混合计算
package net.ittimeline.java.core.jdk.foundational.operator.logic;
/**
* 短路与和自增运算符的混合计算
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 4:02 下午
* @since JDK11
*/
public class LogicExample2 {
public static void main(String[] args) {
int x=1;
int y=1;
// x++ ==2 结果为false 但是 逻辑与(&&)具备短路特性,因此不会计算++y ==2 而且整体表达式结果为false,不会执行x = 7的赋值操作
if(x++ ==2 && ++y ==2){
x=7;
}
// x=2 y=1
System.out.println("x = "+x+" y = "+y);
}
}
逻辑或与自增运算符的混合运算
package net.ittimeline.java.core.jdk.foundational.operator.logic;
/**
* 逻辑或与自增运算符的混合运算
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 4:07 下午
* @since JDK11
*/
public class LogicExample3 {
public static void main(String[] args) {
int x = 1;
int y = 1;
//x++ ==1 运算结果为true x=2,但是逻辑或(|)不具备短路特性,因此还会计算++y ==1 结果为false,但是true|false的逻辑或结果为true
if (x++ == 1 | ++y == 1) {
//因此会将7赋值给x
x = 7;
}
// x=7 y=2
System.out.println("x = " + x + " y = " + y);
}
}
短路或与自增运算符的混合运算
package net.ittimeline.java.core.jdk.foundational.operator.logic;
/**
* 短路或与自增运算符的混合运算
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 4:07 下午
* @since JDK11
*/
public class LogicExample4 {
public static void main(String[] args) {
int x = 1;
int y = 1;
//x++ ==1 运算结果为true x=2,但是逻辑或(||)具备短路特性,因此不会计算++y ==1 ,但是if表达式的结果为true
if (x++ == 1 || ++y == 1) {
//因此会将7赋值给x
x = 7;
}
// x=7 y=1
System.out.println("x = " + x + " y = " + y);
}
}
短路与、短路或与自增运算符和基本数据类型的混合运算
package net.ittimeline.java.core.jdk.foundational.operator.logic;
/**
* 短路与、短路或与自增运算符和基本数据类型的混合运算
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 4:12 下午
* @since JDK11
*/
public class LogicExample5 {
public static void main(String[] args) {
boolean x = true;
boolean y = false;
short st = 42;
// st++==42运算结果为true st=43
// y==true 结果为false
// true && false 结果为false,不会计算st++
if ((st++ == 42) && (y == true)) {
st++;
}
//x==false结果为false
//++st==45结果为false
// false||false的结果为false,不会计算st++
if ((x == false) || (++st == 45)) {
st++;
}
//st=44
System.out.println("st = " + st);
}
}
短路与、短路或与自增运算符和基本数据类型的混合运算
package net.ittimeline.java.core.jdk.foundational.operator.logic;
/**
* 短路与、短路或与自增运算符和基本数据类型的混合运算
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 4:12 下午
* @since JDK11
*/
public class LogicExample6 {
public static void main(String[] args) {
boolean x = true;
boolean y = false;
short st = 42;
//这里实际上是将true赋值给y
if(y=true){
}
// st++==42运算结果为true st=43
// y==true 结果为false
// true && true =true,计算st++
if ((st++ == 42) && (y == true)) {
// st++=43
st++;
// st=44
}
//x==false结果为false
//++st==45结果为true
// false||true的结果为true,会计算st++
if ((x == false) || (++st == 45)) {
//st++ =44
st++;
//st=45
}
//st=45
System.out.println("st = " + st);
}
}
逻辑运算的案例:判断一个给定的年份是否是闰年
package net.ittimeline.java.core.jdk.foundational.operator.logic;
/**
* 定义一个int类型的变量,保存年份,判断这个年份是否是闰年
* 判断一年是否是闰年的标准 ①可以被4整除,不能被100整除 ② 可以被400整除
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 5:30 下午
* @since JDK11
*/
public class LogicExample7 {
public static void main(String[] args) {
int year=2000;
boolean isLeapYear=(year%4==0&&year%100!=0)||(year%400==0);
if(isLeapYear){
System.out.printf("%d是闰年",year);
}else{
System.out.printf("%d不是闰年",year);
}
}
}
三元运算符
三元运算符也被称为条件运算符,其表现形式为表达式1?表达式2:表达式3,其中如果表达式1的条件成立,那么表达式的计算结果就是表达式2,否则计算结果就是表达式3。
三元运算符和if/else的作用是等价的。
三元运算符的使用
package net.ittimeline.java.core.jdk.foundational.operator.ternary;
/**
* 三元运算符
* 三元运算符结构: 条件表达式?结果表达式1:结果表达式2
* 运算流程:如果条件表达式为true,运算结果为结果表达式1,否则为结果表达式2
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 4:26 下午
* @since JDK11
*/
public class TernaryTest {
public static void main(String[] args) {
boolean isMarry=true;
System.out.println(isMarry?"已婚":"未婚");
int x=4;
int y=22;
int max= x>y?x:y;
System.out.println("x和y的最大值是"+max);
}
}
三元运算符与if/else的比较
package net.ittimeline.java.core.jdk.foundational.operator.ternary;
/**
* 三元运算符对比if/else
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 6:06 下午
* @since JDK11
*/
public class TernaryIfElse {
static int ternary(int i) {
return i < 100 ? i * 100 : i * 10;
}
static int standIfElse(int i) {
if (i < 100) {
return i * 100;
} else {
return i * 10;
}
}
public static void main(String[] args) {
System.out.println(ternary(9));
System.out.println(ternary(10));
System.out.println(standIfElse(9));
System.out.println(standIfElse(10));
}
}
三元运算符与类型转换
package net.ittimeline.java.core.jdk.foundational.operator.ternary;
/**
* 三元运算符与类型转换
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 6:35 下午
* @since JDK11
*/
public class TernaryTypeCastTest {
public static void main(String[] args) {
int number=20;
float flt=10.9f;
//三元运算符会自动进行类型转换
//因为float表示数据的范围大于int,三元运算符在运算时会自动类型转换,将结果转换为浮点类型,
System.out.println((number>flt?number:flt));
}
}
三元运算符的案例:求三个整数中的最大值
package net.ittimeline.java.core.jdk.foundational.operator.ternary;
/**
* 定义三个int类型的变量,x,y,z,随意赋整数值,求最大值
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 5:35 下午
* @since JDK11
*/
public class TernaryExample1 {
public static void main(String[] args) {
int x,y,z;
x=10;
y=20;
z=15;
int max=0;
int tmp=x>y?x:y;
max=tmp>z?tmp:z;
System.out.println("x,y,z中的最大值是"+max);
//组合表达式
max=(x>y?x:y)>z?(x>y?x:y):z;
System.out.println("x,y,z中的最大值是"+max);
}
}
三元运算符案例:求四个整数中的最大值
package net.ittimeline.java.core.jdk.foundational.operator.ternary;
/**
* 三元运算符求四个整数中的最大值
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 6:40 下午
* @since JDK11
*/
public class TernaryExample2 {
public static void main(String[] args) {
//声明并初始化四个整数变量
int i, j, k, l;
i = 20;
j = 40;
k = 30;
l = 10;
int tmp1 = i > j ? i : j;
int tmp2 = k > l ? k : l;
int max = tmp1 > tmp2 ? tmp1 : tmp2;
System.out.println("i,j,k,l四个整数的最大值是" + max);
//综合表达式
max=(i > j ? i : j)>(k > l ? k : l)?i > j ? i : j: k > l ? k : l;
System.out.println("i,j,k,l四个整数的最大值是" + max);
}
}
三元运算符案例:模拟硬币反转
package net.ittimeline.java.core.jdk.foundational.operator.ternary;
import java.util.Random;
/**
* 使用三元运算符模拟硬币反转的程序
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 9:24 下午
* @since JDK11
*/
public class TernaryExample3 {
public static void main(String[] args) {
//创建随机数对象
Random random = new Random(88);
//产生一个随机布尔值
boolean flip = random.nextBoolean();
System.out.print("OUTCOME:");
System.out.println(flip ? "HEAD" : "TAIL");
}
}
位运算符
位运算是基于二进制补码的运算,其运算效率是最高的,但是可读性不太好。在Java底层(例如ArrayList源码)大量使用了基于二进制的位运算。
Java语言持7种位运算符
-
左移(<<) :左移几位就是乘以2的几次方,二进制补码左移n位,右边补0
-
右移(>>):右边移几位就是除以2的几次方,二进制补码右n位,左边补0还是1取决于二进制补码的最高位
-
无符号右移(>>>):右边移几位就是左边补0,对于负数来说,右移后会变成正数
-
按位与(&)
- 1&1=1
- 1&0=0
- 0&1=0
- 0&0=0
-
按位或(|)
- 1 | 1=1
- 1| 0=1
- 0 | 1=1
- 0 | 0=0
-
按位异或(^)
- 1 ^ 1=0
- 1 ^ 0=1
- 0 ^ 1=1
- 0 | 0 = 0
-
按位取反(~)
- ~0=1
- ~1=0
正整数、负整数的按位左移运算的计算流程
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
* 按位左移 <<
* 左移运算符 就是乘以2的N次方,例如左移1位,就是乘以2的1次方,左移2位,就是乘以2的2次方 依次类推。,二进制补码左移n位,右边补0
*
* 总结 原码、反码、补码三者之间的换算
*
* 正数的原码、补码、反码都一样;
* 负数的原码、反码转换:符号位不变,数值位按位取反;
* 负数的原码、补码转换:符号位不变,数值位按位取反,末位+1,【快速求法为:符号位不变,从右往左找第一个1,这个1左边的取反,右边的不变】;
* 负数的反码、补码转换:反码转补码,末位+1;补码转反码,末位-1。
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 7:00 下午
* @since JDK11
*/
public class BitLeftMoveTest {
public static void main(String[] args) {
// 按照1个字节 10的二进制是0000 1010
//左移2位 二进制是0010 1000
// 101000 转换为十进制的结果是40
System.out.println("10左移2位的结果是" + (10 << 2));
// 按照一个字节 -10 的二进制是 1000 1010
//此时需要转换成补码运算
// 1000 1010 转换成反码的结果是1111 0101
// 1111 0101 转换成补码的结果是1111 0110
//1111 0110 左边两位,右边补上00 即 1101 1000
// 然后将 补码 1101 1000 转换成反码 1101 0111
// 反码1101 0111 转换成原码 1010 1000
// 1010 1000转换从十进制的结果是-40
System.out.println("-10 左移2位的结果是" + (-10 << 2));
}
}
正整数、负整数的按位右移运算的计算流程
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
* 按位右移
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 7:03 下午
* @since JDK11
*/
public class BitRightMoveTest {
public static void main(String[] args) {
//32的二进制是0010 0000
//0010 0000 右边移动2位的二进制是0000 1000
//1000转换为十进制的结果是8
System.out.println("32右移2位的结果是" + (32 >> 2));
//按照四个字节 -32的二进制是1000 0000 0000 0000 0000 0000 0010 0000
// 运算之前还要将其转换成补码
//原码 1000 0000 0000 0000 0000 0000 0010 0000
//转为反码 1111 1111 1111 1111 1111 1111 1101 1111
//转换为补码 1111 1111 1111 1111 1111 1111 1110 0000
// 1111 1111 1111 1111 1111 1111 1110 0000 右边移动2位 左边的最高位是1,因此左边需要补2个1
//1111 1111 1111 1111 1111 1111 1110 0000 右边移2位的结果 1111 1111 1111 1111 1111 1111 1111 1000
//计算完成后需要将结果转换成1111 1111 1111 1111 1111 1111 1111 1000 转换成原码
// 1111 1111 1111 1111 1111 1111 1111 1000此时是补码,首先转换成反码, 反码=补码-1
//1111 1111 1111 1111 1111 1111 1111 1000 转换从反码的结果是1111 1111 1111 1111 1111 1111 1111 0111
//转换成反码后还需要转换成原码
//1111 1111 1111 1111 1111 1111 1111 0111 转换成原码 1000 0000 0000 000 000 000 0000 1000
// 1000 0000 0000 000 000 000 0000 1000 换算成十进制的结果是-8 因为左边最高位的符号位是1表示为负数
System.out.println("-32右移2位的结果是" + (-32 >> 2));
}
}
负数无符号右移的计算流程
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
* 无符号右移运算 将操作数按照指定的位数右移动N位,左边补0,如果是负数,右移会变成正数
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 7:09 下午
* @since JDK11
*/
public class BitUnsignedRightMoveTest {
public static void main(String[] args) {
/*
-32的二进制(4个字节)
原码 1000 0000 0000 0000 0000 0000 0010 0000
反码 1111 1111 1111 1111 1111 1111 1101 1111
补码 1111 1111 1111 1111 1111 1111 1110 0000
1111 1111 1111 1111 1111 1111 1110 0000 无符号右移4位等于0000 1111 1111 1111 1111 1111 1111 1110
0000 1111 1111 1111 1111 1111 1111 1110转换为十进制的结果是 268435454
*/
System.out.println("-32 >>> 4 = " + (-32 >>> 4));
}
}
按位与的二进制计算流程
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
* 按位与
* 如果两边都是1,运算结果为1,否则为0
* 1&1=1
* 0&1=0
* 1&0=0
* 0&0=0
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 6:44 下午
* @since JDK11
*/
public class BitAndTest {
public static void main(String[] args) {
int left = 255;
int right = 15;
// 因为left和right都是正数,因此不需要考虑转换为补码计算
// left的二进制表示方式 0000 0000 0000 0000 0000 0000 1111 1111
// right的二进制表示方式 0000 0000 0000 0000 0000 0000 0000 1111
// 1111 1111 & 0000 1111=1111
// 1111转换为十进制的结果是15
System.out.println(left & right);
}
}
按位或的二进制计算流程
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
* 按位或
* 如果两边都是0,那么结果为0,否则结果为1
* 0|0=0
* 0|1=1
* 1|0=1
* 1|1=1
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 6:47 下午
* @since JDK11
*/
public class BitOrTest {
public static void main(String[] args) {
int left = 255;
int right = 15;
// 因为left和right都是正数,因此不需要考虑转换为补码计算
// left的二进制表示方式 0000 0000 0000 0000 0000 0000 1111 1111
// right的二进制表示方式 0000 0000 0000 0000 0000 0000 0000 1111
// 1111 1111 | 0000 1111=1111 1111
// 1111 1111转换为十进制的结果是255
System.out.println(left|right);
}
}
按位亦或的应用场景
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
* 按位异或运算
* 如果^两边都是0或者都是1,结果为0,否则为1
* 0^0=1
* 0^1=0
* 1^0=1
* 1^1=1
*
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 6:50 下午
* @since JDK11
*/
public class BitXorTest {
public static void main(String[] args) {
int left = 10;
int right = 20;
System.out.printf("swap before left = %d right =%d\n", left, right);
//异或运算实现变量交换
left = left ^ right;
right = left ^ right;
left = left ^ right;
System.out.printf("swap after left = %d right =%d\n", left, right);
}
}
按位取反的二进制计算流程
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
* 按位取反运算
* ~1=0
* ~0=1
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 6:55 下午
* @since JDK11
*/
public class BitNotTest {
public static void main(String[] args) {
byte b=1;
byte notB=(byte)~b;
// 1的二进制表示方式为0000 0001
// ~1 取反
// 补码 1111 1110
// 反码 1111 1101
// 原码 1000 0010
// 转换为十进制的结果为-2
System.out.println("notB = "+notB);
}
}
位运算符的综合案例和说明
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
* 位运算符
* 基于二进制的补码运算,运算效率是最高的,但是可读性不好
* Java底层源码大量使用位运算符
* 左移<<:
* 运算规则:左移<<几位就是乘以2的几次方,二进制补码左移n位,右边补0
* 右移>>: 右移>>几位就是除以2的几次方,二进制补码右移n位,左边补0还是1取决于最高位。
*
* 无符号右移>>>:二进制补码右移n位,左边补0,对于负数来说,移完后变成正数
* 按位与 &:
* 1&1=1
* 1&0=0
* 0&1=0
* 0&0=0
*
*
* 按位或 |:
* 1|1=1
* 1|0=0
* 0|1=1
* 0|0=0
* 按位异或^
* 1^1=0
* 1^0=1
* 0^1=1
* 0^0=0
* 按位取反~: 一元运算符
* ~0=1
* ~1=0
*
* 按位与、按位或和按位异或操作的整数,而逻辑与、逻辑或、逻辑亦或操作的是布尔值
* @author liuguanglei aaa@qq.com
* @version 2020/11/9 4:29 下午
* @since JDK11
*/
public class BitTest {
public static void main(String[] args) {
// 4左移3位 左移3位就是乘以2的三次方 4*8=32
// 4的二进制0000 0100,左移3位就是0001 00000,换算成十进制就是32
System.out.println("4 << 3 = " + (4 << 3));
//32右移4位,右移4位就是除以2的四次方,32/16=2
//32的二进制是0010 00000
//0010 0000 右移4位就是0000 0010 换算成十进制就是2
System.out.println("32 >> 4 = " + (32 >> 4));
/*
-32的二进制
原码 1010 0000
反码 1101 1111
补码 1110 0000
1110 0000 右移4位 的结果是1110
由于-32是负数 因此左边补1的结果是11111110,此时是补码
反码等于补码-1 ,1111 1110 转换成反码就是1111 1101
原码就是反码取反,最左边的符号位不变,1111 1101换算成原码为 1000 0010
1000 0010转换为十进制的结果是-2
因此-32 >> 4 = -2
*/
System.out.println("-32 >> 4 = " + (-32 >> 4));
//等价于32<<4
System.out.println("32 >>> 4 = "+(32>>>4));
/*
-32的二进制(4个字节)
原码 1000 0000 0000 0000 0000 0000 0010 0000
反码 1111 1111 1111 1111 1111 1111 1101 1111
补码 1111 1111 1111 1111 1111 1111 1110 0000
1111 1111 1111 1111 1111 1111 1110 0000 无符号右移4位等于0000 1111 1111 1111 1111 1111 1111 1110
0000 1111 1111 1111 1111 1111 1111 1110转换为十进制的结果是 268435454
*/
System.out.println("-32 >>> 4 = "+(-32 >>>4));
//按位与
/*
32 二进制表示 10 0000
25 二进制表示 01 1001
32&25=10 0000 & 01 1001=0
*/
System.out.println("32 & 25 = "+(32&25));
//按位或
/*
32 二进制表示 10 0000
25 二进制表示 01 1001
32|25=10 0000 01 1001=11 1001
11 1001 转换为十进制的结果是57
*/
System.out.println("32 | 25 = "+(32|25));
//按位异或
/*
32 二进制表示 10 0000
25 二进制表示 01 1001
32^25=10 0000 01 1001=11 1001
11 1001 转换为十进制的结果是57
*/
System.out.println("32 ^ 25 = "+(32^25));
//按位取反
/*
3补码 0000 0000 0000 0000 0000 0000 0000 0011
~3补码: 1111 1111 1111 1111 1111 1111 1111 1100
~3反码 1111 1111 1111 1111 1111 1111 1111 1011
~3原码 1000 0000 0000 0000 0000 0000 0000 0100
1000 0000 0000 0000 0000 0000 0000 0100 转换为十进制的结果是-4
*/
System.out.println(~3);
}
}