前言

运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算。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基本数据类型的类型转换了的,可以参考链接:仔细看一下即可。不过在这里大致说一下其中几个重要的点。

  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

从左向右

== !=

从左向右

&

从左向右

^

从左向右

|

从左向右

&&

从左向右

||

从左向右

?:

从右向左

=

从右向左