Thinking in Java 4 : 控制程序流程-阿里云开发者社区

开发者社区> 柳叶一刀> 正文

Thinking in Java 4 : 控制程序流程

简介: “就象任何有感知的生物一样,程序必须能操纵自己的世界,在执行过程中作出判断与选择。”
+关注继续查看

“就象任何有感知的生物一样,程序必须能操纵自己的世界,在执行过程中作出判断与选择。”

1 使用 J a v a 运算符

1 优先级

运算符的优先级决定了存在多个运算符时一个表达式各部分的计算顺序,Java 对计算顺序作出了特别的规定,其中,最简单的规则就是乘法和除法在加法和减法之前完成。

2 赋值

赋值是用等号运算符( =)进行的。它的意思是“取得右边的值,把它复制到左边”。右边的值可以是任何常数、变量或者表达式,只要能产生一个值就行。但左边的值必须是一个明确的、已命名的变量。

对主数据类型的赋值是非常直接的,由于主类型容纳了实际的值,而且并非指向一个对象的句柄。

但在为对象“赋值”的时候, 情况却发生了变化。对一个对象进行操作时,我们真正操作的是它的句柄,所以倘若“从一个对象到另一个对象”赋值,实际就是将句柄从一个地方复制到另一个地方。

3 算术运算符

Java 的基本算术运算符与其他大多数程序设计语言是相同的。其中包括加号( +)、减号( -)、除号( /)、乘号( *)以及模数( %,从整数除法中获得余数)。

整数除法会直接砍掉小数,而不是进位。

Java 也用一种简写形式进行运算,并同时进行赋值操作。 这是由等号前的一个运算符标记的,而且对于语言中的所有运算符都是固定的。例如,为了将 4 加到变量 x,并将结果赋给 x,可用: x+=4。

4 自动递增和递减

递减运算符是“ --”,意为“减少一个单位”;递增运算符是“ ++”,意为“增加一个单位”。举个例子来说,假设 A 是一个 int(整数)值,则表达式++A 就等价于( A = A + 1)。

对每种类型的运算符,都有两个版本可供选用;通常将其称为“前缀版”和“后缀版”。对于前递增和前递减(如++A 或--A),会先执行运算,再生成值。而对于后递增和后递减(如A++或 A--),会先生成值,再执行运算。

下面是一个例子:

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

从中可以看到,对于前缀形式,我们在执行完运算后才得到值。但对于后缀形式,则是在运算执行之前就得到值。

5 关系运算符

关系运算符生成的是一个“布尔”( Boolean)结果。它们评价的是运算对象值之间的关系。若关系是真实的,关系表达式会生成true(真);若关系不真实,则生成 false(假)。

关系运算符包括小于( <)、大于( >)、小于或等于( <=)、大于或等于( >=)、等于( ==)以及不等于( !=)。等于和不等于适用于所有内建的数据类型,但其他比较不适用于 boolean 类型。

==和!=比较的正好就是对象句柄,若想对比两个对象的实际内容是否相同,又该如何操作呢?此时, 必须使用所有对象都适用的特殊方法
equals(),但 equals()的默认行为是比较句柄,除非在类中改变了equals(),否则不可能表现出我们希望的行为。

6 逻辑运算符

逻辑运算符 AND( &&)、 OR( ||)以及 NOT( !)能生成一个布尔值( true 或 false)—以自变量的逻辑关系为基础。只可将 AND, OR 或 NOT 应用于布尔值。

1.短路

操作逻辑运算符时,我们会遇到一种名为“短路”的情况。这意味着只有明确得出整个表达式真或假的结论,才会对表达式进行逻辑求值。因此,一个逻辑表达式的所有部分都有可能不进行求值:

if(test1(0)) && test2(2) && test3(2))

如上述案例,假设第一个测试生成一个 true 结果,所以表达式求值会继续下去。然而,第二个测试产生了一个 false 结果。由于这意味着整个表达式肯定为 false,所以为什么还要继续剩余的表达式呢?这样做只会徒劳无益。

