java操作符(2)

我的未来我决定 提交于 2020-01-17 13:43:26

一、关系操作符

    关系操作符生成的是一个boolean结果,它们计算的是操作数的值之间的关系。如果关系是真实的,关系表达式会生成的true;如果关系不真实,则生成false。关系操作符包括小于(<)、大于(>)、小于或等于(<=)、大于或等于(>=)、等于(==)以及不等于(!=)。等于和不等于适用于所有的基本数据类型,而其他比较符不适用于boolean类型。因为boolean值只有为true或false,“大于”和“小于”没有实际意义。

(1)测试对象的等价性

    关系操作符==和!=也适用于所有对象,但这两个操作符通常会使第一次接触java的程序员感到迷惑。下面是一个例子:

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);
	}
}

    语句System.out.println(n1 == n2)将打印出括号的比较式的布尔值结果。可能认为输出结果肯定是true,再试false,因为两个Integer对象都是相同的。但是尽管对象的内容相同,然而对象的引用却是不同的,而==和!=比较的就是对象的引用。所以输出结果实际上先是false,再是true。

    如果想比较两个对象的实际内容是否相同,又该如何操作呢?此时,必须使用所有对象都适用于特殊方法equals()。但这个方法不适用于“基本类型”,基本类型直接使用==和!=即可。

public class EqualsMethod {
	public static void main(String[] args) {
		Integer n1=new Integer(47);
		Integer n2=new Integer(47);
		System.out.println(n1.equals(n2));
	}
}

    结果正如我们所预料的那样。但事情并不总是这么简单。假设你创建了自己的类,就像下面这样:

class Value{
	int i;
}

public class EqualsMethod2 {
	public static void main(String[] args) {
		Value v1=new Value();
		Value v2=new Value();
		v1.i=v2.i=100;
		System.out.println(v1.equals(v2));
	}
}

    事情再次变得费解了:结果又是false。这是由于equals()的默认行为是比较引用。所以除非在自己的新类中覆盖equals()方法,否则不可能表现出我们希望的行为。大多数java类库都实现了equals()方法,以便用来比较对象的内容,而非比较对象的引用。

二、逻辑操作符

    逻辑操作符“与”(&&)、“或”(||)、“非”(!)能根据参数的逻辑关系,生成一个布尔值(true或false)。下面这个例子就使用了关系操作符和逻辑操作符。

import java.util.Random;

public class Bool {

	public static void main(String[] args) {
		Random r = new Random(47);
		int i = r.nextInt(100);
		int j = r.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));
	}
}

    注意,如果在应该使用String值的地方使用了boolean值,布尔值会自动转换成适当的文本形式。在上述程序中,可将整数类型替换成除布尔型以外的其他任何基本数据类型。但要注意,对浮点数的比较是非常严格的。即使一个数仅在小数部分与另一个数存在极微小的差异,仍然认为它们是“不相等”的。即使一个数只比零大一点点,它仍然是“非零”值。

(1)短路

    当使用逻辑操作符时,我们会遇到一种“短路”现象。即一旦能够明确无误地确定整个表达式的值,就不再计算表达式余下的部分了。因此,整个逻辑表达式靠后的部分有可能不会被运算。例如:

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 = test1(0) && test2(2) && test3(2);
		System.out.println("expression is " + b);
	}
}

    每个测试都会比较参数,并返回true或false。它也会打印信息告诉你正在调用测试。这些测试都作用于下面这个表达式:

test1(0) && test2(2) && test3(2)

    你会很自然的认为所有这三个测试都会得以执行。但输出显示却并非这样。第一个测试生成结果true,所以表达式计算会继续下去。然而,第二个测试产生了一个false结果。由于这意味着整个表达式肯定为false,所以没有必要继续计算剩余的表达式,那样只是浪费。“短路”一词的由来正源于此。事实上,如果所有的逻辑表达式都有一部分不必计算,那将获得潜在的性能提升。

三、三元操作符

    三元操作符也称为条件操作符,它显得比较特别,因为它有三个操作数;但它确实属于操作符的一种,因为它最终也会生成一个值。其表达式形式:boolean-exp? value0:value1

    如果boolean-exp(布尔表达式)的结果为true,就计算 value0,而且这个计算结果也就是操作符最终产生的值。如果boolean-exp的结果为false,就计算value1,同样,它的结果也就成为操作符最终产生的值。

    条件操作符与if-else完全不同,因为它会产生一个值。下面是这两者进行比较的示例:

public class TernaryIfElse {
	static int ternary(int i) {
		return i < 10 ? i * 100 : i * 10;
	}

	static int standardIfElse(int i) {
		if (i < 10)
			return i * 100;
		else
			return i * 10;
	}

	public static void main(String[] args) {
		System.out.println(ternary(9));
		System.out.println(ternary(10));
		System.out.println(standardIfElse(9));
		System.out.println(standardIfElse(10));
	}
}

    可以看出,上面的ternary()中的代码与standardIfElse()中不用三元操作符的代码相比,显得更加紧凑;但standardIfElse()更易理解,而且不需要太多的录入。所以在选择使用三元操作符时,请务必仔细考虑。

