写在前面:Hubert建议我看看《think in Java》并写博客记录一下笔记,于是有了此文,之后会持续更新。文中许多内容是直接引用书中的,因为大家都知道,本文介绍的是Java基础,就是这么多这么繁琐的东西。希望大家耐心看。
1.Java运算符
1.1 优先级
运算符的优先级决定了一个存在多个运算符的表达式各部分的执行顺序。
1.2 赋值
赋值是用等号运算符(=)进行的,它的意思是“取得右边的值,把它复制到左边”。右边可以是任意常量、变量、表达式,只要能产生值就可以了,但是左边必须是一个明确的、已命名的变量。举个例子,可以将一个变量赋值给一个常量(A = 4),但是不能将任何东西赋值给常量(4 = A)。
对于主数据类型的赋值是非常直接的。由于主类型容纳了实际的值,并不是指向一个对象的句柄,所以在赋值的时候,可以将一个地方的内容直接复制到另一个地方。例如,假设主类型使用“A = B”,那么B处的内容就复制到了A。接着修改A的值,B不会受影响。
但是对“对象”赋值却发生了变化。对一个对象进行操作时,实际操作的是它的句柄。例如对对象使用“C = D”,这时候C和D都指向了原来D指向的那个对象。
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);
}
}
f()表面上是要在方法的作用域内制作一个自变量y的副本,但实际上传递的是一个句柄,所以y.c = 'z'实际改变的是f()之外的对象。输出结果如下:
1:x.c:a
2:x.c:z
1.3 算数运算符
Java的基本算数运算符和其他编程语言是相同的。其中包括加号(+)、减号(-)、乘号(*)、除号(/)以及模运算符(%,从整数除法运算中获得余数)。整数除法会直接砍掉余数,而不是四舍五入。
把运算符和等号连在一起用,可以在进行运算的同时进行赋值操作。例如把变量x+4并将结果赋值给x,可用x += 4。
1.4 自动递增和递减运算符
递增运算符“++”意思是“增加一个单位”,递减运算符“--”意思是“减少一个单位”。例如,A是一个int值,则表达式++A,表示A = A + 1。递增和递减生成的是变量的值。
对于递增和递减有两个版本——“前缀版”和“后缀版”。对于前缀版,会先执行运算,再生成值;对于后缀版,会先生成值,再执行运算。下面是一个例子:
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);
}
}
则程序输出如下:
i : 1
++i : 2
i++ : 2
i : 3
--i : 2
i-- : 2
i : 1
1.5 关系运算符
关系运算符会生成一个Boolean结果,它评价的是运算对象之间的关系。如果关系是真的,表达式会生成true,反之表达式会生成false。关系运算符有大于(>)、大于或等于(>=)、小于(<)、小于或等于(<=)、等于(==)以及不等于(!=)。其中==和!=适用于所有内建数据类型,其他的不适用于boolean类型。
1.5.1 检查对象是否相等
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);
}
}
对于结果,初学者一般会认为先打印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));
}
}
输出结果,正如我们所料输出true。但是事情并未结束,如果是我们自己创建的类,结果会如何呢?例如:
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(),使其作用比较两个对象的内容,而不是句柄。
1.6 逻辑运算符
1.6.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) {
if(test1(0) && test2(2) && test3(2))
System.out.println("expression is true");
else
System.out.println("expression is false");
}
}
每一次测试都会返回自变量,并比较真假。
if(test1(0)) && test2(2) && test3(2)),新手一般会认为三个测试都会执行,但是不是滴。第一个测试返回true,所以程序继续执行。第二个测试返回false,意味着整个表达式肯定为false,所以表达式就没有继续执行的必要了,剩下部分将不会执行,潜在的提高了性能。
1.7 按位运算符
按位运算来源于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逻辑运算符,它并未包括在“逻辑”运算符的列表中。在移位表达式中,我们被禁止使用布尔运算,原因将在下面解释。
1.8 移位运算符
若对char,byte或者short进行移位处理,那么在移位进行之前,它们会自动转换成一个int。只有右侧的5个低位才会用到。这样可防止我们在一个int数里移动不切实际的位数。若对一个long值进行处理,最后得到的结果也是long。此时只会用到右侧的6个低位,防止移动超过long值里现成的位数。但在进行“无符号”右移位时,也可能遇到一个问题。若对byte或short值进行右移位运算,得到的可能不是正确的结果(Java 1.0和Java 1.1特别突出)。它们会自动转换成int类型,并进行右移位。但“零扩展”不会发生,所以在那些情况下会得到-1的结果。可用下面这个例子检测自己的实现方案:
public class URShift {
public static void main(String[] args) {
int i = -1;
i >>>= 10;
<p> System.out.println(i);</p> 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);
}
}
1.9 三元运算符
1.10 逗号运算符
1.11 字符串运算符+
int x=0,y=1,z=2;
String str = "string";
System.out.println(str + x + y + z);
Java编译程序会将x、y、z分别转成对应的字符串形式'x'、"y"、"z",而不是将它们加在一起。如果使用System.out.println(x + str),早期的Java版本会出错,之后版本会将x转成"x",所以为了避免出错,请保证表达式的第一个对象为string类型。
1.12 运算符的常规操作
while(x = y) {
//...
}
程序的意图是测试是否“相等”(==),而不是进行赋值操作。在C和C++中,若y是一个非零值,那么这种赋值的结果肯定是true。这样使可能得到一个无限循环。在Java里,这个表达式的结果并不是布尔值,而编译器期望的是一个布尔值,而且不会从一个int数值中转换得来。所以在编译时,系统就会提示出现错误,有效地阻止我们进一步运行程序。所以这个缺点在Java里永远不会造成更严重的后果。唯一不会得到编译错误的时候是x和y都为布尔值。在这种情况下,x = y属于合法表达式。而在上述情况下,则可能是一个错误。
2. 执行控制
2.1 真和假
2.2 if-else
2.3 反复
public class WhileTest {
public static void main(String[] args) {
double r = 0;
while(r < 0.99d) {
r = Math.random();
System.out.println(r);
}
}
}
它用到了Math库里的static(静态)方法random()。该方法的作用是产生0和1之间(包括0,但不包括1)的一个double值。while的条件表达式意思是说:“一直循环下去,直到数字等于或大于0.99”。由于它的随机性,每运行一次这个程序,都会获得大小不同的数字列表。
2.4 do...while
2.5 for
2.5.1 逗号运算符
public class CommaOperator {
public static void main(String[] args) {
for(int i = 1, j = i + 10; i < 5;
i++, j = i * 2) {
System.out.println("i= " + i + " j= " + j);
}
}
}
结果如下:
i= 1 j= 11
i= 2 j= 4
i= 3 j= 6
i= 4 j= 8
大家可以看到,无论在初始化还是在步进部分,语句都是顺序执行的。此外,尽管初始化部分可设置任意数量的定义,但都属于同一类型。
2.6 中断和继续
下面这个程序向大家展示了break和continue在for和while循环中的例子:
public class BreakAndContinue {
public static void main(String[] args) {
for(int i = 0; i < 100; i++) {
if(i == 74) break; // Out of for loop
if(i % 9 != 0) continue; // Next iteration
System.out.println(i);
}
int i = 0;
// An "infinite loop":
while(true) {
i++;
int j = i * 27;
if(j == 1269) break; // Out of loop
if(i % 10 != 0) continue; // Top of loop
System.out.println(i);
}
}
}
在这个for循环中,i的值永远不会到达100。因为一旦i到达74,break语句就会中断循环。通常,只有在不知道中断条件何时满足时,才需象这样使用break。只要i不能被9整除,continue语句会使程序流程返回循环的最开头执行(所以使i值递增)。如果能够整除,则将值显示出来。
第二部分向大家揭示了一个“无限循环”的情况。然而,循环内部有一个break语句,可中止循环。除此以外,大家还会看到continue移回循环顶部,同时不完成剩余的内容(所以只有在i值能被9整除时才打印出值)。输出结果如下:
0
9
18
27
36
45
54
63
72
10
20
30
40
之所以显示0,是由于0%9等于0。无限循环的第二种形式是for(;;)。编译器将while(true)与for(;;)看作同一回事。所以具体选用哪个取决于自己的编程习惯。