7 按位运算符

按位运算符允许我们操作一个整数主数据类型中的单个“比特”,即二进制位。按位运算符会对两个自变量中对应的位执行布尔代数,并最终生成一个结果。

按位运算来源于 C 语言的低级操作。我们经常都要直接操纵硬件,需要频繁设置硬件寄存器内的二进制位。

Java 的设计初衷是嵌入电视顶置盒内,所以这种低级操作仍被保留下来了。然而,由于操作系统的进步,现在也许不必过于频繁地进行按位运算。

  • 若两个输入位都是 1,则按位 AND 运算符( &)在输出位里生成一个 1;否则生成 0。
  • 若两个输入位里至少有一个是 1,则按位 OR 运算符( |)在输出位里生成一个 1;只有在两个输入位都是 0 的情况下,它才会生成一
    个 0。
  • 若两个输入位的某一个是 1,但不全都是 1,那么按位 XOR( ^,异或)在输出位里生成一个 1。
  • 按位NOT( ~,也叫作“非”运算符)属于一元运算符;它只对一个自变量进行操作(其他所有运算符都是二元运算符)。按位 NOT 生成与输入位的相反的值—— 若输入 0,则输出 1;输入 1,则输出 0。

8 移位运算符

移位运算符面向的运算对象也是二进制的“位”。可单独用它们处理整数类型(主类型的一种)。

  • 左移位运算符( <<)能将运算符左边的运算对象向左移动运算符右侧指定的位数(在低位补 0)。
  • “有符号”右移位运算符( >>)则将运算符左边的运算对象向右移动运算符右侧指定的位数。 “有符号”右移位运算符使用了“符号扩展”:若值为正,则在高位插入 0;若值为负,则在高位插入 1。
  • “无符号”右移位运算符( >>>), 它使用了“零扩展”:无论正负,都在高位插入0。这一运算符是 C 或 C++没有的。

移位可与等号( <<=或>>=或>>>=)组合使用。此时,运算符左边的值会移动由右边的值指定的位数,再将得到的结果赋回左边的值。

9 三元 i f - e l s e 运算符

表达式采取下述形式:布尔表达式 ? 值 0:值 1

若“布尔表达式”的结果为 true,就计算“值 0”,而且它的结果成为最终由运算符产生的值。但若“布尔表达式”的结果为 false,计算的就是“值 1”,而且它的结果成为最终由运算符产生的值。

当然,也可以换用普通的 if-else 语句,但三元运算符更加简洁。但假若您打算频繁用它,还是要先多作一些思量—它很容易就会产生可读性极差的代码。

1 0 字串运算符+

这个运算符在 Java 里有一项特殊用途:连接不同的字串。尽管与+的传统意义不符,但用+来做这件事情仍然是非常自然的。在 C++里,这一功能看起来非常不错,所以引入了一项“运算符过载”机制。

若表达式以一个 String 起头,那么后续所有运算对象都必须是字串。

public class StrTest {
    public static void main(String[] args) {
        int x = 0, y = 1, z = 2;
        String sString = "x, y, z ";
        System.out.println(sString + x + y + z);
    }
}

输出为:

x, y, z 012

在这里, Java 编译程序会将 x, y 和 z 转换成它们的字串形式,而不是先把它们加到一起。

1 2 造型运算符

“造型”( Cast)的作用是“与一个模型匹配”。在适当的时候, Java 会将一种数据类型自动转换成另一种。例如,假设我们为浮点变量分配一个整数值,计算机会将 int 自动转换成 float。通过造型,我们可明确设置这种类型的转换,或者在一般没有可能进行的时候强迫它进行。为进行一次造型,要将括号中希望的数据类型(包括所有修改符)置于其他任何值的左侧。

void casts() {
        int i = 200;
        long l = (long) i;
        long l2 = (long) 200;
    }