四、字符串操作符 + 和 +=

    这个操作符在java中有一项特殊用途:连接不同的字符串。字符串操作符有一些很有趣的行为。如果表达式以一个字符串起头,那么后续所有操作数都必须是字符串型(编译器会把双引号内的字符序列自动转成字符串):

public class StringOperators {
	public static void main(String[] args) {
		int x = 0, y = 1, z = 2;
		String s = " x, y, z ";
		System.out.println(s + x + y + z);
		System.out.println(x + s);
		s += "(summed) = ";
		System.out.println(s + (x + y + z));
		System.out.println("" + x);
	}
}

    请注意,第一个打印语句的输出是012而不是3,而3正式将这些整数求和之后应该得到的结果,之所以出现这种情况,是因为java编译器会将x、y和z转换成它们的字符串形式,然后连接这些字符串,而不是先把它们加到一起。第二个打印语句把先导的变量转换成String,因此这个字符串转换将不依赖于第一个变量的类型。最后,看到+=操作符将一个字符串追加到了s上,并且使用了括号来控制表达式的赋值顺序,以使得int类型的变量在显示之前确实进行了求和操作。

    请注意main()中的最后一个示例:有时会看到这种一个空的String后面跟随+和一个基本类型变量,以此作为不调用更加麻烦的显示方法(Integer.toString())而执行字符串转换的方式。

五、类型转换操作符

    类型转换(cast)的愿意是“模型铸造”。在适当的时候,java会将一种数据类型自动转换成另一种。例如,假设我们为某浮点变量赋以一个整数值,编译器会将int自动转换成float。类型转换运算允许我们显式地进行这种类型的转换,或者在不能自动进行转换的时候强制进行类型转换。

    要想执行类型转换,需要将希望得到的数据类型置于圆括号内,放在要进行类型转换的值得左边,可以在下面的示例中看到它:

public class Casting {
	public static void main(String[] args) {
		int i = 200;
		long lng = i;
		long lng2 = (long) 100;
		short a = (short) lng;
	}
}

    正如所看到的,既可对数值进行类型转换,亦可对变量进行类型转换。请注意,这里可能会引入“多余的类型”,例如,编译器在必要的时候会自动进行int值到long值的提升。但是你仍然可以做这样“多余的”事,以提醒自己需要注意,也可以使代码更清楚。在其他情况下,可能只有先进行类型转换,代码编译才能通过。

    在C和C++中,类型转换有时会让人头痛。但是在java中,类型转换则是一种比较安全的操作。然而,如果要执行一种名为窄化转换(narrowing conversion)的操作(也就是说,将能容纳更多信息的数据类型转换成无法容纳那么多信息的类型),就有可能面临信息丢失的危险。此时编译器会强制我们进行类型转换,这实际上是说:“这可能是一件危险的事情,如果无论如何要这么做,必须显式地进行类型转换。”而对于扩展转换(videning conversion),则不必显式地进行类型转换,因为新类型肯定能容纳原来类型的信息,不会造成任何信息的丢失。

    java允许我们把任何基本数据类型转换成别的基本数据类型,但布尔型除外,后者根本不允许进行任何类型的转换处理。“类”数据类型不允许进行类型转换。为了将一种类转换成另一种,必须采用特殊的方法。

(1)截尾和舍入
    在执行窄化转换时,必须注意截尾与舍入的问题。例如,如果将一个浮点值转换为整型值,java会如何处理呢?例如,将29.7转换为int,结果是30还是29?例如:

public class CastingNumbers {
	public static void main(String[] args) {
		double above = 0.7, below = 0.4;
		float fabove = 0.7f, fbelow = 0.4f;
		System.out.println("(int)above:" + (int) above);
		System.out.println("(int)below:" + (int) below);
		System.out.println("(int)fabove:" + (int) fabove);
		System.out.println("(int)fbelow:" + (int) fbelow);
	}
}

    因此答案是在将float和double转型为整型时,总是对该数字执行截尾。如果想要得到舍入的结果,就需要使用java.lang.Math中的round()方法:

public class RoundingNumbers {
	public static void main(String[] args) {
		double above = 0.7, below = 0.4;
		float fabove = 0.7f, fbelow = 0.4f;
		System.out.println("(int)above:" + Math.round(above));
		System.out.println("(int)below:" + Math.round(below));
		System.out.println("(int)fabove:" + Math.round(fabove));
		System.out.println("(int)fbelow:" + Math.round(fbelow));
	}
}

    由于round()是java.lang的一部分,因此在使用它时不需要额外地导入。

(2)提升

    如果对基本数据类型执行算术或按位运算,大家会发现,只要类型比int小(即char、byte或者short),那么在运算之前,这些值会自动转换成int。这样一来,最终生成的结果就是int类型。如果想把结果赋值给较小的类型,就必须使用类型转换(既然把结果赋给较小的类型,就可能出现信息丢失)。通常,表达式中出现的最大的数据类型决定了表达式最终结果的数据类型。如果将一个float值与一个double值相乘,结果就是double;如果将一个int和long值相加,则结果为long。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!