thinking in java 笔记之控制程序流程

写在前面: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 逻辑运算符

逻辑运算符AND(&&)、OR(||)、NOT(!)只能用于布尔值(true或者false),然后生成一个布尔值。

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 移位运算符

移位运算符面向的运算对象也是二进制的“位”。可单独用它们处理整数类型(主类型的一种)。左移位运算符(<<)能将运算符左边的运算对象向左移动运算符右侧指定的位数(在低位补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的结果。可用下面这个例子检测自己的实现方案:

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 三元运算符

运算符的表达式是:布尔表达式 ? 值0 : 值1 。如果表达式的值为true,结果为"值0",否则为"值1"。

1.10 逗号运算符

Java中使用逗号运算符的唯一场所是for循环,本文稍后会介绍。

1.11 字符串运算符+

这一个运算符有一个特殊的用途:用于连接不同的字符串。若表达式以一个string开头,则之后的运算对象必须都是string类型。如下:
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 运算符的常规操作

在C和C++中,一个特别常见的错误如下:
while(x = y) {
//...
}
程序的意图是测试是否“相等”(==),而不是进行赋值操作。在C和C++中,若y是一个非零值,那么这种赋值的结果肯定是true。这样使可能得到一个无限循环。在Java里,这个表达式的结果并不是布尔值,而编译器期望的是一个布尔值,而且不会从一个int数值中转换得来。所以在编译时,系统就会提示出现错误,有效地阻止我们进一步运行程序。所以这个缺点在Java里永远不会造成更严重的后果。唯一不会得到编译错误的时候是x和y都为布尔值。在这种情况下,x = y属于合法表达式。而在上述情况下,则可能是一个错误。

2. 执行控制

在Java里,涉及的关键字包括if-else、while、do-while、for以及一个名为switch的选择语句。

2.1 真和假

所有条件语句都利用条件表达式的真或假来决定执行流程。例如A == B,根据条件运算符"=="来判断A的值是否等于B,该表达式返回true或false。本文早些说到的所有条件运算符都可以用来构造一个条件语句。

2.2 if-else

if-else是控制程序流程最基本的形式,其中else是可选的。条件表达式必须产生一个Boolean值。格式如下:
if(条件表达式)
    语句

或者另外一种形式
if(条件表达式)
    语句
else 
    语句

2.3 反复

while、do...while、for控制着循环,有时候把它们划分成反复语句。除非控制反复的布尔表达式得到"假"结果,否则语句会重复执行下去。while循环的格式如下:
while(布尔表达式)
语句

循环刚开始会计算一次表达式的值,对于之后的每一次循环,都会重新计算一次。下面举个栗子:
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

do...while格式如下:
do
语句
while(布尔表达式)

while和do...while的唯一区别是,do...while至少会执行一次,即使表达式第一次计算结果为false,在while循环语句中,第一次表达式结果为false,语句就不会被执行。while比do...while更常用。

2.5 for

for循环在第一次反复之前会进行初始化。随后它会进行条件测试,而且在每一次反复的时候会进行某种形式的"步进"。for循环形式如下:
for(初始化表达式; 布尔表达式; 步进)
语句

初始化表达式、布尔表达式、步进都可以为空。每次返回前都会测试一下表达式的值,若为false,则循环结束。

2.5.1 逗号运算符

早在第1章,我们已提到了逗号运算符——注意不是逗号分隔符;后者用于分隔函数的不同自变量。Java里唯一用到逗号运算符的地方就是for循环的控制表达式。在控制表达式的初始化和步进控制部分,我们可使用一系列由逗号分隔的语句。而且那些语句均会独立执行。下面是一个例子:
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控制循环的流程。其中,break用于强行退出循环,不执行循环中剩余的语句。而continue则停止执行当前的反复,然后退回循环起始和,开始新的反复。
下面这个程序向大家展示了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(;;)看作同一回事。所以具体选用哪个取决于自己的编程习惯。













  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值