但在这儿展示的两种情况下,造型均是多余的,因为编译器在必要的时候会自动进行 int 值到 long 值的转换。当然,仍然可以设置一个造型,提醒自己留意,也使程序更清楚。

在 Java 里,造型则是一种比较安全的操作。但是,若进行一种名为
“缩小转换”( Narrowing Conversion)的操作(也就是说,脚本是能容纳更多信息的数据类型,将其转换成容量较小的类型),此时就可能面临信息丢失的危险。此时,编译器会强迫我们进行造型,就好象说:“这可能是一件危险的事情— 如果您想让我不顾一切地做,那么对不起,请明确造型。”而对于“放大转换”( Widening conversion),则不必进行明确造型,因为新类型肯定能容纳原来类型的信息,不会造成任何信息的丢失。

1. 字面值

  • 十六进制( Base 16) —它适用于所有整数数据类型—— 用一个前置的 0x 或 0X 指示。并在后面跟随采用大写或小写形式的 0-9 以及 a-f。
  • 八进制( Base 8)是用数字中的一个前置 0 以及 0-7 的数位指示的。
  • 字面值后的尾随字符标志着它的类型。若为大写或小写的 L,代表 long;大写或小写的 F,代表 float;大写或小写的 D,则代表 double。
  • 在科学与工程学领域,“ e”代表自然对数的基数,约等于 2.718,在计算机语言中 e 代表“ 10 的多少次幂”,比如在 Java中“ 1.39e-47f”它真正的含义是“ 1.39× 10 的-47 次方”。

2. 转型

通常,表达式中最大的数据类型是决定了表达式最终结果大小的那个类型。若将一个float 值与一个 double 值相乘,结果就是 double;如将一个 int 和一个 long 值相加,则结果为 long。

在 char, byte 和 short 中,我们可看到算术运算符的“转型”效果。对这些类型的任何一个进行算术运算,都会获得一个 int 结果。必须将其明确“造型”回原来的类型(缩小转换会造成信息的丢失),以便将值赋
回那个类型。但对于 int 值,却不必进行造型处理,因为所有数据都已经属于 int 类型。然而,不要放松警惕,认为一切事情都是安全的。如果对两个足够大的int 值执行乘法运算,结果值就会溢出。

2 执行控制

Java 使用了 C 的全部控制语句,在Java 里,涉及的关键字包括if-else、 while、 do-while、 for 以及一个名为 switch 的选择语句。然而, Java 并不支持非常有害的 goto,仍然可以进行象goto 那样的跳转。

1 真和假

所有条件语句都利用条件表达式的真或假来决定执行流程。条件表达式的一个例子是 A==B。它用条件运算符“ ==”来判断 A 值是否等于 B 值。该表达式返回 true 或 false。

2 i f - e l s e

if-else 语句或许是控制程序流程最基本的形式,其中的 else 是可选的;条件必须产生一个布尔结果,“语句”要么是用分号结尾的一个简单语句,要么是一个复合语句—— 封闭在括号内的一组简单语句。

3 反复

while, do-while 和 for 控制着循环,有时将其划分为“反复语句”。除非用于控制反复的布尔表达式得到“假”的结果,否则语句会重复执行下去。 在循环刚开始时,会计算一次“布尔表达式”的值。而对于后来每一次额外的循环,都会在开始前重新计算一次。

4 d o - w h i l e

do-while 的格式如下:

do
  语句
while(布尔表达式)

while 和 do-while 唯一的区别就是 do-while 肯定会至少执行一次;也就是说,至少会将其中的语句“过一遍” — 即便表达式第一次便计算为 false。而在 while 循环结构中,若条件第一次就为 false,那么其中的语句根本不会执行。在实际应用中, while 比 do-while 更常用一些。

5 f o r

for 循环在第一次反复之前要进行初始化,随后,它会进行条件测试,而且在每一次反复的时候,进行某种形式的“步进”( Stepping)。
for 循环的形式如下:

