天天看點

java中運算符詳解

前言

運算符用于執行程式代碼運算,會針對一個以上操作數項目來進行運算。JAVA中常見的運算符有很多種,大緻分為以下幾種,常見的幾種運算符如下圖:

算術運算符 加、減、乘、除、求餘。例++、--、%、/、 
指派運算符 為變量或常量起到指派作用的。例如=、+=、*=
關系運算符 判斷資料大小的,結果為一個布爾值。例如>、>=、!=、==
邏輯運算符 進行邏輯運算,運算的成員為布爾值,結果也為布爾值。例如&&、||、!。
條件運算符 也稱三目運算符,表達式為(a<b)?a:b
位運算符 對二進制進行操作的,例如&、|、^、~、

一、算術運算符

1.1、常見的算術運算符

+ 加法運算;例如x+y
- 減法運算;例如x-y
* 乘法運算;例如x*y
/ 除法運算;例如x/y 10/3=3; 10/2.0=5.0
% 取模運算(求餘運算);例如x%y  10%3=1
++ 自增運算;例如x++,++x
-- 自減運算;例如x--,--x

1.2、算術運算中的類型轉換

    我在我的上一篇部落格裡面詳細講解了關于JAVA基本資料類型的類型轉換了的,可以參考連結:https://blog.csdn.net/qq_37688023/article/details/85106894仔細看一下即可。不過在這裡大緻說一下其中幾個重要的點。

  1. 運算時,運算結果會向資料類型大的轉換;
  2. 在運算時,byte、short、char類型先自動轉換為int類型
@Test
	public void a() {
		int a=3;
		double b=4;
		System.out.println(a+b);//輸出7.0
		
		float c=3.2f;
		/*c=c+3.14; 編譯錯誤,運算之後變為double類型*/	
		
		byte a1=3;
		byte b1=4;
		/*byte c1=a+b;
		 * 編譯錯誤,此處由于byte類型參與運算時,先直接轉換為int類型,
		 * 是以最後的結果也是int類型,但是得出的結果不能叫做int類型的直接量,是以編譯錯誤
		 * */
		int d1=a1+b1;
	}
           

1.3、關于i++與++i的差別

  • i++是++在後,是以先用了再加
  • ++i是++在前,是以先加了再用

注意點:a+++b與a+ ++b的差別  a+++b的含義是(a++)+b;而a+ ++b的含義為a+(++b)

@Test
	public void b() {
		int a=1;
		int b=1;
		System.out.println(a++);//輸出1
		System.out.println(++b);//輸出2
		
		int a1=10;
		int b1=10;
		int sum=a1+++b1;
		System.out.println(a1);//輸出11
		System.out.println(b1);//輸出10
		System.out.println(sum);//輸出20
		
		int a2=10;
		int b2=10;
		int sum1=a2+ ++b2;
		System.out.println(a2);//輸出10
		System.out.println(b2);//輸出11
		System.out.println(sum1);//輸出21	
	}
           

二、指派運算符

2.1、常見的指派運算符

運算符 含義 舉例
= 等于 c=a+b就是将a+b的值賦給c
+= 加等于 a+=1等價于a=a+1
-= 減等于 a-=1等價于a=a-1
*= 乘等于 a*=b等價于a=a*b
/= 除等于 a/=b等價于a=a/b
%= 求餘等于 a%=b等價于a=a%b

2.2、關于a+=1與a=a+1的差別

雖然在上面說的a+=1是等價于a=a+1,但是他們兩還是有差別的。

@Test
	public void c() {
		byte a=1;
		/*a=a+1;
		 * 這裡會報編譯錯誤,由于a+1出現類型轉換,變為int類型,是以再指派給byte類型是以編譯錯誤
		 * */
		a+=1;
		/*這裡是不會出現編譯錯誤的,這就是它的特殊之處
		 * */
		System.out.println(a);//輸出2
		
		byte b=127;	
		b+=1;
		System.out.println(b);
		//輸出-128,是以其實就是b=(byte)(b+1);b+1為128,超出byte類型的範圍,資料溢出為-128
	}
           

