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} ~ 2 31 1 2^{31}-1 Integer
float 32 bits 2 31 -2^{31} ~ 2 31 1 2^{31}-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

Reference

Bruce Eckel, Thinking in Java 4th Edition

猜你喜欢

转载自blog.csdn.net/qq_32483589/article/details/87868820
今日推荐