Topic: JAVA面试题解惑系列(十一)——这些运算符你是否还记得?

  Print this page

1.JAVA面试题解惑系列(十一)——这些运算符你是否还记得? Copy to clipboard
Posted by: 臧圩人
Posted on: 2008-08-25 09:08

------------------------------------------------------------------------------------
我想出一本名为《JAVA面试题解惑系列》的书籍,详情请见:
http://rmyd.group.javaeye.com/group/topic/6193
目前网络连载中:http://zangweiren.javaeye.com/
我的邮箱:zangweiren@163.com
请大家多关注,多提宝贵意见!
------------------------------------------------------------------------------------

作者:臧圩人(zangweiren)
网址:http://zangweiren.javaeye.com

>>>转载请注明出处!<<<

有些运算符在JAVA语言中存在着,但是在实际开发中我们或许很少用到它们,在面试题中却时常出现它们的身影,对于这些运算符的含义和用法,你是否还记得呢?

自增(++)和自减(--)运算符

我们先来回答几个问题吧:
Java代码

int i = 0;
int j = i++;
int k = --i;


这段代码运行后,i等于多少?j等于多少?k等于多少?太简单了?好,继续:
Java代码

int i = 0;
int j = i++ + ++i;
int k = --i + i--;


代码执行后i、j、k分别等于多少呢?还是很简单?好,再继续:
Java代码

int i=0;
System.out.println(i++);


这段代码运行后输出结果是什么?0?1?
Java代码

float f=0.1F;
f++;
double d=0.1D;
d++;
char c='a';
c++;


上面这段代码可以编译通过吗?为什么?如果你能顺利回答到这里,说明你对自增和自减运算符的掌握已经很好了。

为了分析出上面提出的几个问题,我们首先来回顾一下相关知识:

1、自增(++):将变量的值加1,分前缀式(如++i)和后缀式(如i++)。前缀式是先加1再使用;后缀式是先使用再加1。
2、自减(--):将变量的值减1,分前缀式(如--i)和后缀式(如i--)。前缀式是先减1再使用;后缀式是先使用再减1。

在第一个例子中,int j=i++;是后缀式,因此i的值先被赋予j,然后再自增1,所以这行代码运行后,i=1、j=0;而int k=--i;是前缀式,因此i先自减1,然后再将它的值赋予k,因此这行代码运行后,i=0、k=0。

在第二个例子中,对于int j=i++ + ++i;,首先运行i++,i的值0被用于加运算(+),之后i自增值变为1,然后运行++i,i先自增变为2,之后被用于加运算,最后将i两次的值相加的结果0+2=2赋给j,因此这行代码运行完毕后i=2、j=3;对于int k=--i + i--;用一样的思路分析,具体过程在此不再赘述,结果应该是i=0、k=2。

自增与自减运算符还遵循以下规律:

1、可以用于整数类型byte、short、int、long,浮点类型float、double,以及字符串类型char。
2、在Java5.0及以上版本中,它们可以用于基本类型对应的包装器类Byte、Short、Integer、Long、Float、Double、Character。
3、它们的运算结果的类型与被运算的变量的类型相同。

下面的这个例子验证以上列出的规律,它可以编译通过并执行。
Java代码

public class Test {
public static void main(String[] args) {
// 整型
byte b = 0;
b++;
// 整型
long l = 0;
l++;
// 浮点型
double d = 0.0;
d++;
// 字符串
char c = 'a';
c++;
// 基本类型包装器类
Integer i = new Integer(0);
i++;
}
}


按位运算符

你还能说出来按位运算符一共有哪几种吗?对比下面的列表看看,有没有从你的记忆中消失了的:

1、按位与运算(&):二元运算符。当被运算的两个值都为1时,运算结果为1;否则为0。
2、按位或运算(|):二元运算符。当被运算的两个值都为0时,运算结果为0;否则为1。
3、按位异或运算(^):二元运算符。当被运算的两个值中任意一个为1,另一个为0时,运算结果为1;否则为0。
4、按位非运算(~):一元运算符。当被运算的值为1时,运算结果为0;当被运算的值为0时,运算结果为1。

这里不像我们看到的逻辑运算符(与运算&&、或运算||、非运算!)操作的是布尔值true或false,或者是一个能产生布尔值的表达式;“按位运算符”所指的“位”就是二进制位,因此它操作的是二进制的0和1。在解释按位运算符的执行原理时,我们顺便说说它们和逻辑运算符的区别。