原因:+=運算符,是java語言規定的一進制運算符(這裡我将它歸為指派運算符有些不妥),Java有自動轉換機制,java編譯器會對其進行特殊處理,預設的向右轉換類型,不需要人工轉換。

2.3、各種指派運算符的例子

@Test
	public void d() {
		int a=10;
		double b=10;
		System.out.println(a+=b);
		//輸出20 a=(int)(a+b)=(int)(10+10)=20
		System.out.println(b+=a);
		//輸出30.0 b=a+b;double類型大,是以不需要自動轉換 b=20+10=30.0
		
		int a1=10;
		int b1=10;
		System.out.println(a1*=b1);//輸出100
		System.out.println(a1/=b1);//輸出10
		System.out.println(a1%=b1);//輸出0
		System.out.println(a1-=b1);//-10
	}
           

三、關系運算符

3.1 常見關系運算符

關系運算符有時候也稱為比較運算符,運算的結果一定為布爾值

符号 含義 舉例
> 判斷是否大于 boolean a=3>2;  a=true
< 判斷是否小于 boolean a=3<2;  a=false
>= 判斷是否大于等于 boolean a=3<=3;  a=true
<= 判斷是否小于等于 boolean a=3>=4;  a=fasle
== 判斷是否等于 boolean a=3==4;  a=false
!= 判斷是否不等于 boolean a=3!=4;  a=true

注意點:

  1. < 、>、>=、<=隻能比較數值類型,是以不能比較布爾類型和大多數應用類型,但是有些基本資料類型包裝類型可以。
  2. ==、!= 既能比較基本資料類型,也能比較引用資料類型。但是在比較引用資料類型的時候比較的就是位址了。
@Test
	public void e() {
		int a1=10;
		int b1=10;
		boolean a2=false;
		boolean b2=false;
		Integer a3=10;
		Integer b3=10;
		A a4=new A();
		A b4=new A();
		
		System.out.println(a1>b1);
		/*System.out.println(a2>b2); 非數值類型不能比較大小*/
		System.out.println(a3>b3);
		/*System.out.println(a4>b4);不能比較大小*/
		
		System.out.println(a1==b1);
		System.out.println(a2==b2);//true
		System.out.println(a3==b3);
		//相等,由于基本資料類型包裝類Integer的自動裝箱的特性,直接從靜态數組中取的,是以不是new的
		System.out.println(a4==b4);	
		//false,由于是new出來的不等
	}
           

3.2、==與equals的差別

首先我們要知道,equals方法是Object類中的一個方法,而Object類是所有類的父類。首先我們先看一下Object類中equals方法的源代碼:

public boolean equals(Object obj) {
        return (this == obj);
    }
           

可以看出Object類中該方法的邏輯就是==,是以Object類中給出的equals方法就是判斷是否==;

那我們再看一下String類的equals方法,如下圖:

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不再是簡簡單單的直接判斷==;它的equals方法是先判斷它們是否==,如果不等再判斷兩個引用是否為同類或父子類關系,是的話再判斷是否值一樣,一樣則傳回true,否則傳回fasle。是以使用equals方法時要看該引用對應的類的equals方法的實作邏輯,一般情況下是String的話就是先比較位址,位址相等就為true,不為再比較内容,内容一樣也是為true。

是以==與equals的差別可以總結為以下幾點:

  1. ==是既能比較基本資料類型,比較基本資料類型的時候比較的是值是否相等。又能比較引用資料類型,比較引用資料類型比較的是引用對應的位址是否相等。
  2. 而equals是一個方法,是以隻能用來比較引用資料類型,要根據類中該方法實作的邏輯是什麼來看比較的是什麼。一般情況下就是String類型和自定義的類。對應String類型是判斷内容是否一緻。
