二、操作符
二、操作符
2.1 测试对象是否相等
关系操作符==和!=适用于所有对象,但它们的执行结果可能会让人困惑:
public class Equivalence {
static void show(String desc, Integer n1, Integer n2) {
System.out.println(desc + ":");
System.out.printf(
"%d==%d %b %b%n", n1, n2, n1 == n2, n1.equals(n2));
}
@SuppressWarnings("deprecation")
public static void test(int value) {
Integer i1 = value; // [1]
Integer i2 = value;
show("Automatic", i1, i2);
// Old way, deprecated since Java 9:
Integer r1 = new Integer(value); // [2]
Integer r2 = new Integer(value);
show("new Integer()", r1, r2);
// Preferred since Java 9:
Integer v1 = Integer.valueOf(value); // [3]
Integer v2 = Integer.valueOf(value);
show("Integer.valueOf()", v1, v2);
// Primitives can't use equals():
int x = value; // [4]
int y = value;
// x.equals(y); // Doesn't compile
System.out.println("Primitive int:");
System.out.printf("%d==%d %b%n", x, y, x == y);
}
public static void main(String[] args) {
test(127);
test(128);
}
}
/* Output:
Automatic:
127==127 true true
new Integer():
127==127 false true
Integer.valueOf():
127==127 true true
Primitive int:
127==127 true
Automatic:
128==128 false true
new Integer():
128==128 false true
Integer.valueOf():
128==128 false true
Primitive int:
128==128 true
*/
show()方法将==的行为和每个对象都有的equals()方法进行了比较。printf()通过使用指定的符号来对参数进行格式化处理,%d用于int类型参数的输出,%b用于boolean类型的输出,%n用于换行。
对于“不等于”,请使用n1 != n2和!n1.equals(n2)这两种方式。
[1] 自动转换为Integer。这其实是通过对Integer.valueOf()
的自动调用来完成的。 [2] 使用标准的对象创建语法new。这是以前创建“包装/装箱”Integer对象的首选方法。 [3] 从Java 9开始,valueOf()优于[2]
。如果尝试在Java 9中使用方式[2],你将收到警告,并被建议使用[3]代替。很难确定[3]是否的确优于[1],不过[1]看起来更简洁。 [4] 基本类型int也可以当作整数值对象使用。
对于参数值127来说,比较操作产生了预期的结果,不过方式[2]中操作的结果却是false。这是因为,虽然参与比较的两个引用包含的内容相同,但它们指向了内存中的不同对象。操作符和!=比较的是对象的引用,而通过不同方式创建的Integer对象,会让操作符产生不同的结果——比如说,方式[1]和方式[3]会生成指向内存中相同位置的Integer对象。对于值范围在-128~127的Integer类型来说,它生成的对象就是这样的3,这影响到了操作符==和!=的比较结果。但在该范围之外的值则不会这样,正如test(128)所演示的那样。
出于效率原因,Integer会通过享元模式来缓存范围在-128~127内的对象,因此多次调用Integer.valueOf(127)生成的其实是同一个对象。而在此范围之外的值则不会这样,比如每次调用Integer.valueOf(128)返回的都是不同的对象。因此需要特别注意,在进行==和!=的比较时,范围不同的值生成对象的方式并不一样,这会影响到比较的行为,从而产生不同的结果。另外,通过new Interger()生成的对象都是新创建的,无论其值处于什么范围。所以通过不同方式创建的Integer对象,也会影响到比较的结果。
在使用Integer的时候,你应该只使用equals()
。如果不小心使用了和!=,并且没有测试-128~127范围外的值,那么虽然你的代码能运行,但在运行中可能悄悄地就会出现错误。如果使用了基本类型int,你就不能使用equals()而必须使用和!=。如果你开始使用基本类型int,然后更改为包装类型Integer,这可能会导致问题,反之亦然。
在Java 9及更新版本中已经弃用new Integer(),因为它的效率远远低于Integer.valueOf()。因此,你应该避免使用new Integer()、new Double()之类的方法,在Java 8中也一样。我之前没有遇到过为了效率而弃用某些东西的例子
当操作非基本类型时,直接使用equals()似乎是理所当然的选择,不过没有那么简单。考虑类ValA:
class ValA {
int i;
}
class ValB {
int i;
// Works for this example, not a complete equals():
public boolean equals(Object o) {
ValB rval = (ValB)o; // Cast o to be a ValB
return i == rval.i;
}
}
public class EqualsMethod {
public static void main(String[] args) {
ValA va1 = new ValA();
ValA va2 = new ValA();
va1.i = va2.i = 100;
System.out.println(va1.equals(va2));
ValB vb1 = new ValB();
ValB vb2 = new ValB();
vb1.i = vb2.i = 100;
System.out.println(vb1.equals(vb2));
}
}
/* Output:
false
true
*/
在main()中,va1和va2包含相同的i值,但使用equals()比较的结果是false,这令人困惑。这是因为equals()方法的默认行为是比较引用。如果只想比较内容,你必须像ValB所示的那样重写equals()方法。ValB.equals()方法只包含了解决示例问题所必需的最简代码,但这不是一个恰当的equals()。注意equals()方法的标准参数是一个Object类型(而不是ValB类型),我们必须通过代码(ValB)o将o强制类型转换为ValB。然后我们就可以用==直接比较两个i的值了,因为它们是基本类型。
2.2 字面量里的下划线
Java 7中有一个十分有用的新增功能:可以在数字字面量里使用下划线,这样更易于阅读。这对在大数值里分组数字特别有帮助:
// operators/Underscores.java
public class Underscores {
public static void main(String[] args) {
double d = 341_435_936.445_667;
System.out.println(d);
int bin = 0b0010_1111_1010_1111_1010_1111_1010_1111;
System.out.println(Integer.toBinaryString(bin));
System.out.printf("%x%n", bin); // [1]
long hex = 0x7f_e9_b7_aa;
System.out.printf("%x%n", hex);
}
}
/* 输出:
3.41435936445667E8
101111101011111010111110101111
2fafafaf
7fe9b7aa
*/
这里有几条合理的规则:
只能使用单个下划线,不能连续使用多个; 数字的开头或结尾不能有下划线; 像F、D或L这样的后缀周围不能有下划线; 在二进制或十六进制标识符b和x的周围不能有下划线。
注意%n的使用。如果你熟悉C风格的语言,可能已经习惯用\n来表示换行符。问题在于这是一个“UNIX风格”的换行符。如果你使用的是Windows平台,就必须改为\r\n。这种差异是一个不必要的麻烦,编程语言应该替你处理这个问题。这就是Java用%n来实现的功能,它会根据程序运行的平台生成合适的换行符,不过这仅会在使用System.out.printf()或System.out.format()时起作用。对于System.out.println(),你仍然必须使用\n;如果使用了%n,println()只会输出%n而不是将其当作换行符。
2.3 操作符小结
public class AllOps {
// To accept the results of a Boolean test:
void f(boolean b) {}
void boolTest(boolean x, boolean y) {
// Arithmetic operators:
//- x = x * y;
//- x = x / y;
//- x = x % y;
//- x = x + y;
//- x = x - y;
//- x++;
//- x--;
//- x = +y;
//- x = -y;
// Relational and logical:
//- f(x > y);
//- f(x >= y);
//- f(x < y);
//- f(x <= y);
f(x == y);
f(x != y);
f(!y);
x = x && y;
x = x || y;
// Bitwise operators:
//- x = ~y;
x = x & y;
x = x | y;
x = x ^ y;
//- x = x << 1;
//- x = x >> 1;
//- x = x >>> 1;
// Compound assignment:
//- x += y;
//- x -= y;
//- x *= y;
//- x /= y;
//- x %= y;
//- x <<= 1;
//- x >>= 1;
//- x >>>= 1;
x &= y;
x ^= y;
x |= y;
// Casting:
//- char c = (char)x;
//- byte b = (byte)x;
//- short s = (short)x;
//- int i = (int)x;
//- long l = (long)x;
//- float f = (float)x;
//- double d = (double)x;
}
void charTest(char x, char y) {
// Arithmetic operators:
x = (char)(x * y);
x = (char)(x / y);
x = (char)(x % y);
x = (char)(x + y);
x = (char)(x - y);
x++;
x--;
x = (char) + y;
x = (char) - y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//- f(!x);
//- f(x && y);
//- f(x || y);
// Bitwise operators:
x= (char)~y;
x = (char)(x & y);
x = (char)(x | y);
x = (char)(x ^ y);
x = (char)(x << 1);
x = (char)(x >> 1);
x = (char)(x >>> 1);
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
x <<= 1;
x >>= 1;
x >>>= 1;
x &= y;
x ^= y;
x |= y;
// Casting:
//- boolean bl = (boolean)x;
byte b = (byte)x;
short s = (short)x;
int i = (int)x;
long l = (long)x;
float f = (float)x;
double d = (double)x;
}
void byteTest(byte x, byte y) {
// Arithmetic operators:
x = (byte)(x* y);
x = (byte)(x / y);
x = (byte)(x % y);
x = (byte)(x + y);
x = (byte)(x - y);
x++;
x--;
x = (byte) + y;
x = (byte) - y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//- f(!x);
//- f(x && y);
//- f(x || y);
// Bitwise operators:
x = (byte)~y;
x = (byte)(x & y);
x = (byte)(x | y);
x = (byte)(x ^ y);
x = (byte)(x << 1);
x = (byte)(x >> 1);
x = (byte)(x >>> 1);
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
x <<= 1;
x >>= 1;
x >>>= 1;
x &= y;
x ^= y;
x |= y;
// Casting:
//- boolean bl = (boolean)x;
char c = (char)x;
short s = (short)x;
int i = (int)x;
long l = (long)x;
float f = (float)x;
double d = (double)x;
}
void shortTest(short x, short y) {
// Arithmetic operators:
x = (short)(x * y);
x = (short)(x / y);
x = (short)(x % y);
x = (short)(x + y);
x = (short)(x - y);
x++;
x--;
x = (short) + y;
x = (short) - y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//- f(!x);
//- f(x && y);
//- f(x || y);
// Bitwise operators:
x = (short) ~ y;
x = (short)(x & y);
x = (short)(x | y);
x = (short)(x ^ y);
x = (short)(x << 1);
x = (short)(x >> 1);
x = (short)(x >>> 1);
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
x <<= 1;
x >>= 1;
x >>>= 1;
x &= y;
x ^= y;
x |= y;
// Casting:
//- boolean bl = (boolean)x;
char c = (char)x;
byte b = (byte)x;
int i = (int)x;
long l = (long)x;
float f = (float)x;
double d = (double)x;
}
void intTest(int x, int y) {
// Arithmetic operators:
x = x * y;
x = x / y;
x = x % y;
x = x + y;
x = x - y;
x++;
x--;
x = +y;
x = -y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//- f(!x);
//- f(x && y);
//- f(x || y);
// Bitwise operators:
x = ~y;
x = x & y;
x = x | y;
x = x ^ y;
x = x << 1;
x = x >> 1;
x = x >>> 1;
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
x <<= 1;
x >>= 1;
x >>>= 1;
x &= y;
x ^= y;
x |= y;
// Casting:
//- boolean bl = (boolean)x;
char c = (char)x;
byte b = (byte)x;
short s = (short)x;
long l = (long)x;
float f = (float)x;
double d = (double)x;
}
void longTest(long x, long y) {
// Arithmetic operators:
x = x * y;
x = x / y;
x = x % y;
x = x + y;
x = x - y;
x++;
x--;
x = +y;
x = -y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//- f(!x);
//- f(x && y);
//- f(x || y);
// Bitwise operators:
x = ~y;
x = x & y;
x = x | y;
x = x ^ y;
x = x << 1;
x = x >> 1;
x = x >>> 1;
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
x <<= 1;
x >>= 1;
x >>>= 1;
x &= y;
x ^= y;
x |= y;
// Casting:
//- boolean bl = (boolean)x;
char c = (char)x;
byte b = (byte)x;
short s = (short)x;
int i = (int)x;
float f = (float)x;
double d = (double)x;
}
void floatTest(float x, float y) {
// Arithmetic operators:
x = x * y;
x = x / y;
x = x % y;
x = x + y;
x = x - y;
x++;
x--;
x = +y;
x = -y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//- f(!x);
//- f(x && y);
//- f(x || y);
// Bitwise operators:
//- x = ~y;
//- x = x & y;
//- x = x | y;
//- x = x ^ y;
//- x = x << 1;
//- x = x >> 1;
//- x = x >>> 1;
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
//- x <<= 1;
//- x >>= 1;
//- x >>>= 1;
//- x &= y;
//- x ^= y;
//- x |= y;
// Casting:
//- boolean bl = (boolean)x;
char c = (char)x;
byte b = (byte)x;
short s = (short)x;
int i = (int)x;
long l = (long)x;
double d = (double)x;
}
void doubleTest(double x, double y) {
// Arithmetic operators:
x = x * y;
x = x / y;
x = x % y;
x = x + y;
x = x - y;
x++;
x--;
x = +y;
x = -y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//- f(!x);
//- f(x && y);
//- f(x || y);
// Bitwise operators:
//- x = ~y;
//- x = x & y;
//- x = x | y;
//- x = x ^ y;
//- x = x << 1;
//- x = x >> 1;
//- x = x >>> 1;
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
//- x <<= 1;
//- x >>= 1;
//- x >>>= 1;
//- x &= y;
//- x ^= y;
//- x |= y;
// Casting:
//- boolean bl = (boolean)x;
char c = (char)x;
byte b = (byte)x;
short s = (short)x;
int i = (int)x;
long l = (long)x;
float f = (float)x;
}
}