|
楼主 |
发表于 2003-11-10 15:21:04
|
显示全部楼层
Java随笔(续)
首先还和上次的开头一样,我还是应该向大家表示一下歉意,从上一篇文章到现在屈指算来已经要一个月了,希望大家不要认为我是一个没有责任感的人,我其实十分关注我们的变成小组,几乎每天都要到这个版块来看看,不过斑竹现在搞的我是实在插不上嘴,虽然我可以自学一些所涉及的知识,不过我现在毕竟已经大四,又要按时上课,又要为自己的毕业设计做准备(我希望以一个优秀的毕业设计结束我的大学生涯),当然还要找工作,还有就是总是在梦里梦到自己未来女朋友的样子,而现实却……..所以总是担心自己会抑郁而死,总而言之,大家只要相信我不是一个没有责任心的人就可以了,呵呵。
其实还有一个困惑我的问题,就是我不知道大家希望我写什么,当然以我现在的水平,大家的要求我应该也不太可能满足(废话),所以还是按照一些传统书上的顺序,希望大家体谅。
一想起下面要说的很是心烦,不过要想能自由的支配一门语言,扎实的基础是必须的,所以…….耐下心来,一个字:学。在这里我有一个个人的看法,就是这部分虽然很容易懂,学起来应该很快,但是应该说忘记它的速度也不慢,所以就要求我们要有愚公移山的精神,这样才能打下牢固的基础,好了,废话不多说,下面开始正题:
声明:以下内容是引用Thinking in java 的内容(本人贵在自知之明,因为有些东西我没有资格讲,就是讲也远远不及Bruce Eckel)
引用开始:
“就象任何有感知的生物一样,程序必须能操纵自己的世界,在执行过程中作出判断与选择。”
在Java里,我们利用运算符操纵对象和数据,并用执行控制语句作出选择。Java是建立在C++基础上的,所以对C和C++程序员来说,对Java这方面的大多数语句和运算符都应是非常熟悉的。当然,Java也进行了自己的一些改进与简化工作。
使用Java运算符
运算符以一个或多个自变量为基础,可生成一个新值。自变量采用与原始方法调用不同的一种形式,但效果是相同的。根据以前写程序的经验,运算符的常规概念应该不难理解。
加号(+)、减号和负号(-)、乘号(*)、除号(/)以及等号(=)的用法与其他所有编程语言都是类似的。
所有运算符都能根据自己的运算对象生成一个值。除此以外,一个运算符可改变运算对象的值,这叫做 “副作用”(Side Effect)。运算符最常见的用途就是修改自己的运算对象,从而产生副作用。但要注意生成的值亦可由没有副作用的运算符生成。
几乎所有运算符都只能操作“主类型”(Primitives)。唯一的例外是“=”、“==”和“!=”,它们能操作所有对象(也是对象易令人混淆的一个地方)。除此以外,String类支持“+”和“+=”。
优先级
运算符的优先级决定了存在多个运算符时一个表达式各部分的计算顺序。Java对计算顺序作出了特别的规定。其中,最简单的规则就是乘法和除法在加法和减法之前完成。程序员经常都会忘记其他优先级规则,所以应该用括号明确规定计算顺序。例如:
A = X + Y - 2/2 + Z;
为上述表达式加上括号后,就有了一个不同的含义。
A = X + (Y - 2)/(2 + Z);
赋值
赋值是用等号运算符(=)进行的。它的意思是“取得右边的值,把它复制到左边”。右边的值可以是任何常数、变量或者表达式,只要能产生一个值就行。但左边的值必须是一个明确的、已命名的变量。也就是说,它必须有一个物理性的空间来保存右边的值。举个例子来说,可将一个常数赋给一个变量(A=4;),但不可将任何东西赋给一个常数(比如不能4=A)。
对主数据类型的赋值是非常直接的。由于主类型容纳了实际的值,而且并非指向一个对象的句柄,所以在为其赋值的时候,可将来自一个地方的内容复制到另一个地方。例如,假设为主类型使用“A=B”,那么B处的内容就复制到A。若接着又修改了A,那么B根本不会受这种修改的影响。作为一名程序员,这应成为自己的常识。
但在为对象“赋值”的时候,情况却发生了变化。对一个对象进行操作时,我们真正操作的是它的句柄。所以倘若“从一个对象到另一个对象”赋值,实际就是将句柄从一个地方复制到另一个地方。这意味着假若为对象使用“C=D”,那么C和D最终都会指向最初只有D才指向的那个对象。下面这个例子将向大家阐示这一点。
为运行程序,必须保证在classpath里包含了我们安装本书源码文件的根目录(那个目录里包含了c02,c03c,c04等等子目录)。
对于Java后续的版本(1.1.4和更高版本),如果您的main()用package语句封装到一个文件里,那么必须在程序名前面指定完整的包裹名称,否则不能运行程序。在这种情况下,命令行是:
java c03.Assignment
运行位于一个“包裹”里的程序时,随时都要注意这方面的问题。
下面是例子:[code:1]
//: Assignment.java
// Assignment with objects is a bit tricky
package c03;
class Number {
int i;
}
public class Assignment {
public static void main(String[] args) {
Number n1 = new Number();
Number n2 = new Number();
n1.i = 9;
n2.i = 47;
System.out.println("1: n1.i: " + n1.i +
", n2.i: " + n2.i);
n1 = n2;
System.out.println("2: n1.i: " + n1.i +
", n2.i: " + n2.i);
n1.i = 27;
System.out.println("3: n1.i: " + n1.i +
", n2.i: " + n2.i);
}
} ///:~
[/code:1]
Number类非常简单,它的两个实例(n1和n2)是在main()里创建的。每个Number中的i值都赋予了一个不同的值。随后,将n2赋给n1,而且n1发生改变。在许多程序设计语言中,我们都希望n1和n2任何时候都相互独立。但由于我们已赋予了一个句柄,所以下面才是真实的输出:[code:1]
1: n1.i: 9, n2.i: 47
2: n1.i: 47, n2.i: 47
3: n1.i: 27, n2.i: 27
[/code:1]看来改变n1的同时也改变了n2!这是由于无论n1还是n2都包含了相同的句柄,它指向相同的对象(最初的句柄位于n1内部,指向容纳了值9的一个对象。在赋值过程中,那个句柄实际已经丢失;它的对象会由“垃圾收集器”自动清除)。
这种特殊的现象通常也叫作“别名”,是Java操作对象的一种基本方式。但假若不愿意在这种情况下出现别名,又该怎么操作呢?可放弃赋值,并写入下述代码:
n1.i = n2.i;
这样便可保留两个独立的对象,而不是将n1和n2绑定到相同的对象。但您很快就会意识到,这样做会使对象内部的字段处理发生混乱,并与标准的面向对象设计准则相悖。由于这并非一个简单的话题,所以留待第12章详细论述,那一章是专门讨论别名的。其时,大家也会注意到对象的赋值会产生一些令人震惊的效果。
1. 方法调用中的别名处理
将一个对象传递到方法内部时,也会产生别名现象。[code:1]
//: PassObject.java
// Passing objects to methods can be a bit tricky
class Letter {
char c;
}
public class PassObject {
static void f(Letter y) {
y.c = 'z';
}
public static void main(String[] args) {
Letter x = new Letter();
x.c = 'a';
System.out.println("1: x.c: " + x.c);
f(x);
System.out.println("2: x.c: " + x.c);
}
} ///:~
[/code:1]
在许多程序设计语言中,f()方法表面上似乎要在方法的作用域内制作自己的自变量Letter y的一个副本。但同样地,实际传递的是一个句柄。所以下面这个程序行:
y.c = 'z';
实际改变的是f()之外的对象。输出结果如下:
1: x.c: a
2: x.c: z
别名和它的对策是非常复杂的一个问题。尽管必须等至第12章才可获得所有答案,但从现在开始就应加以重视,以便提早发现它的缺点。
算术运算符
Java的基本算术运算符与其他大多数程序设计语言是相同的。其中包括加号(+)、减号(-)、除号(/)、乘号(*)以及模数(%,从整数除法中获得余数)。整数除法会直接砍掉小数,而不是进位。
Java也用一种简写形式进行运算,并同时进行赋值操作。这是由等号前的一个运算符标记的,而且对于语言中的所有运算符都是固定的。例如,为了将4加到变量x,并将结果赋给x,可用:x+=4。
下面这个例子展示了算术运算符的各种用法:[code:1]
//: MathOps.java
// Demonstrates the mathematical operators
import java.util.*;
public class MathOps {
// Create a shorthand to save typing:
static void prt(String s) {
System.out.println(s);
}
// shorthand to print a string and an int:
static void pInt(String s, int i) {
prt(s + " = " + i);
}
// shorthand to print a string and a float:
static void pFlt(String s, float f) {
prt(s + " = " + f);
}
public static void main(String[] args) {
// Create a random number generator,
// seeds with current time by default:
Random rand = new Random();
int i, j, k;
// '%' limits maximum value to 99:
j = rand.nextInt() % 100;
k = rand.nextInt() % 100;
pInt("j",j); pInt("k",k);
i = j + k; pInt("j + k", i);
i = j - k; pInt("j - k", i);
i = k / j; pInt("k / j", i);
i = k * j; pInt("k * j", i);
i = k % j; pInt("k % j", i);
j %= k; pInt("j %= k", j);
// Floating-point number tests:
float u,v,w; // applies to doubles, too
v = rand.nextFloat();
w = rand.nextFloat();
pFlt("v", v); pFlt("w", w);
u = v + w; pFlt("v + w", u);
u = v - w; pFlt("v - w", u);
u = v * w; pFlt("v * w", u);
u = v / w; pFlt("v / w", u);
// the following also works for
// char, byte, short, int, long,
// and double:
u += v; pFlt("u += v", u);
u -= v; pFlt("u -= v", u);
u *= v; pFlt("u *= v", u);
u /= v; pFlt("u /= v", u);
}
} ///:~
[/code:1]
我们注意到的第一件事情就是用于打印(显示)的一些快捷方法:prt()方法打印一个String;pInt()先打印一个String,再打印一个int;而pFlt()先打印一个String,再打印一个float。当然,它们最终都要用System.out.println()结尾。
为生成数字,程序首先会创建一个Random(随机)对象。由于自变量是在创建过程中传递的,所以Java将当前时间作为一个“种子值”,由随机数生成器利用。通过Random对象,程序可生成许多不同类型的随机数字。做法很简单,只需调用不同的方法即可:nextInt(),nextLong(),nextFloat()或者nextDouble()。
若随同随机数生成器的结果使用,模数运算符(%)可将结果限制到运算对象减1的上限(本例是99)之下。
1. 一元加、减运算符
一元减号(-)和一元加号(+)与二元加号和减号都是相同的运算符。根据表达式的书写形式,编译器会自动判断使用哪一种。例如下述语句:
x = -a;
它的含义是显然的。编译器能正确识别下述语句:
x = a * -b;
但读者会被搞糊涂,所以最好更明确地写成:
x = a * (-b);
一元减号得到的运算对象的负值。一元加号的含义与一元减号相反,虽然它实际并不做任何事情。
自动递增和递减
和C类似,Java提供了丰富的快捷运算方式。这些快捷运算可使代码更清爽,更易录入,也更易读者辨读。
两种很不错的快捷运算方式是递增和递减运算符(常称作“自动递增”和“自动递减”运算符)。其中,递减运算符是“--”,意为“减少一个单位”;递增运算符是“++”,意为“增加一个单位”。举个例子来说,假设A是一个int(整数)值,则表达式++A就等价于(A = A + 1)。递增和递减运算符结果生成的是变量的值。
对每种类型的运算符,都有两个版本可供选用;通常将其称为“前缀版”和“后缀版”。“前递增”表示++运算符位于变量或表达式的前面;而“后递增”表示++运算符位于变量或表达式的后面。类似地,“前递减”意味着--运算符位于变量或表达式的前面;而“后递减”意味着--运算符位于变量或表达式的后面。对于前递增和前递减(如++A或--A),会先执行运算,再生成值。而对于后递增和后递减(如A++或A--),会先生成值,再执行运算。下面是一个例子:[code:1]
//: AutoInc.java
// Demonstrates the ++ and -- operators
public class AutoInc {
public static void main(String[] args) {
int i = 1;
prt("i : " + i);
prt("++i : " + ++i); // Pre-increment
prt("i++ : " + i++); // Post-increment
prt("i : " + i);
prt("--i : " + --i); // Pre-decrement
prt("i-- : " + i--); // Post-decrement
prt("i : " + i);
}
static void prt(String s) {
System.out.println(s);
}
} ///:~
[/code:1]
该程序的输出如下:[code:1]
i : 1
++i : 2
i++ : 2
i : 3
--i : 2
i-- : 2
i : 1
[/code:1]
从中可以看到,对于前缀形式,我们在执行完运算后才得到值。但对于后缀形式,则是在运算执行之前就得到值。它们是唯一具有“副作用”的运算符(除那些涉及赋值的以外)。也就是说,它们会改变运算对象,而不仅仅是使用自己的值。
递增运算符正是对“C++”这个名字的一种解释,暗示着“超载C的一步”。在早期的一次Java演讲中,Bill Joy(始创人之一)声称“Java=C++--”(C加加减减),意味着Java已去除了C++一些没来由折磨人的地方,形成一种更精简的语言。正如大家会在这本书中学到的那样,Java的许多地方都得到了简化,所以Java的学习比C++更容易。
关系运算符
关系运算符生成的是一个“布尔”(Boolean)结果。它们评价的是运算对象值之间的关系。若关系是真实的,关系表达式会生成true(真);若关系不真实,则生成false(假)。关系运算符包括小于(<)、大于(>)、小于或等于(<=)、大于或等于(>=)、等于(==)以及不等于(!=)。等于和不等于适用于所有内建的数据类型,但其他比较不适用于boolean类型。
1. 检查对象是否相等
关系运算符==和!=也适用于所有对象,但它们的含义通常会使初涉Java领域的人找不到北。下面是一个例子:[code:1]
//: Equivalence.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);
}
} ///:~
[/code:1]
其中,表达式System.out.println(n1 == n2)可打印出内部的布尔比较结果。一般人都会认为输出结果肯定先是true,再是false,因为两个Integer对象都是相同的。但尽管对象的内容相同,句柄却是不同的,而==和!=比较的正好就是对象句柄。所以输出结果实际上先是false,再是true。这自然会使第一次接触的人感到惊奇。
若想对比两个对象的实际内容是否相同,又该如何操作呢?此时,必须使用所有对象都适用的特殊方法equals()。但这个方法不适用于“主类型”,那些类型直接使用==和!=即可。下面举例说明如何使用:[code:1]
//: EqualsMethod.java
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));
}
} ///:~
[/code:1]
正如我们预计的那样,此时得到的结果是true。但事情并未到此结束!假设您创建了自己的类,就象下面这样:[code:1]
//: EqualsMethod2.java
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));
}
} ///:~
[/code:1]
此时的结果又变回了false!这是由于equals()的默认行为是比较句柄。所以除非在自己的新类中改变了equals(),否则不可能表现出我们希望的行为。不幸的是,要到第7章才会学习如何改变行为。但要注意equals()的这种行为方式同时或许能够避免一些“灾难”性的事件。
大多数Java类库都实现了equals(),所以它实际比较的是对象的内容,而非它们的句柄。
逻辑运算符
逻辑运算符AND(&&)、OR(||)以及NOT(!)能生成一个布尔值(true或false)——以自变量的逻辑关系为基础。下面这个例子向大家展示了如何使用关系和逻辑运算符。[code:1]
//: Bool.java
// Relational and logical operators
import java.util.*;
public class Bool {
public static void main(String[] args) {
Random rand = new Random();
int i = rand.nextInt() % 100;
int j = rand.nextInt() % 100;
prt("i = " + i);
prt("j = " + j);
prt("i > j is " + (i > j));
prt("i < j is " + (i < j));
prt("i >= j is " + (i >= j));
prt("i <= j is " + (i <= j));
prt("i == j is " + (i == j));
prt("i != j is " + (i != j));
// Treating an int as a boolean is
// not legal Java
//! prt("i && j is " + (i && j));
//! prt("i || j is " + (i || j));
//! prt("!i is " + !i);
prt("(i < 10) && (j < 10) is "
+ ((i < 10) && (j < 10)) );
prt("(i < 10) || (j < 10) is "
+ ((i < 10) || (j < 10)) );
}
static void prt(String s) {
System.out.println(s);
}
} ///:~
[/code:1]
只可将AND,OR或NOT应用于布尔值。与在C及C++中不同,不可将一个非布尔值当作布尔值在逻辑表达式中使用。若这样做,就会发现尝试失败,并用一个“//!”标出。然而,后续的表达式利用关系比较生成布尔值,然后对结果进行逻辑运算。
输出列表看起来象下面这个样子:[code:1]
i = 85
j = 4
i > j is true
i < j is false
i >= j is true
i <= j is false
i == j is false
i != j is true
(i < 10) && (j < 10) is false
(i < 10) || (j < 10) is true
[/code:1]
注意若在预计为String值的地方使用,布尔值会自动转换成适当的文本形式。
在上述程序中,可将对int的定义替换成除boolean以外的其他任何主数据类型。但要注意,对浮点数字的比较是非常严格的。即使一个数字仅在小数部分与另一个数字存在极微小的差异,仍然认为它们是“不相等”的。即使一个数字只比零大一点点(例如2不停地开平方根),它仍然属于“非零”值。
1. 短路
操作逻辑运算符时,我们会遇到一种名为“短路”的情况。这意味着只有明确得出整个表达式真或假的结论,才会对表达式进行逻辑求值。因此,一个逻辑表达式的所有部分都有可能不进行求值:[code:1]
//: ShortCircuit.java
// Demonstrates short-circuiting behavior
// with logical operators.
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) {
if(test1(0) && test2(2) && test3(2))
System.out.println("expression is true");
else
System.out.println("expression is false");
}
} ///:~
[/code:1]
每次测试都会比较自变量,并返回真或假。它不会显示与准备调用什么有关的资料。测试在下面这个表达式中进行:
if(test1(0)) && test2(2) && test3(2))
很自然地,你也许认为所有这三个测试都会得以执行。但希望输出结果不至于使你大吃一惊:
if(test1(0) && test2(2) && test3(2))
第一个测试生成一个true结果,所以表达式求值会继续下去。然而,第二个测试产生了一个false结果。由于这意味着整个表达式肯定为false,所以为什么还要继续剩余的表达式呢?这样做只会徒劳无益。事实上,“短路”一词的由来正种因于此。如果一个逻辑表达式的所有部分都不必执行下去,那么潜在的性能提升将是相当可观的。
按位运算符
按位运算符允许我们操作一个整数主数据类型中的单个“比特”,即二进制位。按位运算符会对两个自变量中对应的位执行布尔代数,并最终生成一个结果。
按位运算来源于C语言的低级操作。我们经常都要直接操纵硬件,需要频繁设置硬件寄存器内的二进制位。Java的设计初衷是嵌入电视顶置盒内,所以这种低级操作仍被保留下来了。然而,由于操作系统的进步,现在也许不必过于频繁地进行按位运算。
若两个输入位都是1,则按位AND运算符(&)在输出位里生成一个1;否则生成0。若两个输入位里至少有一个是1,则按位OR运算符(|)在输出位里生成一个1;只有在两个输入位都是0的情况下,它才会生成一个0。若两个输入位的某一个是1,但不全都是1,那么按位XOR(^,异或)在输出位里生成一个1。按位NOT(~,也叫作“非”运算符)属于一元运算符;它只对一个自变量进行操作(其他所有运算符都是二元运算符)。按位NOT生成与输入位的相反的值——若输入0,则输出1;输入1,则输出0。
按位运算符和逻辑运算符都使用了同样的字符,只是数量不同。因此,我们能方便地记忆各自的含义:由于“位”是非常“小”的,所以按位运算符仅使用了一个字符。
按位运算符可与等号(=)联合使用,以便合并运算及赋值:&=,|=和^=都是合法的(由于~是一元运算符,所以不可与=联合使用)。
我们将boolean(布尔)类型当作一种“单位”或“单比特”值对待,所以它多少有些独特的地方。我们可执行按位AND,OR和XOR,但不能执行按位NOT(大概是为了避免与逻辑NOT混淆)。对于布尔值,按位运算符具有与逻辑运算符相同的效果,只是它们不会中途“短路”。此外,针对布尔值进行的按位运算为我们新增了一个XOR逻辑运算符,它并未包括在“逻辑”运算符的列表中。在移位表达式中,我们被禁止使用布尔运算,原因将在下面解释。
移位运算符
移位运算符面向的运算对象也是二进制的“位”。可单独用它们处理整数类型(主类型的一种)。左移位运算符(<<)能将运算符左边的运算对象向左移动运算符右侧指定的位数(在低位补0)。“有符号”右移位运算符(>>)则将运算符左边的运算对象向右移动运算符右侧指定的位数。“有符号”右移位运算符使用了“符号扩展”:若值为正,则在高位插入0;若值为负,则在高位插入1。Java也添加了一种“无符号”右移位运算符(>>>),它使用了“零扩展”:无论正负,都在高位插入0。这一运算符是C或C++没有的。
若对char,byte或者short进行移位处理,那么在移位进行之前,它们会自动转换成一个int。只有右侧的5个低位才会用到。这样可防止我们在一个int数里移动不切实际的位数。若对一个long值进行处理,最后得到的结果也是long。此时只会用到右侧的6个低位,防止移动超过long值里现成的位数。但在进行“无符号”右移位时,也可能遇到一个问题。若对byte或short值进行右移位运算,得到的可能不是正确的结果(Java 1.0和Java 1.1特别突出)。它们会自动转换成int类型,并进行右移位。但“零扩展”不会发生,所以在那些情况下会得到-1的结果。可用下面这个例子检测自己的实现方案:[code:1]
//: URShift.java
// Test of unsigned right shift
public class URShift {
public static void main(String[] args) {
int i = -1;
i >>>= 10;
System.out.println(i);
long l = -1;
l >>>= 10;
System.out.println(l);
short s = -1;
s >>>= 10;
System.out.println(s);
byte b = -1;
b >>>= 10;
System.out.println(b);
}
} ///:~
[/code:1]
移位可与等号(<<=或>>=或>>>=)组合使用。此时,运算符左边的值会移动由右边的值指定的位数,再将得到的结果赋回左边的值。
下面这个例子向大家阐示了如何应用涉及“按位”操作的所有运算符,以及它们的效果:[code:1]
//: BitManipulation.java
// Using the bitwise operators
import java.util.*;
public class BitManipulation {
public static void main(String[] args) {
Random rand = new Random();
int i = rand.nextInt();
int j = rand.nextInt();
pBinInt("-1", -1);
pBinInt("+1", +1);
int maxpos = 2147483647;
pBinInt("maxpos", maxpos);
int maxneg = -2147483648;
pBinInt("maxneg", maxneg);
pBinInt("i", i);
pBinInt("~i", ~i);
pBinInt("-i", -i);
pBinInt("j", j);
pBinInt("i & j", i & j);
pBinInt("i | j", i | j);
pBinInt("i ^ j", i ^ j);
pBinInt("i << 5", i << 5);
pBinInt("i >> 5", i >> 5);
pBinInt("(~i) >> 5", (~i) >> 5);
pBinInt("i >>> 5", i >>> 5);
pBinInt("(~i) >>> 5", (~i) >>> 5);
long l = rand.nextLong();
long m = rand.nextLong();
pBinLong("-1L", -1L);
pBinLong("+1L", +1L);
long ll = 9223372036854775807L;
pBinLong("maxpos", ll);
long lln = -9223372036854775808L;
pBinLong("maxneg", lln);
pBinLong("l", l);
pBinLong("~l", ~l);
pBinLong("-l", -l);
pBinLong("m", m);
pBinLong("l & m", l & m);
pBinLong("l | m", l | m);
pBinLong("l ^ m", l ^ m);
pBinLong("l << 5", l << 5);
pBinLong("l >> 5", l >> 5);
pBinLong("(~l) >> 5", (~l) >> 5);
pBinLong("l >>> 5", l >>> 5);
pBinLong("(~l) >>> 5", (~l) >>> 5);
}
static void pBinInt(String s, int i) {
System.out.println(
s + ", int: " + i + ", binary: ");
System.out.print(" ");
for(int j = 31; j >=0; j--)
if(((1 << j) & i) != 0)
System.out.print("1");
else
System.out.print("0");
System.out.println();
}
static void pBinLong(String s, long l) {
System.out.println(
s + ", long: " + l + ", binary: ");
System.out.print(" ");
for(int i = 63; i >=0; i--)
if(((1L << i) & l) != 0)
System.out.print("1");
else
System.out.print("0");
System.out.println();
}
} ///:~
[/code:1]
程序末尾调用了两个方法:pBinInt()和pBinLong()。它们分别操作一个int和long值,并用一种二进制格式输出,同时附有简要的说明文字。目前,可暂时忽略它们具体的实现方案。
大家要注意的是System.out.print()的使用,而不是System.out.println()。print()方法不会产生一个新行,以便在同一行里罗列多种信息。
除展示所有按位运算符针对int和long的效果之外,本例也展示了int和long的最小值、最大值、+1和-1值,使大家能体会它们的情况。注意高位代表正负号:0为正,1为负。下面列出int部分的输出:[code:1]
-1, int: -1, binary:
11111111111111111111111111111111
+1, int: 1, binary:
00000000000000000000000000000001
maxpos, int: 2147483647, binary:
01111111111111111111111111111111
maxneg, int: -2147483648, binary:
10000000000000000000000000000000
i, int: 59081716, binary:
00000011100001011000001111110100
~i, int: -59081717, binary:
11111100011110100111110000001011
-i, int: -59081716, binary:
11111100011110100111110000001100
j, int: 198850956, binary:
00001011110110100011100110001100
i & j, int: 58720644, binary:
00000011100000000000000110000100
i | j, int: 199212028, binary:
00001011110111111011101111111100
i ^ j, int: 140491384, binary:
00001000010111111011101001111000
i << 5, int: 1890614912, binary:
01110000101100000111111010000000
i >> 5, int: 1846303, binary:
00000000000111000010110000011111
(~i) >> 5, int: -1846304, binary:
11111111111000111101001111100000
i >>> 5, int: 1846303, binary:
00000000000111000010110000011111
(~i) >>> 5, int: 132371424, binary:
00000111111000111101001111100000
[/code:1]
数字的二进制形式表现为“有符号2的补值”。
好了,好了,我觉得引用应该结束了,不然大家要崩溃了,其实世界上所有的事情都是这样,开始的时候会费一些周折,不过既然已经走上了这条路,还是应该塌实的走好没一步,对不对???以上的这些代码我都运行过,全部没有问题,不要谢我,运行它们的同时我也复习了一遍(呵呵,其实也没必要谢我,应该谢谢Bruce Eckel)补充一点本人关于优先级问题的理解,其实我们真的是没有必要背这个东西,虽然很多书,还有老师把它编成了一些容易记住的口诀,但我认为假如你不确定的时候,加个括号就OK了。
这次就到这里吧,虽然以上的内容自己的东西不是很多,但是应该会对初学者有一些启发(也可能没有)但应该不会有相反的作用,谢谢
待续………下一章写什么?征求大家的意见,不过不一定能满足大家的要求(我很诚实,是不是???)
参考资料:Thinking in java
21天学通Java2
Java 2编程指南(SDK 1.4版) |
|