for(初始表达式; 布尔表达式; 步进)
 语句

无论初始表达式,布尔表达式,还是步进,都可以置空。每次反复前,都要测试一下布尔表达式。若获得的结果是 false,就会继续执行紧跟在 for 语句后面的那行代码。在每次循环的末尾,会计算一次步进。for 循环通常用于执行“计数”任务:

逗号运算符

之前已提到了逗号运算符— 注意不是逗号分隔符;后者用于分隔函数的不同自变量。 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);
        }
    }
}

6 中断和继续

在任何循环语句的主体部分,亦可用 break 和 continue 控制循环的流程。其中, break 用于强行退出循环,不执行循环中剩余的语句。而 continue 则停止执行当前的反复,然后退回循环起始和,开始新的反复。

break 本身只能中断最内层的循环(对于 continue 同样如此)。

7 开关

“开关”( Switch)有时也被划分为一种“选择语句”。根据一个整数表达式的值, switch 语句可从一系列代码选出一段执行。它的格式如下:

switch(整数选择因子) {
  case 整数值 1 : 语句; break;
  case 整数值 2 : 语句; break;
  case 整数值 3 : 语句; break;
  case 整数值 4 : 语句; break;
  case 整数值 5 : 语句; break;
  //..
  default:语句;

其中,“整数选择因子”是一个特殊的表达式,能产生整数值。 switch 能将整数选择因子的结果与每个整数值比较。若发现相符的,就执行对应的语句(简单或复合语句)。若没有发现相符的,就执行default 语句。

这是构建 switch 语句的一种传统方式,但 break 是可选的。若省略 break,会继续执行后面的 case 语句的代码,直到遇到一个 break 为止。尽管通常不想出现这种情况,但对有经验的程序员来说,也许能够善加利用。注意最后的 default 语句没有 break。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Java中的访问控制权限
简介 Java中为什么要设计访问权限控制机制呢?主要作用有两点: (1)为了使用户不要触碰那些他们不该触碰的部分,这些部分对于类内部的操作时必要的,但是它并不属于客户端程序员所需接口的一部分。 (2)为了让类库设计者可用更改类的内部工作方式,而不必担心会对用户造成重大影响。
670 0
java多线程 -- Condition 控制线程通信
Api文档如此定义: Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。
692 0
java 中线程执行顺序控制
[java] view plain copy   Thread t1 = new Thread(new Runnable() {          @Override       public void run() {           System.
421 0
(六)java结构控制语句
   <span style="font-size:18px"> 选择语句,也叫条件分支语句:if……else和switch……case;其中if……else中的else是可选的,但是switch……case中的case是必须的。<br>     switch后的条件必须是byte、short、int或者char类型,case后的值必须是一个常量而不是变量,且这个值不允许重复,使用bre
899 0
JavaScript异步精讲,让你更加明白Js的执行流程!
JavaScript异步精讲,让你更加明白Js的执行流程! 问题点 什么是单线程,和异步有什么关系 什么是 event-loop jQuery的Deferred Promise 的基本使用和原理 async/await(和 Promise的区别、联系) 一、什么是单线程,和异步有什么关系 单线程.
1830 0
[Java 基础]控制语句
选择语句 if语句 if语句会判断括号中的条件是否成立,如果成立则执行if语句中的代码块,否则跳过代码块继续执行。 语法 if(布尔表达式) {   //如果布尔表达式为true将执行的语句} 例 public class Test {   public static v...
499 0
JavaScript基础(三)流程控制
流程控制 switch switch底层用的是 === 比较。 参考链接:http://www.cnblogs.com/codetker/p/4680996.html switch ("111"){ case 111: alert(111); break;...
668 0
java 一个线程控制另一个线程
两种方法,一个用标记变量,另一个用语法 不多说,直接代码: 主方法一:         send s = new send();         s.
751 0
+关注
4
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载