Java架构师成长之道之Java数据计算
3.1 Java数据计算概述
计算机最主要的任务就是完成生产生活中的各种数据的运算,在Java中提供了诸多的运算符来完成相关数据的运算,在日常程序开发中最常用的有算术运算符、自增运算符、关系运算符、逻辑运算符以及三目运算符,在JDK源码中会使用到基于二进制的位运算符。
在了解具体运算符的使用之前,先了解关于数据计算的几个概念
package net.ittimeline.java.core.operator;
import static java.lang.System.out;
/**
* 数据计算的相关概念
* 运算符:数据执行的运算类型,例如算术、逻辑、关系、位运算
* 操作数:参与运算的数据,例如2,(3+2)都是操作数
* 表达式:由操作数和运算符组成,通常是完成某种业务数据计算而设计的,例如计算圆的面积
* 优先级:在各种运算符参与运算时,有不同的执行顺序,由它们的优先级决定,可以使用()改变优先级
* 结合性:执行运算的方向,例如从左到右或者从右到左
* @author liuguanglei [email protected]
* @create 2019-07-30 11:51
* @website www.ittimeline.net
* @since JDK11.03
*/
public class OperatorConceptTest {
public static void main(String[] args) {
// 3+5就是表达式,3,5就是操作数
int result=3+5;
System.out.println("result ="+result);
/**
* 算术运算的优先级是先乘除后加减
* 可以使用()改变优先级
*/
int precedence=(45+5)/5*10;
out.println("precedence = "+precedence);
}
}
-
运算符:指定数据执行的运算类型,例如算术,逻辑,关系,比较,位运算等等
-
操作数:就是参与运算的数据,例如2,(3+2)都是一个操作数
-
表达式:由操作数和运算符组成,通常都是完成某种业务数据计算而设计的,例如计算圆的面积等等。
-
优先级:在各种运算符参与运算时,有不同的执行顺序,由它们的优先级决定,可以使用()来改变执行的顺序
-
结合性:执行计算的方向,一般是从左到右,但是赋值运算是从右到左。
3.2 算术运算符
算术运算就是数学意义上的加减乘除以及取模运算,Java中支持的算术运算符包含加法(+)、减法(-)、乘法(*)、除法(/)、和取模(/)
- 算术运算符的优先级是先乘除,后加减。
- 算术运算符的结合性是从左到右
- 算术运算计算结果的类型是参与运算的最大数据类型,例如整数和浮点数运算的结果是浮点型
- 赋值(=)时会进行自动类型转换
算术运算案例
package net.ittimeline.java.core.operator.arithmetic;
/**
* 算术运算符
* Java中的算术运算符支持数学意义的+ - * /以及取模(%)运算(求余数)
*
* @author liuguanglei [email protected]
* @create 2019-07-28 09:40
* @website www.ittimeline.net
* @since JDK11.03
*/
public class ArithmeticDivideTest {
/**
* 除法运算
* @param args
*/
public static void main(String[] args) {
//算术计算的结果的数据类型是参与运算的最大数据类型
int source=12;
int target=5;
//int和int运算的结果是int
int result=source/target;
System.out.println("result = "+result);
//算术运算符结合性是从左向右运算
result=source/target*target;
//result=10
System.out.println("result = "+result);
//赋值时会执行自动类型转换
double dblResult=source/target;
//运算结果是2.0
System.out.println("dblResult ="+dblResult);
//double和int运算结果是double
//通过强制类型转换将int转换为double
dblResult=(double) source/target;
//获取精确的运行结果
System.out.println("dblResult ="+dblResult);
}
}
取模运算是求余数,开发中通常用于判断数据是否能够整除。取模也会作为数据库中间件MyCAT,ShardingJDBC实现取模算法。
- 取模运算结果的符号类型(正负)和被模数一致
- 整数和浮点数可以进行取模运算
package net.ittimeline.java.core.operator.arithmetic;
/**
* 取模(求余数)运算
* 开发中经常用取模运算符(%)来判断数据是否能够整除的情况
*
* @author liuguanglei [email protected]
* @create 2019-07-28 09:49
* @website www.ittimeline.net
* @since JDK11.03
*/
public class ArithmeticRemainderTest {
/**
* 求余数
*
* @param args
*/
public static void main(String[] args) {
int source = 12;
int target = 5;
int result = source % target;
//12/5=2..2 即余数的结果就是2
System.out.println("result = " + result);
//运算结果表明求余数结果的符号和被余数相同
source = -12;
result = source % target;
System.out.println("result = " + result);
source = 12;
target = -5;
result = source % target;
System.out.println("result =" + result);
double dblSource = 12.5;
double dblTarget = 5.0;
double dblResult = dblSource % dblTarget;
//浮点数也可以取模
System.out.println("dblResult = " + dblResult);
}
}
取模运算应用案例:将五位整数反转
package net.ittimeline.java.core.operator.arithmetic;
/**
* 取模运算案例
* 将一个五位整数反转
* 例如12345变成54321
* @author liuguanglei [email protected]
* @create 2019-08-03 09:39
* @website www.ittimeline.net
* @since JDK11.03
*/
public class ArithmeticRemainderApp {
public static void main(String[] args) {
int number=12345;
System.out.println("反转之前的五位整数是"+number);
//首先使用取模运算获取各个位的数字
//个位
int theUnit=number/10000;
//十位
int decade=number/1000%10;
//百位
int hundreds=number/100%10;
//千位
int kilobit=number%100/10;
//万位
int myriabit=number%100%10;
int invert=myriabit*10000+kilobit*1000+hundreds*100+decade*10+theUnit;
System.out.println("反转之后的五位整数是"+invert);
}
}
使用基本数据类型的包装类、字符串数组以及算术运算根据距离和时间计算速度
package net.ittimeline.java.core.operator.arithmetic;
/**
* @author liuguanglei [email protected]
* @create 2019-07-30 15:44
* @website www.ittimeline.net
* @since JDK11.03
*/
public class Velocity {
public static void main(String[] args) {
//实例化数组
args=new String[]{"1200","300"};
//数组中需要两个元素
if(args.length<2){
System.err.println("需要俩参数");
//系统异常退出
System.exit(1);
}
//使用包装类将字符串转换为float
float distance=Float.parseFloat(args[0]);
//args[0]表示访问数组的第一个元素
//args[1]表示访问数组的第二个元素
float time=Float.parseFloat(args[1]);
//float和float运算的类型是float
System.out.print("Velocity = ");
System.out.print(distance/time);
}
}
3.3 自增(自减)运算符
自增运算符主要用于变量自增1,用于变量自增1的运算符是++,但是++可以放在变量前面,也可以放在变量后面,放在前面时,变量先自增1,然后再参与运算,++放在变量的后面,变量先运算,再自增1。
自减运算符使用--表示,用于变量自减1,也可以放在变量的前面和后面,分别表示先减1,再参与运算和先参与运算,再减1。
- 自增(自减)运算符不会改变变量本身的数据类型,因此在运算时需要考虑当前数据类型的极限值
- 自增(自减)运算符如果是一条单独的语句,前置++(--)和后置++(--)的效果是一样的
自增运算符案例
package net.ittimeline.java.core.operator.autoincrement;
/**
* 前置++,后置++
* 作用是针对整型变量加1
* 自增运算不会改变变量本身的数据类型
* @author liuguanglei [email protected]
* @create 2019-07-28 10:32
* @website www.ittimeline.net
* @since JDK11.03
*/
public class AutoincrementAddTest {
public static void main(String[] args) {
//自增运算符是单独的语句,前置++和后置++的结果是一样的
int tmp=10;
tmp++;
System.out.println("tmp = "+tmp);
int val=10;
++val;
System.out.println("val = "+val);
int num=10;
//后置++,先运算,后自增1
int result=num++;
//result=10
System.out.println("result = "+result);
//num=11
System.out.println("num ="+num);
int source=10;
//前置++ 先自增1,后运算
result=++source;
//result=11
System.out.println("result ="+result);
//source=11
System.out.println("source ="+source);
short shortVal=10;
//shortVal的类型还是short
System.out.println(shortVal++);
byte byteVal=10;
//byteVal的数据类型还是byte
System.out.println(byteVal++);
}
}
自减运算符应用案例
package net.ittimeline.java.core.operator.autoincrement;
/**
* 自减运算符
* @author liuguanglei [email protected]
* @create 2019-07-28 10:42
* @website www.ittimeline.net
* @since JDK11.03
*/
public class AutoincrementSubTest {
/**
* 自减运算符
* @param args
*/
public static void main(String[] args) {
int num=10;
//先运算,后减1
int result=num--;
System.out.println("result = "+result);
System.out.println("numb = "+num);
int source=10;
//先减1再运算
int target= --source;
System.out.println("target = "+target);
System.out.println("source = "+source);
}
}
自增(自减)运算符的常用应用场景就是在for循环中改变循环条件的值。
package net.ittimeline.java.core.operator.autoincrement;
/**
* 自增和自减运算符的应用场景
*
* @author liuguanglei [email protected]
* @create 2019-08-03 09:23
* @website www.ittimeline.net
* @since JDK11.03
*/
public class AutoincrementApp {
public static void main(String[] args) {
System.out.println("自增运算符的应用场景");
//自增运算符的应用场景
//定义一个字符串
String content="Java架构师成长之道";
//将字符串转换为字符数组
char[] contents=content.toCharArray();
//循环遍历字符数组 这里的c++表示将循环的初始条件自增1
for (char c =0;c<content.length();c++){
System.out.print(contents[c]+" ");
}
//自减运算的应用场景
System.out.println("自减运算的应用场景");
String car="法拉利拉法";
char[] carDesc=car.toCharArray();
for(int i=carDesc.length-1;i>=0;i--){
System.out.print(carDesc[i]+"");
}
}
}
自增自减运算符复杂案例
package net.ittimeline.java.core.operator.autoincrement;
/**
* 自增运算符复杂案例
*
* @author liuguanglei [email protected]
* @create 2019-08-03 09:40
* @website www.ittimeline.net
* @since JDK11.03
*/
public class AutoIncrementComplexTest {
public static void main(String[] args) {
int i = 10;
int j = 20;
// i++ 等价于i+=1 等价于i=
int k = i++;
//k=10
System.out.println("k = " + k);
//i =11
System.out.println("i = " + i);
k = ++i;
//k = 12
System.out.println("k = " + k);
//i =12
System.out.println("i = " + i);
k = j--;
//k=20
System.out.println("k =" + k);
//j=19
System.out.println("j = " + j);
k=--j;
//k=18
System.out.println("k = " + k);
//j=18
System.out.println("j = " + j);
}
}
自增运算符的本质:让变量自增的三种方式
package net.ittimeline.java.core.operator.autoincrement;
/**
* 自增运算的本质
*
* @author liuguanglei [email protected]
* @create 2019-08-03 10:33
* @website www.ittimeline.net
* @since JDK11.03
*/
public class AutoincrementEssence {
public static void main(String[] args) {
int number = 12;
number++;
System.out.println("number = " + number);
//等价于
number = 12;
number += 1;
System.out.println("number = " + number);
//等价于
number=12;
number=number+1;
System.out.println("number = " + number);
}
}
3.4 赋值运算符
赋值通常是给变量赋值,Java中使用"="来表示赋值,而"=="表示相等,赋值是将=右边的值或者表达式赋值给左边的变量。
- 当赋值号(=)两边的数据类型不一致时,可以使用自动类型转换或者强制类型转换进行处理
- 支持连续赋值,即同时声明并赋值多个变量
- 赋值运算不会改变变量本身的数据类型
package net.ittimeline.java.core.operator.assignment;
/**
* 赋值运算符
* =
* 算术运算和赋值运算
* += -= *= /= %=
*
* 赋值运算不会改变变量的数据类型
*
* @author liuguanglei [email protected]
* @create 2019-07-28 10:59
* @website www.ittimeline.net
* @since JDK11.03
*/
public class AssignmentTest {
public static void main(String[] args) {
//声明变量并赋值
int i=10;
int j=10;
//连续赋值,同时声明两个变量k,l并赋值为10
int k=10,l=10;
System.out.println("i = "+i+ " j = "+j +" k = "+k+ " l = "+l);
//赋值的左边必须是变量,右边可以是变量值,也可以是表达式
//计算四个整数的和
int result=i+j+k+l;
System.out.println("result = "+result);
int o,p,q;
//同时赋值
o=p=q=20;
System.out.println("o = "+o+ " p = "+p +" q = "+q);
//赋值不会改变变量的数据类型
//通常情况下byte和int运算的类型是int
byte byteVal=12;
byteVal=(byte)(byteVal+1);
System.out.println("byteVal = "+byteVal);
//但是如果使用赋值运算,不会改变变量的数据类型
byteVal=12;
byteVal+=1;
System.out.println("byteVal = "+byteVal);
}
}
JDK提供了java.util.Random类用于生成随机数,详细的使用说明可以查阅JDK API文档,后续在编写应用案例时,可以使用它来生成测试数据。
package net.ittimeline.java.core.operator.assignment;
import java.util.Random;
/**
*产生0-99之间的随机数
* @author liuguanglei [email protected]
* @create 2019-07-28 10:59
* @website www.ittimeline.net
* @since JDK11.03
*/
public class RandomTest {
public static void main(String[] args) {
/**
* 创建一个随机数
* 88表示初始种子,种子相同,每次产生的序列相同,种子不同,每次产生的序列不同
*
*/
Random random=new Random(88);
//生成0-99之间的整数
int left=random.nextInt(100);
int right=random.nextInt(100);
System.out.println("left = "+left+" right ="+right);
}
}
赋值运算符还可以可算术运算符结合使用,例如+= ,-=,*=,/=,%=。
package net.ittimeline.java.core.operator.assignment;
import static java.lang.System.out;
import java.util.Random;
/**
* 算术运算与赋值运算结合运算
*
* @author liuguanglei [email protected]
* @create 2019-07-29 14:02
* @website www.ittimeline.net
* @since JDK11.03
*/
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;
k=random.nextInt(100)+1;
out.println("j = "+j);
out.println("k = "+k);
i=j+k;
out.println("j + k = "+i);
i=j-k;
out.println("j - k = "+i);
i=j*k;
out.println("j * k = "+i);
i=j/k;
out.println("j / k = "+i);
i=j%k;
out.println("j % k = "+i);
j%=k;
out.println("j %= k "+j);
float u,v,w;
v=random.nextFloat();
w=random.nextFloat();
out.println("v = "+v);
out.println("w = "+w);
u=v+w;
out.println("v + w = "+u);
u=v-w;
out.println("v - w = "+u);
u=v*w;
out.println("v * w = "+u);
u=v/w;
out.println("v / w = "+u);
u+=v;
out.println("u+v = "+u);
u-=v;
out.println("u-v = "+u);
u*=v;
out.println("u*v = "+u);
u/=v;
out.println("u/v = "+u);
}
}
复杂的赋值运算符
package net.ittimeline.java.core.operator.assignment;
/**
* 赋值运算符的复杂案例
* 开发中尽量将表达式写的简单明了,有利于程序的维护
* @author liuguanglei [email protected]
* @create 2019-08-03 11:24
* @website www.ittimeline.net
* @since JDK11.03
*/
public class AssignmentComplex {
public static void main(String[] args) {
int m=2;
int n=3;
/**
* 拆解表达式
* n*=m++
* n=n*m++
* n=3*2
* n=6
* m=3
*/
n*=m++;
System.out.println("n = "+n);
System.out.println("m = "+m);
n=4;
/**
* 拆解表达式
* n+=(n++)+(++n);
* n=n+(n++)+(++n)
* n=4+4+6
* n=14
*/
n+=(n++)+(++n);
System.out.println("n = "+n);
}
}
3.5 关系运算符
关系运算符用于判断数据的关系,Java中常用的关系运算符有大于(>),小于(<),等于(==),不等于(!=),大于等于(>=),小于等于(<-=)。
- 关系运算符的运算结果是boolean类型,也就是true或者false
- 相等性用==表示,而不是=
-
=表示大于或者等于,>=表示小于或则等于
==
和=
的差别
package net.ittimeline.java.core.operator.relation;
/**
* ==和=的差别
* @author liuguanglei [email protected]
* @create 2019-08-03 11:36
* @website www.ittimeline.net
* @since JDK11.03
*/
public class Equals {
public static void main(String[] args) {
int i=10;
int j=20;
//== 判断相等性,运算结果是false
System.out.println("i == j = "+(i==j));
//=表示将右边的值或者表达式赋值给左边的变量
System.out.println("i = j = "+(i=j));
}
}
在使用关系运算符之前先对之前的输出语句System.out.println()
结合JDK5.0新特性之静态导入
做一个更加"简短"的输出。
- 静态导入是使用import static关键字加上类名[.变量名][.方法名],其中[.变量名]和[.方法名]是可选的,如果没有,默认就是导入类的所有变量和方法到当前类中,这样就可以直接在当前类中使用。
package net.ittimeline.java.core.jdk.feature.jdk5;
/**
* import static关键字是直接导入某个类的所有变量和方法到本类中
* 这样就可以直接在当前类中引用导入导入类的变量和方法
*
* import static java.lang.System.*表示导入System的所有变量和方法
*/
import static java.lang.System.*;
/**
* JDK5新特性之静态导入
* @author liuguanglei [email protected]
* @create 2019-08-03 11:38
* @website www.ittimeline.net
* @since JDK11.03
*/
public class StaticImport {
public static void main(String[] args) {
//因为静态导入过System类,这样可以在当前类中调用System的成员变量out和err的println方法
out.println("Hello World");
err.println("Hello World Again");
}
}
使用静态导入和SimpleDateFormat实现输出当前日期
package net.ittimeline.java.core.operator;
import java.text.SimpleDateFormat;
import java.util.Date;
import static java.lang.System.out;
/**
* 静态导入
*
* @author liuguanglei [email protected]
* @create 2019-07-29 13:48
* @website www.ittimeline.net
* @since JDK11.03
*/
public class HelloDate {
/**
* 声明日期格式
*/
public static final String PATTERN="yyyy-MM-dd HH:mm:ss";
public static void main(String[] args) {
//创建日期格式化对象
SimpleDateFormat simpleDateFormat=new SimpleDateFormat(PATTERN);
String now =simpleDateFormat.format(new Date());
//显示输出当前日期
out.print(now);
}
}
然后借助JDK的Random类生成两个1-100以内的随机整数,用关系运算符运算,并输出运算结果
package net.ittimeline.java.core.operator.relation;
import java.util.Random;
import static java.lang.System.out;
/**
* 关系运算符
*
* @author liuguanglei [email protected]
* @create 2019-07-29 13:19
* @website www.ittimeline.net
* @since JDK11.03
*/
public class Bool {
public static void main(String[] args) {
Random random = new Random(88);
//产生两个1-100之内的随机整数
int i = random.nextInt(100)+1;
int j = random.nextInt(100)+1;
//输出 i和 j的值
out.println("i = " + i);
out.println("j = " + j);
//使用关系运算符比较i和j的值
out.println("i > j is " + (i > j));
out.println("i < j is " + (i < j));
out.println("i >= j is " + (i >= j));
out.println("i <= j is " + (i <= j));
out.println("i == j is " + (i == j));
out.println("i != j is " + (i != j));
}
}
3.6 ==
和equals()的区别
==
和equals都是比较的值是否相等,通常基本数据类型使用判断,引用数据类型使用equals判断,而如果使用判断引用数据类型,比较的是对象的引用地址。
Java中所有的类(无论是JDK自带的还是开发人员自己定义的)的直接或者间接父类都是java.lang.Object,该类有个成员方法equals,用于比较对象的相等性。
从Object的equals方法看的出来,默认比较的是对象的引用地址
public boolean equals(Object obj) {
return (this == obj);
}
那么问题来了
EqualsMethod2类中,明明Value的成员变量i的值是100,理论上来说应该是相等的。
但是使用equals判断的时候是不相等,因为只要使用关键字new创建对象时,会开辟新的堆内存空间存储对象。
package net.ittimeline.java.core.operator.relation;
/**
* 相等性判断
*
* @author liuguanglei [email protected]
* @create 2019-07-29 13:40
* @website www.ittimeline.net
* @since JDK11.03
*/
public class EqualsMethod2 {
public static void main(String[] args) {
Value v1=new Value();
Value v2=new Value();
v1.i=v2.i=100;
//没有重写equals时,默认比较的是对象的地址
//这里创建了两个Value对象,因此equals()判断为false
System.out.println("v1.equals(v2) = "+(v1.equals(v2)));
}
}
class Value{
int i;
}
因为Object类的equals方法比较的是对象的地址是否相等,但是在实际开发中,经常比较的是对象的成员变量是否相等,因此绝大多数类都重写了equals方法。
这里以Integer类为例子,分别使用==和equals来判断Integer对象的相等性。
当创建Integer对象的值在-128-127之间时,无论是equals还是==都是相等的。
因为Integer类中有个内部类IntegerCache,用于缓存Integer的值在-128-127之间的对象
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
而如果Integer的值超过了缓存的范围,那么使用判断对象相等时就不会相等了。
在开发过程中尽量使用equals方法来判断,而不是使用来判断。
package net.ittimeline.java.core.operator.relation;
/**
* 相等性判断
*
* @author liuguanglei [email protected]
* @create 2019-07-29 13:34
* @website www.ittimeline.net
* @since JDK11.03
*/
public class EqualsMethod {
public static void main(String[] args) {
//因为Integer类缓存-128-127之间的实例
Integer n1= Integer.valueOf(Byte.MAX_VALUE);
Integer n2= Integer.valueOf(Byte.MAX_VALUE);
//因此无论是equals还是==都是相等
System.out.println("n1.equals(n2) = "+(n1.equals(n2)));
System.out.println("n1==n2 = "+(n1==n2));
//但是超过了缓存的范围
Integer n3=Integer.valueOf(300);
Integer n4=Integer.valueOf(300);
System.out.println("n3.equals(n4) = "+(n3.equals(n4)));
//==就为false了,因为对象的地址不同
System.out.println("n3==n4 = "+(n3==n4));
}
}
3.7 逻辑运算符
逻辑运算符用于布尔类型变量或者布尔表达式的逻辑运算,Java支持的逻辑运算符有如下几种:
- 逻辑与(&):&两边同时为true,结果为true
- 短路与(&&):当&&两边为true,结果为true,如果&&的一边为false,将不再执行剩余的表达式,也就是短路特性。
- 逻辑或(|):当|两边只要有一个为true,结果为true,否则结果为false
- 短路或(||):当||两边只要有一个为true,结果为true,否则结果为false,并且不再执行剩余表达式,也就是短路特性。
- 逻辑非(!):当一个变量或者表达式的结果为true,!的结果为false,否则结果为true,逻辑非就是相反的结果
- 逻辑亦或():当两边结果同时为true或者false,结果为false,否则为true。
一般开发中使用短路与、短路或,不会使用逻辑与和逻辑或。
package net.ittimeline.java.core.operator.logic;
import java.util.Random;
/**
* 逻辑运算
* @author liuguanglei [email protected]
* @create 2019-08-03 13:02
* @website www.ittimeline.net
* @since JDK11.03
*/
public class LogicTest {
public static void main(String[] args) {
Random random=new Random(88);
//创建两个随1-100之内的随机数
int left=random.nextInt(100)+1;
int right=random.nextInt(100+1);
System.out.println("left ="+left);
System.out.println("right ="+right);
//判断两个数都是大于100
boolean result=left>100&&right>100;
System.out.println("left>100&&right>100 = "+result);
//判断左边的数加上100大于100或者右边的数大于100
result=left+100>100||right>100;
System.out.println("left+100>100||right>100 = "+result);
//逻辑非
result=!result;
System.out.println("!result = "+result);
//逻辑亦或
//^左右两边都为true或者false,结果为false
result =left>10^right>10;
System.out.println("left>10^right>10 = "+result);
result=left>50^right>50;
//^左右两边结果不同结果为true
System.out.println("left>50^right>50 = "+result);
}
}
逻辑与和短路与的区别
package net.ittimeline.java.core.operator.logic;
/**
* 逻辑与与短路与
* 逻辑与&左右两边都是true,结果为true,不具备短路特性
* 短路与&&左右两边都是true,结果为true,具备短路特性:明确整体的计算结果,不在计算剩余的表达式
* @author liuguanglei [email protected]
* @create 2019-08-03 13:02
* @website www.ittimeline.net
* @since JDK11.03
*/
public class LogicAndShowCircuitTest {
public static void main(String[] args) {
boolean flag=false;
int num=10;
//逻辑与不具备短路特性,虽然&左边已经是false
boolean logicAnd=flag&num++>0;
//但是从输出结果看出num依然自增1
System.out.println("num = "+num);
//而 短路与&&具备短路特性
num=10;
// &&左边已经为false,右边的num没有自增1
boolean logicAndCircuit=flag&&num++>0;
//输出结果依然为10
System.out.println("num = "+num);
}
}
逻辑与的短路特性
package net.ittimeline.java.core.operator.logic;
import static java.lang.System.out;
/**
* 逻辑运算的短路现象
*
* @author liuguanglei [email protected]
* @create 2019-07-29 19:41
* @website www.ittimeline.net
* @since JDK11.03
*/
public class ShowCircuit {
static boolean test1(int val){
out.println("test1("+val+")");
out.println("result: "+(val<1));
return val<1;
}
static boolean test2(int val){
out.println("test1("+val+")");
out.println("result: "+(val<2));
return val<2;
}
static boolean test3(int val){
out.println("test1("+val+")");
out.println("result: "+(val<3));
return val<3;
}
public static void main(String[] args) {
/**
* 逻辑与的短路特性
* 当执行到test1方法时,2<2的结果为false,整体表达式的结果为false,因此不会再执行test3方法
*/
boolean flag=test1(0)&&test1(2)&&test3(2);
out.println("expression is "+flag);
}
}
逻辑或的短路特性
package net.ittimeline.java.core.operator.logic;
import static java.lang.System.out;
/**
* 逻辑运算的短路现象
*
* @author liuguanglei [email protected]
* @create 2019-07-29 19:41
* @website www.ittimeline.net
* @since JDK11.03
*/
public class LogicOrCircuit {
static boolean test1(int val){
out.println("test1("+val+")");
out.println("result: "+(val<1));
return val<1;
}
static boolean test2(int val){
out.println("test1("+val+")");
out.println("result: "+(val<2));
return val<2;
}
static boolean test3(int val){
out.println("test1("+val+")");
out.println("result: "+(val<3));
return val<3;
}
public static void main(String[] args) {
/**
* 逻辑或的短路特性
* 当执行到test1方法时,0<2的结果为true,整体表达式的结果为false,因此不会再执行tes2和test3方法
*/
boolean flag=test1(0)||test2(2)||test3(2);
out.println("expression is "+flag);
}
}
逻辑与、短路与和逻辑或、短路或的应用案例
package net.ittimeline.java.core.operator.logic;
/**
* 逻辑运算符的复杂案例
* 结合自增运算、if(true){}
* @author liuguanglei [email protected]
* @create 2019-08-03 15:55
* @website www.ittimeline.net
* @since JDK11.03
*/
public class LogicComplex {
public static void main(String[] args) {
int x = 1;
int y = 1;
//x++=1 y++=1
if (x++ == 2 & y++ == 2) {
x = 7;
}
//x=2 y=2
System.out.println("x = " + x + " y = " + y);
x = 1;
y = 1;
// ++x=2 ++y =2 if true
if (++x == 2 && ++y == 2) {
x = 7;
}
//x=7 y=2
System.out.println("x = " + x + " y = " + y);
x = 1;
y = 1;
//x++ =1 ++y=2 if true
if (x++ == 1 | ++y == 1) {
x = 7;
}
//x=7 y=2
System.out.println("x = " + x + " y = " + y);
x = 1;
y = 1;
// x++=1
// x=7 y=1
if (x++ == 1 || ++y == 1) {
x = 7;
}
//x=7 y=1
System.out.println("x = " + x + " y = " + y);
}
}
3.8 三元运算符
三元运算符用于布尔变量或者布尔表达式判断,需要三个操作数,等价于if/else,其表现形式为
bool-exp?value0:value1,如果布尔表达式的结果为true,三目运算的结果为value0,否则为value1。
使用三目运算符模拟扔硬币
package net.ittimeline.java.core.operator.ternary;
import java.util.Random;
/**
* 使用三元运算符模拟扔硬币
* @author liuguanglei [email protected]
* @create 2019-07-30 16:07
* @website www.ittimeline.net
* @since JDK11.03
*/
public class CoinFlipping {
public static void main(String[] args) {
Random random=new Random(88);
boolean flip=random.nextBoolean();
System.out.print("OUTCOME :");
System.out.println(flip?"人头":"字");
}
}
三目运算符和if/else
package net.ittimeline.java.core.operator.ternary;
import static java.lang.System.out;
/**
* 三目运算符和if/else
*
* @author liuguanglei [email protected]
* @create 2019-07-30 10:21
* @website www.ittimeline.net
* @since JDK11.03
*/
public class TernaryIfElese {
/**
* 三目运算符
* @param i
* @return
*/
static int ternary(int i){
return i<10?i*100:i*10;
}
/**
* 标准的if/else
* @param i
* @return
*/
static int standardIfElse(int i){
if(i<10){
return i*100;
}
else{
return i*10;
}
}
public static void main(String[] args) {
out.println(ternary(9));
out.println(ternary(11));
out.println(standardIfElse(9));
out.println(standardIfElse(11));
}
}
3.9 位运算符
位运算符是直接对整数的二进制进行运算,在JDK的原码中大量使用了位运算,
以下是截取Integer类的numberofLeadingZeros方法
@HotSpotIntrinsicCandidate
public static int numberOfLeadingZeros(int i) {
// HD, Count leading 0's
if (i <= 0)
return i == 0 ? 32 : 0;
int n = 31;
if (i >= 1 << 16) { n -= 16; i >>>= 16; }
if (i >= 1 << 8) { n -= 8; i >>>= 8; }
if (i >= 1 << 4) { n -= 4; i >>>= 4; }
if (i >= 1 << 2) { n -= 2; i >>>= 2; }
return n - (i >>> 1);
}
位运算符操作的都是整数类型,因为是基于二进制运算,其运行效率高。
在日常开发中几乎不会使用到位运算,但是后期会阅读大量JDK源码,了解底层实现机制,因此必须掌握位运算符的基本使用。
Java中支持的位运算符有如下几种:
-
左移(<<): 左移N位相当于乘以2的n次方,空位补0,被移除的高位丢弃,空缺位补0
-
右移(>>):右移N位相当于除以2的n次方,被移位的二进制最高位是0,右移后,空缺位补0,最高位是1,最高位补1
-
无符号右移(>>>):不管最高位的符号位,右移N位相当于除以2的N次方,被移位的二进制最高位无论是0还是1,空缺位都补0
-
按位与(&):只有&两边都是1,结果是1,否则就是0
-
按位或(|):只要|两边都是0,结果是0,否则就是1
-
按位亦或(^):相同的二进制位进行亦或运算,结果是0,不相同的二进制位运算结果是1
-
取反运算(~):无论正负数取反运算,各二进制位按照补码取反
-
&,|,在操作布尔类型的时候表示为逻辑与、逻辑或、逻辑亦或,&、|、在操作整数的时候表示为按位与与按位或、按位亦或。
左移运算
package net.ittimeline.java.core.operator.bit;
/**
* 左移运算
*
* @author liuguanglei [email protected]
* @create 2019-08-03 16:26
* @website www.ittimeline.net
* @since JDK11.03
*/
public class BitLeftMoveTest {
public static void main(String[] args) {
//8的二进制表示为
// 0000 0000 0000 0000 0000 0000 0000 1000
int number=8;
//因为8是正数,左移动2位,右边补上0,相当于乘以2的两次方 也就是乘以4
// 0000 0000 0000 0000 0000 0000 0010 0000
number=number<<2;
System.out.println("8<<2 ="+number);
/**
* 在进行移位运算时需要考虑数据越界的问题
*/
int value=21;
// 0000 0000 0000 0000 0000 0000 0101 0100
//左边移动26位
//101 0100 0000 0000 0000 0000 0000 0000 0
//最高位1 表示结果为负数
System.out.println("21<<26 = "+(value<<26));
}
}
右移运算
package net.ittimeline.java.core.operator.bit;
/**
* 右移运算
*
* @author liuguanglei [email protected]
* @create 2019-08-03 16:45
* @website www.ittimeline.net
* @since JDK11.03
*/
public class BitRightMoveTest {
public static void main(String[] args) {
//0000 0000 0000 0000 0000 0000 0001 0000
int number=16;
//右移N位,相当于除以2的N次方 16/4 结果是4
//00 0000 0000 0000 0000 0000 0000 0001 00
number=number>>2;
System.out.println("16 >> 2 = "+(number));
number=-16;
// -16/4 结果是-4
number=number>>2;
System.out.println("-16 >> 2 ="+(number));
}
}
按位与、按位或、按位亦或、按位非运算
package net.ittimeline.java.core.operator.bit;
/**
* 按位运算符
* & 两边都是1,结果为1
* |
* ^
* @author liuguanglei [email protected]
* @create 2019-08-03 17:22
* @website www.ittimeline.net
* @since JDK11.03
*/
public class BitAndOrXor {
public static void main(String[] args) {
/**
* 5的二进制表示方式为 0000 0000 0000 0000 0000 0000 0000 0101
* 9的二进制方式表示为 0000 0000 0000 0000 0000 0000 0000 1001
*
* 0101&1001 =0001 因此 5&9的结果是1
* 0101|1001 = 1101 因此 5|9的结果是13
* 0101^1001 = 1100 因此 5^9的结果是12
* 9 ->0000 0000 0000 0000 0000 0000 0000 1001
* ~9 1111 1111 1111 1111 1111 1111 1111 0110 原码
* 1000 0000 0000 0000 0000 0000 0000 1001 反码
* 1000 0000 0000 0000 0000 0000 0000 1010 补码
* ~9 最终的结果是-10
*
*
*/
System.out.println("5&9 = "+(5&9));
System.out.println("5|9 = "+(5|9));
System.out.println("5^9 = "+(5^9));
System.out.println("~9 = "+(~9));
System.out.println(Integer.toBinaryString(-10));
}
}
有符号左移
package net.ittimeline.java.core.operator.bit;
import static java.lang.System.out;
/**
* 有符号右移
*
* @author liuguanglei [email protected]
* @create 2019-07-30 16:23
* @website www.ittimeline.net
* @since JDK11.03
*/
public class SignedRightShift {
public static void main(String[] args) {
int i = 0x80000000;
out.println(Integer.toBinaryString(i));
//等价于i=i>>1
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
}
}
无符号右移
package net.ittimeline.java.core.operator.bit;
import static java.lang.System.out;
/**
* 无符号右移
*
* @author liuguanglei [email protected]
* @create 2019-07-30 16:33
* @website www.ittimeline.net
* @since JDK11.03
*/
public class UnsignedRightShift {
public static void main(String[] args) {
int i = -1 << 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
i >>>= 1;
i >>>= 1;
i >>>= 1;
i >>>= 1;
i >>>= 1;
i >>>= 1;
i >>>= 1;
i >>>= 1;
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
}
}