目标1,应用运算符
确定使用任意运算符的结果,包括运算符赋值和instanceof来操作任意类型类的作用域或可达域以及以上的组合。
instanceof运算符
instanceof运算符是一个很陌生的东西,在我眼里,它更像是一个方法而不是一个运算符。你可能没用过它而写了大量的Java代码,但为了考试的目的,你需要弄懂它。instanceof运算符在运行时测试一个类的类型然后返回一个布尔值。它一般用来说:这个类是一个instanceof的那个类吗?
如果你像下面这样的小地方用它,看起来不是很有用
public class InOf {
public static void main(String argv[]){
InOf i = new InOf();
if(i instanceof InOf){
System.out.println("It's an instance of InOf");
}//End if
}//End of main
}
你可能会认为这段代码会输出
"It's an instance of InOf"
但是当你访问了一个涉及到向下多层的对象引用的时候,情况可能会改变。你可能有一个把组件作为参数的方法,它可能真正指向一个Button,Label或其他任何东西。这种情况下,instanceof运算符就可以用来测试对象的类型,执行匹配的角色,然后调用适当的方法。用下面的代码举例说明:
import java.awt.*;
public class InOfComp {
public static void main(String argv[]){
}//End of main
public void mymethod(Component c){
if( c instanceof Button){
Button bc = (Button) c;
bc.setLabel("Hello");
}
else
if (c instanceof Label){
Label lc = (Label) c;
lc.setText("Hello");
}
}//End of mymethod
}
如果运行时的测试和角色匹配没有执行适当的方法,setLabel和setText将不可用。注意,instanceof测试反对一个类名但不反对类的对象引用。
+运算符
像你期望的一样,+运算符会把两个数相加。因此下面的代码将会输出10
int p=5;
int q=5;
System.out.println(p+q);
+运算符在Java中是一个罕见的操作符重载的例子。C++程序员习惯于能够重载运算符为他们定义的任何意义。Java程序员没有这种便利,但是由于对于字符串来讲,加号用来做串联是很有用的。因此,下面的代码将编译
String s = "One";
String s2 = "Two"
String s3 = "";
s3 = s+s2;
System.out.println(s3);
这段代码将会输出字符串OneTwo。注意,两个连接的字符串中间没有空格。
如果你是Visual Basic背景的程序员,下面的语法可能不熟悉
s2+=s3
这句代码在Java中可以表达成更接近于Visual Basic形式的语句
s2= s2+s3
在某些情况下,Java可能在后台调用toString方法。就像名字显示的那样,这个方法会试着转换为一个String表达。对一个整型来讲,这就意味着数字10在toString调用后会返回字符串“10”
这点在下面的代码中有所展现。
int p = 10;
String s = "Two";
String s2 = "";
s2 = s + p;
System.out.printlns(s2);
这段代码会输出Two10
记住,只有+运算符可以对字符串进行重载,如果你对字符串使用除号和减号(/ -),你会得到一个错误。
为不同类型的原始型变量赋值
一个布尔类型不能赋值给除了布尔类型外的变量。对于C/C++程序员来讲,记得这说明一个布尔类型不能赋值为-1或0,而一个Java布尔类型不能用零或非零来替换。
除了布尔型之外,我们学习这个目标的一般规则就是扩展转换是允许的,当然不考虑精确性的危险。由于缩小转换会产生降低精确性的结果,因此是不允许的。对于扩展,我的意思是一个例如byte这样的变量,占用一个byte(8bit),可以赋值给一个integer这样占用多个bit的变量。
如果你试着将一个integer赋值给一个byte,你会得到一个编译时错误
byte b= 10;
int i = 0;
b = i;
原始类型可能赋值给一个“更宽”的数据类型,一个boolean只能赋值给另一个boolean。
你可能预料到你不能将一个原始类型赋值给一个对象或相反的操作,这包括原始类型的包装类。因此,下面的代码是非法的
int j=0;
Integer k = new Integer(99);
j=k; //Illegal assignment of an object to a primitive
赋值对象和赋值原始类型的一个重要区别是,原始类型在编译时被检查而对象在运行时被检查。我们将会在后面提到,当一个对象在编译时没有被完全处理的话,还会有重要的含义。
你当然可以运行一个角色来阻止一个变量适应一个窄一些的数据类型。一般不建议你放任精确性降低。但是如果你真的想这样干,Java使用C/C++的习惯,将数据类型封装进()中,因此,下面的代码将会编译运行。
public class Mc{
public static void main(String argv[]){
byte b=0;
int i = 5000;
b = (byte) i;
System.out.println(b);
}
}
输出结果是
-120
可能不是很有必要这么干。
给不同类型的对象引用赋值
将一个对象引用赋值给另一个的一般规则是,你可以对继承树向上赋值但不能向下复制。你可以这样想,如果你将一个子类的实例赋值给基类,Java知道哪个方法会在子类中。但是一个子类可能有基类没有的额外方法。你可以使用操作符强制转换。
对象引用可以从子类向基类赋值。
下面举例说明如何向上转换对象引用。
class Base{}
public class ObRef extends Base{
public static void main(String argv[]){
ObRef o = new ObRef();
Base b = new Base();
b=o;//This will compile OK
/*o=b; This would cause an error indicating
an explicit cast is needed to cast Base
to ObRef */
}
}
++和-运算符
你可能因为发现在一个并非微不足道的Java程序没有使用++或-运算符而感到巨大的压力,因而很容易认为你知道的一切只是为了测试的目的而需要知道的知识。这些运算符可以用于变量的前增或者后增。如果你不明白它们的差别,可能会因为一个相当容易的问题而导致测试丢分。举例来说,下面的代码编译运行后,你认为它会向控制台发送什么呢?
public class PostInc{
static int i=1;
public static void main(String argv[]){
System.out.println(i++);
}
}
如果你编译运行了这段代码,可以看到输出是1而不是2。这是因为++放在i的后面,因此自增(加1)会在这行运行后发生,对于-运算符也是一样的规则。
位移运算符
我恨整个的位移部分。它需要你的大脑充满了非直觉的能力,因此很少有程序员用它。现实中的典型应用的例子是密码系统和低级别的镜像文件。你可能写了大量的Java程序但却从没自己移动过一个bit。但是学习它的大部分的原因是为了针对测试而不是为了其他想法。测试一般会有至少一个问题是关于位移运算符的。如果你具有C++背景,你可能会被误导成认为你的所有C++知识都可以直接转换成Java语言。
为了清楚的理解这点,你必须用二进制方式思考,也就是知道每个bit位的值
32, 16, 8, 4, 2, 1
不仅仅是欣赏二进制,你还需要从整体上把握“高度褒扬的二进制”编号系统。在这个系统描述中,第一bit位表示这个数是正数还是负数。尽量依靠你的直觉,你会发现当你理解二进制系统的工作原理类似于汽车里程表时,事情开始变得奇怪了。想象每个轮子有一个1或者0在上面。如果你想把下面的显示回退的时候
00000000 00000000 00000000 00000001
如果后退了一下,就会显示
11111111 11111111 11111111 11111111 11111111
这表示-1。如果再后退一下,会显示
11111111 11111111 11111111 11111111 1111110
这些例子有些过于单纯化了。直到我学习Java程序测试,我都只是认为二进制系统只是利用第一位来表明标记部分。但你可以看到,实际情况要复杂的多。为了帮你多理解一点关于符号的知识,我写了一个相当简单的程序,它会显示一个在命令行给定的数的bit模式。也可以改进为八进制形式,但同样很容易的得到大体的结果。
public class Shift{
public static void main(String argv[]){
int i = Integer.parseInt(argv[0]);
System.out.println(Integer.toBinaryString(i));
}
}
如果你是从C/C++背景转到Java,你可以为Java中的右移运算符比C/C++稍微明确一点而得到一点安慰。在C/C++中,右移运算符可能是取决于编译器执行的有符号数或无符号数。如果你是从Visual Basic背景转来,恭喜你可以在低级别的情况下编程了。
注意,本节的目标仅仅是要求你理解应用这些运算符到int值后的结果。这比应用运算符到byte或者short上显得容易一些,尤其是负数,更有可能得到不可预料的结果。
正数的无符号右移
我以无符号右移开始是因为它是最怪异的位移,而且需要对二进制表示法有充分的理解。而处理负数时会更加的怪异,因此我以正数开始。无符号右移操作将一个数字作为一个纯粹的bit模式,忽略特定的标识位。记住,一旦你开始将一个数字作为一系列bit时,任何bit级别的操作可能带给你将它作为一个普通数字时想不到的结果。
无符号右移操作包括两个操作数,第一个数字是需要位移的数,跟在运算符后面的数字是需要位移的位置,例如下面
3 >>> 1
表示你将数字3的bit向右移动一位。
二进制补码方式的系统意味着数字开头的bit位表示它是正数还是负数。如果是0,表示数字是正数,如果是1,表示它是负数。无符号数的右移的第一位总是被0补上,这就意味着一个无符号数右移操作总是得到正数的结果。
如果你能想起数字3的二进制形式
011
并且将它右移一位
3 >> 1
你会得到
001
注意,有新值的位离开了数的末端并被有效的抛弃了。
如果你执行了两位的右移,你可能因为数字变成了0并且0覆盖了所有的bit位置而感到有点惊讶。如果你一直增加右移的数量,例如6位,10位或20位,你会发现像你预料的那样结果一直是0。可是如果你坚持到
3 >>>32
得到了令人惊讶的结果3,为什么会这样?
在移动之前的后台,一个模32被作为操作数。模运算符,在Java中指一个数被另一个数通过%字符除,然后返回余数。同时一个数如果小于求模的数,将会直接返回原值。同时,如果一个数位移不到32时,模运算符不会注意到这种情况,一旦到了32,模运算符就会起作用了。
因此,32%32返回0,当作没有东西剩余而使运算符作用于
3 >>> 32
的值是3,也就是说3被移动了0位。
我开始依靠直觉没能发现这点,因此我写了下面的代码
public class shift{
static int i=2;
public static void main(String argv[]){
System.out.println(32 % 32);
System.out.println( 3 >>> 32);
}
}
这段代码的输出是
0
3
一个模32在位移操作数执行时起作用,这影响超过32位的位移。
负数的无符号右移
一个负数的无符号右移通常会得到一个正数的结果。我说的通常是因为有一个例外,就是你移动的包括符号位的原始数刚好是在32位结束。像刚才解释的,你通常得到一个正数的原因是无符号右移把第一个符号位用0代替了,这表明是一个正数。
一个负数的无符号右移有时看起来很怪异。看下面的代码
System.out.println( -3 >>> 1);
你可能认为会得到这样的数
1
也就是说符号位被0代替,使它成为正数,然后右移一位。但这是不会发生的,真正的结果是
2147483646
有些奇怪但却是事实。
这种奇怪的结果的背后原因跟二进制数的表示方式有关。如果你将数字的表示想象成汽车里程表代表的轮子,当你从最大可能的数开始往下数到0会发生什么呢?然后再回到低于0的第一个数?所有的数位包括表示负数的符号位都会变成1。当你执行无符号右移时,你打破了这种数字表示方式,仅仅把符号位当作另一个数。因此,即使你从例子中-3这样的很小的负数开始,你也会得到一个很大的正数。你可能会在测试中遇到这样的问题,问你一个负数的无符号右移的结果。正确的答案可能看起来很不可靠。
一个很小的负数通过无符号右移,可能得到一个很大的正数返回值。
有符号移动运算符<<和>>
<<和>>运算符用0设置新位。因此,下面的例子
System.out.println(2 << 1)
这句代码将数字2左移一位然后将最右边一位置0
因此,值
010
变成
100
即十进制4。你可以认为这个操作每次将原数翻倍。因此下面的代码
System.out.println(2 << 4)
结果是32
你可以这样认为
2*2=4(第一次位移)
2*4=8(第二次位移)
2*8=16(第三次位移)
2*16=32(第四次位移)
当你移动到数字结尾时,这种思考可能导致非常错误的结果,因此下面的代码
System.out.println(2<<30)
结果是
-2147483648
这看起来相当不符合直觉,但是你可以想到数字2可以移动最多的位置,现在变成了二进制整型可以表示的最大的负数。如果你再移动一位
system.out.println(2 << 31)
结果是0,因为每一位都变成了0,而数字2已经到了末端而被抛弃了。
随着有符号右移,左边的数位(新的)在移动前带着符号位(作为对照,左移的新位置被置0)。这意味着右移将不会影响结果数的符号位。
2 >> 2;
这次将数字2的所有位右移两个位置,因此,值
0010
变成
0000
或者说是十进制的0(我认为在所有进制中0都是这样)
这跟执行一个重复的integer除法等价。在这个例子中,所有的位置都置0。
有符号右移运算符的结果数字有同样的符号位。
我创建了一个applet,允许你尝试各种移动运算符,并且查看十进制或者bit模式的结果。我已经在网页中包含了这个applet代码,你可以看看它是如何工作的。
BitShift Applet
运算符优先
运算符优先是指哪个运算符执行的优先权的顺序。下表是运算符优先的总结
Operator Precedence
()
++expr --expr +expr -expr ~ !
* / %
+ -
<< >> >>>
< > <= >= instanceof
== !=
&
^
|
&&
||
? :
= += -= *= /= %= &= ^= |= <<= >>= >>>=
我放在同一行的运算符有同样的优先权。通常在真正编程的时候,你将会用圆括号指定你期望的执行的表达式的顺序。这意味着你可以不必真正掌握优先级的顺序并且可以让读你代码的其他程序员更清楚。但你可能在测验中遇到建立在运算符优先级上的问题,尤其是常用的运算符如+,-,*。
如果运算符优先级的概念对你没什么意义,你可以试试下面的代码会输出什么
public class OperPres{
public static void main(String argv[]){
System.out.println(2 + 2 * 2);
System.out.println(2 + (2 * 2));
System.out.println(8 / 4 + 4);
System.out.println(8 /(4 +4));
int i = 1;
System.out.println(i++ * 2);
}
第一条语句2+2*2意味着2+2的值乘2得到输出结果8,还是表示2*2再加2得到结果6呢?关于其他计算的类似问题可能被问到。顺便说一下,这个程序的输出结果是66612。
问题
问题1)在下面给定的类中,哪一个能够不出错的编译?
interface IFace{}
class CFace implements IFace{}
class Base{}
public class ObRef extends Base{
public static void main(String argv[]){
ObRef ob = new ObRef();
Base b = new Base();
Object o1 = new Object();
IFace o2 = new CFace();
}
}
1) o1=o2;
2) b=ob;
3) ob=b;
4) o1=b;
问题2)在下面给定的包含变量的语句,哪一个能够不出错的编译?
String s = "Hello";
long l = 99;
double d = 1.11;
int i = 1;
int j = 0;
1) j= i <<s;
2) j= i<<j;
3) j=i<<d;
4)j=i<<l;
问题3)给定下面的变量
char c = 'c';
int i = 10;
double d = 10;
long l = 1;
String s = "Hello";
哪一个能够不出错的的编译?
1) c=c+i;
2) s+=i;
3) i+=s;
4) c+=s;
问题4)下面的语句会输出什么?
System.out.println(-1 >>>1);
1) 0
2) -1
3) 1
4) 2147483647
问题5)下面的语句会输出什么?
System.out.println(1 <<32);
1) 1
2) -1
3) 32
4)-2147483648
问题6)下面的哪个语句是正确的?
1) System.out.println(1+1);
2) int i= 2+'2';
3) String s= "on"+'one';
4) byte b=255;
问题7)当你试着编译运行下面的代码时会出现什么情况?
Public class Pres{
public static void main(String argv[]){
System.out.println( 2 * 2 | 2);
}
}
1) Compile time errors, operators cannot be chained together in this manner
2) Compilation and output of 4
3) Compilation and output of 6
4) Compilation and output of 2
问题8)当你试着编译运行下面的代码时会出现什么情况?
public class ModShift{
static int i = 1;
static int j =1;
static int k = 0;
public static void main(String argv[]){
i = i << 32;
j = j >>32;
k = i + j;
System.out.println(k++);
}
1 )Compile time error
2) Compilation and output of 3
3) Compilation and output of -3
4) Compilation and output of 2
问题9)当你试着编译运行下面的代码时会出现什么情况?
public class Shift{
static int i;
static int j;
public static void main(String argv[]){
i = 2;
j = i <<31;
i =i++;
System.out.println(j);
System.out.println(i);
}
}
1) -2147483648 followed by 2
2) -2147483648 followed by 3
3) 0 followed by 3
4) 0 followed by 2
问题10)下面程序的输出是什么?
public class Mac{
public static void main(String argv[]){
System.out.println( -1 >>>1 & 2);
}
}
1) 2147483647
2) -1
3) 10
4) 2
答案
答案1)
1)o1=o2;
2)b=ob;
4)o1=b;
答案2)
2)j= i<<j;
4)j=i<<l;
答案3)
2)s+=i;
如果你想测试各种可能出现的情况,可以试着编译下面的代码
public class Llandaff{
public static void main(String argv[]){
Llandaff h = new Llandaff();
h.go();
}
public void go(){
char c = 'c';
int i = 10;
double d = 10;
long l = 1;
String s = "Hello";
//Start commenting these out till it all compiles
c=c+i;
s+=i;
i+=s;
c+=s;
}
}
答案4)
4) 2147483647
即使你的大脑中可能没有出现这个数字,对于无符号右移的理解也能告诉你其他答案是错误的。
答案5)
1) 1
bit位会随着左移运算符“滚动”,因此
System.out.println(1 <<31);
的结果将是-2147483648
答案6)
1) System.out.println(1+1);
2) int i= 2+'2';
第三个选项是不正确的,因为单引号指明一个字符而不是字符串。
第四个选项不会被编译,因为255超过了一个byte的范围
答案7)
3) Compilation and output of 6
*运算符的优先级高于|运算符。因此,该计算等价于(2*)|2,或者认为是4|2。|运算符比较每个位置的bit位,如果任意数字的该位为1,则此位的输出也是1。4的顺序是100,2的顺序是10,因此|运算符的结果为110,即十进制的6
答案8)
4) Compilation and output of 2
当你移动数字的位置为32位时,模32会在移位时执行。32%32是说如果你用32除32时会剩多少,答案是0。因此数字被移动0位。也就是返回原数。这个问题有点阴险,因为输出结果中使用了后增运算符++。这表示在当前行结束执行后数字被增加,因此1加1得到2被输出。
答案9)
4) 0 followed by 2
这道题没有看起来那么难。结合int型数的位数及数字2的bit模式和对于有符号左移运算符的理解,得知数字2的唯一一个bit位会被抛弃,所有bit位置0。
输出结果中包括2而不是3是因为后增运算符在=运算符赋值后将i加1。
答案10)
4) 2
这个问题可能让你有些发狂的举动。但是如果你有正确的背景知识的话,你可以得到答案的。如果你了解二进制表示法,你应该知道-1是在int型数的每一位都是1。根据运算符优先级,你可以知道>>>运算符在&前执行。如果你去掉&运算符,你会发现输出结果是选项1中给出的最大数。但是由于&执行,结果是2,表示输出中的bit位当两个数字的位都为1时才为1。因此输出为2。
目标二 equals方法
确定在任何java.lang.String,java.lang.Boolean和java.lang.Object类的对象应用布尔类型的equals(Object)方法的结果。
如果你有Visal Basic的背景(像我一样),用"="号比较两部分变量的想法是很奇怪的.然而事实上,对于通常应用的字符串的引用来讲,这是相当重要的。为了测验的目的,你可能被问到关于equals操作符对于对象引用的引用和布尔类型的引用这样的问题.注意是关于布尔类的问题而不是关于布尔基本类型(你不能用它来调用方法).
equals和==的不同点
equals方法可以被认为是对两个对象值的深层比较,而==操作是浅层比较。equals方法比较两个对象的所指对象,而不是两个指针本身(如果你承认Java有指针)。这种间接方式对C++程序员可能很清楚,但是在Visal Basic中没有直接的比较方式。
将equals方法用于字符型
equals方法返回一个布尔型基本类型值。这表明它可以被用于if,while或者其他循环语句。它可以被用于使用==操作符比较基本类型的情况。当比较字符串时equals方法和==操作的执行有一些不同结果。对于字符串恒量和它被Java处理的方式是很混乱的。
Java中有两种方法创建字符串。一个方法是用new操作符,这是通常的字符串创建方法
String s = new String("Hello");
但是更简短的方法是
String s= "GoodBye";
通常这两种创建字符串的方法有些微不同,但是在考试中往往会问到这个不同。
两种创建字符串的方法,当用于字符串时有相同的结果,但不用new关键字会创建Java字符串池中指向同一个字符串的指针。字符串池是Java存储资源的一种方法。举例说明这个结果
String s = "Hello";
String s2 = "Hello";
if (s==s2){
System.out.println("Equal without new operator");
}
String t = new String("Hello");
string u = new String("Hello");
if (t==u){
System.out.println("Equal with new operator");
}
在上一个目标中你可能认为第一个输出"Equal without new operator"不会出现,因为s和s2时不同的对象,但是==运算符判断两个对象的指针,而不是他们的值。因为Java存储资源的方式是重用不用new关键字创建的字符串,所以s和s2有相同的“地址”,所以会输出以下字符串。
"Equal without new operator"
但是对于第二组字符串t和u,new操作符迫使Java创建不同的字符串。因为==操作符之比较两个对象的地址而不是值,t和u有不同的地址,所以"Equal with new operator"将不会输出。
关键概念
equals方法用于字符串,但是字符串被创建后,会执行字符和字符的比较。
应用字符串池的作用,以及使用==和equals方法的区别不是很显而易见,
尤其是如果你有Visal Basic的背景。理解它的最好方法是自己写个例子看它是如何工作的。试试用new和不用new方法创建字符串。
在布尔型上应用equals方法
理解在java.lang.Boolean上应用equals运算是可能的要求。Boolean是boolean基本类型的封装类型。它是对象型的可以在其上应用equals方法。
依照JDK文档,equals方法用于Boolean封装类型,“只是在参数非空而且布尔对象有相同的布尔值时返回真”。
例如
Boolean b1 = new Boolean(true);
Boolean b2 = new Boolean(true);
if(b1.equals(b2)){
System.out.println("We are equal");
}
boolean和Boolean只有微小的区别,当你对Java的if运算非常熟悉,你就知道不能像C/C++程序员一样应用隐含的方式,就像这样
int x =1;
if(x){
//do something, but not in Java
}
这在Java中不能执行,因为if操作的变量必须是boolean判断,Java没有C/C++中任何非空值都可被认为真的概念。但是以下Java代码可以通过
boolean b1=true;
if(b1){
//do something in java
}
虽然这是不好的编程方法,但是在语法上是正确的。因为if操作的变量是boolean类型。
在对象类型上应用equals方法
对于Java的基本设计,任何类的实例也是java.lang.Object的实例。试试equals判断对象类型的返回值应用toString()方法.对于对象变量toString方法简单返回内存地址。所以与使用==操作的结果一样。因为Java不是设计成操作内存地址和指针的,所以这不是个有用的判断。
看下面的例子
public class MyParm{
public static void main(String argv[]){
Object m1 = new Object();
Object m2 = new Object();
System.out.println(m1);
System.out.println(m2);
if (m1.equals(m2)){
System.out.println("Equals");
}else{
System.out.println("Not Equals");
}
}
}
如果你编译运行这段代码,会得到如下输出
java.lang.Object@16c80b
java.lang.Object@16c80a
Not Equals
这些奇怪的值是内存地址,大概根本不是你想得到的。
问题
问题 1)编译运行以下代码时会发生什么情况?
public class MyParm{
public static void main(String argv[]){
String s1= "One";
String s2 = "One";
if(s1.equals(s2)){
System.out.println("String equals");
}
boolean b1 = true;
boolean b2 = true;
if(b1.equals(b2)){
System.out.println("true");
}
}
}
1) Compile time error
2) No output
3) Only "String equals"
4) "String equals" followed by "true"
问题 2) 编译运行以下代码时会发生什么情况?
String s1= "One";
String s2 = new String("One");
if(s1.equals(s2)){
System.out.println("String equals");
}
Boolean b1 = new Boolean(true);
Boolean b2 = new Boolean(true);
if(b1==b2){
System.out.println("Boolean Equals");
}
1) Compile time error
2) "String equals" only
3) "String equals" followed by "Boolean equals"
4) "Boolean equals" only
问题 3)编译运行以下代码的结果是什么?
What will be the result of attempting to compile and run the following code?
Object o1 = new Object();
Object o2 = new Object();
o1=o2;
if(o1.equals(o2))
System.out.println("Equals");
}
1) Compile time error
2) "Equals"
3) No output
4) Run time error
答案
答案 1)
1) Compile time error
b1.equals() 这一行会引发错误,因为b1是简单类型,简单类型没有任何方法。如果创建基本类型的封装类Boolean你就可以应用equals方法。
答案 2 )
2) "String equals" only
用==操作符简单判断基本类型的封装类Boolean的一个实例的内存地址。
答案 3)
2) "Equals"
因为一个对象的实例可以赋值给另一个对象用
o1=o2;
它们现在就指向同一个内存地址,equals方法判断将返回true。
目标三 &、|、&&和||运算符
在一个包含运算符&、|、&&、||和值已知的变量的表达式中,指出哪个运算符被求值,表达式的值是多少?
很容易忘记哪个逻辑运算用哪个运算符和它们所做的操作,确保你可以在考试中说出它们的区别。如果你初次接触这些运算符,你可能值得花时间好好记忆你才不会对它们这些按位运算符和逻辑运算符的操作搞乱。你可能会记得“双逻辑”这种表达很奇怪。
逻辑运算符的短路效应
逻辑运算符(&&、||)在用于“短路”逻辑像C/C++的AND和逻辑OR操作时有一点特别的结果。如果你来自Visal Basic背景这就有些奇怪了,因为Visal Basic会计算所有的操作数的值。如果你理解AND,你就会理解Java的方法,如果第一个操作数为假,第二个操作数的值就没有作用了,所有的结构都为假。对于逻辑OR也是,如果第一个操作数为真,所有的计算结果将为真,因为只要一个操作数为真最后的结果就为真。这种依靠一边结果的压缩计算可能有一个结果。请看下面的例子
public class MyClass1{
public static void main(String argv[]){
int Output=10;
boolean b1 = false;
if((b1==true) && ((Output+=10)==20))
{
System.out.println("We are equal "+Output);
}else
{
System.out.println("Not equal! "+Output);
}
}
}
"Not equal 10"会被输出。这说明Output+=10这个运算永远不会被执行,因为在第一个操作数的值为false时运算就停止了。如果你把b1的值改成true,运算就会像你想得一样执行,输出会是"We are equal 20".
当你真的不想在有任何值为false时进行其它运算时,这也许有时是便捷的方法,但是当你完全不熟悉时这也许会产生意外的结果。
按位运算符
&和|运算符用于做整形的按位与和或操作.在考试中你会遇到这样的问题,给出一个十进制的数,然你用按位与和或运算计算.要执行这些操作你需要熟悉从十进制到二进制的转换,并且知道其比特形式.这有一个典型的例子
下面运算的结果是什么?
3 | 4
3的二进制比特形式是
11
4的二进制比特形式是
100
要执行二进制或运算,两个数的每个比特都要互相比较.如果任一个比特为1则结果中的比特数为1.所以这个操作的结果的二进制形式是
111
也就是十进制的7.
目标没有特别要求你知道按位XOR运算,用^符号执行。
用二进制思考
如果你感到用二进制思考不舒服(我更习惯用十进制思考),你也可能想做一些练习来掌握这个问题和二进制转换操作。如果你使用windows你可能发现用计算器的科学模式很有用。你可以在标准模式下选则View和switch来变成科学模式。在科学模式中,你可以转换数值来看它的十进制和二进制模式,这会显示数的二进制形式。有一个很方便的窍门,我如果在写比特转换applet之前知道就好了,就是怎样用整形来表示比特形式。这里有一个实现这个的小程序。
public class BinDec{
public static void main(String argv[]){
System.out.println(Integer.parseInt("11",2));
System.out.println(Integer.toString(64,2));
}
}
如果你编译运行这段程序,将会得到输出
3
1000000
注意程序怎样把二进制11转换成十进制的对应数3,又是怎样把十进制数64转换成相应的比特形式。每个方法的第二个参数是基数。所以在这个例子中会把书转换成2为基数的,而我们常常会用10为基数的数。
问题
问题 1 )你尝试编译运行以下代码时会发生什么情况
int Output=10;
boolean b1 = false;
if((b1==true) && ((Output+=10)==20)){
System.out.println("We are equal "+Output);
}else
{
System.out.println("Not equal! "+Output);
}
1) Compile error, attempting to perform binary comparison on logical data type
2) Compilation and output of "We are equal 10"
3) Compilation and output of "Not equal! 20"
4) Compilation and output of "Not equal! 10"
问题 2 )下面一行代码会有什么输出
System.out.println(010|4);
1) 14
2) 0
3) 6
4) 12
问题 3 )下面哪项编译没有错误
1)
int i=10;
int j = 4;
System.out.println(i||j);
2)
int i=10;
int j = 4;
System.out.println(i|j);
3)
boolean b1=true;
boolean b2=true;
System.out.println(b1|b2);
4)
boolean b1=true;
boolean b2=true;
System.out.println(b1||b2);
答案
答案 1)
4) Compilation and output of "Not equal! 10"
输出是"Not equal 10". 这表明运算Output+=10没有被执行,因为运算在第一个操作数被算出为true时就停止了。如果你把b1的值改成true,运算会像你想得那样进行,输出结果为"We are equal 20";.
答案2 )
4) 12
和二进制OR目标相同,这个问题要求你理解开头的零表示八进制符号,第一个1表示数中有一个8,没有其他数。所以十进制运算是
8|4
转换成二进制形式为
1000
0100
----
1100
|运算符表示两个数每一位进行运算,其中有一个为1,相应位的结果就为1。
答案 3)
2,3,4
选项一不会通过编译,因为它试图在整型上运行逻辑OR操作。逻辑或只能用于操作boolean类型参数。
目标四 在方法中传递对象和基本类型值
判断传递对象和基本类型参数到方法中的结果,在方法中进行赋值或者其它的修改操作。
目标中注意事项
目标可能会问你是否理解传递值到方法中时会发生什么结果。如果方法中的代码改变了变量,对外部的方法是否可见?直接引用Peter van der Lindens的Java程序员解答的一段话(在http://www.afu.com)
//引用
所有的变量(基本类型值和对象的引用值)都是值传递。但是这不是全部情况,对象是经常通过Java的引用变量来操作的。所以也可以说对象是通过引用传递的(引用变量通过值传递)。这是变量不使用对象的值而是像前一个问题那样描述的使用对象引用的值的结果。
最后一行:调用者对基本类型参数(int,char等)的拷贝在相应参数变化时不会改变
。但是,在被调用方法改变相应作为参数传递的对象(引用)字段时,调用者的对象也改变其字段。
//引用结束
如果你来自C++背景,你可能对值传递参数和用&符号传递引用参数熟悉。在Java中没有这样的选择,所有的都是值传递。但是看起来并不总是这样。如果你传递的对象是对象的引用,你不能直接对对象的引用进行操作。
所以如果你操作一个传递到方法的对象的字段,结果就好像你按引用传递(任何改变结果都会返回到调用函数)。
将对象引用作为方法变量
请看下面的例子
class ValHold{
public int i = 10;
}
public class ObParm{
public static void main(String argv[]){
ObParm o = new ObParm();
o.amethod();
}
public void amethod(){
ValHold v = new ValHold();
v.i=10;
System.out.println("Before another = "+ v.i);
another(v);
System.out.println("After another = "+ v.i);
}//End of amethod
public void another(ValHold v){
v.i = 20;
System.out.println("In another = "+ v.i);
}//End of another
}
程序的输出结果是
Before another = 10
In another = 20
After another = 20
看变量i是怎么被修改的。如果Java总是值传递(也就是对变量的拷贝),它是怎么被修改的呢?过程是这样的方法收到了句柄的拷贝或者对象的引用,但是这个引用的作用类似于指向真实的指针。对这个字段的改变会反映到它所指的值。这有些像是在C/C++中指针的自动间接应用的的作用。
基本类型作为方法参数
当你对方法传递基本类型参数,是直接传递值。方法得到它的拷贝,任何修改都不会在外部方法得到反映。请看以下例子
public class Parm{
public static void main(String argv[]){
Parm p = new Parm();
p.amethod();
}//End of main
public void amethod(){
int i=10;
System.out.println("Before another i= " +i);
another(i);
System.out.println("After another i= " + i);
}//End of amethod
public void another(int i){
i+=10;
System.out.println("In another i= " + i);
}//End of another
}
程序的输出结果如下
Before another i= 10
In another i= 20
After another i= 10
习题 1)
以下所给代码的输出是什么?
class ValHold{
public int i = 10;
}
public class ObParm{
public static void main(String argv[]){
ObParm o = new ObParm();
o.amethod();
}
public void amethod(){
int i = 99;
ValHold v = new ValHold();
v.i=30;
another(v,i);
System.out.println(v.i);
}//End of amethod
public void another(ValHold v, int i){
i=0;
v.i = 20;
ValHold vh = new ValHold();
v = vh;
System.out.println(v.i+ " "+i);
}//End of another
}
1) 10,0, 30
2) 20,0,30
3) 20,99,30
4) 10,0,20
答案
答案 1)
4) 10,0,20