Thinking in Java学习杂记(1-4章)
程序流程控制
移位运算符
移位运算符面向的运算对象是二进制的位,可单独用它们处理整数类型。左移位运算符(<<)能将运算符左边的运算对象向左移动运算符右侧指定的位数(在低位补0)。“有符号”右移位运算符使用了“符号扩展”:若值为正,则在高位插入0;若值为负,则在高位插入1。Java也添加了一种“无符号”右移位运算符(>>>),它使用“零扩展”:无论正负,都在高位插入0。若对char, byte或者short进行移位处理,那么在移位进行之前,它们会自动转换成一个int。只用右侧的5个低位才会用到。这样可防止我们在一个int数里移动一个不切实际的位数。若对一个long值进行移位,最后得到的结果也是long型。此时只会用到右侧6个低位,防止移动超过long值现成的位数。但在进行“无符号”右移位时,也可能遇到一个问题,若对byte和short值进行又移位运算,得到的可能不是正确的结果。它们会自动转换成int类型,并进行右移位。但“零扩展”不会发生,所以在那些情况下会得到-1的结果。
移位可与等号(<<=或>>=或>>>=)组合使用。此时,运算符左边的值会移动由右边的值指定的位数,再将得到的结果赋值回左边的值。
public class Test {
public static void main(String[] args) {
int i = 8;
i >>= 2;
System.out.println(i);
i <<= 1;
System.out.println(i);
i <<= 1;
System.out.println(i);
i >>>= 1;
System.out.println(i);
i >>>= 1;
System.out.println(i);
}
}
// output
/*
2 4 8 4 2
*/
三元运算符
布尔表达式 ? 值0 : 值1
public class Test {
public static void main(String[] args) {
int i = 10;
System.out.println(i < 10 ? 5 : 11);
System.out.println(i == 10 ? i / 2 : 0);
}
}
// output
/*
11 5
*/
表达式运算符优先级
一元运算符 > 算术(移位)运算符 > 关系运算符 > 逻辑(按位)运算符 > 条件运算符 > 赋值
- 一元运算符:++、--
- 算术(移位)运算符:*、/、%、+、-、<<、>>
- 关系运算符:>、<、>=、<=、==、!=
- 逻辑(按位)运算符:&&、||、&、|、^
- 条件(三元):A>B?X:Y
- 赋值:= (以及复合赋值,如*=)
Java中goto标签的使用
尽管goto的滥用会导致程序的不可读,但是在一些情况下,goto是组织控制流程的最佳手段,因此很多的语言中仍然或多或少地保留了其一些用法,对Java来说,唯一用到标签的地方就是在循环语句之前。
public class Test {
public static void main(String[] args) {
int i = 0;
outer:
for (;true;){
inner:
for (;i < 10;i++){
System.out.println("i = " + i);
if (i == 2){
System.out.println("continue");
continue ;
}
if (i == 3){
System.out.println("break");
i++;
break ;
}
if (i == 7){
System.out.println("continue outer");
i++;
continue outer;
}
if (i == 8){
System.out.println("break outer");
break outer;
}
for (int k = 0; k < 5;k++){
if (k ==3) {
System.out.println("continue inner");
continue inner;
}
}
}
}
}
}
// output
/*
i = 0
continue inner
i = 1
continue inner
i = 2
continue
i = 3
break
i = 4
continue inner
i = 5
continue inner
i = 6
continue inner
i = 7
continue outer
i = 8
break outer
*/
需要注意的是当你使用switch...case语句时,遇到满足的一个case之后,如果之后没有break,将会继续执行接下来的语句而不会管符不符合之后条件,直到遇到break语句或者程序片段执行完毕。一般应该在switch...case语句之中,最后放置一个default条件,以执行没有备选条件下的任务。
public class Test {
public static void main(String[] args) {
int i = 5;
switch (i){
case 3:
System.out.println("case3: " + i);
case 5:
System.out.println("case5: " + i);
case 7:
System.out.println("case7: " + i);
default:
System.out.println("default: " + i);
}
}
}
// output
/*
case5: 5
case7: 5
default: 5
*/
public class Test {
public static void main(String[] args) {
int i = 5;
switch (i){
case 3:
System.out.println("case3: " + i); break;
case 5:
System.out.println("case5: " + i); break;
case 7:
System.out.println("case7: " + i); break;
default:
System.out.println("default: " + i); break;
}
}
}
// output
/*
case5: 5
*/
需要注意的是switch要求的选择因子必须是int或char的整数值。而假如将一个字符串或者浮点数作为选择因子使用,那么它们在switch语句中是不会工作的。
Math.random()
会产生0-1之间的值,其范围为[0, 1)。
当我们使用构造器的时候,如果我们自己并没有在类中进行任何定义,则系统会替我们创建一个默认的无参构造器,而当我们定义了任何一个构造器时,无论有没有参数,系统都不会再继续帮我们进行创建无参构造器。
注:尽管我们可以在非静态方法中访问静态方法和静态变量,但是反过来却是不行的。因为静态数据的构造要优先于非静态方法。
Java中使用垃圾收集器回收由不再使用的对象占据的内存。但是垃圾收集器只知道释放那些由new分配的内存,所以不知道如何释放对象的“特殊”内存。为解决这一问题,Java提供了一个名为finalize()的方法,可为我们的类定义它。在理想情况下,它的工作原理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),不过只有在下一次垃圾收集过程中,才会真正回收对象的内存。所以如果使用finalize(),就可以在垃圾收集期间进行一些重要的清除或清扫工作。
垃圾收集只跟内存有关,也就是说垃圾收集器存在的唯一原因就是为了回收程序不再使用的内存。
finalize()最有用的地方之一就是观察垃圾收集的过程。
在一个类里,初始化顺序是由变量在类内的定义顺序决定的。即使变量定义大量遍布于方法定义的中间,那些变量仍会在调用任何方法之前得到初始化(甚至在构建器调用之前)。