1、逻辑运算符只能操作布尔值或者一个能产生布尔值的表达式;按位运算符能操作整型值,包括byte、short、int、long,但是不能操作浮点型值(即float和double),它还可以操作字符型(char)值。按位运算符不能够操作对象,但是在Java5.0及以上版本中,byte、short、int、long、char所对应的包装器类是个例外,因为JAVA虚拟机会自动将它们转换为对应的基本类型的数据。

下面的例子验证了这条规律:
Java代码

public class BitOperatorTest {
public static void main(String[] args) {
// 整型
byte b1 = 10, b2 = 20;
System.out.println("(byte)10 & (byte)20 = " + (b1 & b2));
// 字符串型
char c1 = 'a', c2 = 'A';
System.out.println("(char)a | (char)A = " + (c1 | c2));
// 基本类型的包装器类
Long l1 = new Long(555), l2 = new Long(666);
System.out.println("(Long)555 ^ (Long)666 = " + (l1 ^ l2));
// 浮点型
float f1 = 0.8F, f2 = 0.5F;
// 编译报错,按位运算符不能用于浮点数类型
// System.out.println("(float)0.8 & (float)0.5 = " + (f1 & f2));
}
}


运行结果:

*(byte)10 & (byte)20 = 0
*(char)a | (char)A = 97
*(Long)555 ^ (Long)666 = 177

2、逻辑运算符的运算遵循短路形式,而按位运算符则不是。所谓短路就是一旦能够确定运算的结果,就不再进行余下的运算。下面的例子更加直观地展现了短路与非短路的区别:
Java代码

public class OperatorTest {
public boolean leftCondition() {
System.out.println("执行-返回值:false;方法:leftCondition()");
return false;
}

public boolean rightCondition() {
System.out.println("执行-返回值:true;方法:rightCondition()");
return true;
}

public int leftNumber() {
System.out.println("执行-返回值:0;方法:leftNumber()");
return 0;
}

public int rightNumber() {
System.out.println("执行-返回值:1;方法:rightNumber()");
return 1;
}

public static void main(String[] args) {
OperatorTest ot = new OperatorTest();

if (ot.leftCondition() && ot.rightCondition()) {
// do something
}
System.out.println();

int i = ot.leftNumber() & ot.rightNumber();
}
}


运行结果:

*执行-返回值:false;方法:leftCondition()
*
*执行-返回值:0;方法:leftNumber()
*执行-返回值:1;方法:rightNumber()

运行结果已经很明显地显示了短路和非短路的区别,我们一起来分析一下产生这个运行结果的原因。当运行“ot.leftCondition() && ot.rightCondition()”时,由于方法leftCondition()返回了false,而对于“&&”运算来说,必须要运算符两边的值都为true时,运算结果才为true,因此这时候就可以确定,不论rightCondition()的返回值是什么,“ot.leftCondition() && ot.rightCondition()”的运算值已经可以确定是false,由于逻辑运算符是短路的形式,因此在这种情况下,rightCondition()方法就不再被运行了。
而对于“ot.leftNumber() & ot.rightNumber()”,由于“leftNumber()”的返回值是0,对于按位运算符“&”来说,必须要运算符两边的值都是1时,运算结果才是1,因此这时不管“rightNumber()”方法的返回值是多少,“ot.leftNumber() & ot.rightNumber()”的运算结果已经可以确定是0,但是由于按位运算符是非短路的,所以rightNumber()方法还是被执行了。这就是短路与非短路的区别。

移位运算符

移位运算符和按位运算符一样,同属于位运算符,因此移位运算符的位指的也是二进制位。它包括以下几种:

1、左移位(<<):将操作符左侧的操作数向左移动操作符右侧指定的位数。移动的规则是在二进制的低位补0。
2、有符号右移位(>>):将操作符左侧的操作数向右移动操作符右侧指定的位数。移动的规则是,如果被操作数的符号为正,则在二进制的高位补0;如果被操作数的符号为负,则在二进制的高位补1。
3、无符号右移位(>>>):将操作符左侧的操作数向右移动操作符右侧指定的位数。移动的规则是,无论被操作数的符号是正是负,都在二进制位的高位补0。

注意,移位运算符不存在“无符号左移位(<<<)”一说。与按位运算符一样,移位运算符可以用于byte、short、int、long等整数类型,和字符串类型char,但是不能用于浮点数类型float、double;当然,在Java5.0及以上版本中,移位运算符还可用于byte、short、int、long、char对应的包装器类。我们可以参照按位运算符的示例写一个测试程序来验证,这里就不再举例了。

与按位运算符不同的是,移位运算符不存在短路不短路的问题。

写到这里就不得不提及一个在面试题中经常被考到的题目:

引用
请用最有效率的方法计算出2乘以8等于几?


