操作符
优先级
运算符的优先级别,基本顺序是(一级最高,16级最小 ):
1 级: . ()
2 级: ++ --
3 级: new
4 级: * / %
5 级: + -
6 级: >> << >>>
7 级: > < >= <=
8 级: == !=
9 级: &
10 级: ^
11 级: !
12 级: &&
13 级: ||
14 级: ?:
15 级: = += -= *= /= %= ^=
16 级: &= <<= >>=
赋值
一、基本类型数据赋值
基本数据类型是值赋值
int a = 3; int b = a ; a = 5 ; System.out.println(" a = " + a+" b = "+b );
二、对象类型数据赋值
对对象进行赋值,我们实际操作的是对象的引用,所以倘若将一个对象赋值给另外一个对象,实际上是将“引用”从一个地方复制到另外一个地方,如对对象使用a=b,那么a和b都指向了原本只有b指向的那个对象。这种特殊的现象被称作别名现象,在Java中是操作对象的一种基本方式。
class Tank { int level; } public class Assignment { public static void main(String[] args) { Tank t1 = new Tank(); Tank t2 = new Tank(); t1.level = 9; t2.level = 47; System.out.println("1: t1.level: " + t1.level + ", t2.level: " + t2.level); t1 = t2; System.out.println("2: t1.level: " + t1.level + ", t2.level: " + t2.level); t1.level = 27; System.out.println("3: t1.level: " + t1.level + ", t2.level: " + t2.level); } } /* Output: 1: t1.level: 9, t2.level: 47 2: t1.level: 47, t2.level: 47 3: t1.level: 27, t2.level: 27 */
如果只是想将b中的属性的值赋值给a属性,则可以直接将b对象的某一属性值赋值给a对象的某一属性值。这样操作的结果a和b对象的存储空间则是不一样的。
t1.level=t2.level;
同样适用这种别名现象的还有调用函数的参数,将对象的引用传递过去,而不是将对象的值传递过去。
class Letter { char c; } public class PassObject { static void f(Letter y) { y.c = 'z'; } public static void main(String[] args) { Letter x = new Letter(); x.c = 'a'; System.out.println("1: x.c: " + x.c); f(x); System.out.println("2: x.c: " + x.c); } } /* Output: 1: x.c: a 2: x.c: z */
三、String类型数据赋值
String s1 = "a" ; String s2 = s1; s1 = "b" ; System.out.println("s1 = "+s1+" s2 = "+ s2);
java 中String是 immutable的,也就是不可变,一旦初始化,引用指向的内容是不可变的(注意:是内容不可变)。也就是说,假设代码中有String str = “aa”;str=“bb”;,则第二条语句不是改变“aa”原来所在存储地址中的内容,而是另外开辟了一个空间用来存储“bb”;同时由于str原来指向的“aa”现在已经不可达,jvm会通过GC自动回收。
package com.test; public class Example { String str = new String("good"); char[] ch = { 'a', 'b', 'c' }; public static void main(String[] args) { Example ex = new Example(); ex.change(ex.str, ex.ch); System.out.println(ex.str); System.out.println(ex.ch); } public void change(String str, char ch[]) { str = "test ok"; ch[0] = 'g'; } }
结果如下:
good gbc
package com.soft; public class ExecutorsDemo { public static void main(String[] args) { String s1="abc"+"def"; String s2=new String(s1); if(s1.equals(s2)) System.out.println("equals succeeded"); if(s1==s2) System.out.println("==succeeded"); } }
结果:
equals succeeded
解说:上述代码中,s1与s2指向不同的对象,但是两个对象的内容却是一样的,故“s1==s2”为假,s1.equals(s2)为真。
此处我们来细说一下"=="与equals的作用:
(1)"=="操作符的作用
A、用于基本数据类型的比较
B、判断引用是否指向堆内存的同一块地址
(2)equals的作用
用于判断两个变量是否是对同一个对象的引用,即堆中的内容是否相同,返回值为布尔类型
参考资料
(1)java中String类型变量的赋值问题https://www.cnblogs.com/studyLog-share/p/5307913.html
(2)字符串http://www.cnblogs.com/dengrenning/articles/9023580.html
(3)Java之内存分析和String对象 http://www.cnblogs.com/devinzhang/archive/2012/01/25/2329463.html
算术运算符
常用算术运算符:
Java复合分配操作符
语句如下
a = a + 4;
可以重写为
a += 4;
两个语句执行相同的操作:它们将a
的值增加4。
public class Main { public static void main(String args[]) { int a = 1; int b = 2; int c = 3; a += 1; b *= 2; c += a * b; c %= 3; System.out.println("a = " + a); System.out.println("b = " + b); System.out.println("c = " + c); } }
此程序的输出如下所示:
Java增量和减量运算符
++
和--
是Java的递增和递减运算符。增量运算符++
会将其操作数增加1。递减运算符--
将其操作数减1。
增量和减量运算符之间的不同:
例如,这个语句:
x = x + 1;
可以这样通过使用增量运算符重写:
x++;
这句话:
x = x - 1;
相当于
x--;
在后缀形式中,它们遵循操作数,例如i++
。在前缀形式中,它们在操作数之前,例如,-- i
。
当增量和/或减量运算符是部分时,这两种形式之间的差异出现的较大表达式。在前缀形式中,操作数在表达式中使用值之前递增或递减。在后缀形式中,在表达式中使用该值,然后修改操作数。
下表总结了前后递增和递减操作之间的差异:
特殊的时候:
i=i++;
等价于:
temp=i;
i=i+1;
i=temp;
详细了解请看http://www.cnblogs.com/dengrenning/articles/9057401.html
运算符的精度:
- 当使用运算符把两个操作数结合到一起时,首先会将两个操作数转化成相同类型的数据。
- 两个操作数中如有一个是 double 型,那么另一个操作数一定先转化成 double 型,再进行运算。
- 两个操作数中如有一个是 float 型,那么另一个操作数一定先转化成 float 型,再进行运算。
- 两个操作数中如有一个是 long 型,那么另一个操作数一定会先转化成 long 型,再进行运算。
- 其他任何两个基本类型数据操作,两个操作数都会自动转化成 int 型。
关系运算符
关系运算符就是指两个操作数之间的关系,它包括了:“>”、“<”、“>=”、“<=”、“==”、“!=”。
算术运算符的结果都是数字,而关系运算符的结果则是布尔型的数据
等于和不等于适用于所有的基本类型,而其他比较符不适用于boolean类型。因为boolean值只能为true或false,“大于”和“小于”没有实际意义
逻辑运算符
逻辑运算符共有三种,即“非”、“和”、“或”,分别用 " ! "、“&&”、“||”表示。其中:
- 非运算(!)表示否定,如:!true等于false、!false等于true、!2等于false、!0等于true。
- 和运算(&&)前后两个条件都为真时,才返回true,否则返回false。
- 或运算(||)前后两个条件有一个为真是,返回true,都为假时,返回false。
还有逻辑异或'^',逻辑与'&',逻辑或'|'
package random; import java.util.*; public class Bool { public static void main(String[] args) { Random rand = new Random(47); int i = rand.nextInt(100); int j = rand.nextInt(100); System.out.println("i = " + i); System.out.println("j = " + j); System.out.println("i > j is " + (i > j)); System.out.println("i < j is " + (i < j)); System.out.println("i >= j is " + (i >= j)); System.out.println("i <= j is " + (i <= j)); System.out.println("i == j is " + (i == j)); System.out.println("i != j is " + (i != j)); // Treating an int as a boolean is not legal Java: //! System.out.println("i && j is " + (i && j));//“与”、“或”、“非”操作只可应用与boolean值 //! System.out.println("i || j is " + (i || j)); //! System.out.println("!i is " + !i); System.out.println("(i < 10) && (j < 10) is " + ((i < 10) && (j < 10)) ); System.out.println("(i < 10) || (j < 10) is " + ((i < 10) || (j < 10)) ); } }
输出结果:
i = 58
j = 55
i > j is true
i < j is false
i >= j is true
i <= j is false
i == j is false
i != j is true
(i < 10) && (j < 10) is false
(i < 10) || (j < 10) is false
短路
两种逻辑与(&&和&)的运算规则基本相同,两种逻辑或(||和|)的运算规则也基本相同。 &和|运算是把逻辑表达式全部计算完,而&&和||运算具有短路计算功能。 对于&来说,如果左侧条件为false,也会计算右侧条件的值,而对于&&来说,如果左侧的条件为false,则不计算右侧的条件,这种现象被称作短路现象。 所谓短路计算,是指系统从左至右进行逻辑表达式的计算,一旦出现计算结果已经确定的情况,则计算过程即被终止。 对于&&运算来说,只要运算符左端的值为false,则因无论运算符右端的值为true或为false,其最终结果都为false。 所以,系统一旦判断出&&运算符左端的值为false,则系统将终止其后的计算过程; 对于 || 运算来说,只要运算符左端的值为true,则因无论运算符右端的值为true或为false,其最终结果都为true。 所以,系统一旦判断出|| 运算符左端的值为true,则系统将终止其后的计算过程。
利用短路现象:在程序设计时使用&&和||运算符,不建议使用&和|运算符。
public class ShortCircuit { static boolean test1(int val) { System.out.println("test1(" + val + ")"); System.out.println("result: " + (val < 1)); return val < 1; } static boolean test2(int val) { System.out.println("test2(" + val + ")"); System.out.println("result: " + (val < 2)); return val < 2; } static boolean test3(int val) { System.out.println("test3(" + val + ")"); System.out.println("result: " + (val < 3)); return val < 3; } public static void main(String[] args) { boolean b = test1(0) && test2(2) && test3(2); System.out.println("expression is " + b); System.out.println("---------------------"); boolean b2 = test1(0) & test2(2) & test3(2); System.out.println("expression is " + b2); } }
输出结果:
test1(0) result: true test2(2) result: false expression is false --------------------- test1(0) result: true test2(2) result: false test3(2) result: true expression is false
位运算
位运算符主要针对二进制,它包括了:“与”、“非”、“或”、“异或”,分别用"&"、"~"、" | "、"^"。其中:
- 与运算(&)两个操作数的位都为 1,结果才为 1,否则结果为 0。
- 非运算(~)的操作数的位如果为 0,结果是 1,如果为 1,结果是 0。
- 或运算(|)两个操作数的位只要有一个为 1,那么结果就是 1,否则就为 0。
- 异或运算(^)的两个操作数的位相同时结果为 0,不同时结果为 1。
还有右移'>>',左移'<<',0填充的右移'>>>'
位运算的位与'&',位或'|',位非'~',位异或'^'与逻辑运算的相应操作的真值表完全相同,其差别只是位运算操作的操作数和运算结果都是二进制整数,而逻辑运算相应操作的操作数和运算结果都是逻辑值boolean型。
下边为位&运算: int a = 15; //x等于二进制数的00001111 int b = 6; //y等于二进制数的00000110 int c = x&y //z等于二进制数的00000110
“>>"右移是将一个二进制数按指定移动的位数向右移位,移掉的被丢弃,左边移进的部分或者补0(当该数为正时),或者补1(当该数为负时)。
这是因为整数在机器内部采用补码表示法,正数的符号位为0,负数的符号位为1。
将一个数右移>>"会使该值除以2的幂。
"<<"左移:位移后最高位如果是1,则用1补齐,如果是0,则用0补齐
将一个数左移"<<"会使该值乘以2的幂。
">>>" :(补零)运算符,即无符号右移,">>>"永远不会产生负号,因为其符号位总是被补零。 不论被移动数是正数还是负数,左边移进的部分一律补0。
package random; public class Demo1 { public static void main(String args[]){ // 1、左移( << ) // 0000 0000 0000 0000 0000 0000 0000 0101 然后左移2位后,低位补0:// // 0000 0000 0000 0000 0000 0000 0001 0100 换算成10进制为20 System.out.println(5 << 2);// 运行结果是20 System.out.println(5<<3);//40 5*(2^3) // 2、右移( >> ) 高位补符号位 // 0000 0000 0000 0000 0000 0000 0000 0101 然后右移2位,高位补0: // 0000 0000 0000 0000 0000 0000 0000 0001 System.out.println(5 >> 2);// 运行结果是1 // 3、无符号右移( >>> ) 高位补0 // 例如 -5换算成二进制后为:0101 取反加1为1011 // 1111 1111 1111 1111 1111 1111 1111 1011 // 我们分别对5进行右移3位、 -5进行右移3位和无符号右移3位: System.out.println(5 >> 3);// 结果是0 System.out.println(-5 >> 3);// 结果是-1 // 0001 1111 1111 1111 1111 1111 1111 1111 System.out.println(-5 >>> 3);// 结果是536870911 // 4、位与( & ) // 位与:第一个操作数的的第n位于第二个操作数的第n位如果都是1,那么结果的第n为也为1,否则为0 //5: 0101 //3: 0011 //5&3: 0001 ------->1 System.out.println(5 & 3);// 结果为1 System.out.println(4 & 1);// 结果为0 // 5、位或( | ) // 第一个操作数的的第n位于第二个操作数的第n位 只要有一个是1,那么结果的第n为也为1,否则为0 //5: 0101 //3: 0011 //5|3: 0111 ------->7 System.out.println(5 | 3);// 结果为7 // 6、位异或( ^ ) // 第一个操作数的的第n位于第二个操作数的第n位 相反,那么结果的第n为也为1,否则为0 //5: 0101 //3: 0011 //5|3: 0110 ------->6 System.out.println(5 ^ 3);//结果为6 // 7、位非( ~ ) // 操作数的第n位为1,那么结果的第n位为0,反之。 System.out.println(~5);// 结果为-6 } }
Java中使用异或语句实现两个变量的互换
public class Demo1 { public static void main(String args[]){ int a=5; int b=10; a=a^b; b=a^b; a=a^b; System.out.println("a="+a+" b="+b); } }
输出结果:a=10 b=5
int num=32; System.out.println(num >> 32);//0
三元运算符
用来完成简单的选择逻辑,即根据条件判断,从两个选择中选择一种执行。
使用格式:(条件表达式)?表达式1:表达式2;
运算规则:
- a) 判断条件表达式,结果为一个布尔值。
- b) true,运算结果为表达式1
- c) false,运算结果为表达式2
例如:
int a=10;
int b=20;
int c=a==b?a:b;
c结果为20
等价的if-else语句如下所示:
if(a==b) c=a;
else c=b;
三元运算符可以和if-else语句进行互换,它们两个可以等价的实现判断的效果。但是三元运算符与if-else语句也还是有不同之处的,下面来具体的谈谈不同之处。
第一,两者之间对于返回值有不同的要求,三元运算符是必须要有返回值要求,其运算后一定会有一个结果返回供程序开发人员使用;而if-else语句并不一定有返回值,其执行结果可能是赋值语句或者打印输出语句(相信这一点大家都比较好理解)。
第二,两者的性能不同,三元运算符的运算性能相对于if-else语句来说要高一些,但是在一些小型的applet开发和日常学习开发中可以忽略不计。
第三,两者使用范围不同,在ireport的一些动态执行的情况下,只能用三元运算符而不能用if-else语句,当然在大多数情况下两者是可以等价互换的。
第四,两者的语言特性不同,三元运算符涉及到一种成为双目数值提升(binary numeric promotion)的语言特性。所谓的双目数值提升,在三目运算符java开发的环境下可以简单的理解为双目运算符的类型转换问题(便于理解)。其具体规则总结如下:
1)如果定义了数据类型的变量与未定义变量的数值共同参与三元运算符的后双目运算,,那么返回的结果就是范围大(精度高)类型
2)如果两个定义了数据类型的变量共同参与三元运算符的后双目运算,那么返回的结果就是范围大(精度高)类型
3)如果直接进行数值的比较,会自动转型成为范围大(精度高)的数据类型
而jvm在给数值分配数据类型的时候会选取刚好能装下该数据大小精度的数据类型进行分配(99.0为float,99为byte/short),在java中常见数据类型其范围从小到大(精度由高到低):byte<short<char<int<float<double。下面用一段代码来解释上述的三条规则
public class Demo1 { public static void main(String args[]) { char a = 'a'; int i = 96; // 规则1,定义了数据类型的变量与未定义变量的数值,结果自动转换为精度高的 System.out.println(2 == 2 ? i : 9.0); // 96.0 // jvm给数值分配的数据类型,98并不是int类型的,而是byte/short,故结果会变为ASCII码98对应的字符 System.out.println(2 == 2 ? 98 : a); // b // 规则2,两个已经定义数据类型的变量,结果自动转换为精度高的 System.out.println(2 == 2 ? a : i);// 97 // 规则3,两个未定义的数值,结果自动转换为精度高的 System.out.println(2 == 2 ? 99 : 9.0);// 99.0 System.out.println(2 == 2 ? 99 : 'b');// c } }
如果一个表达式的类型是byte、short、char类型的,而另外一个是int类型的常量表达式,且它的值可以用类型byte、short、char三者之一表示的,那么条件表达式的类型就是三者之一
char ch = 'a'; int num = 0; boolean bool = true; System.out.println(bool ? ch : 0);// a System.out.println(bool ? ch : num);//97
All
//用”//!“注释的语句表示会导致编译错误的语句 public class AllOps { // To accept the results of a boolean test: void f(boolean b) { } void boolTest(boolean x, boolean y) { // Arithmetic operators: // ! x = x * y; // ! x = x / y; // ! x = x % y; // ! x = x + y; // ! x = x - y; // ! x++; // ! x--; // ! x = +y; // ! x = -y; // Relational and logical: // ! f(x > y); // ! f(x >= y); // ! f(x < y); // ! f(x <= y); f(x == y); f(x != y); f(!y); x = x && y; x = x || y; // Bitwise operators: // ! x = ~y; x = x & y; x = x | y; x = x ^ y; // ! x = x << 1; // ! x = x >> 1; // ! x = x >>> 1; // Compound assignment: // ! x += y; // ! x -= y; // ! x *= y; // ! x /= y; // ! x %= y; // ! x <<= 1; // ! x >>= 1; // ! x >>>= 1; x &= y; x ^= y; x |= y; // Casting: // ! char c = (char)x; // ! byte b = (byte)x; // ! short s = (short)x; // ! int i = (int)x; // ! long l = (long)x; // ! float f = (float)x; // ! double d = (double)x; } void charTest(char x, char y) { // Arithmetic operators: x = (char) (x * y); x = (char) (x / y); x = (char) (x % y); x = (char) (x + y); x = (char) (x - y); x++; x--; x = (char) +y; x = (char) -y; // Relational and logical: f(x > y); f(x >= y); f(x < y); f(x <= y); f(x == y); f(x != y); // ! f(!x); // ! f(x && y); // ! f(x || y); // Bitwise operators: x = (char) ~y; x = (char) (x & y); x = (char) (x | y); x = (char) (x ^ y); x = (char) (x << 1); x = (char) (x >> 1); x = (char) (x >>> 1); // Compound assignment: x += y; x -= y; x *= y; x /= y; x %= y; x <<= 1; x >>= 1; x >>>= 1; x &= y; x ^= y; x |= y; // Casting: // ! boolean bl = (boolean)x; byte b = (byte) x; short s = (short) x; int i = (int) x; long l = (long) x; float f = (float) x; double d = (double) x; } void byteTest(byte x, byte y) { // Arithmetic operators: x = (byte) (x * y); x = (byte) (x / y); x = (byte) (x % y); x = (byte) (x + y); x = (byte) (x - y); x++; x--; x = (byte) +y; x = (byte) -y; // Relational and logical: f(x > y); f(x >= y); f(x < y); f(x <= y); f(x == y); f(x != y); // ! f(!x); // ! f(x && y); // ! f(x || y); // Bitwise operators: x = (byte) ~y; x = (byte) (x & y); x = (byte) (x | y); x = (byte) (x ^ y); x = (byte) (x << 1); x = (byte) (x >> 1); x = (byte) (x >>> 1); // Compound assignment: x += y; x -= y; x *= y; x /= y; x %= y; x <<= 1; x >>= 1; x >>>= 1; x &= y; x ^= y; x |= y; // Casting: // ! boolean bl = (boolean)x; char c = (char) x; short s = (short) x; int i = (int) x; long l = (long) x; float f = (float) x; double d = (double) x; } void shortTest(short x, short y) { // Arithmetic operators: x = (short) (x * y); x = (short) (x / y); x = (short) (x % y); x = (short) (x + y); x = (short) (x - y); x++; x--; x = (short) +y; x = (short) -y; // Relational and logical: f(x > y); f(x >= y); f(x < y); f(x <= y); f(x == y); f(x != y); // ! f(!x); // ! f(x && y); // ! f(x || y); // Bitwise operators: x = (short) ~y; x = (short) (x & y); x = (short) (x | y); x = (short) (x ^ y); x = (short) (x << 1); x = (short) (x >> 1); x = (short) (x >>> 1); // Compound assignment: x += y; x -= y; x *= y; x /= y; x %= y; x <<= 1; x >>= 1; x >>>= 1; x &= y; x ^= y; x |= y; // Casting: // ! boolean bl = (boolean)x; char c = (char) x; byte b = (byte) x; int i = (int) x; long l = (long) x; float f = (float) x; double d = (double) x; } void intTest(int x, int y) { // Arithmetic operators: x = x * y; x = x / y; x = x % y; x = x + y; x = x - y; x++; x--; x = +y; x = -y; // Relational and logical: f(x > y); f(x >= y); f(x < y); f(x <= y); f(x == y); f(x != y); // ! f(!x); // ! f(x && y); // ! f(x || y); // Bitwise operators: x = ~y; x = x & y; x = x | y; x = x ^ y; x = x << 1; x = x >> 1; x = x >>> 1; // Compound assignment: x += y; x -= y; x *= y; x /= y; x %= y; x <<= 1; x >>= 1; x >>>= 1; x &= y; x ^= y; x |= y; // Casting: // ! boolean bl = (boolean)x; char c = (char) x; byte b = (byte) x; short s = (short) x; long l = (long) x; float f = (float) x; double d = (double) x; } void longTest(long x, long y) { // Arithmetic operators: x = x * y; x = x / y; x = x % y; x = x + y; x = x - y; x++; x--; x = +y; x = -y; // Relational and logical: f(x > y); f(x >= y); f(x < y); f(x <= y); f(x == y); f(x != y); // ! f(!x); // ! f(x && y); // ! f(x || y); // Bitwise operators: x = ~y; x = x & y; x = x | y; x = x ^ y; x = x << 1; x = x >> 1; x = x >>> 1; // Compound assignment: x += y; x -= y; x *= y; x /= y; x %= y; x <<= 1; x >>= 1; x >>>= 1; x &= y; x ^= y; x |= y; // Casting: // ! boolean bl = (boolean)x; char c = (char) x; byte b = (byte) x; short s = (short) x; int i = (int) x; float f = (float) x; double d = (double) x; } void floatTest(float x, float y) { // Arithmetic operators: x = x * y; x = x / y; x = x % y; x = x + y; x = x - y; x++; x--; x = +y; x = -y; // Relational and logical: f(x > y); f(x >= y); f(x < y); f(x <= y); f(x == y); f(x != y); // ! f(!x); // ! f(x && y); // ! f(x || y); // Bitwise operators: // ! x = ~y; // ! x = x & y; // ! x = x | y; // ! x = x ^ y; // ! x = x << 1; // ! x = x >> 1; // ! x = x >>> 1; // Compound assignment: x += y; x -= y; x *= y; x /= y; x %= y; // ! x <<= 1; // ! x >>= 1; // ! x >>>= 1; // ! x &= y; // ! x ^= y; // ! x |= y; // Casting: // ! boolean bl = (boolean)x; char c = (char) x; byte b = (byte) x; short s = (short) x; int i = (int) x; long l = (long) x; double d = (double) x; } void doubleTest(double x, double y) { // Arithmetic operators: x = x * y; x = x / y; x = x % y; x = x + y; x = x - y; x++; x--; x = +y; x = -y; // Relational and logical: f(x > y); f(x >= y); f(x < y); f(x <= y); f(x == y); f(x != y); // ! f(!x); // ! f(x && y); // ! f(x || y); // Bitwise operators: // ! x = ~y; // ! x = x & y; // ! x = x | y; // ! x = x ^ y; // ! x = x << 1; // ! x = x >> 1; // ! x = x >>> 1; // Compound assignment: x += y; x -= y; x *= y; x /= y; x %= y; // ! x <<= 1; // ! x >>= 1; // ! x >>>= 1; // ! x &= y; // ! x ^= y; // ! x |= y; // Casting: // ! boolean bl = (boolean)x; char c = (char) x; byte b = (byte) x; short s = (short) x; int i = (int) x; long l = (long) x; float f = (float) x; } } /// :~