本文参考自Java编程思想第四版,并结合自己现有知识做的一些总结。
在最底层,Java中的数据都是通过使用 *** 作符来 *** 作的。
Java中大多数 *** 作符的使用都与C一致,但同时Java也做了一些改进和优化。在本文中,我将结合书中所读给大家分享一下我认为的Java中使用 *** 作符的注意点和技巧。
1. 优先级当一个表达式中存在多个 *** 作符时, *** 作符的优先级就决定了各部分的计算顺序。Java中完整的运算符优先级规则很多,我们只需要记住最简单常用的——先乘除后加减,先算数运算后按位运算,逻辑与(&&)>逻辑或(||),运算符优先级相同时先左后右。当遇到不确定谁该先计算的时候,不要节省括号的使用,把你想要先计算的表达式用括号括起来,记住()是第一优先级。
2. 赋值符号赋值符号“=“,将右边的值赋给左边的值。
如下:
int a = 4; int b = a; float c = 3.0f; float d = c;
这是最常见也是正确的写法,他不会带来任何相关的影响(如在后面的语句中改变a的值,不会影响b,改变c的值不会影响d),因为基本数据类型的值也存在于堆栈中,在赋值的时候,只是将右边的值内容复制一份,然后存储到左边的值中,二者在之后的语句中不会有相关影响。
但是在为对象“赋值”的时候,结果又与基本数据类型大不相同。
public class A{ int id; public static void main(String[] args){ A a1 = new A(); A a2 = new A(); a1.id = 1; a2.id = 2; System.out.println(a1.id + " " + a2.id); a1 = a2; System.out.println(a1.id); a1.id = 1; System.out.println(a2.id); } }
输出:
1 2 2 1
在Java中,我们 *** 作一个对象时,实际上 *** 作对这个对象的引用。这个时候若将一个对象的引用赋值给另一个对象的引用,如上面代码中的a1 = a2;,两个不同的对象引用都指向了同一个实际的对象。这时,无论是利用哪个对象引用 *** 作了实际对象,在另外一个对象引用 *** 作实际对象时,该实际对象已经发生了改变。
因此在实际编码的时候,我们需要尽量减少这种显示的对象引用间的赋值。因为很可能在某一个地方你不小心改变了实际对象的值,当你用其它指向该实际对象的对象引用访问它时,你会觉得很奇怪(你没有意识到还有其它的对象引用指向到了它,并且 *** 作会同步到这个实际对象上)。
3. 关系 *** 作符关系 *** 作符包括:> < >= <= == !=,生成的值一个boolean数据。如果操作数之间的关系是真的,则返回true,否则返回false。
所有的关系 *** 作符都可以作用于基本数据类型(boolean只能被==和!=作用),这些与我们在C语言中所学到的基本一致。需要注意的是,==和!=可以作用于两个对象,并且会产生让你觉得不正确的答案。
public class test2 { public static void main(String[] args) { Integer i1 = new Integer(1); Integer i2 = new Integer(1); System.out.println(i1 == i2); } }
大家可以猜一下上面这段代码会输出什么,按照我们前面所学的知识以及常识来说,应该会输出true(因为它们实际的值相等),但事实上它输出了false。这是为什么呢?
关系 *** 作符和!=作用两个对象时,实际上是比较这两个对象的引用是否相同。那如何知道两个对象的引用的是不相同的呢。还记得我们在上一篇文章中所说的,对象的引用是存放在堆栈中的,声明了两个不同的对象引用,并且不是某一个对象引用指向了另一个对象引用,它们自然是不相同的。如果声明Integer i2 = i1;这时i1i2就会返回true。
如果想要比较两个对象的真实内容是否相等时,我们可以重写并调用equals方法(来自Object类)。Integer中已经重写了equals方法。
public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }
可以看出Integer类重写的equals方法是比较两个Integer对象的真实值是否相等。
public class test2 { public static void main(String[] args) { Integer i1 = new Integer(1); Integer i2 = new Integer(1); System.out.println(i1.equals(i2)); } }
输出:
true
**注意:**我们自己定义的类的equals方法是继承自Object类的,它默认还是比较对象的引用,如果想要通过其它方式比较两个对象是否相等,我们需要重写equals方法。
4. 逻辑 *** 作符逻辑 *** 作符:&&(与),||(或),!(非)
注意:逻辑 *** 作符只能作用于boolean值!!!
在C语言中
while(2){ }
是可以编译通过的,同时c语言中0可以表示false,1可以表示true,同时!0=1,这在Java中是不允许的,编译器会在编译期间直接报错。
下面是Java中逻辑 *** 作符允许的一些 *** 作:
if(true || false) if(true && false) if((2 > 1) || (1 > 2)) if((2 > 1) || (1 > 2)) if(!true) if(!false) .......
"短路"现象:
表达式1 || 表达式2 || 表达式3 || 表达式4 …
在表达式x为true时,表达式x+1到最后一个表达式就不会去计算了,因为最后结果一定为true
表达式1 && 表达式2 && 表达式3 && 表达式4 …
在表达式x为false时,表达式x+1到最后一个表达式就不会去计算了,因为最后结果一定为false
public class shorttest { static boolean test1(int val){ System.out.println("test1"); return val > 1; } static boolean test2(int val){ System.out.println("test2"); return val > 2; } static boolean test3(int val){ System.out.println("test3"); return val > 3; } public static void main(String[] args) { boolean b = test1(2) && test2(1) && test3(4); } }
如果没有短路现象的话,输出的值应该为
test1 test2 test3
但由于短路现象的存在,并且test2返回false,全是&&逻辑运算符,所以最后不会执行方法test3。
5. 直接常量程序中有的直接常量是模棱两可的,这时我们需要对编译器加以适当的“指导”,用于给直接量添加一些额外信息。
long:2324l,2324L
float:2.0f,2.0F
double:2.0d,2.0D
八进制:0开头,后面跟0-7的数
十六进制:0x223abc 0x223ABC
// short占两个字节 short a = 0x1;
上述代码是可以通过编译的,你试想一下0x1这个常量会被识别为什么数据类型是short,还是识别为int然后截取为short(丢掉高24位)?这里我们无法得到答案。
char a = 0xffff; short b = 0xffff;
这个时候,a是可以通过编译的,但b不行,这是为什么呢?二者明明都是2个字节。这就牵扯到了0xffff是被识别为char或short,还是先识别为int,再进行截取。
- 如果是识别为char或short,那么它刚好两个字节,在计算机中,常量用补码存在,0xffff对应的值为-1,那么按道理二者都是通过编译的。但又不对,Java中char支持的编码是Unicode编码,为此char是个无符号数,是不存在负号的。那么结果应该是a不能通过编译,b可以,这与实际结果相反,所有0xffff没有被识别为两个字节。事实是会被识别为int,这时0xffff先被识别为0x0000ffff(对应的值为255),然后自动转化为两个字节(值还是255,由于char是无符号数,255刚好是它的最大值,而short就不行(255大于short的最大值),编译器要求你0xffff,进行强制转化(short b = (short) 0xffff;)。这刚好符合实际结果。
Java中浮点数的默认值是double
float = 1.0;是无法通过编译的,因为1.0的数据类型默认是double,double->float需要显示转化,或者我们可以写成float = 1.0f;
6. 位 *** 作符当算数 *** 作符作用于byte,short,char时,会自动将它们的数据类型升到int,这个是否如果最终的值还希望并且确实是byte,short,char时,需要进行显示的数据类型转化,如下:
byte c = 1; byte d = 1; byte e = (byte) (c + d); byte e = (byte) (c - d); byte e = (byte) (c * d); byte e = (byte) (c / d); byte e = (byte) (c << d); byte e = (byte) (c >> d); byte e = (byte) (c & 1); byte e = (byte) (c | 1); byte e = (byte) (~c);
按位与:
同为1时为1,否则为0 00000011 3 & 00000010 2 = 00000010 2
按位或:
同为0时为0,否则为1 00000011 3 & 00000010 2 = 00000011 3
按位非:
0变1,1变0 ~1 ~00000001 11111110(负数,补码) 为得到源码,减1取反 -1= 11111101 取反= 10000010 结果=-2
左移:
低位补0 1 << 2; 00000001 << 2; 00000100 等于4
带符号右移:
高位补符号位的值 4 >> 1; 00000100 >> 1; 00000010 等于2 -4 >> 1; 11111100 >> 1; 11111110 等于 减一取反 ->11111101->10000010 -2
无符号右移:
无论符号位为什么,高位均补0 使用byte类型的数据进行无符号右移时,会先转化为int类型,然后移位, 如果最后要再赋值给byte类型的数据需要截断。(移动时符号位也会移动)。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)