第三章 操作符
在最底层,Java中的数据是通过使用操作符来操作的。Java建立在C++的基础之上,所以C和C++的程序员应该非常熟悉Java的大多数操作符,当然,Java也做了一些改进和变化。
3.1 更简单的打印语句
上一章我们介绍了System.out.println(“some text”)的打印语句,本章介绍一种更简单的打印语句:print。
例如可以把上一章的程序改成这样:
import java.util.*; import static net.mindview.util.Print.*; public class HelloDate { public static void main(String[] args) { print(“Hello. it’s: ”); print(new Date()); } }
改进后的程序清爽了许多。尽管使用print可以很好的简化代码,但它并非在任何场合都能显得很恰当。
* 博主尝试了书本给出代码,并未成功,static net.mindview.util.Print.*这个包需要自己下载,安装包链接:https://pan.baidu.com/s/1_SVUHD5NG6faymGu55eCWw 提取码:m3db。下载后下载之后放入java/jre/lib/ext目录下,如果用的是eclipse 可以设置Windows -> Preferences -> Java -> Compiler -> Errors/Warnings -> Deprecated and trstricted API -> Forbidden reference (access rules): -> change to warning,之后在文件里面添加import static .net.mindview.util.Print.*使用。
3.2 使用Java操作符
操作符接收一个或多个参数,并生成一个新值。参数的形式与普通的方法调用不同,但效果是相同的,+、-、*、/、=的用法与其他编程语言类似。
几乎所有的操作符都只能操作基本类型,例外的是”=”、”==”、”!=”,这些操作符能操作所有的对象。
3.3 优先级
当一个表达式中存在多个操作符时,操作符的优先级就决定了各部分的计算顺序。
Java优先级与C、C++相类似,在这里不再过多阐述,举个简单的例子:
public class Precedence { public static oid main(String[] args) { int x = 1, y = 2, z = 3; int a = x + y - 2/2 + z; int b = x + (y - 2) / (2 + z); System.out.println(“a = ” + a + “ b = ” + b); } }
输出结果为a = 5 b = 1
3.4 赋值
赋值使用操作符”=”,他的意思是:取右边的值,把它复制给左边。 右边可以是任何常数、变量或者表达式,但左边必须是一个明确的,已命名的变量,也就是说必须要有一个物理空间可以存储等号右边的值。
举 例:
a = 4;
但是不能把任何东西赋给一个常数,比如不能说4 = a:。
对基本类型使用a = b,那么b的内容就复制给了a,若接着又修改了a,b根本不会受到这种修改的影响。但是在为对象赋值的时候,情况就不一样了。我们真正能操作的是对对象的引用,如果将一个对象赋值给另一个对象,实际是将引用从一个地方复制到另一个地方。也就是说,如果对对象使用c = d,那么c和d都指向原本只有d指向的那个对象。
3.5 算术操作符
Java的基本算术操作符与其他大多数程序语言是相同的,包括+、-、*、/、%,整数除法会直接去掉结果的小数位,而不是四舍五入。Java也使用一种和C、C++一样的操作,例如要将x加4,并将结果返回给x,可以这么写:x += 4。
要生成随机数,程序首先要创建一个Random类的对象,通过这个对象可以生成许多不同类型的随机数字,只需调用nextInt()和nextFloat()即可。
3.6 自动递增和递减
和C、C++类似,自增操作符是”++”,自减操作符是”--”,表达式++a就等价于 a = a+1。
这两个操作符各有两种使用方式,分别为前缀式和后缀式,前缀式(如++a,--a)会先执行运算再生成值,后缀式(如a++,a--)会先生成值再执行运算。
3.7 关系操作符
关系操作符生成的是一个boolean结果,计算的是操作数的值之间的关系,如果关系为真返回true,反之返回false。关系操作符包括>、<、<=、>=、==、!=。
3.7.1 测试对象的等价性
举个例子:
public static void main(String[] args) { Integer a = new Integer(111); Integer b = new Integer(111); System.out.println(a == b); System.out.println(a != b); }
读者可能会认为输出结果肯定先是true再是false,因为两个Integer对象都是相同的,但是尽管对象内容相同,然而对象的引用却是不同的,所以实际上先输出false,再输出true。
如果想比较对象的实际内容是否相同,我们使用方法equals(),但是这个方法不适用于基本类型,基本类型直接使用==和!=即可。
如下程序输出true:
public static void main(String[] args) { Integer a = new Integer(111); Integer b = new Integer(111); System.out.println(a.equals(b)); }
3.8 逻辑操作符
逻辑操作符与(&&)或(||)非(!)能根据参数的逻辑关系生成布尔值。与或非操作符只可应用于布尔值。如果在String值的地方使用了布尔值,布尔值会自动转换成适当的文本形式。
短路:当能够明确无误确定整个表达式的值时,就不再计算表达式余下部分。例如:test1() && test2() && test3(),你会很自然地以为这三个函数都会执行,但是其实并非如此,假设第test1产生的结果是true,test2的结果是false,这就意味着不管test3结果如何,整个表达式的值肯定是false,所以没必要浪费精力再去计算test3。通过短路,可以避免一部分不必要的计算,获取潜在的性能提升。
3.9 直接常量
一般来说,如果在程序中使用了直接常量,编译器可以准确地知道要生成什么样的类型,但有时候却是模棱两可的,这时候需要对编译器进行适当的“指导”,用一些字符来额外增加一些信息。
int i1 = 0x2f; int i2 = 0x2F; int i3 = 0177; char c = 0xffff; byte b = 0x7f; short s = 0x7fff; long n1 = 200L; long n2 = 200l; long n3 = 200; float f1 = 1; float f2 = 1F; float f3 = 1f; double d1 = 1dl double d2 = 1D;
大写/小写的L:表示long
大写/小写的F:表示float
大写/小写的D:表示double
十六进制数适用于所有的整数数据类型,以前缀0x,后跟0-9或大小写的a-f来表示。
八进制数由前缀0跟0-7数字表示。
3.10 按位操作符
按位操作符来操作基本数据类型中的单个bit,即二进制位,按位操作符会对两个参数中对应的位执行布尔代数运算并产生一个结果。
按位“与”操作符(&),按位“或”操作符(|),按位“非”操作符(~),与逻辑运算相似,这里不再过多解释用法。
按位操作符和逻辑操作符都是用了相同的符号,但由于位是很小的单位,按位操作符仅适用一个字符。按位操作符可以与“=”联用,&=,!=,^=都是合法的,由于“~”是一元操作符,所以不能与“=”联用。
3.11 移位操作符
移位操作符操作的对象也是二进制的“位”,只能用来处理整数类型。左移位操作符“<<”能按照操作符右侧指定的位数将操作符左边的操作数向左移动,并在低位补0;“有符号”右移位操作符“>>”则按照操作符右侧执行的位数将操作符左边的操作数向右移动。右移位操作符使用符号扩展,若符号为正则在高位插入0,若符号为负责在高位插入1。Java增加了一种无符号右移位操作符“>>>”,使用零扩展,无论正负,都在高位插入0.
对于char、byte、short的数值进行移位处理前,会先把他们转换为int类型,并得到一个int类型的结果。只有右端的低5位才有用,这样可以防止移位超过int所具有的最高位数(2的5次方是32,而int类型只有32位)。若对long类型的数值进行处理,最后得到的结果也是long,这时只用到右端的低6位。
移位操作符可以和“=”组合使用(<<=、>>=),这时操作符的值经过移位后再将得到的结果赋给左边的变量。
3.12 三元操作符if-else
三元操作符也称条件操作符,其基本形式为:
boolean-exp ? value0 : value1
意思是,如果boolean-exp 表达式的结果为true,就计算value0,如果为false,则计算value1,并成为最终产生的值。
以下两个函数的效果是一样的:
int judge1(int i) { return i < 10 ? i * 100 : i * 10; } int judge2(int i) { if(i < 10) return i * 100; else return i * 10; }
3.13 字符串操作符+和+=
这个操作符在Java中有一项特殊用途:连接不同的字符串。如果表达式以一个字符串起头,那么后续所有的操作数都必须是字符串类型,
例如:
int x = 0, y = 1, z = 2; String s = “x, y, z ”; print(s + x + y + z);
请注意这里输出的结果是012而不是3,Java编译器会将x、y、z转换成他们的字符串形式并链接起来。
3.14 使用操作符时常犯的错误
while(x = y) { //... }
这个程序很明显是想测试是否相等,而不是赋值,如果y是非0值,这个式子的结果肯定是true,这样便会得到一个无限循环。在Java中,这个表达式的结果并不是布尔值,但编译器期望一个布尔值,Java不会自动将int转为布尔,所以在编译时就会抛出一个错误,从而阻止我们进一步运行程序。(除非x和y都是布尔值)
3.15 类型转换操作符
类型转换(cast)是指在适当的时候,Java会将一种数据类型自动转换为另一种。例如,我们把一个浮点数变量赋值了一个整数值,那么编译器会将int自动转换成float。类型转换运算允许我们显式进行这种转换,或者不能自动进行转换时强制进行类型转换。
例如:
int i = 200; long lng = (long)200; long lng2 = (long)i;
正如你所看到的那样,我们既可以对数值直接进行转换,也可以对变量进行转换。
如果要执行一种窄化转换(narrowing conversion)的操作,也就是将能容纳更多信息的数据类型转换成无法容纳那么多信息的类型,就可能面临着信息修饰的危险,这时编译器会强制我们进行显式转换。而对于扩展转换(widening conversion)则不必显式地进行类型转换,因为新类型肯定嗯呢该容纳原来的信息,不会造成信息丢失。
3.15.1 截尾和舍入
如果我们要将29.7转换为int,结果是30还是29?
答案是在将float或者double转型为int时,总是对该数字进行截尾,如果想要得到舍入的结果,就需要使用java.lang.Math中的round()方法。
3.15.2 提升
通常,表达式中出现的最大的数据类型决定了表达式最终结果的数据类型。如果一个int和一个long值相加,则结果为long;如果一个float与一个double相乘,结果是double;
3.16 Java没有sizeof
在C、C++中,sizeof()操作符可以告诉你为数据项分配的字节数,但Java不需要sizeof()来满足这方面的需求,所有数据类型在所有机器中的大小都是相同的。