这里所谓的最有效率,实际上就是通过最少、最简单的运算得出想要的结果,而移位是计算机中相当基础的运算了,用它来实现准没错了。左移位“<<”把被操作数每向左移动一位,效果等同于将被操作数乘以2,而2*8=(2*2*2*2),就是把2向左移位3次。因此最有效率的计算2乘以8的方法就是“2<<3”。

下一期预告:JAVA面试题解惑系列(十二)——你真的了解数组吗?

2.Re:JAVA面试题解惑系列(十一)——这些运算符你是否还记得? [Re: 臧圩人] Copy to clipboard
Posted by: JiafanZhou
Posted on: 2008-08-27 19:45

Maybe you should add the "? :" operators, I found that they are useful.

Regards,
Jiafan

3.Re:JAVA面试题解惑系列(十一)——这些运算符你是否还记得? [Re: 臧圩人] Copy to clipboard
Posted by: scottding
Posted on: 2008-09-10 17:23

哎,说句实在话,这些问题,似乎有些钻牛角尖了,以前刚刚学习编程的时候,也经常搞这些问题,但是,实际项目中的时候可能一年也用不到这么搞得东西。即使遇到了,也会找一个简单的解决方法。

汗一下

4.Re:JAVA面试题解惑系列(十一)——这些运算符你是否还记得? [Re: 臧圩人] Copy to clipboard
Posted by: andy_wang_5
Posted on: 2008-09-11 10:01

同意楼上, "? :" 好像也没什么意义. "if" 更通用一点.

研究技术是为了寻找一种更好的解决问题的方法. 千万别做孔乙己

5.Re:JAVA面试题解惑系列(十一)——这些运算符你是否还记得? [Re: 臧圩人] Copy to clipboard
Posted by: JiafanZhou
Posted on: 2008-09-11 22:17

normally ? : can simplify if ... else... , which will make code shorter.

e.g.

String ppName = (dualStackvoipUa.getPacketProcessor() != null)
? dualStackvoipUa.getPacketProcessor().getName() : null;


Using if ..else..., then it would look like

String ppName;
if (dualStackvoipUa.getPacketProcessor() != null)
{
ppName = dualStackvoipUa.getPacketProcessor().getName();
}
else
{
ppName = null;
}


I have shortened 9 lines only to 2 lines, not bad...

6.Re:JAVA面试题解惑系列(十一)——这些运算符你是否还记得? [Re: 臧圩人] Copy to clipboard
Posted by: newfish
Posted on: 2008-09-24 00:23

// 基本类型的包装器类
Long l1 = new Long(555), l2 = new Long(666);
System.out.println("(Long)555 ^ (Long)666 = " + (l1 ^ l2));

这里不应该是用Long 而应该是用long吧
进行运算的应该是原始类型呀

7.Re:JAVA面试题解惑系列(十一)——这些运算符你是否还记得? [Re: newfish] Copy to clipboard
Posted by: JiafanZhou
Posted on: 2008-09-25 16:15

newfish wrote:
// 基本类型的包装器类
Long l1 = new Long(555), l2 = new Long(666);
System.out.println("(Long)555 ^ (Long)666 = " + (l1 ^ l2));

这里不应该是用Long 而应该是用long吧
进行运算的应该是原始类型呀

No, when Java performs operations with primitive types, it uses integer not long. And Java5 will use its new feature "Autobox" to convert any Long, Integer type into integer.

8.Re:JAVA面试题解惑系列(十一)——这些运算符你是否还记得? [Re: 臧圩人] Copy to clipboard
Posted by: 690716494
Posted on: 2008-09-26 23:14

I agree with jiafan
?:make the code shorter

9.Re:JAVA面试题解惑系列(十一)——这些运算符你是否还记得? [Re: 臧圩人] Copy to clipboard
Posted by: andy_wang_5
Posted on: 2008-09-27 09:30

个人认为. if 更易读.
虽然多了几行代码, 算不上累赘.

我看?:的代码时大脑反应时间总比if慢. 可能是我第一次认识"?:"就比较反感. 我从来不用"?:"

10.Re:JAVA面试题解惑系列(十一)——这些运算符你是否还记得? [Re: 臧圩人] Copy to clipboard
Posted by: JiafanZhou
Posted on: 2008-09-29 16:39

IMHO, using "?:" properly can greatly simplify the code. And it is really a matter of taste of each individual Java programmer. Once you get used to "?:", you will find the code is more readable and easier to understand.

Actually, it is called shortcut version (or compact version) of if-else statement. In other words, they are the same from a compiler's perspective.

Regards,
Jiafan


   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2021 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号-1
客服电话 18559299278    客服信箱 714923@qq.com    客服QQ 714923