在最底层,Java中的数据是通过使用操作符来操作的
擦作符的分类
算术运算符
表格中的实例假设整数变量A的值为10,变量B的值为20,C是A和B经过操作符运算后的值
操作符 | 名称 | 描述 | 举例 |
---|---|---|---|
+ | 加法 | 相加运算符两侧的值 | C=A+B //c:30 |
- | 减法 | 左操作数减去右操作数 | C=A-B //c:-10 |
* | 乘法 | 相乘操作符两侧的值 | C=A*B //c:200 |
/ | 除法 | 左操作数除以右操作数 | C=B/A //c:2 |
% | 取余 | 左操作数除以右操作数的余数 | C=B%A //c:0 |
++ | 自增 | 操作数的值增加1 | B++或++B // B:21 |
-- | 自减 | 操作数的值减少1 | B--或--B // B=19 |
优先级
当一个表达式中存在多个操作符时,操作符的优先级就决定了各部分的计算顺序。Java对计算顺序做了特别的规定。其中,最简单的规则就是先乘除后加减。因为有时会忘记其他优先级规则,所以应该用括号明确规定规定计算顺序
字符串连接符
当一个String后面紧跟着一个+,而这个+的后面又紧跟一个非String类型的元素时,就会尝试着将这个非String类型的元素转换为String
public class Precedence{
public static void main(String[] args){
System.out.println("abc" + 1);
}
}
// Output
abc1
赋值运算符
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
分析:Tank类非常简单,它的两个实例(t1和t2)是在main()里创建的。对每个Tank类对象的level域都赋予了一个不同的值,然后,将t2赋给t1.由于赋值操作的是一个对象的引用,所以修改t1的同时也改变了t2.这是由于t1和t2包含的是相同的引用,它们指向相同的对象。(原本t1包含的对对象的引用,是指向一个值为9的对象。在对t1赋值的时候,这个引用被覆盖,也就是丢失了;而那个不再被引用的对象会由“垃圾回收器”自动清理)
这种特殊的现象通常称作“别名现象”,是Java操作对象的一种基本方式。在上面的例子中,如果想避免别名问题应该怎么办呢?可以这样写:
t1.level = t2.level;
这样便可以保持两个对象彼此独立,而不是将 t1和t2绑定到相同的对象。但直接操作对象内的域容易导致混乱,并且违背了良好的面向对象程序设计的原则。
方法调用中的别名问题
将一个对象传递给方法时,也会产生别名问题:
// f()传递只是x的引用,所以可以f()之外的对象
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;
一元加、减操作符 一元减号用于转变数据的符号而一元加号只是为了与一元减号相对应,但是它位于的作用仅仅是将较小类型的操作数提升为int
x = a * -b; // 编译器能正确识别
x = a * (-b) // 比较明确的写法
自动递增和递减
public class AutoInc{
public static void main(String[] args){
int i = 1;
System.out.println("i : " + i);
System.out.println("i : " + ++i);
System.out.println("i++ : " + i++);
System.out.println("i: " + i);
System.out.println("--i: " + --i);
System.out.println("i-- : " + i--);
System.out.println("i: " + i);
}
}
// Output:
i : 1
++i : 2
i ++ : 2
i : 3
--i : 2
i-- : 2
i : 1
关系操作符
public class Equivalence{
public static void main(String[] args){
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1 == n2);
System.out.println(n1 != n2);
}
}
// Output
false // 虽然两个对象的内容相同,但是队形的引用确是不同的,== 和 !=比较的就是对象的引用
true
equals()
用来比较两个对象的实际内容是否相同,但这个方法不适用于“基本类型”,基本类型直接使用== 和!=即可
public class EqualMethod{
public static void main(String[] args){
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1.equals(n2));
}
}
// Output
true
equals()重写
class Value{
int i;
}
public class EqualMethod2{
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));
}
}
// Output
false
当我们用自己创建的类,使用equals()方法来比较,虽然v1和v2两个引用不同,但对象内容是相同的,而且equals()比较的是对象内容的值,但结果确实false。这是由于equals()的默认行为是比较引用。所以除非在自己的新类中覆盖equals()方法,否则比较的还是引用
逻辑操作符
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));
System.out.println(" (i < 10) && (j < 10) is " + (i < 10) && (j < 10) ;
System.out.println(" (i < 10) || (j < 10) is " + (i < 10) || (j < 10) ;
}
}
// Output
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 fasle
(i < 10) || (j < 10) is fasle
短路
一旦能够明确无误地确定整个表达式的值,就不再计算表达式余下部分了
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 = test(0) && test2(2) && test3(2);
System.out.println("expression is : " + b);
}
}
// Output
// test1()结果是true,表达式计算会继续下去,test2()结果是false,这意味着整个表达式肯定为false,所以没必要继续计算剩余的表达式。“短路”一词的由来正源于此。事实上。如果所有的逻辑表达式都有一部分不必计算,那将获得潜在的性能提升
test1(0)
result:true
test2(2)
result:false
expression is false
三元操作符
三元操作符也称条件操作符,它比较特别的是有三个操作数;但它确实属于操作符的一种,因为它最终也会生成一个值。
// 如果boolean-exp(布尔表达式)的结果为true,就计算value0,而且这个计算结果也就是操作符最终产生的值。
// 如果boolean-exp的结果为false,就计算value1,同样,它的结果也就成为了操作符最终产生的值
boolean-exp? value0 : value1