赋值
“=”,取右边的值把它复制给左边。基本类型存储了实际的数值,而并非指向一个对象的引用,所以在为其赋值时,是直接将一个地方的内容复制到了另一个地方。例如a=b
,那么b的内容就复制给a,若接着修改a,那么b不会受到此影响。但是对象“赋值”的时候,真正操作的是对对象的引用。若c=d
,那么c和d都指向原本d指向的那个对象。修改c指向对象的内容,也会影响d。
方法调用中的别名问题
在许多编程语言中,方法f()似乎要在它的作用域内复制一个参数副本,但是实际上只是传递了一个引用,可能因为方法参数别名问题引起复杂的问题。
算数操作符
Random类的对象如果在创建中没有传递任何参数,则Java会以当前时间作为随机数生成器的种子,并由此在程序每一次执行时都产生不同的输出(相同种子生成的随机数序列是相同的)。
一元加、减操作符
一元减号(-)和一元加号(+)与二元减号(-)和二元加号(+)都是用相同的符号,根据表达式的书写形式,编译器会自动判断出使用的是哪一种。
x = -a;
x = a * -b;
x = a * (-b);
一元减号用于转变数据的符号,而一元加号的唯一作用仅仅是将较小类型的操作数提成为int。
关系操作符
等于和不到呢关于适用于所有的基本数据类型,而其他比较符不适合boolean类型。
测试对象的等价性
Integer n1=new Integer(47);
Integer n2=new Integer(47);
n1==n2;
结果是false,尽管对象的内容相同,都为47,然后对象的引用是不同的,而==和!=比较的就是对象的引用。如果想要比较两个对象的实际内容是否相同时,必须使用所有对象都适用的特殊方法equals()
。但这个方法不适用于基本类型,基本类型直接使用==和!=即可。
//Value是你自己创建的类
Value v1=new Value();
Value v2=new Value();
v1.equals(v2);
结果仍然是false,这是由于equals()
默认的行为是比较引用,除非你在自己的新类中覆盖equals()
方法。大多数Java类库都实现了equals()
方法,以便用于比较内容而非引用。
逻辑操作符
“与(&&)”、“或(||)”、“非(!)”操作只可应用于布尔值。与C和C++不同的是:不可将一个非布尔值当作布尔值在逻辑表达式中使用,例如1||2
这样是不错误的。如果在应该使用String值的地方使用了布尔值,布尔值会自动转换成适当的文本形式,例如print()
输出中。浮点数的比较是非常严格的,即使仅在小数部分相差非常小的差异,也会被认为是“不相等”的。即使一个数只比零大一点点,它仍然是“非零”值,如果一定要比较可以设定一个非常小的差值,如果判定在差值内就近似的认为它们是相等的。
float a=1.0;
float b=1.01;
float diff=0.0000001;
if(a-b
短路
当使用逻辑操作符时,会遇到“短路”现象。因此,整个逻辑表达式靠后的部分有可能不会被运算。
if((a=1)>0&&(a=-1)>0&&(a=20)>0);
......
if((a=-5)<0||(a=20)<0);
......
第一个判断表达式中a=1>0为true,此时a=1,而a=-1>0为false,此时a=-1,由于&&操作符一旦遇到false,结果就为false,所以后面的表达式也不用运算了,a的值就等于-1。同理第二个判断表达式中a的值为-5。“短路”由来正源于此。
直接常量
如果编译器对“直接常量”的类型模棱两可,则必须对编译器加以适当的“指导”,用与直接常量相关的某些字符来额外增加一些信息。
int i=0x2f; //十六进制
int i=0x2F;
int i=0177; //八进制
long l=100L; //长整型后缀,一般不用l,因为会与1混淆
float f=1f; //浮点型后缀
double d=2d; //双精度浮点型后缀
如果给出的常量超出了char、byte所能表示的最大值,编译器会自动转换为int型,并给出一个错误。如果将比较小的类型传递给Integer.toBinaryString()
(或Long的静态方法),则该类型自动被转换为int。
byte b=0x7f; //long b=0x7f; 编译器报错
System.out.println(Integer.toBinaryString(b)); //1111111
指数计数法
在科学与工程领域,“e”代表自然对数的基数,约等于2.718(Java中的Math.E给出了更精确的double型的值,Math是java.lang类库中的,java.lang是程序默认加载的一个类库)。在Java中看到的e/E代表“10的幂次”。如果编译器能够正确识别类型,如long l=200
,就不需要加L,而对于语句float f=1e-43f
,编译器通常会将指数作为双精度数(double)处理,如果没有尾随的f,编译器会报错。
按位操作符
“与(&)”、“或(|)”、“异或(^)”、“非(~)” 操作符用来操作整数基本数据类型中的单个“比特(bit)”,即二进制位。由于“~”是一元操作符所以不能和“=”联合使用。我们将布尔值作为一种单比特值对待,可以按位“与”、“或”和异或,但不能按位“非”(大概是为了避免和逻辑上的NOT混淆)。按位操作符不会造成“短路”。
移位操作符
移位操作符只能处理整数类型。左移(<<)低位补0,右移(>>)高位补符号位的值,使用“符号扩展”。无符号右移(>>>)无论正负高位都插入0.这是C和C++所没有的。如果对char、byte或者short类型的数值进行移位处理,那么在移位之前它们会转换为int类型,得到的结果也是int类型的值。移位操作符只使用其右操作数的低5位作为移位长度,这样防止我们移位超过int型值所具有的位数。若处理的是long类型的数值,则只会用到右操作数的低6位,防止移位超过long型数值具有的位数。“移位”可与“等号”组合使用。此时操作符左边的值会移动指定的位数,然后将结果赋给左边的变量。但在进行“无符号”右移时,char、byte或者short值进行这样的运算,得到的可能不会是正确的结果。它们会先被转换成int,再进行右移操作,然后被截断,赋值给原来的类型。
字符串操作符+和+=
这个操作符在Java中负责连接不同的字符串。C++引入了操作符重载(operator overloading)
机制,可以为几乎所有操作符增加功能。如果表达式以一个字符串起头,那么后续所有操作数都必须是字符串类型(编译器会把双引号内的字符序列自动转换为字符串)。
int x=1,y=2,z=3;
String s="x,y,z " +x+y+z; //第一个是字符串,后面每一个操作数都是字符串,编译器分别将x、y和z转换为字符串,s为 x,y,z 123
s="x,y,z" +(x+y+z); //由于第二个括号里面不以字符串起头,所以编译器执行x+y+z后的结果转换为字符串,s为 x,y,z6
s="x,y,z" +(x+y+z+” d“); //这个更为明显一些,s为 x,y,z6 d
s=(x+" "); //同上一个,将x的结果转换为字符串再与后面的字符串拼接,s为1
s=(""+x); //以此作为不显示调用toString()方法
类型转换操作符
如果执行窄型转换(narrowing conversion)
的操作,就可能面临丢失信息的危险。此时,编译器会强制我们进行类型转换。而对于扩展转换(widening conversion)
,则不必显式地进行类型转换,因为新类型肯定会容纳原来类型的信息,不会造成任何信息的丢失。
Java允许我们把任何基本数据类型转换成别的基本数据类型,但布尔型除外,后者根本不允许任何类型的转换处理。”类“数据类型不允许进行类型转换。为了将一种类转换成另一种,必须采用特殊的方法。
截尾和舍入
浮点型转整型时总是对该数字进行截尾,如果要舍入的话,需要调用java.lang.Math.round()
方法。
提升
如果对基本数据类型执行算术运算或按位运算,只要类型比int小,那么运算之前,这些值会自动转换成int。这样一来,最终生成的结果就是int类型的。如果想把结果赋值给较小的类型,就必须使用类型转换。通常表达式中出现的最大的数据类型决定了表达式最终结果的数据类型。
Java中没有sizeof
Java不需要sizeof()
,因为所有数据类型在所有机器中的大小都是相同的,我们不必考虑移植的问题。
优先级
优先级 | 操作符 | 结合性 |
---|---|---|
1 | () , [] , . | 左->右 |
2 | ! , +(正) , -(负) , ~ , ++ , -- | 右->左 |
3 | * , / , % | 左->右 |
4 | +(加) -(减) | 左->右 |
5 | << , >> , >>> | 左->右 |
6 | < , <= , > , >= , instanceof | 左->右 |
7 | == , != | 左->右 |
8 | &(按位与) | 左->右 |
9 | ^ | 左->右 |
10 | | | 左->右 |
11 | && | 左->右 |
12 | | | | 左->右 |
13 | ? : | 右->左 |
14 | = += -= *= /= %= &= | = ^= ~= <<= >>= >>>= | 右->左 |
小结
能够对布尔值进行的运算非常有限。我们只能赋予它true和false的值,并测试它是真还是假,而不能将布尔值相加,或对布尔值进行其他任何运算。
在char、byte和short中,对这些的任何一个进行算术运算,都会获得一个int结果,必须显式地将其类型转换回原来的类型。但是仍要小心结果溢出的问题,你不会从编译器那里收到出错或警告信息,运行时也不会出现异常。
对于char、byte或者short,复合赋值并不需要类型转换。尽管它们执行类型提升,但是也会获得与直接算术运算相同的结果。
short a=1;
a=a+1; //报错,a是short类型的,结果是int,所以要强制转换a=(short)(a+1)
a+=1; //通过编译,原因是因为a+=1等价于a=(short)(a+1),Java对其做了特殊处理