运算符
程序由许多语句组成,而语句组成的基本单位就是表达式与运算符。
【运算符分类】
按功能分:算术运算符、赋值运算符、关系运算符、逻辑运算符、位运算符、其他运算符。
按操作数数目:一元、二元、三元。
1、赋值运算符:=
它的意思是“取右值,把它复制给左值”。右值可以是任何常数、变量或表达式(只要它能生成一个值就行)。但左值必须是一个明确的、已命名的变量,即必须有一个物理空间可以存储等号右边的值。如下:
int a;
a = 4; // 可以将常量赋给变量
//以下是错误
4 = a; // 不能把任何东西赋给一个常数
注1:对基本数据类型的赋值是很简单的,基本类型存储了实际的值,而并非指向一个对象的引用,所以对其赋值的时候,是直接将一个地方的内容复制到了另一个地方。例如:a = b; 那么b的内容就复制给了a。若是修改a,b并不受影响。
注2:但是,在为对象赋值的时候,情况却不同。对一个对象进行操作时,我们真正操作的是对对象的引用。所以,若是“将一个对象赋值给另一个对象”,实际是将“引用”从一个地方复制到另一个地方。这意味着,假如对对象使用c = d,那么c和d都指向原本只有d指向的那个对象。如下:
class Num {
int num;
Num(int num){
this.num = num;
}
}
public class Assignment {
public static void main(String[] args) {
Num a = new Num(1);
Num b = new Num(2);
System.out.println("a.num = " + a.num + "\t" + "b.num = " + b.num);
a = b; // a和b都指向原来b指向的对象
System.out.println("a.num = " + a.num + "\t" + "b.num = " + b.num);
a.num = 3; // 因为此时a与b是指向同一对象,所以此时改变a所指向对象的值,b所指向的结果也改变
System.out.println("a.num = " + a.num + "\t" + "b.num = " + b.num);
}
}
【结果】
注:在许多编程语言中,我们可能会期望a,b是相互独立的,但由于赋值运算符操作的是一个对象的引用,所以修改a的同时也改变了b!这是因为a和b包含的是相同的引用,它们指向相同的对象。(原本a包含的对对象的引用,是指向一个值为1的对象,但在对a赋值时,这个引用被覆盖了,也即丢失了;而那个不再被引用的值为1的对象会由“垃圾回收器”自动清理。)
这种特殊的现象通常称作“别名现象”,是Java操作对象的一种基本方式。而若要避免别名问题,则应该如下:
a.num = b.num;
这样就可以保持两个对象彼此独立,而不是将a和b绑定到相同的对象。
2、算术运算符:+ - * / %
注
(1)整数除法会直接去掉结果的小数位,而不是四舍五入。因为整形变量无法保存小数点后面的数据。
(2)Java中取余运算符%的操作数可以是负数和浮点数。而在C/C++中,取余运算符只能是整数。
(3)+运算符也可以连接两个字符串。
【示例】
public class Unary {
public static void main(String[] args) {
int x = 10/3; // 取值为3,而不是3.33333...
System.out.println("x = " + x);
int a = 10%-3;
System.out.println("a = " + a);
float b = 5.2f % 3.1f;
System.out.println("b = " + b);
}
}
【结果】
3、关系运算符:> < <= >= == !=
关系运算符生成的是一个boolean结果,它们计算的是操作数的值之间的关系。
等于(==)和不等于(!=)适用于所有的基本数据类型。而其他比较符不适用于布尔类型,因为布尔类型只有true和false,大小没有意义。
测试对象的等价性【==、!=、equals()方法】
【示例】
public class Equal {
public static void main(String[] args) {
int m = 3;
int n = 3;
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(m == n);
System.out.println(n1 == n2);
System.out.println(n1 != n2);
}
}
【结果】
【结果分析】
等于(==)和不等于(!=)也适用于所有的对象,但在使用时一定要注意,对于基本数据类型,它们比较的是值(基本数据类型中存储的值)“本身”;对于引用数据类型,它们比较的变量对对象的引用(其所指向的对象在内存中的地址)。
(1)对于基本数据类型的变量,变量存储的是值“本身”。而所有关系运算符比较的就是值“本身”,而
int m = 3;
int n = 3;
变量n和变量m都是直接存储的"3"这个数值,所以用==比较的时候结果是true。
(2)对于引用类型变量,引用类型的变量存储的并不是 “值”本身,而是于其关联的对象在内存中的地址。如:
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
引用类型变量n1中存储的是他所指向的对象在内存中的地址,而不是47这个值,同理,n2也是如此。
System.out.println(n1 == n2);
当运行上面语句时,实际上比较的是n1和n2的引用(其指向的对象在内存中的地址)。然而,n1和n2对象的引用却是不同的,所以输出false。
【equals()方法】
equals方法是基类Object中的方法,因此对于所有的继承于Object的类都会有该方法。
equals()方法是用来比较两个对象的引用是否相等,即是否指向同一个对象。
但equals()这个方法不适用于基本数据类型,基本类型直接使用==和!=即可。
public class Equal {
public static void main(String[] args) {
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1.equals(n2));
}
}
【结果】
【结果分析】
不是说equals()比较的是引用嘛,那上面结果为什么对呢?这是因为,equals()默认确实是比较引用,但是Integer类中重写了equals()方法,使其比较引用对象的内容。
两个引用对象的内容都是47,所以结果是true。
假设你创建了自己的类,没有重写equals()方法,如下:
public class Equal {
public static void main(String[] args) {
Value v1 = new Value();
Value v2 = new Value();
v1.i = 100;
v2.i = 100;
System.out.println(v1.equals(v2));
}
}
【结果】
【结果分析】
这是由于equals()的默认行为是比较引用,没有重写equals()方法,所以它无法比较引用对象的内容,只能默认比较对象的引用,引用不同,所以结果false。
4、逻辑运算符:与(&&)、或(||)、非(!)
与、或、非操作只能运用于布尔值,并返回一个布尔值。
(表达式1)&&(表达式2) // 其中表达式1和表达式2必须的结果必须是布尔值
(表达式1)||(表达式2)
!(表达式)
注:使用与(&&)、或(||)时,一旦能够明确无误地确定整个表达式的值,就不用再计算表达式余下的部分了,这种现象被称为“短路”。因此,整个逻辑表达式靠后的部分有可能不会被运算。
非短路:&,|
& 与 | 两边的表达式都要进行运算。
【分享一道Java面试题】
- &和&&、|和||的关系是怎么样?(Java面试题)
答:
对于“与操作”:有一个条件不满足,结果就是false。
普通与(&):所有的判断条件都要执行;短路与(&&):如果前面有条件已经返回了false,不再向后判断,那么最终结果就是false。
对于“或操作”:有一个条件满足,结果就是true。
对于普通或(|):所有的判断条件都要执行;
短路或(||):如果前面有条件返回了true,不再向后判断,那么最终结果就是true。
5、位运算符
其中,除 ~ 运算符外,其他位运算符都可以与=联合使用。&=、|=、^=、>>=、<<=、>>>=
运算符优先级
【一个小问题】
int i;;
int n;;;;;;;;;;;;;
上面两条语句合法吗?
答案是合法,事实上,由于多个Java语句可以处于同一行,那么int i ;;就可以解读为“int i;”这条语句和另外一个空语句“;”共处于一行之上。int i语句后面即使跟10个分号也合法。