@Test
	public void f() {
		String a=new String("abc");//new出來的對象放在了堆中
		String b=new String("abc");//不管内容中有無,都是直接new出一個新的對象
		String c="abc";//放在常量池中
		String d="abc";//現在常量池中找師傅有abc,有的話就将d指向該對象
		
		System.out.println(a.equals(b));//true 隻判斷内容是否相等
		System.out.println(a==b);//false 判斷位址是的相等
		
		System.out.println(a.equals(c));//true 
		System.out.println(a==c);//false 位址不同
		System.out.println(a==d);//true 位址一樣,由于都指向一個常量池中的同一個對象
	}
           

四、邏輯運算符

4.1、常用邏輯運算符

 首先邏輯運算符運算的對象必須還是布爾值,即符号兩邊必須是布爾類型。

運算符 名稱 舉例
&& a&&b,a和b都為true,結果才為true,否則為false
|| a||b,a和b都為false,結果才為false,否則為true
! !a,a為false,則結果為true。即結果相反
變量a 變量b a&&b a||b !a
true true true true false
true false false true false
false false false false true
false true false true true

4.2、關于邏輯運算符中"短路"的問題

  1. 對于&&而言,當第一個操作數為false時,将不會判斷第二個操作數。
  2. 對于 || 而言,當第一個操作數為true是,将不會判斷第二個操作數。
@Test
	public void g() {
		int a=10;
		int b=10;
		boolean c=a>10 && b++>10;
		System.out.println(c);
		System.out.println(b);//輸出10,由于a>10已經為False之後,後面的b++也就不會執行
		boolean d=a<11 || b++>10;
		System.out.println(d);
		System.out.println(b);//也是輸出10,前面的a<11已經為true,是以後面的b++也不執行
	}
           

五、條件運算符

 即為三目運算符,形式為:布爾表達式 ? 表達式1 :表達式2  

運算過程:如果布爾表達式的值為 true ,則傳回 表達式1 的值,否則傳回 表達式2 的值

用處:和if else的分支結構作用有點類似。但是寫法比 if else簡潔。

@Test
	public void h() {
		int[] a= {-1,2,0};
		for(int i=0;i<a.length;i++) {
			a[i]=a[i]>=0?a[i]:0;//将數組中小于0的數字變為0
		}
		for(int b:a) {
			System.out.println(b);//利用增強for循環輸出數組中的值
		}
	}
           

六、位運算符

6.1、常用位運算符

符号 含義
& 按位與
| 按位或
~ 非(取反運算符)
^ 異或
<< 左移運算符
>> 右移運算符
>>> 無符号右移運算符

6.2、按位與 & 詳解

       按位與 '&' 與邏輯與 '&&' 不同,邏輯與 '&&' 隻能操作布爾類型的值,而按位與 '&'不僅能操作布爾類型的值,還能操作整數型基本類型。且按位與 '&' 不像邏輯與 '&&'一樣有短路效應。

  • 操作布爾類型的值時,與邏輯與 '&&' 類似,也是見false則false,就是沒有短路效應。
  • 操作整數基本資料類型是,即将整數化為二進制,然後相同位上計算,相同位都是1則結果為1,否則為0

例如:3&4

        3的二進制為    0000 0000 0000 0011

        4的二進制為    0000 0000 0000 0100

        則結果為         0000 0000 0000 0000 對應的二級制為0,是以3&4=0;

(1)按位與的常見作用:清0,取一個數中的指定位數(例如取int類型值的低八位)

例如int類型變量a對應的二進制為 0010 0001 0000 1010 ,現在求它的低八位,也就是1010,就可以使用a&15

                       15對應的二進制為 0000 0000 0000 1111  是以 ,a&15的結果就為 0000 0000 0000 1010

(2)判斷一個數的奇偶性:n&1 == 1?”奇數”:”偶數”

(3)面試題:優化n%8                   

解答:n&7

原因:對8取模(求餘)運算的定義就是整除8後的餘數,而對于8來說,這個餘數恰好就是這個數的二進制表示的低3位; 是以對8取模等價于擷取資料的低3位,這由恰好等價于 &7 的結果。

