一元操作符、强制类型转换表达式、乘除操作符、加法操作符、移位操作符、关系操作符、判等操作符、位操作符与逻辑操作符、条件与操作符、条件或操作符--运算机制与返回值
操作符处理及返回值
- 先遣提示
- 一元操作符
- 前缀递增操作符 ++
- 前缀递增操作符 - -
- 一元加号操作符 +
- 关于一元数值提升或二元数值提升 可以看这篇博客
- 一元减号操作符 -
- 这里涉及了 原码,反码,补码 相关内容有兴趣 可以去看 这篇博客
- 这里涉及了浮点数的表示方式和存储方式,可以去看一下两篇博客
- 按位取反操作符 ~
- 逻辑取反操作符 !
- 强制类型转换表达式
- 乘法操作符
- 乘法操作符 *
- 这里涉及了strictfp这个关键字的使用,可以看看这篇博客
- 除法操作符 /
- 取余操作符 %
- 加法操作符
- 字符串连接操作符 +
- 用于数字类型的加法操作符( + 和 -)
- 移位操作符
- 关系操作符
- 数字比较操作符 <、<=、>和>=
- 类型比较操作符 instanceof
- 判等操作符
- 数字判等操作符 == 和 !=
- 布尔判等操作符 == 和 !=
- 引用判等操作符 == 和 !=
- 位操作符与逻辑操作符
- 整数位操作符 &、^、和 |
- 布尔逻辑操作符 &、^、和 |
- 条件与操作符 &&
- 条件或操作符 ||
- 参考来源
先遣提示
表达式就是操作数和运算符的组合.
简单类型 或叫做 原始类型
float fi= -0f;
需要在 -0后面 加上 f,因为 int类型中没有负0的说法,所以在int向float转换过程中,fi会被赋值正0.反编译class文件可以使用
javap -c (class文件名)
javap是JDK提供的一个命令行工具,javap能对给定的class文件提供的字节代码进行反编译。
一元操作符
操作符 +、-、++、- -、~、!和强制类型转换操作符被称为一元操作符
前缀递增操作符 ++
由++前导的一元表达式是前缀递增表达式
一元表达式的操作数必须是变量,其类型可转换为数字类型,否则就会产生编译时错误。
前缀递增表达式的类型就是这个变量的类型,前缀递增表达式的结果不是变量,而是值。
short st=10,test;test=++st; //还是 short类型。现在是值,不能被赋值,只能把自己赋值给别的变量。System.out.println(test);
final声明的变量不能递增,因为当将这样的final变量的访问用作表达式时,结果是值,而不是变量。因此,它不能用作前缀增量运算符的操作数。
前缀递增操作符 - -
由- -前导的一元表达式是前缀递减表达式
一元表达式的操作数必须是变量,其类型可转换为数字类型,否则就会产生编译时错误。
前缀递减表达式的类型就是这个变量的类型,前缀递减表达式的结果不是变量,而是值。
short st=10,test;test=--st; //还是 short类型。现在是值,不能被赋值,只能把自己赋值给别的变量。System.out.println(test);
final声明的变量不能递减,因为当将这样的final变量的访问用作表达式时,结果是值,而不是变量。因此,它不能用作前缀增量运算符的操作数。
一元加号操作符 +
一元+操作符表达式的操作数的类型必须可转换为简单数字类型,否则就会产生编译时错误。
在操作数上会执行一元数值提升。一元加号表达式的类型是操作数提升后的类型。一元加号表达式的结果不是变量,而是值。
在运行时,一元加号表达式的值是操作数提升后的值。
short st=10;int test;test=+st; //st执行了一元数字提升,因为自身类型低于int类型,所以被转换为了int类型。同样地,现在是值,不能被赋值,只能把自己赋值给别的变量。System.out.println(test);
一元减号用于转变数据的符号,而一元加号只是为了与一元减号相对应,但是 它唯一的作用仅仅是将较小类型的操作数提升为int.
关于一元数值提升或二元数值提升 可以看这篇博客
https://blog.csdn.net/yangcheng33/article/details/76408580
一元减号操作符 -
一元 - 操作符表达式的操作数的类型必须可转换为简单数字类型,否则就会产生编译时错误。
在操作数上会执行一元数值提升。一元减号表达式的类型是操作数提升后的类型。
注意
一元数字提升会执行值集转换。无论提升后的操作数的值是从哪个值集导出的,都会执行一元取反操作,其结果是从相同的值集导出的。该结果之后会经历进一步的值集转换。
在运行时,一元减号表达式的值是操作数提升后的值的算数取反值。
对于整数值,(加负号)取反与 0 减去该值是相同的。Java编程语言对整数使用 2 的补码表示,而 2 的补码表示的范围不是对称的,也因此对最大的负 int 或 long 值 取反将产生相同的最大的负值。在这种情况中发生了上溢,但是不会抛出任何异常。对于所有整数值 x,-x 等于(~x)+1。
这里涉及了 原码,反码,补码 相关内容有兴趣 可以去看 这篇博客
https://blog.csdn.net/chenchao2017/article/details/79733278?utm_medium=distribute.pc_relevant_t0.none-task-blog-searchFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-searchFromBaidu-1.control
在此 总结四点
计算机中没法直接做减法的,它的减法是通过加法来实现的。减去一个数,也就是加上这个数的补数(或同余数)。
反码的应用是为了使负数和它对应的二进制码在加法运算上,得到同步递增或递减的效果。
补码是为了消除正负体系中的两个0,负数整体全部加 1 ,从而负0与正0合并,最小值的补码依然保留,表示值比原来更小(小1),同时负数表示范围相较之前大1。
正数在计算机中是用二进制表示的,负数在计算机中是用补码表示的.
int it=10,iu=10,ic= Integer.MIN_VALUE,ig=10;it=-it; //执行了 一元数字提升,操作数变为int类型 (当前情况)iu=0-iu; //执行了 二元数字提升,两个操作数都变为int 类型。(当前情况)System.out.println("int类型最小值:"+ic);ic=-ic; //发生上溢 溢出,补码形式首先按位取反,再加1时,出现溢出。ig=~ig; //执行了 一元数字提升,操作数变为int类型 (当前情况)System.out.println("加了负号的10:"+it);System.out.println("被0减的10:"+iu);System.out.println("加了负号的int类型最小值:"+ic); //加上负号后,仍然等于原值System.out.println("按位取反的10:"+ig);
对于浮点值,取反与 0 减去该值是不相同的,因为如果 x 是 +0.0,那么 0.0-x 是 +0.0,但是 -x 是 -0.0。一元负号仅仅只是转换了浮点数的符号位。有趣的情况是:
- 如果操作数是 NaN,那么结果就是 NaN。(NaN没有符号位)
- 如果操作数是无穷大,那么结果就是符号相反的无穷大。
- 如果操作数是 0,那么结果就是符号相反的 0。
float f1,f2,f3=0;f1=-Float.NaN; //NaN(“非数字"标识(Not-a-Number),用来表示某些无效的运算操作)存在于IEEE 754标准中,因为浮点的各种依据来源于IEEE 754标准,所以NaN只在浮点计算中出现。f2=-(2/0f); //在浮点数的体系中,有穷数/0 等于无穷大f3=-f3;System.out.println("加负号的NaN:"+f1);System.out.println("加了负号的无穷大:"+f2);System.out.println("加了负号的0:"+f3);System.out.println(0.0f==f3); //在浮点数体系中,负0和正0是相等的。
这里涉及了浮点数的表示方式和存储方式,可以去看一下两篇博客
这篇 重点 在于 浮点原理的阐述
https://dablelv.blog.csdn.net/article/details/50487127?utm_medium=distribute.pc_relevant.none-task-blog-searchFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-searchFromBaidu-1.control
这篇 重点在于 浮点数的存储和表达
https://blog.51cto.com/yjplxq/821567
这个网站可以将十进制数以及16进制数,转换成IEEE-754的存储浮点二进制表示
https://babbage.cs.qc.cuny.edu/ieee-754.old/decimal.html
和
https://babbage.cs.qc.cuny.edu/ieee-754.old/32bit.html
在此总结几句:
指数位决定了大小范围,小数位决定了计算精度。当十进制数值转换为二进制科学表达式后,得到的尾数位数是有可能很长甚至是无限长。所以当使用浮点格式来存储数字的时候,实际存储的尾数是被截取或执行舍入后的近似值。需要精确比较大小时,“==”并不可靠,需要使用BigDecimal来比较。
浮点数的基本数据类型不能用 == 比较,包装数据类型不能用 equals 比较
阶码全0,的表示范围为0或非规约数,阶码全1,的表示范围为NaN或无穷大。
规约数的存储形式中小数左边隐藏位默认有 1,非规约数没有,为0.(非规约数通常都很接近于0)
因为二进制数无法准确的表达十进制小数,所以才会出现各种各样的近似取值而造成误差。
Float.intBitsToFloat() 方法可以直接用float存储格式给float数值赋值,并返回这个float数字。
Float.floatToRawIntBits()方法可以使一个float的数值用存储格式的二进制数直接表示一个int类型的数字,并返回这个int数字。
Float.floatToIntBits()方法可以使一个float的数值用存储格式的二进制数直接表示一个int类型的数字,并返回这个int数字(与上一个方法不同的是,此方法不能很好返回NaN范围的值(如果参数是NaN,结果是0x7fc00000 ))。
float haji=Float.intBitsToFloat(0xffffffff); //此时 haji 表示为NaN,NaN的float数值表示范围有很多(0x7f800001到0x7fffffff ,或在范围0xff800001到0xffffffff ),也就是阶码的8位全为1,(注意尾数域 不能全为0,(阶码全为1且尾数域全为0的数为无穷大))System.out.println("floatToRawIntBits方法的返回值:"+Float.floatToRawIntBits(haji));
System.out.println("floatToIntBits方法的返回值:"+Float.floatToIntBits(haji));//floatToIntBits方法并没有,正确的返回这个NaN值存储格式的int值和二进制值。
//所以对于方法floatToIntBits,只要参数是NaN,那么返回值就是0x7fc00000 。System.out.println("floatToRawIntBits方法返回值二进制化:"+Integer.toBinaryString(Float.floatToRawIntBits(haji))+" "+"位数:"+Integer.toBinaryString(Float.floatToRawIntBits(haji)).length());
System.out.println("floatToIntBits方法返回值二进制化:"+Integer.toBinaryString(Float.floatToIntBits(haji))+" "+"位数:"+Integer.toBinaryString(Float.floatToIntBits(haji)).length());
按位取反操作符 ~
一元 ~ 操作符的操作数表达式的类型必须可转换为简单整数类型,否则就会产生编译时错误。
在操作数上会执行一元数字提升。一元按位取反表达式的类型是操作数提升后的类型。
在运行时,一元按位取反表达式的值是操作数提升后的值按位取反的结果。在所有情况下,~x都等于(-x)-1.
int it=10; it=~it; //只有简单整数类型,可以使用System.out.println("按位取反的10:"+it);
逻辑取反操作符 !
一元 !操作符的操作数表达式的类型必须必须是boolean或Boolean,否则就会产生编译时错误。
一元逻辑取反表达式的类型是boolean。
在运行时,操作数在必要时会经历拆箱转换。如果操作数的值为false,那么一元逻辑取反表达式的值为true,并且如果操作数的值为true,那么一元逻辑取反表达式的值为false。
Boolean t=false;t=!t; //此处经历了,拆箱-装箱操作。 t首先拆箱,然后再装箱。System.out.println(t);
强制类型转换表达式
强制类型转换表达式在运行时将一种数字类型的值转换为另一种数字类型的类似的值;或者在编译时确定表达式的类型是boolean;或者在运行时检查引用值引用的对象所属的类是否与指定的引用类型或引用类型列表兼容。
( PrimitiveType ) UnaryExpression
( ReferenceType { AdditionalBound } ) UnaryExpressionNotPlusMinus
( ReferenceType { AdditionalBound } ) LambdaExpression
它们包含的括号和类型或类型列表有时被称为强制类型转换符。
如果强制类型转换操作符包含类型列表,即ReferenceType后面跟着一个或多个AdditionBound项,那么下列条件必须都为真,否则就会产生编译时错误:
- ReferenceType必须表示的是类或接口类型。
- 所以列出的类型的擦除必须两两之间不同。
- 没有任何两个列出的类型可以是同一个泛化接口的不同参数化版本的子类型。
由强制类型转换表达式引入的强制类型转换上下文的目标类型要么是出现在该强制类型转换操作符中Primitive和ReferenceType(如果后面没有跟着AdditionalBound项),要么是由出现在该强制类型转换操作符中的ReferenceType和AdditionalBound项所表示的交集类型。
强制类型转换表达式的类型是将捕获转换应用于目标类型之后得到的结果。
强制类型转换表达式的结果不是变量,而是值,(不能被赋值了,只能将自己赋值给他人)
如果按照强制类型转换规则,操作数的编译时类型永远都不能强制转换为类型强制转换操作符所指定的类型,那么就是一个编译时错误。
否则,在运行时,操作数的值将通过强制类型转换而转换为强制类型转换符指定的类型(如果有必要)。
如果在运行时强制类型转换被发现是不允许的,那么就会抛出一个ClassCastException
实例。
某些强制类型转换会在编译时产生错误。某些强制类型转换可以在编译时证明在运行时总是正确的。例如,将类类型的值转换为其超类的类型就总是正确的,这种强制类型转换在运行时应该不要求任何特殊动作。最后,某些强制类型转换无法在编译时证明总是正确的还是总是不正确的。这种强制类型转换要求在运行时进行测试。
int it=10; short rt=10;rt=(short)it;
class father{}
class son extends father{public static void main(String[] args) {son ht=new son();father hu=ht; //多态的格式}
}
乘法操作符
操作符 * 、/ 和 % 被称为乘法操作符。
乘法操作符有相同的优先级,并且在语法上是左结合的(它们自左向右结合)。
乘法操作符的每个操作符的类型都必须可转换为简单数字类型,否则就会产生编译时错误。
在操作数上会执行二元数值提升。
注意 二元数字提升会执行值集转换,并且可能会执行拆箱操作。
乘法表达式的类型是其操作数被提升后的类型。
如果提升后的类型是int 或 long ,那么会执行整数算数运算。
如果提升后的类型是float 或 double,那么会执行浮点运算。
乘法操作符 *
二元 * 操作符执行乘法,产生其操作数的积。
乘法满足交换律,如果操作数表达式没有副作用。
整数乘法满足结合律,如果所有操作数都具有相同的类型。
浮点乘法不满足结合律。
如果整数乘法上溢,那么其结果就是算数乘积低阶的各个位,表示为某种足够大的二进制补码形式。因此,如果发生上溢,那么结果的符号位可能与两个操作数的算数乘积的符号位不同。
int it=Integer.MAX_VALUE; //获取 int 类型最大值int iu=2,ic;ic=it*iu;//使用 toBinaryString,让整数变为32位二进制数,//下面,长度不为32的二进制数,前面都省去了不等数量的 0。//由结果可知,上溢效果,符合上述描述。System.out.print(Integer.toBinaryString(it).length()+" "); System.out.println(Integer.toBinaryString(it));System.out.print(Integer.toBinaryString(iu).length()+" "); System.out.println(Integer.toBinaryString(iu));System.out.print(Integer.toBinaryString(ic).length()+" "); System.out.println(Integer.toBinaryString(ic));System.out.println(ic);
浮点乘法的结果由 IEEE 754 算术规则确定:
- 如果两个操作数之一是NaN,那么结果就是NaN。
- 如果结果不是NaN,那么如果两个操作数具有相同的符号,那么结果的符号位为正,如果两个操作数符号不同,那么结果的符号位为负。
- 无限大乘以0的结果为NaN。
- 无限大乘以有穷值的结果是有符号的无限大,符号由上述规则确定。
- 在其余情况中,不涉及无限大或NaN,将会产生精确的算数乘积。然后选择浮点值集:
如果乘法表达式是FP-strict的:
- 如果乘法表达式的类型是float,那么必须选择浮点值集。
- 如果乘法表达式的类型是double,那么必须选择双精度值集。
如果乘法表达式不是FP-strict的:
- 如果乘法表达式的类型是float,那么可以选择浮点值集或浮点扩展指数值集,由Java编程语言的实现自己选择。
- 如果乘法表达式的类型是double,那么可以选择双精度值集或双精度扩展指数值集,由Java编程语言的实现自己选择。
接下来,必须从选中的值集中选择一个值来表示乘积。
如果乘积的数量级太大以致无法表示,我们就称该操作上溢,然后其结果为具有恰当符号的无限大。
否则,将会使用 IEEE 754 最近舍入模式将该乘积四舍五入到所选值中最接近的值。Java编程语言要求支持IEEE 754定义的渐进下溢。
尽管可能会发生上溢、下溢、或信息丢失,乘法操作符 * 的计算永远都不会抛出运行时异常。
这里涉及了strictfp这个关键字的使用,可以看看这篇博客
https://blog.csdn.net/swjtu2014112194/article/details/85113015?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-1.control
float f1= Float.NaN,f2=2/0f,f3=10,f4=0f,f5=-10;f3=f3*f1;f4=f4*f2;f5=f5*f2;System.out.println("NaN和有穷值的乘积:"+f3);System.out.println("无穷大和0的乘积:"+f4);System.out.println("无穷大和有穷值的乘积:"+f5);
除法操作符 /
二元 / 操作符执行除法,产生其操作数的商。左操作数是被除数,右操作数是除数。
整数除法向 0 舍入。即,对操作数 n 和 d,在其进行二元数字提升之后,产生的商是整数值 q,其数量级在满足 |d*q|<=|n|的情况下取尽可能大的值。并且,如果|n|>=|d| 且n 和 d具有相同的符号,那么 q是正数;如果|n|>=|d|且n 和 d 具有不同的符号,那么q 是负数q。
int ig=3,ik=1;//二元数字提升的情况中,为int整数的除法运算。且满足上述描述。System.out.println("整数型3除以2:"+ig/2);System.out.println("整数型1除以2:"+ik/2);
有一种特殊情况不满足这条规则:如果被除数是其所属类型最大可能级数的负数,并且除数为-1,那么就会发生正数上溢,其结果就等于被除数。尽管会上溢,但是在这种情况中不会抛出任何异常。另一方面,如果在整数除法中除数的值为0,那么就会抛出一个ArithmeticException
实例
int it=10,iu=Integer.MIN_VALUE,ic=0;System.out.println("int最大负数除以-1:"+iu/-1); //可以参考 -x=(~x)+1 来运算System.out.println("整型有穷数除以0:"+it/ic); //只有整数除法中除以0会报,运行时错误。
只有整数除法中除以0会报错,且是运行时错误。
浮点除法的结果由 IEEE 754 算术规则确定:
- 如果两个操作数之一是NaN,那么结果就是NaN。
- 如果结果不是NaN,那么如果两个操作数具有相同的符号,那么结果的符号位为正,如果两个操作数符号不同,那么结果的符号位为负。
- 无限大除以无限大的结果为NaN。
- 无限大除以有穷值的结果是有符号的无限大,符号由上述规则确定。
- 有穷值除以无限大的结果是有符号的0,符号由上述规则确定。
- 0 除以 0的结果是NaN;0除以其它任何有穷值的结果为有符号的 0,符号位由上述规则确定。
- 非 0 有穷值 除以 0的结果是有符号无限大,符号位由上述规则确定。
- 在其余情况中,不涉及无限大或NaN,将会产生精确的算数商。然后选择浮点值集:
如果除法表达式是FP-strict的:
- 如果乘法表达式的类型是float,那么必须选择浮点值集。
- 如果乘法表达式的类型是double,那么必须选择双精度值集。
如果乘法表达式不是FP-strict的:
- 如果乘法表达式的类型是float,那么可以选择浮点值集或浮点扩展指数值集,由Java编程语言的实现自己选择。
- 如果乘法表达式的类型是double,那么可以选择双精度值集或双精度扩展指数值集,由Java编程语言的实现自己选择。
接下来,必须从选中的值集中选择一个值来表示商。
如果商的数量级太大以致无法表示,我们就称该操作上溢,然后其结果为具有恰当符号的无限大。
否则,将会使用 IEEE 754 最近舍入模式将该乘积四舍五入到所选值中最接近的值。Java编程语言要求支持 IEEE 754 定义的渐进下溢。
尽管可能会发生上溢、下溢、除 0 或信息丢失,浮点除法操作符 / 的计算永远都不会抛出运行时异常。
float it=0,ic=2/0.f,iu= Float.NaN,ig=-10;System.out.println("NaN除以0:"+iu/it);System.out.println("正无穷大除以正无穷大:"+ic/ic);System.out.println("正无穷大除以负有穷值:"+ic/ig);System.out.println("负有穷值除以正无穷大:"+ig/ic);System.out.println("0除以0:"+it/it);System.out.println("0除以负有穷值:"+it/ig); //浮点体系中,负0和正0是相等的。System.out.println("0除以正无穷大:"+it/ic);System.out.println("负有穷值除以正0:"+ig/it);
取余操作符 %
二元 % 操作符执行隐含的除法,产生操作数的余数,左操作数是被除数,右操作数是除数。
在C和C++中,取余操作符只接受整数型操作数,但是在Java编程语言中,它也可以接受浮点值。
在经过二元数值提升的操作数上的取余操作产生的结果会使得(a/b)*b+(a%b) 等于 a(对于整数运算来说)。
int a=10,b=3;System.out.println((a/b)*b+(a%b));System.out.println(((a/b)*b+(a%b))==a);
即使对于被除数是其类型所允许的最大数量级的负数且除数是 -1这种
特殊情况(余数为0),也仍旧具有这种特征。
float a=Float.MAX_VALUE,b=-1;System.out.println((a/b)*b+(a%b)==a); //对于 整数,是有效的
按照上面的规则,取余操作的结果只有在被除数为负数时才能是负数,且只有被除数为正数时才能是正数。而且,结果的数量级永远都比除数的数量级小。
如果整数取余操作符的除数值为 0,那么就会抛出一个 ArithmeticException
实例。
int it=10,ic=0; //只有在整数操作中,这种操作会报错,运行时报错。System.out.println(it%ic);
按照 % 操作符计算的浮点取舍操作的结果与IEEE 754 定义的浮点取余操作所产生的结果不同。IEEE 754 取余操作按照舍入除法而不是截尾除法计算余数,因此其行为不能类比通常的整数取余操作符的行为。因此,Java编程语言在浮点操作上定义 % 以类比整数取余操作符的行为,这可以对应C类库的函数 fmod 。IEEE 754 取余操作可以用类库例程Math.IEEEremainder 来计算。
浮点取余的结果由 IEEE 754 算术规则确定:
- 如果两个操作数之一是NaN,那么结果就是NaN。
- 如果结果不是NaN,那么结果的符号等于被除数的符号。
- 如果被除数是无穷大,或除数是0,或者两者同时满足,那么结果就是NaN。
- 如果被除数是有穷值且除数是无穷大,那么结果就是被除数。
- 如果被除数是 0 且除数是有穷值,那么结果就是被除数。
- 在其余情况中(即不涉及无限大、0或NaN),会从被除数 n和除数 b的除法中产生浮点余数 r,产生方式满足算数关系r=n-(d-q),其中q只有在n/d为负时是负整数,在n/d为正时是正整数,并且其数量级取在不超过 n 和 d 的真正数学上的商的数量级的情况下尽可能大的值。
浮点取余操作符 % 的计算永远都不会抛出运行时异常,尽管右操作数可能是0.上溢、下溢或精度丢失也都不会发生。
float fl=-10f,ft=2f/0,fc=Float.NaN,fg=0;System.out.println("负有穷值余NaN:"+fl%fc);System.out.println("无穷大余0:"+ft%fg);System.out.println("无穷大余负有穷值:"+ft%fl);System.out.println("负有穷值余0:"+fl%fg);System.out.println("负有穷值余无穷大:"+fl%ft);System.out.println("0余负有穷值:"+fg%fl);
加法操作符
操作符 +和 - 都被称为加法操作符。
加法操作符有相同的优先级,并且在语法上左结合的(它们自左向右结合)。
如果 + 操作符的任何一个操作数的类型是String,那么该操作就是字符串连接操作。
否则,+ 操作符的每个操作数的类型都必须可以转换为简单数字类型,否则就会产生编译时错误。
在每种情况下,二元 - 操作符的每个操作数的类型都必须可转换为简单数字类型,否则会产生编译时错误。
字符串连接操作符 +
如果只有一个操作数表达式是String类型,那么就会在另一个操作数上执行字符串转换以在运行时产生字符串。
字符串连接操作的结果是一个对String对象的引用,该对象是两个操作数字符串的连接。在新创建的字符串中,左操作数的字符在右操作数的字符的前面。
该String对象是新创建的,除非该表达式是常量表达式
Java语言的实现可以选择在一个步骤中执行转换和连接,以避免先创建然后又丢弃中间的String对象。为了提高重复的字符串连接操作的性能,Java编译器可以使用StringBuffer类或类似的技术来减少计算表达式所创建的中间String对象的数量。
对于基本类型,Java语言的实现还可以通过直接将基本类型转换为字符串而优化掉包装器对象的创建。
String st="不好"; int it=1,ic=2;System.out.println(st+it+ic); //左结合的 加法操作符
用于数字类型的加法操作符( + 和 -)
二元 + 操作符在应用于数字类型的两个操作数时,会执行加法,并产生操作数的和。
二元 - 操作符在应用于数字类型的两个操作数时,会执行减法,并产生操作数差。
在操作数上会执行二元数值提升。
注意 二元数值提升会执行值集转换,并可能会执行拆箱转换。
在数字类型操作数上的加法表达式的类型是其操作数提升后的类型。
如果提升后的类型是 int 或 long,那么就会执行整数算术运算。
如果提升后的类型是 float 或 double,那么就会执行浮点数算术运算。
加法满足交换律,如果操作数表达式没有副作用。
整数加法满足结合律,如果所有操作数都具有相同的类型。
浮点加法不满足结合律。
如果整数加法上溢,那么其结果就是算术和低阶的各个位,表示为某种足够大的二进制补码形式。因此,如果发生上溢,那么结果的符号位可能与两个操作数的算术和的符号位不同。
int ic=Integer.MIN_VALUE,iu=1;System.out.println("int最小数:"+ic+" int最小数的二进制:"+Integer.toBinaryString(ic)+" "+Integer.toBinaryString(ic).length()+"位");System.out.println("1:"+iu+" 1的二进制:"+Integer.toBinaryString(iu)+" "+Integer.toBinaryString(iu).length()+"位");System.out.println("int最小数减1:"+(ic-1)+" int最小数减1的二进制:"+Integer.toBinaryString(ic-1)+" "+Integer.toBinaryString(ic-1).length()+"位");
//上方表现出来的下溢,符合上述文字描述。
浮点加法的结果是由 IEEE 754算术规则确定:
- 如果两个操作数之一是 NaN,那么结果就是NaN。
- 两个符号相反的无穷大的和为NaN。
- 两个符号相同的无穷大的和为同符号的无穷大。
- 无穷大和有穷值的和等于无穷大操作数。
- 两个符号相反的 0 的和为正0.
- 两个符号相同的 0 的和为同符号的0.
- 0 和 非0有穷值的和等于非 0 操作数。
- 两个具有相同数量级且符号相反的非0有穷值的和为正0。
- 在其余情况中(即不涉及无限大、0、或NaN),且操作数具有相同符号或不同数量级,将会产生精确的算术和。然后选择浮点值集:
如果加法表达式是FP-strict的:
- 如果加法表达式的类型是float,那么必须选择浮点值集。
- 如果加法表达式的类型是double,那么必须选择双精度值集。
如果加法表达式不是FP-strict的:
- 如果加法表达式的类型是float,那么可以选择浮点值集或浮点扩展指数值集,由Java编程语言的实现自己选择。
- 如果加法表达式的类型是double,那么可以选择双精度值集或双精度扩展指数值集,由Java编程语言的实现自己选择。
接下来,必须从选中的值集中选择一个值来表示和。
如果和的数量级太大以致无法表示,我们就称该操作上溢,然后其结果为具有恰当符号的无限大。
否则,将会使用 IEEE 754 最近舍入模式将该和四舍五入到所选值集中最接近的值。Java编程语言要求支持 IEEE 754 定义的渐进下溢。
二元 - 操作符在应用于数字类型的两个操作数时,会执行减法,并产生操作数的差;左操作数称为被减数,右操作数称为减数。
对于整数和浮点减法,a - b 总是会产生与 a+(-b)相同的结果。
注意 对于整数值,0减去它与取反相同。但是,对于浮点数操作数,0减去它不等于取反,因为如果 x是 +0.0,那么0.0-x 是 +0.0,但是 -x是 -0.0。
尽管可能会发生上溢、下溢或信息丢失,数字型加法操作符的计算不会抛出运行时异常。
float fi=Float.NaN,fu=-0f,fg=0,fd=10,fh=-10;System.out.println("NaN加上有穷数:"+(fi+fd));System.out.println("负0加上正0:"+(fu+fg));System.out.println("负有穷值加上正有穷值:"+(fh+fd));
移位操作符
操作符 <<(左移)、>>(带符号右移) 和 >>>(无符号右移) 被称为移位操作符。移位操作符的左操作数是要被移位的值,右操作数指定了移位的距离。
移位操作符在语法上是左结合的(它们自左向右结合)。
在每个操作数上都会单独地执行一元数字提升(而不是在两个操作数上执行二元数字提升)。
如果移位操作符的每个操作数的类型在一元数字提升后不是简单整数类型,那么就是一个编译时错误。
移位表达式的类型是左操作数提升后的类型。
如果左操作数提升后的类型是 int,那么右操作数只有最低的5位(bit)可以用作移位距离。这就像是右操作数被按位逻辑与操作符 & 用掩码值 0x1f(0b11111)进行处理一样。因此,实际使用的移位距离总是在 0 到 31 的闭区间内。
int g=1;System.out.println(g<<31); //成功的 左移了 31位(所有位数左移,右边补0)System.out.println(g<<32); //由于二进制状态下低五位,全是0,所以相当于没有移位。
如果左操作数提升后的类型是 long,那么右操作数只有最低的6位(bit)可以用作移位距离。这就像是右操作数被按位逻辑与操作符 & 用掩码值 0x3f(0b111111)进行处理一样。因此,实际使用的移位距离总是在 0 到 63 的闭区间内。
在运行时,移位操作是在左操作数的值的二进制补码整数表示上执行的。
n<<s 的值是n左移s位后的值,这等价于(即使发生上溢)乘以2的s次幂。
n>>s的值是n右移s位后的值,其结果为【n / 2的s次幂】。对于非负的n,等于由整数除法操作符 / 计算的除以2的s次幂的截尾整数除法。(正数高位补0,负数高位补1)
n>>>s的值是n用 0 扩展右移s位后的值,其中:
- 如果 n 是正数,那么结果与 n>>s相同。
- 如果 n 是负数,且左操作数的类型是 int,那么结果等于表达式 ((n>>s)+(2<<~s))的值。()
- 如果 n 是负数,且左操作数的类型是 long,那么结果等于表达式 ((n>>s)+(2L<<~s))的值。
添加 (2<<~s) 和 (2L<<~s) 项是为了抵消符号位的传播。
注意 因为移位操作符的右操作数的隐式掩码机制,~s 作为移位距离,在 int 值移位时等于 31-s,在long 值移位时等于 63-s。
int g=Integer.MIN_VALUE,jh=14;System.out.println(Integer.toBinaryString(g)+" "+Integer.toBinaryString(g).length()+"位");System.out.println(Integer.toBinaryString(g>>jh)+" "+Integer.toBinaryString(g>>jh).length()+"位");// 负数右移位,上面中负数的带符号右移(正数高位补0,负数高位补1)System.out.println(Integer.toBinaryString(2<<~jh)+" "+Integer.toBinaryString(2<<~jh).length()+"位");// 与上方相加后,成功在19位与上方高位相加进位,产生上溢,结果为下方数字。符合上述描述。System.out.println(Integer.toBinaryString(g>>>jh)+" "+Integer.toBinaryString(g>>>jh).length()+"位");
关系操作符
数字比较符 <、>、<=和>=,以及 instanceof 操作符,被称为关系操作符。
关系操作符在语法上是左结合的(它们自左向右结合)。但是,这个事实并没有作用。例如,a<b<c被解析为 (a<b)<c,而这总是编译时错误,因为 a<b 的类型总是 boolean,而 <不是在boolean值上的操作符。
关系表达式的类型总是boolean。
数字比较操作符 <、<=、>和>=
数字比较操作符的每个操作数的类型都必须可转换为简单数字类型,否则就会产生编译时错误。
在操作数上会执行二元数字提升。
注意 二元数字提升会执行值集转换,并可能会执行拆箱转换。
如果提升后的类型是 int 或 long,那么就会执行有符号整数的比较。
如果提升后的类型是 float 或 double,那么就会执行浮点数的比较。
比较是在浮点值上精确执行的,无论它们表示的值是从哪个值集中导出的。
浮点比较的结果由 IEEE 754 标准的规则描述确定,即:
- 如果两个操作数之一是NaN,那么结果就是false。
- 所有不是NaN的值都是有序的,即负无穷大值小于所有有穷值,并且正无穷大值大于所有有穷值。
- 正 0 和 负 0 被认为是相等的。 例如,-0.0<0.0 为false,但是 -0.0<=0.0 为true。 但是,请注意,Math.min 方法和 Math.max方法会严格地将负0作为小于正0处理。
除了上面针对浮点数的考虑,下面的规则将对整数操作数和非NaN的浮点操作数起作用:
- 如果左操作数的值小于右操作数的值,由 < 操作符产生的值为 true,否则就是 false。
- 如果左操作数的值小于等于右操作数的值,由 <=操作符产生的值为true,否则就是false。
- 如果左操作数的值大于右操作数的值,由 > 操作符产生的值为true,否则就是false。
- 如果左操作数的值大于等于右操作数的值,由 >= 操作符产生的值为true,否则就是false。
float fi=Float.NaN,fc=0f,fg=-0f; //需要在 -0后面 加上 f,因为 int类型中没有负0的说法,所以在int向float转换过程中,就是float的正0.System.out.println("NaN与0的比较:"+(fi>=fc));System.out.println("正0与负0的比较:"+(fg>=fc)); //对于 == 正0和负0被认为是相等的System.out.println("正0与负0的精确比较:"+Math.min(fg,fc));System.out.println("正0与负0的精确比较:"+Math.max(fg,fc));//Math.min和Math.max 成功的分辨除了正0和负0 的大小
类型比较操作符 instanceof
之前已经写过的博客,可参考。
https://blog.csdn.net/norang/article/details/111409136
判等操作符
操作符 ==(等于)和 !=(不等)被称为判等操作符。
判等操作符在语法上是左结合(它们自左向右结合)。
但是,这个事实实际上并没有什么用。例如,a== b== c 被解析为 (a== b)== c ,而 a== b 的结果类型总是 boolean,c因此必须是boolean类型,否则就会产生编译时错误。因此,a== b==c并不是在测试 a、b和 c 是否全部相等。
判等操作符满足交换律,如果操作数表达式没有副作用。
判等操作符可以类比关系操作符,但是他们优先级更低。因此 a<b==c<d 为true,只要 a<b且c<d有相同的真值。
判等操作符可以用来比较两个可转换为数字类型的操作数,或两个 boolean 或 Boolean 类型的操作数,或者是两个都是引用类型 或 空类型的操作数。所有其他情况都会产生编译时错误。
判等表达式的类型总是 boolean。
在所有情况中,a!=b 总会产生与 !(a==b)相同的结果。
数字判等操作符 == 和 !=
如果判等操作符的操作数都是数字类型,或者其中之一是数字类型,而另一个可转换为数字类型,那么就会在操作数上执行二元数字提升。
注意 二元数字提升会执行值集转换,并可能会执行拆箱转换。
如果提升后的类型是 int 或 long,那么就会执行整数判等比较。
如果提升后的类型是 float 或 double,那么就会执行浮点数判等比较。
比较是在浮点值上精确执行的,无论它们表示的值是从哪个值集中导出的。
浮点判等测试是按照 IEEE 754标准而执行的,即:
如果两个操作数之一是NaN,那么 == 的结果是false,而 !=的结果是true。
实际上,测试 x!=x 是true,当且仅当 x 的值是 NaN。
Float.isNaN 和 Double,isNaN 也可以用来测试某个值是不是NaN。正 0 和 负 0 被认为是相等的。
例如,-0.0==0.0 为true。否则,两个有区别的浮点数就会被判等操作符认为是不等的。
特别是,有一个值表示正无穷大,并且有另一个值表示负无穷大。这两个值每个都只有在和自身比较时为true,在和所有其他值比较时都为false。
除了上面针对浮点数的考虑,下面的规则将对整数操作数和非NaN的浮点操作数起作用:
4. 如果左操作数的值等于右操作数的值,由 == 操作符产生的值为 true,否则就是false。
5. 如果左操作数的值不等于右操作数的值,由 != 操作符产生的值为 true,否则就是false。
float fi=Float.NaN,fc=0,fu=-0f;System.out.println(fi!=fi);System.out.println(Float.isNaN(fi));System.out.println(fc==fu);
布尔判等操作符 == 和 !=
如果判等操作符的操作数都是boolean类型,或者其中一操作数是boolean类型,而另一个是Boolean类型,那么该操作就是布尔判等操作。
布尔判等操作符满足结合律。
如果其中一个操作数是Boolean类型,那么它将执行拆箱转换。
如果操作数(在执行了所有必须的拆箱转换后)都是true或者都是false,那么 == 的结果就是 true,否则,结果是 false。
如果操作数都是true或都是false,那么!=的结果就是false,否则,结果是true。
因此,!= 的行为与将 ^ 应用于boolean 操作数时的行为相同。
boolean g=false;System.out.println(g^g); //异或操作符,相同即为false,其它为true(当前针对boolean的情况)(整数时,按位异或操作)System.out.println(g!=g);
引用判等操作符 == 和 !=
如果判等操作符的操作数都是引用类型或空类型,那么该操作就是对象判等操作。
如果两个操作数其中一个的类型不允许通过强制类型转换而转换为另一个的类型,那么就是一个编译时错误。这两个操作数运行时的值将必须是不等的。
在运行时,如果操作数的值都是null,或者都引用相同的对象或数组,== 的结果为 true,否则,结果为false。
如果操作数的值都是null,或者都引用相同的对象或数组,!= 的结果为 false,否则,结果为 true。
虽然 == 可以用来比较String 类型的引用,但是这种判等测试确定的是两个操作数引用的是否是同一个String 对象。如果两个操作数是有区别的String 对象,那么结果为false,即使它们包含相同的字符序列。两个字符串 s 和 t 的内容可以通过方法调用 s.equals(t) 来测试其是否相等。
位操作符与逻辑操作符
位操作符和逻辑操作符包括 与操作符 &、异或操作符 ^ 和 或操作符 | 。
这些操作符有不同的优先级,其中 & 具有最高优先级而 | 具有最低优先级。
这些操作符的每一个在语法上都是左结合的(每个都自左向右结合)。
每个操作符都满足交换律,如果操作数表达式没有副作用。
每个操作符都满足结合律。
位操作符和逻辑操作符可以用来比较两个数字类型的操作数或两个boolean类型的操作数。所有其他情况都会产生编译时错误。
整数位操作符 &、^、和 |
当操作符 &、^ 或 | 的两个操作数都可转换为简单整数类型时,会首先在操作数上执行二元数字提升。
位操作表达式的类型是操作数提升后的类型。
对于 &,结果值是操作数的值按位与的值。
对于 ^,结果值是操作数的值按位异或的值。
对于 |,结果值是操作数的值按位或的值。
int it=8;System.out.println(it&15); //按位与System.out.println(it|15); //按位或System.out.println(it^15); //按位异或
布尔逻辑操作符 &、^、和 |
当操作符 &、^ 、或 | 的两个操作数都是boolean 或 Boolean类型时,位操作符表达式的类型时boolean。在所有情况中,操作数都必须在必要时进行拆箱转换。
对于 &,如果两个操作数的值都是true,结果值是true,否则,结果为false。
对于 ^,如果两个操作数的值不同,结果值是true,否则,结果为false。
对于 |,如果两个操作数的值都是false,结果值是false,否则,结果为true。
boolean bi=true,bt=false;System.out.println(bi&bi); //与操作符System.out.println(bi^bt); //异或操作符System.out.println(bt|bt); //或操作符
条件与操作符 &&
条件与操作符 && 与 & 相似,但是只有在其左操作数的值为 true 时,才会计算其右操作数。
条件与操作符在语法上是左结合的(它自左向右结合)。
条件与操作符的副作用和结果值都是完全满足结合律的。即,对于任何表达式 a、b 和 c,计算表达式 ((a)&&(b))&&( c) 所产生的结果,与计算表达式 (a)&&((b)&&( c)) 所产生的结果相同,并且具有以相同顺序发生的相同的副作用。
条件与操作符的每个操作数都必须是boolean或Boolean类型,否则就会产生编译时错误。
条件与表达式的类型总是boolean。
在运行时,左操作数表达式先被计算,如果计算结果类型为Boolean,那么它会经历拆箱转换。
如果所产生的值为false,那么条件与表达式的值为false,并且右操作数不会再被计算。
如果左操作数的值为true,那么右操作数就会被计算。如果计算结果的类型为Boolean,那么它会经历拆箱转换。所产生的的值会成为条件与表达式的值。
因此,&& 在boolean操作数上的计算结果与 & 相同,区别仅仅在于其右操作数表达式的计算是有条件的,而并不是总会被计算。
条件或操作符 ||
条件或操作符 || 与 | 相似,但是只有在其左操作数的值为 false 时,才会计算其右操作数。
条件或操作符在语法上是左结合的(它自左向右结合)。
条件或操作符的副作用和结果值都是完全满足结合律的。即,对于任何表达式 a、b 和 c,计算表达式 ((a)||(b))||(c ) 所产生的结果,与计算表达式 (a)||((b)||(c )) 所产生的结果相同,并且具有以相同顺序发生的相同的副作用。
条件或操作符的每个操作数都必须是boolean或Boolean类型,否则就会产生编译时错误。
条件或表达式的类型总是boolean。
在运行时,左操作数表达式先被计算,如果计算结果类型为Boolean,那么它会经历拆箱转换。
如果所产生的值为 true,那么条件与表达式的值为 true,并且右操作数不会再被计算。
如果左操作数的值为 false,那么右操作数就会被计算。如果计算结果的类型为Boolean,那么它会经历拆箱转换。所产生的的值会成为条件或表达式的值。
因此,|| 在boolean操作数上的计算结果与 | 相同,区别仅仅在于其右操作数表达式的计算是有条件的,而并不是总会被计算。
参考来源
https://docs.oracle.com/javase/specs/index.html
一元操作符、强制类型转换表达式、乘除操作符、加法操作符、移位操作符、关系操作符、判等操作符、位操作符与逻辑操作符、条件与操作符、条件或操作符--运算机制与返回值相关推荐
- dim private public static_C++ 强制类型转换操作符 static_cast
(给CPP开发者加星标,提升C/C++技能) 来源:melonstreet https://www.cnblogs.com/QG-whz/p/4509710.html static_cast是一个强制 ...
- C++风格的类型转换操作符与C风格的强制类型转换
很多人对C++中的几个类型转换操作符是有些陌生的,并且代码中类型转换也从来都是用C风格的强制类型转换.而且会有些人认为使用这些操作符麻烦,不方便或者没必要.下面是对网上一些资料的总结,主要分析一下两种 ...
- C++强制类型转换操作符 dynamic_cast
dynamic_cast是四个强制类型转换操作符中最特殊的一个,它支持运行时识别指针或引用. >>>>>>>>>>>编译器的RTTI设 ...
- project facets中没有dynamic_C++强制类型转换操作符 dynamic_cast
dynamic_cast是四个强制类型转换操作符中最特殊的一个,它支持运行时识别指针或引用. >>>>>>>>>>>编译器的RTTI设 ...
- C++ 强制类型转换操作符(static_cast、dynamic_cast、const_cast和reinterpret_cast)
C++中的四种操作符形式类型转换 1.static_cast (静态类型转换) 主要使用场景:适用于将void*转换为其他的指针 int a = 100; void* pv = &a; //i ...
- Java核心技术卷一 -第五章:类的强制类型转换与instanceof操作符
系列文章目录 Java核心技术卷一 -第一章:java"白皮书"的关键术语 Java核心技术卷一 -第三章:数据类型 Java核心技术卷一 -第三章:变量与常量 Java核心技术卷 ...
- 【C语言】C语言成长之路之关于C语言的操作符以及一些表达式的讲解(՞• •՞).
操作符目录 1.算术操作符 2.移位操作符 3.位操作符 4.单目操作符 5.关系操作符 6.赋值操作符 7.逻辑操作符 8.条件操作符(三目操作符) 9.逗号表达式 10.一些关于引用调用的操作符 ...
- Python输出格式化 格式化字符串语法 format f-string 格式化操作符% 数据类型转换 对齐方式 转换标志字符
Python输出格式化 格式化字符串语法 1.format 1.1 Format String Syntax 格式字符串语法 str.format() 方法和 Formatter 类共享相同的格式字符 ...
- postgresql常用函数及操作符及类型转换
为什么80%的码农都做不了架构师?>>> 一.逻辑操作符: 常用的逻辑操作符有:AND.OR和NOT.其语义与其它编程语言中的逻辑操作符完全相同. 二.比较操作符: 下面是Po ...
最新文章
- 将jsp页面转化为图片或pdf(一)(qq:2798641729)
- 我的泰坦尼克数据分析
- EJBCA使用之注册用户及创建证书
- mysql移植海思_minigui在海思解决方案(hi3515芯片)上的移植过程(转)
- oracle dbms overflow,Oracle DBA课程系列笔记(12_1)
- P3804-[模板]后缀自动机【SAM】
- LeetCode——maximal-rectangle
- 深度学习在其他领域的应用1:密码破解
- 如何找到python的安装路径_在cmd中查看python的安装路径方法
- 木老师教笨笨课堂——系列讲座(从函数指针到委托) 四、C#的委托
- python numpy安装步骤-python的numpy模块安装不成功简单解决方法总结
- python scikit_Python SciKit学习教程
- linux# 解读wmctrl一览输出的项目
- 零基础起步Keras+LSTM+CRF的实践命名实体识别NER
- 【bug:鳄梨】【上线前修改其他bug急着提交造成的bug】
- 解决更新Win11后没有ie浏览器问题
- Redis5.0集群搭建(三主三从)、添加一主一从、JedisCluster连接集群
- 石家庄地铁(李秦,王学云)2
- giant和huge的区别
- vs2015 :“64位调试操作花费的时间比预期要长“,无法运行调试解决办法