Chapter3 JAVA中的操作符(operators)
0. Primitive type
首先复习一下java中的primitive type,这是操作符部分非常重要的内容。需要注意的是java中的数据原型大小固定,因此增加了java程序的可移植性(potability),也不需要像c和c++中的sizeof()调用。
primitive type | size | range | wrapper type |
---|---|---|---|
byte | 8 bits | -128~127 | Byte |
char | 16 bits | 0~65535 | Character |
short | 16 bits | -32768~32767 | Short |
int | 32 bits | − 2 31 -2^{31} −231~ 2 31 − 1 2^{31}-1 231−1 | Integer |
float | 32 bits | − 2 31 -2^{31} −231~ 2 31 − 1 2^{31}-1 231−1 | Float |
long | 64 bits | … | Long |
double | 64 bits | … | Double |
boolean | - | - | Boolean |
void | - | - | Void |
1. 优先级(Precedence)
- java中的优先级和C差不多,但个人觉得没有必要去硬记,不仅心累,而且代码可读性不高,因此推荐使用圆括号()(parenthesis)。
2. 赋值(Assignment)
-
赋值符号:=
-
primitive type的赋值很好理解,相当于把地址单元内容赋值给某一变量。如果是不同变量之间的赋值,则相当于进行了copy,即会在内存中存在两个相同的内容。
示例如下, 对int类型a2的操作并不影响a1的值:
public class AssignmentTest1{ public static void main(String[] argv){ int a1 = 1; int a2 = 8; a2 = a1; a2++; System.out.println("a1 = " + a1 + "; a2 = " + a2); } }
Output:
a1 = 1; a2 = 2 -
对于一般的Object进行赋值时,需要格外小心。因为java中的对象变量并不是对象实体,而是指向对象实体的引用(reference,可以理解为c中的指针,保存对象实体的内存地址)。
示例如下,r1和r2此时引用了同一个对象,因此r1的操作也会影响r2:
public class AssignmentTest2{ public static void main(String[] argv){ ReferenceTest r1 = new ReferenceTest(); ReferenceTest r2 = new ReferenceTest(); r1.t = 1; r2.t = 8; r1 = r2; r1.t++; System.out.println("r1.t = " + r1.t + "; r2.t = " + r2.t); } } class ReferenceTest{ public int t; }
Output:
r1.t = 9; r2.t = 9 -
但是看下一个例子,按照此前的思路,String对象b的改变,a也应该随之改变,但结果显示b和a此时已经引用了两个不同的对象。这是由于java对String对象的特殊处理,下一篇将会介绍String以及java的pass-by-value原理,这将非常有助于理解这部分内容,同时“按值传递”也是面试中经常会出现的类型。
public class AssignmentTest3{ public static void main(String[] argv){ String a = new String("iam_a"); String b = new String("iam_b"); b = a; b = "changed!"; System.out.println("a = "+ a + "; b = "+ b); } }
Output:
a = iam_a; b = changed!
3. 关系操作符(Relational operator)
-
< , <=, >, >=, ==, !=
-
关系操作符 ==,!= 可以对任意primitive type进行操作(void默认为非原型);其余操作符能对除boolean外的任意primitive type进行操作。
-
前面说过,java通过引用对象(reference)来实现操作,因此如果对object variable进行关系符操作的话,实际上被操作的是reference,此时可能会出现问题。
如下所示,即使对象r1,r2的内容一样,但是因为它们的reference或者说所引用对象的内存地址不同,因此结果为false。
public class RelationalTest1{ public static void main(String[] argv){ RelationalTest r1 = new RelationalTest(); RelationalTest r2 = new RelationalTest(); r1.t = 1; r2.t = 1; System.out.println(r1==r2); } } class RelationalTest{ public int t; }
Output:
false
-
实际上,java在Object对象(即root对象,java是single-root-hierarchy)中就定义了equals方法,但从源码的解释中可以看到,“当且仅当reference x和y指向同一个object时,才返回true“,即该方法默认比较的还是reference。为了比较对象的内容我们需要override equals方法。
以String部分源码为例,可以看到String的equals方法比较的就是String的内容:public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString =(String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
-
最后再对String对象进行测试,实际上任何自定义的对象都可以改写equals方法。
public class RelationalTest2{ public static void main(String[] argv){ String r1 = "iamr1"; String r2 = "iamr1"; System.out.println(r1.equals(r2)); } }
Output:
true
4. 逻辑操作符(Logical operator)
-
与:&&, 或:||, 非: !
-
逻辑运算符只能操作boolean类型,这和c有所不同。在c中,如x是一个非零的int类型变量,!x可以表示false,但这样的语法在java中不被允许。
-
Shortcircuit
如在if-else判断语句中,当结果确定后便不再执行后续判断
示例如下
public class Test2 { public static void main(String[] argv){ ShortCircuitTest t1 = new ShortCircuitTest(); ShortCircuitTest t2 = new ShortCircuitTest(); System.out.println("test1:"); if(t1.range(11)&&t2.range(2)&&t1.range(11)) {} System.out.println("test2:"); if(t1.range(11)||t2.range(2)||t1.range(11)) {} } } class ShortCircuitTest{ public boolean range(int t) { if(t>10) { System.out.println(t); return true; } else { System.out.println(0); return false; } } }
Output:
test1:
11
0
test2:
11
5. 直接量(Literals)
-
八进制0开头,十六进制0x/0X开头,long类型l/L结尾,f类型f/F结尾,double类型d/D结尾。
-
一般情况下,当数据范围和数据类型匹配时,可以省略结尾字符,如long i = 1(L)。但有些情况下不能省略,如java中小数默认为double类型,因此,float i = 1.2f 时结尾必须加f。具体规则可以参照下面的casting介绍。
6. 位操作(Bitwise operation)
- 与(AND):&, 或(OR):|, 非(NOT):~,异或(XOR, exclusive or):^
- 可以和 = 相结合形成compound operator:&=, |=, ^=,但~是一元操作符,无该操作。
- boolean可以认为是one-bit value,也可以进行位操作。但是不能进行“非”操作,实际上boolean的非操作由逻辑操作符!实现。同时,boolean类型也不能进行移位操作。
7. 移位操作符(Shift operator)
-
左移:<<,算术右移:>>,逻辑右移:>>>
-
移位只能对char,byte,short,int和long进行移位操作。算术右移即有符号右移,当原数为负时,左边添1;为正数时,左边添0。逻辑右移即不考虑符号,右移添0。
-
同样可以和 = 相结合,构成<<=, >>=, >>>=。但这里需要注意,当对char,byte和short进行移位时,存在一种叫做promotion的现象。即这些类型首先会成为int类型,然后再进行移位,最后赋值回原先类型。因此最终的结果可能会出错。
示例如下,注意Integer.toBinaryString(int i)调用时如果i的类型为byte,char或short会自动转换为int类型进行输出。
public class ShiftTest1 { public static void main(String[] argv){ int i = -1; System.out.println(Integer.toBinaryString(i)); i >>>= 10; System.out.println(Integer.toBinaryString(i)); short s = -1; System.out.println(Integer.toBinaryString(s)); s >>>= 10; System.out.println(Integer.toBinaryString(s)); s = -1; System.out.println(Integer.toBinaryString(s>>>10)); } }
Output:
11111111111111111111111111111111
1111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111
1111111111111111111111
8. 造型(Casting)
-
casting是指数据类型的转化,一种是widening casting,即上面提到的promotion。类型长度较短的primitive type可以自动向较长的进行转化,可以指定也可以不指定。这是比较好理解的,因为数据长度的增加意味着更多的信息,包括上面提到的Integer.toBinaryString(int i)调用,因为参数i类型被定义为int,因此当碰到byte等类型时可以自动实现widening casting。
byte a = 1; int b1 = a; int b2 = (int)a; //both can perform casting, but this is superfluous
-
另一种造型是narrowing casting,即和上述相反,数据长度减小,也就意味着信息的减少,java认为信息的减少需要得到确认,因此必须进行指定,即在需要造型的数据前强制类型转换,否则程序会报错。
int a = 1; short b = (short)a;
-
对象同样存在造型,例如Circle对象可以被造型为Shape对象,但不能被造型为Animal对象。具体规则之后的文章将会展开。
9. 删尾(Truncation)& 四舍五入( Round)
-
在对float和double类型造型为整数如int类型时,java默认直接将小数部分去掉,即所谓的truancation。
-
也可以在指定造型时使用Round方法,进行四舍五入。Round方法是java.lang.Math类中的方法,因此已经默认载入,不需要import。
示例如下:
public class RoundTest1 { public static void main(String[] argv){ float below = 0.3f; double above = 2.8; System.out.println("(int)below = "+(int)below); System.out.println("(int)above = "+(int)above); System.out.println("round_below = "+Math.round(below)); System.out.println("round_above = "+Math.round(above)); } }
Output:
(int)below = 0
(int)above = 2
round_below = 0
round_above = 3
10. 三元操作符(Ternary)& Random类
-
boolean-expression?value0:value1
-
相当于if-else操作,当表达式为true时返回value0,否则返回value1。相比于if-else,三元操作符更简洁,但代码可读性不一定更优。
-
Random是包java.util中的类,用来生成随机数,示例使用方法如下,参数22作为Random的seed,具有可预测性,即生成相同的Random序列。如果没有设置参数则默认当前时间为seed。
public class RandomTest1 { public static void main(String[] argv){ Random rand = new Random(22); int t = rand.nextInt(100)+1; int count = 5; while(count--!=0) { System.out.println(t); } } }
Output:
3
3
3
3
3