在最底层,Java中的数据是通过使用操作符来操作的。
操作符作用于操作数,生成一个新值。另外,有些操作符可能会改变操作数自身的值,这被称为“副作用”。那些能改变其操作数的操作符,最普遍的用途就是用来产生副作用;但是要记住,使用此类操作符生成的值,与使用没有副作用的操作符生成的值,没有什么区别。
几乎所有的操作符都只能操作“基本类型”。例外的操作符是“=”、“==”和“!=”,这些操作符能操作所有对象。除此之外,String类支持“+”和“+=”。
当一个表达式中存在多个操作符时,操作符的优先级就决定了各部分的计算顺序。Java对计算顺序做了特别的规定。其中,最简单的规则就是先乘除后加减。程序员经常会忘记其他优先级规则,所以应该用括号明确规定计算顺序。
赋值使用操作符“=”。他的意思是“取右边的值,把它复制给左边”。右值可以是任何常数、变量或者表达式。单左值必须是一个明确的、已命名的变量。也就是说,必须有一个物理空间可以存储等号右边的值。
对基本类型的赋值很简单,是存储了实际的数值,而不是指向一个对象的引用,是直接将一个对象的内容复制到了另一个地方。但是在为对象赋值时,我们操作的是对对象的引用。倘若“将一个对象赋值给另一个对象”,实际是将“引用”从一个地方复制到另一个地方。这意味着假若对对象使用c=d,那么c和d都指向原本只有d指向的那个对象,因为c被赋予了d的值,所以Java会认为c原来的对象不会被使用了,而被垃圾回收器回收。这种特殊的现象通常被称作“别名现象”,是Java操作对象的一种基本方式。
import static net.mindview.util.Print.*;
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 = 46;
print(t1.level); // 9
print(t2.level); // 46
t1 = t2;
print(t1.level); // 46
print(t2.level); // 46
t1.level = 11;
print(t1.level); // 11
print(t2.level); // 11
}
}
Java的基本算术操作符与其他大多数程序设计语言是相通的,整数除法会直接去掉结果的小数位。要生成数字,程序首先会创建一个Random类的对象,通过它程序可生成许多不同类型的随机数字,只需调用nextInt()、nextFloat()等方法即可。传递给nextInt()的参数设置了所产生的随机数上限,而其下限为零,为防止产生0,一般我们在后面进行加1操作。
一元减号(-)用于转变数据的符号,而一元加号只是为了与一元减号相对应,它唯一的作用是将较小类型的操作数提升为int。
++和–有“前缀式”和“后缀式”两种使用方式,前缀先运算后生成值,后缀先生成值再执行运算。它们是熟了那些涉及赋值的操作符以外,唯一具有“副作用”的操作符。他们会改变操作数,而不仅仅是使用自己的值。
import static net.mindview.util.Print.*;
public class AutoInc {
public static voin main(String[] args){
int i = 1;
print("++i:" + ++i); // ++i:2
print("i++:" + i++); // i++:2
print("i:" + i); // i:3
}
}
关系操作符==和!=适用于所有对象,但是对不同的对象其比较的内容性质不同。只有基本类型才会直接比较其内容,其余对象比较的是对象的引用。所以相比较对象内容是否一致时,需要使用equals()方法。但是在使用equals()时要注意所比较的对象是否覆盖了equals()方法。
大多数Java类库都实现了equals()方法,以便用来比较对象的内容,而非比较对象的应用。
class Value{
int i;
}
public class EqualsMethod {
public static void main(String[] args){
Integer n1 = new Integer(12);
Integer n2 = new Integer(12);
System.out.println(n1==n2);
int n3 = new Integer(12);
int n4 = new Integer(12);
System.out.println(n3==n4);
Value v1 = new Value();
Value v2 = new Value();
v1.i = v2.i = 100;
System.out.println(v1==v2);
System.out.println(v1.equals(v2));
}
}
/*
false
true
false
false
*/
逻辑操作符与(&&)、或(||)、非(!)能根据参数的逻辑关系,生成一个布尔值(true或false)。它们的操作只可应用于布尔值。与C和C++不同的是,不可将一个非布尔值在逻辑表达式中使用。如果在应该使用String值的地方使用了布尔值,布尔值会自动转换成适当的文本形式:
import static net.mindview.util.Print.*;
public class Bool {
public static void main(String[] args){
print("istrue".equals("is"+true));
}
}//和"istrue"作比较的"is"+true,后面的true是布尔值,但是这里直接用作文本
当使用逻辑操作符时,我们会遇到一种“短路”现象。即一旦能够明确无误地确定整个表达式的值,就不再计算表达式余下部分了。因此,整个逻辑表达式靠后的部分有可能不会被运算。如果所有的逻辑表达式都有一部分不必计算,那将获得潜在的性能提升。
一般说来,如果在程序里使用了“直接常量”编译器可以准确地知道要生成什么样的类型,但有时候却是模棱两可的。如果发生这种情况,必须对编译器加以适当的“指导”,用与直接量相关的某些字符来额外增加一些信息。
直接常量后面的后缀字符标志了它的类型。L代表long,F代表float,D代表double;都可以使用小写,但是L的小写容易与数字1混淆,所以不推荐。
十六进制数适用于所有整数数据类型,以前缀0x(或0X),后面跟随0-9或小写(或大写)a-f来表示。如果试图将一个变量初始化成超出自身表达范围的值,编译器都会向我们报告一条错误信息。如果超出范围,编译器会将值自动转换成int型,并告诉而我们需要对这次赋值进行“窄化转型”。这样我们就可清楚的知道自己的操作是否越界。
八进制数由前缀0以及后续的0-7数字来表示。
Java采用了一种很不直观的计数法来表示指数。在科学与工程领域,“e”代表自然对数的基数,约等于2.718。但是Java使用了源于FORTRAN语言的C、C++的语言习惯,在计数时e代表“10的幂次”。
编译器通常会将指数作为双精度数(double)处理,所以在指数计数时要时常记得转换对象类型:
float f = 1e-43f;//这里不尾随f则会报错,提示必须将double转换成float
按位操作符用来操作整数基本数据类型中的单个“比特”(bit),即二进制位。按位操作符会对两个参数中对应的位执行布尔代数运算,并最终产生一个结果。按位操作符来源于C语言面向底层的操作,在这种操作中经常需要直接操作硬件,设置硬件寄存器内的二进制位。
我们将布尔类型作为一种单比特值对待,所以它多少有些独特。他不能执行按位“非”操作(避免与逻辑NOT混淆);具有与逻辑操作符相同的效果,但是不会中途“短路”;在移位表达式中,不能使用布尔运算。
移位操作符操作的运算对象也是二进制的“位”。移位操作符只可用来处理整数类型(基本类型的一种)。Java中增加了一种“无符号”右移位操作符(>>>),它使用“零扩展”:无论正负,都在高位插入0,这一操作符是C和C++所没有的。
如果对char、byte或者short类型的数值进行移位处理,那么在移位进行之前,他们会被转换为int类型,并且得到的结果也是一个int类型的值。只有数值右端的低五位才有用。这样可防止我们移位超过int型值所具有的位数(long为低6位)。
“移位”与“等号”组合使用,操作符左边的值会移动到右边的值指定的位数,再将得到的结果赋给左边的变量。但如果对byte或short值进行这样的移位运算,得到的可能不是正确的结果。他们会先被转换成int类型,再进行右移操作,然后被截断,赋值给原来的类型,在这种情况下可能得到-1的结果。
数字的二进制表示形式称为“有符号的二进制补码”。
三元操作符也称为条件操作符,具有三个操作数
import static net.mindview.util.Print.*;
public class TernaryIfElse {
static int ternary(int i){
return i<10 ? i*100 : i*10;//三元操作符
}
static int standardIfElse{
if(i<10)
return i*100;
else
return i*10;
}
public static void main(String[] args){
print(ternary(9));
print(ternary(10));
print(standardIfElse(9));
print(standardIfElse(10));
}
}
前者的代码更加紧凑,但是后者的代码更容易理解,而且不需要太多的录入。所以在选择使用三元操作符时,要仔细考虑。
类型转换(cast)的原意是“模型铸造”。在适当的时候,Java会将一种数据类型自动转换成另一种。类型转换运算允许我们显式的进行这种类型的转换,或者在不能自动进行转换的时候强制进行类型转换。
想要执行类型转换,需要将希望得到的数据类型置于括号内,放在要进行类型转换的值的左边。既可对数值进行类型,亦可对变量进行类型转换。在Java中,类型转换是一种比较安全的操作。但是执行窄化转换时可能面临数据丢失的危险。
Java允许我们把任何基本数据类型转换成别的基本数据类型(布尔类型除外)。“类”数据类型不允许进行类型转换。为了将一种类转换成另一种,必须采用特殊的方法。