6.3、按位或 | 詳解

 按位或 '|' 與邏輯或 '||' 不同,邏輯與 ' || ' 隻能操作布爾類型的值,而按位與 ' | '不僅能操作布爾類型的值,還能操作整數型基本類型。且按位或 '|' 不像邏輯或 ' || '一樣有短路效應。

  • 操作布爾類型的值時,與邏輯或 ' || ' 類似,也是見true則true,就是沒有短路效應。
  • 操作整數基本資料類型是,即将整數化為二進制,然後相同位上計算,隻要有一個1就為1,否則結果為0

例如 3 | 4

        3的二進制為    0000 0000 0000 0011

        4的二進制為    0000 0000 0000 0100

        則結果為          0000 0000 0000 0111 對應的二級制為7,是以3&4=7;

按位或的作用:将一個數的某些位置變為1

6.4、按位非 ~詳解

     也稱為取反運算符,是一個一進制運算符,是以隻能對一個操作數進行操作。按位非就是将各位數字取反,0變為1,1變為0;

例如   4的二進制為 0000 0000 0000 0100

是以~4的二進制為  1111 1111  1111  1011 轉換為是十進制為-5

     常見作用:一個數的相反數=該數取反+1     例如4的相反數-4=~4+1=-5+1=-4

6.5、異或 ^ 詳解

    将數轉換為二進制之後運算,然後不相同的結果就為1;  0^0=0  1^0=1  0^1=1  1^1=0

例子:4的二進制為  0000 0000 0000 0100

           5的二進制為  0000 0000 0000 0110

       4^5的二級制為  0000 0000 0000 0010 轉換為十進制為2

異或^的作用執行個體:不适用臨時變量交換兩個數

 我們經常寫變量交換時,是以如下,用了一個臨時變量來交換的;

@Test
	public void a() {
		int a=3;
		int b=4;
		int c;//定義的臨時變量
		
		c=a;
		a=b;
		b=c;
		System.out.println(a);//輸出4
		System.out.println(b);//輸出3
	}
           

但是利用異或運算符不用使用臨時變量

@Test
	public void b() {
		int a=3;
		int b=4;
		
		a=a^b;
		b=b^a;//b=b^(a^b) 		->b=a
		a=a^b;//a=(a^b)^(b^a)	->a=b
		System.out.println(a);//輸出4
		System.out.println(b);//輸出3
	}
           

6.6、左移、右移、無符号右移動

  1. 左移運算符 << :符号位不變,低位補0;
  2. 右移運算符 >> :是低位溢出,符号位不變,并用符号位補溢出高位
  3. 無符号右移運算符>>>:低位溢出,高位補0,注意,無符号右移(>>>)中的符号位(最高位)也跟着變,無符号的意思是将符号位當作數字位看待。
@Test
	public void c() {
		int a=4;
		System.out.println(a<<2);
		/*   0000 0000 0000 0100  a的二進制
		 * 000000 0000 0001 0000   低位補兩個0
		 *   0000 0000 0001 0000  高位溢出,是以a<<2=8  
		 * */
		
		System.out.println(a>>2);
		/*	0000 0000 0000 0100
		 *  0000 0000 0000 000100  符号位補高位
		 *  0000 0000 0000 0001   低位溢出  是以 a>>2=1
		 * */
		
		int b=-1;
		System.out.println(b>>>1);
		/*  1111 1111 1111 1111
		 *  0111 1111 1111 11111 右移一位,高位補0,
		 *  0111 1111 1111 1111  低位溢出 結果為 2147483647
		 * */
	}
           

七、各種運算符的優先級

    下圖為java中各種運算符的優先級,可以記一下,但有時候不太清楚的時候就直接用括号()吧!

運算符 結合性
[ ]  .  ( ) (方法調用) 從左向右
! ~ ++ -- +(一進制運算) -(一進制運算) 從右向左
*  /  % 從左向右
+ - 從左向右
<< >> >>> 從左向右
< <= > >= instanceof 從左向右
== != 從左向右
& 從左向右
^ 從左向右
| 從左向右
&& 從左向右
|| 從左向右
?: 從右向左
= 從右向左