从大学毕业到现在,马上就六年了,这六年中从事过开发,也从事培训工作,相比而言,参加培训工作的时间要长一些。由于工作的特点,遇到了各种各样的学生,在学习编程时遇到了一系列的问题,也有很多迷茫的时候,希望通过编写本书,把相关的问题进行一下总结,把自己的经验和大家进行分享。当然由于一些知识也只是个人见解,也希望大家积极指正,帮助编程的初学者,以及程序开发人员深刻理解基础的概念,更好的学习编程和从事开发工作。
编写一本书,总要有个名字吧,姑且把书名叫做《Java编程那些事儿》吧,主要是用通俗的语言,解释清楚以下几个内容:
1、 程序设计是什么?
2、 Java语言相关基础语法及应用
3、 如何建立基础的程序逻辑
以及其它编程和Java语言的相关问题,本书的编写打算以在线连载的形式进行,估计编写的周期会比较长,希望能为软件开发行业尽一点自己的微薄之力吧。
程序设计,俗称编程,是一个比较专业的概念。初学者,甚至一部分开发人员,都不能很简单的解释这个概念,所以使初学者觉得程序设计是一件很有科技含量,或者是很高深的学科,其实这些都是误解。那么程序设计到底是什么呢?
程序,其实就是把需要做的事情用程序语言描述出来。类似如作家,就是把自己头脑中的创意用文字描述出来。所以学习程序,主要就是三个问题:做什么、怎么做和如何描述,具体如下:
1. 做什么
做什么就是程序需要实现的功能。
2. 怎么做
怎么做就是如何实现程序的功能,在编程中,称为逻辑,其实就是实现的步骤。
3. 如何描述
就是把怎么做用程序语言的格式描述出来。
所以,对于有经验的程序设计人员来说,学习新的程序设计语言的速度会比较快,就是因为第1和第2个问题基本解决了,只需要学习第3个问题即可了。
对于“做什么”的问题,可能初学者觉得会比较简单,其实在大型项目开发,例如ERP,企业都不能很详细的说明需要实现具体的功能,这就需要有专门的人员去发掘具体的功能,这个用程序的术语叫做需求分析。举个例子吧,例如某个人要找个女朋友,如果你大概去问他,他会说,找个中等的就可以了,但是这个还不是具体的需求,你可能需要问一下,要求女朋友的年龄是多少,身高是多少等等具体的问题。所以说,搞清楚“做什么”也不是简单的事情,需要专门的人员从事该工作。
对于“怎么做”的问题,是初学者,甚至很有经验的开发人员都头疼的事情,这个称作程序逻辑。因为实际的功能描述和程序设计语言之间不能直接转换,就像作家需要组织自己的思路和语言一样,程序设计人员也需要进行转换,而且现实世界和程序世界之间存在一定的差异,所以对于初学者来说,这是一个非常痛苦的过程,也是开始学习时最大的障碍。由于计算机自身的特点,“怎么做”的问题其实就是数据和操作的问题,某个顶级大师曾经说过:“程序=数据结构+算法”,把这个问题描述的简单准确。那么“怎么做”的问题,就变成了持有那些数据,以及如何操作这些数据的问题。先简单的介绍这么多,大家仔细体会吧。
对于“如何描述”的问题,是学习程序最容易,也是最枯燥的问题。其实就是学“透”一套格式,并且深刻理解语言的特点。学程序语言,就像学汉语差不多,需要学习字怎么写,学习语法结构等,只是不需要像汉语这样学那么多年,但是学好一个语言还是要耐得住寂寞。语法的学习需要细致,只有深刻领悟了语法的格式才能够熟练使用该语言。
前面介绍的是程序的概念,那么为什么叫程序设计,其实这个设计和现实中的设计一样。例如你自己盖个小棚子,只需要简单的规划即可,也就是编程中的小程序,而如果需要建造一栋大楼,肯定需要进行设计吧,程序也是这样。所以把编程叫做程序设计了。
程序设计是一个技术活,所以不是适合所有的人。现在很多人听说程序设计人员待遇如何如何的好,都一窝蜂的来学习程序,其实这个现象很不正常,因为程序不一定适合你。其实对于一个人来说,适合你的才是最好的。
就像现在很多小孩子都被家长逼着去学钢琴啊什么,有些小孩根本没有艺术细胞的,所以学习的效果就是差强人意了。
其实程序设计最需要基础扎实了,现在的程序设计学习很偏重程序设计语言的学习,或者直白点说,程序设计课程基本上可以说是在学习程序设计语言,在上一个内容中已经讲解了程序设计是什么的问题,程序设计语言只是程序设计中最后的环节,也是比较简单的环节,只学会程序语言,离实际工作的距离还很遥远,而更多的程序基础其实是在语言之外的东西。就像会写汉字,熟悉汉语语法的人一定能够成为作家吗?
程序设计的基础不外乎以下几个方面:
1、 一定的英文阅读能力
因为程序设计接触的很多文档都是以英文的形式提供的,一个阅读英文很困难的人,可以学会程序设计,但是不会有很深的造诣。就像一个看不懂字典的人,能学好汉语吗?
2、 较强的数学基础
计算机最核心的功能就是计算,各种程序逻辑都会被转成一定格式的运算,运算需要什么知识呢,肯定是数学了。就像一个数学很差的人能做好会计吗?在程序设计中,需要深刻理解数学,用数学来解决你遇到的各种实际问题,类似于做数学应用题吧。这个基础学要长期的积累。
3、 较强的逻辑思维能力
逻辑思维可能每个程序设计人员都很需要,那么逻辑思维是什么呢?其实就是把一个事情分解成一系列的步骤在有限的时间内做完,这个也是程序设计过程中最灵活的地方。例如你要完成“去罗马”这件事情,那么逻辑有多少种呢?借用一句俗话“条条大道通罗马”来解释这个问题吧,所以程序设计是典型的脑力劳动。可能有些人觉得程序设计就是体力活,这也不错,为什么呢,还是借助一个例子来说明吧,买油翁的故事大家都知道吧,如果你反复做一件相同的事情,可能这个事情对外人来说是脑力劳动,对于不断重复做的人来说,也就只是“唯手熟尔”的体力活罢了。
可能很多初学者对于逻辑思维还不是很清楚,那么举一个比较老套的例子吧,例如实现“把一个大象放到冰箱里”这个事情,逻辑是怎样呢?步骤如下:
a、 打开冰箱
b、 把大象推到冰箱里
c、 关上冰箱的门
当然这只是一个很简单的逻辑。在实际的程序设计中还需要严谨的逻辑思维,保证程序可以正常运行。
那么逻辑严谨又是什么呢?还以上面的例子为例,严谨的逻辑思维应该做如下事情:
a、 冰箱打不开怎么办?
b、 大象不进冰箱怎么办?
c、 关不上冰箱门怎么办?
就像一个运动员来说,良好的体质是基础,同样,对于程序员来说,良好的基础可能帮助你达到更高的高度。当然基础不可能每个人都具备,但是数学基础和逻辑思维能力是必须的。
那你的数学基础如何呢,出个简单的数学题目测试一下你的数学基础吧。
已知一组从1开始的数字,第一行从左到右依次是1到10,第二行从左到右依次是11到20,按照每行10个的顺序依次类推,则任意整数n(n>0)位于该组数字的第几行第几列呢?
所以,如何你觉得以上的内容你欠缺很多,可能你就不适合做程序设计这个职业,趁早选择其他的职业吧,这样对你的发展会更有利。如果你觉得以上的内容你大部分都符合,那么你可以尝试学习一下后续的内容——《如何学好程序设计》。
希望大家积极讨论,不足之处请大家积极指正。
俗话说“兴趣是最好的老师”,但是只靠兴趣是远远不够的,还需要付出艰辛的努力。程序设计是一种技能,需要在较短的时间内学会,就不能像学习汉语一样,通过十几年甚至更长的时间来学好,也不能像英语那样进行业余学习,以至很多大学毕业的人英语水平也不敢恭维,也达不到实用的程度。
那么如何学好程序设计呢?或者更现实一点,如何在短时间内成为一个程序员呢?
在接触的学生中,很多人会问:学习程序设计有捷径吗?一般我都不直接回答,而是这样反问他们:大家都看过武侠小说吧,那么练武有没有捷径呢?可能一部分学生会说没有,而另一些同学会说,练武有捷径的啊,比如什么“辟邪剑谱”、“葵花宝典”之类的,但是走这些捷径需要付出很大的代价,但是的确可以快速炼成绝世武功。可惜的是,学习程序设计连这些付出很大代价的秘籍都没有。
但是在实际的学习中,就像练武一样,如果有位前辈对你进行指点或引导,的确可以提高你学习的速度,但是你还是要付出艰辛的努力。
在介绍如何学好程序设计以前,首先要搞明白,学习程序设计需要学什么,其实不外乎以下内容:
l 程序设计语言
语言是程序最终表达的方式,必须熟练。
l 开发工具
开发工具相当于练武的武器,拿个趁手的武器可以发挥你的潜能
l 开发技术
开发技术就是实现好的功能,可以直接拿来用的结构,类似于武功秘籍,但是一定要熟练到可以灵活使用啊。
l 逻辑思维
如何实现程序的要求功能。
l 设计模式
设计模式就是设计的技巧,类似于写作文时的倒序、插叙什么的。
其实学好程序的方法很简单——“勤学苦练”。多读代码,多写代码,是学好程序的基本方式。需要把各种东西熟练到可以灵活使用的程度,由于学习的时间一般都比较紧,不能像汉语那样炼成习惯成自然的方式,所以在开始学习的初期伴随着大量的记忆,在记忆的基础上进行使用,仔细记忆并体会每个细节,把合适的内容用在合适的地方。
当然,学习最好的开始是模仿,但是由于程序很灵活,所以一定要在模仿的基础上理解,理解了以后进行创新,把东西理解了以后,这些内容就变成了自己的,即使忘记了,也很容易的捡起来,而不要囫囵吞枣式的学习,那样无法做到灵活的解决问题。
当学会了程序设计以后,就可以成为大师级的人物,像武侠小说里的那些大侠一样,做到“意由心生”、“无招胜有招”了,祝福每个初学者以及从事开发的人员,能够早日进入到该境界。
PS:关于英文阅读能力的锻炼,坚持读1-2本英文原版书籍(网上很容易下到很多原版电子书),就可以获得比较大的提升。
前面简单的介绍了程序设计的相关知识,程序设计也就是用程序语言表达你自己的思维,所以重要的不是语言,而是你的思维,这个是现在程序设计教学中最大的误区,本书中将以语言和思维并重的方式来介绍Java语言,并培养你的逻辑思维。
程序设计的道路不是一帆风顺的,其中布满艰辛,所以如果你打算学习程序设计,那么要做好长期吃苦的准备,俗话说:“板凳要做十年冷”,要有这样的耐心和恒心才能把程序设计学会、学好。
当然如果基础不好,那么还想学习程序设计课程的话,将需要付出比一般人更多的努力才可以基本赶上一般人的水平,用句不恰当的话“出来混迟早要还的“,前面欠下的基础知识现在就是还的时候了。
关于工作,也简单的说一下,软件开发行业需要各种层次的人才,其实水平只要达到可以独立工作,也就能找到一份工作,但是要想找到一个不错的工作,而且以后还有良好的发展,需要的就是扎实的基础以及长期的努力。
后续讲解语言部分打算分成以下几大块来进行说明:
1、 计算机基础
计算机软件的基本概念、进制的相关知识、计算机内部的数据表达方式等。
2、 Java语言的简介。
注:我也不能免俗。
3、 Java开发环境的安装和配置。
介绍JDK、Eclipse的安装、配置以及使用。
4、 Java相关语法和程序逻辑。
以通俗的语言来介绍语法,深刻理解语法的作用和用途。
可能还会进行一些变更,本人保留最终的解释权,呵呵。
第一章 计算机基础
本部分主要介绍计算机相关的知识,重点介绍和软件编程相关的基础知识。
其实对于编程来说,计算机的基础是越多越深就越好,但是受时间和精力的限制,每个人了解的其实还是很有限,下面就主要编程中常用的基础知识,遗漏的地方请大家积极指正和补充。
计算机本质的结构就是将所有的内容数据化,其实软件编程也采用的是同样的逻辑,把各种需要保存的状态数字化。
1.1 计算机软件基本概念
l 软件的概念
大家都知道,计算机分为硬件和软件,其实看得见摸得着的算硬件,比如硬盘、主板什么的,摸不着的就算软件了。
按照专业点的说法,软件就是一组指令序列,那么如何理解他呢?举个基本的例子,比如大家到学校报名,学校会给你一个单子,一般上面会写,首先到哪里缴费、然后领证件,体检什么的,反正至少也有那么10多条,如果把每条操作都看成指令的话,这个就是软件的本质。
或者按照冯·诺伊曼的计算机体系,计算机就是接受输入,进行处理,反馈结果,其实软件也是这样,提供界面接受用户的输入,根据逻辑进行处理,把结果反馈给用户,无论是普通的软件还是游戏都是这样。
l 计算机为什么是二进制的?
众所周知,现在的计算机都是以二进制存储和运算数据的,那么为什么是二进制而不是常见的十进制呢?
原因很简单,因为现在的计算机是电子计算机,内部只有两个状态,所以就依据这两个状态创建了一种新的进制形式——二进制。这样极大的简化了电子计算机的结构,可以用电流的有无、光线的有无以及磁性的有无等状态来实现数学上的二进制。数学上用0和1来分别代表这两个状态罢了。
当然,随着科学的发展,以后计算机不再是电子计算机了,那么二进制也可能就消失了。
l 计算机存储单位
既然计算机是电子计算机了,那么存储的最小单位就是一个二进制位,英语是bit,简写成b。一位只有2个值,0或者1。
由于位的单位太小,所以就设计了另外一个概念——字节,英语是byte,简写成B。规定1个字节是8位,即1B=8b。比如大家接触的8位机、16位机等等,就是指CPU一次处理的最小的数据单位。
再大点的单位就依次是KB、MB、GB和TB了,他们之间的进制都是2的10次方,也就是1024,也就是1KB=1024B,1MB=1024KB。
这里简单的说一个实际问题,大家买硬盘的时候,比如160GB,这里厂商使用的进制是1000,而不是1024,所以160个GB格式化以后就大概只有:
(160 X 1000 X1000 X1000) / 1024/1024/1024 = 149GB
1.2 进制的概念
因为不可能为每个数值都创造一个符号,所以需要用基本数字组合出复合的数值,这样就有了进制的概念。
其实所有进制都是人为的创造,都是用来计数方便的。现在最常用的进制是十进制,当然其它的进制也在使用中。例如“半斤八两”这个成语,就反映了古代一斤等于十六两的概念,也就是十六进制计数方式。
计算机编程中常用的进制有二进制、八进制、十进制和十六进制,十进制还是最主要的表达形式。在编程中,大家书写的数值默认为十进制。
对于进制,有两个最基本的概念:基数和运算规则。
l 基数
基数指一种进制中组成的基本数字,也就是不能再拆分的数字。例如十进制是0-9,二进制是0和1,八进制是0-7,十六进制是0-9,A-F(大小写均可)。或者可以简单的这样记忆,假设是n进制的话,基数就是[0,n-1]的数字,基数的个数和进制值相同,十进制有十个基数,依次类推。
l 运算规则
运算规则就是进位或借位规则,这个类似于一般计算机书籍中位权的概念,例如对于十进制来说,该规则是“满十进一,借一当十”,也就是低位的数字满十了向高位进一,从高位借到的一,相当于低位上的十。其它的进制也是这样,对于二进制来说,就是“满二进一,借一当二”,八进制和十六进制也是这样。
在数学上表示一个数字是几进制,通常使用如下格式:[数值]进制数,例如[10]2 表示二进制数值10。
1.2.1 二进制
二进制是计算机内部数据表示的形式,所以学习计算机编程必须熟悉二进制。熟悉二进制有以下几个用途:
l 更容易理解计算机的数据存储方式
计算机内部的很多转换,例如数据类型之间的强转,都可以用二进制解释最终的结果的值。
l 二进制的运算速度高
二进制的运算速度比十进制高的多。例如求2的n次方,通过移位实现的效率比数学方法高效。
l 使用二进制数值进行数据存储
以二进制的形式存储数值,一个是比较节约资源,可以使用二进制的位来存储信息,例如常见的硬件控制信息,都是二进制的形式进行提供的。
如前所述,二进制包含0和1两个基数,运算规则是“满二进一,借一当二”,下面简单的介绍一下二进制的计数方式。
例如十进制的0-9用二进制进行表达,则依次是:
0,1,10,11,100,101,110,111,1000,1001
说明:数值之间使用逗号进行间隔。
下面是二进制的一些基本运算结果:
l 加法运算
0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 10
l 减法
0 – 0 = 0
0 – 1 = -1
1 – 0 = 1
1 – 1 = 0
l 乘法
0 × 0 = 0
0 × 1 = 0
1 × 0 = 0
1 × 1 = 1
l 除法
0 / 0 无意义
0 / 1 = 0
1 / 0 无意义
1 / 1 = 1
以下是一些符合的表达式:
110 + 111 = 1101
这些基本的运算结构在实际开发中一般不会直接用到,但是通过这些内容可以加深对于二进制概念的理解。
1.2.2 二进制和十进制之间的转换
由于计算机内部的数据是以二进制进行表达的,而十进制又是日常生活中最常用的进制,所以它们之间经常需要进行转换。下面介绍一下转换的方式。
1.2.2.1 十进制转换为二进制
十进制整数转换为二进制有三种方法,分别是除二取余、计算器转换和经验法。十进制小数的转换方法最后做简单的介绍。
1. 除二取余法
除二取余法是转换时的最基本方法,也是最通用的方法。规则为:使用十进制和2去除,取每次得到的商和余数,用商继续和2相除,直到商为零为止,第一次得到的余数作为二进制的低位,最后一次得到的余数作为二进制的高位,由余数组成的数字就是转换后二进制的值。例如十进制的13转换为二进制的计算步骤如下:
商 余数
13 / 2 = 6 1
6 / 2 = 3 0
3 / 2 = 1 1
1 / 2 = 0 1
则计算的最终结果就是1101。
2. 计算器转换
Windows操作系统中的计算器也可以很方便的实现进制之间的转换。在程序菜单中附件子菜单中打开计算器,从打开的计算器的查看菜单中,选择“科学型”,输入你要转换的十进制的数字,例如13,然后界面上数字显示框西侧的“二进制“,则转换后的数值就直接显示在计算器中。
3. 经验法
对于二进制熟悉以后,那么计算十进制对应的数字可以通过一些基本的数学变换来实现,在使用经验法以前,必须熟记2的0-10次方对应的十进制的值,依次是:
1,2,4,8,16,32,64,128,256,512,1024
则转换一些特殊的数字时可以极大的提高转换速度,例如数字65,则可以这样转换:
65 = 64 + 1
64对应的二进制形式为1000000
1对应的二进制形式为1
则65的二进制形式为1000001
这个只适合转换一些特殊的数字,适应性没有除二取余法广泛。
十进制小数的转换采用的一般方法是乘二取整法,规则为:对于小数部分先乘二,然后获得运算结果的整数部分,然后将结果中的小数部分再次乘二,直到小数部分为零为止,则把第一次得到的整数部分作为二进制小数的高位,后续的整数部分作为地位就是转换后得到的二进制小数。需要说明的是,有些十进制小数无法准确的用二进制进行表达,所以转换时符合一定的精度即可,这也是为什么计算机的浮点数运算不准确的原因。
例如0.25转换为二进制小数的步骤如下:
整数部分
0.25 × 2 = 0.5 0
0.5 × 2 = 1.0 1
则0.25转换为二进制小数为0.01
如果一个十进制数字既有整数部分,也有小数部分,则分开进行转换即可。
1.2.2.2 二进制转换为十进制
二进制转换为十进制采用的方法是:数字乘位权相加法。下面先以十进制为例来说明该方法,例如十进制数字345的值,5的位权是1,4的位权是10,3的位权是100,则有如下表达式成立: 345=5 × 1 + 4 × 10 + 3 × 100,这就是数字乘位权相加法的原理。
其实对于十进制整数的位权很有规则,从右向左第n位的位权是十的(n-1)方,例如个位是10(1-1),十位是10(2-1),依次类推。那么二进制整数的位权规律和这个一致,也就是从右向左第n位的位权是二的(n-1)方。
例如二进制整数1011转换为十进制的表达式为:
[1011]2 = 1 × 20 + 1 × 21 + 0 × 22 + 1 × 23 = 1 + 2 + 0 + 8=11
而对于二进制的小数,也是采用一样的方法,只是二进制小数的位权规则为,小数点后第一位小数的位权是2的-1次方,第二位是2的-2次方,依次类推。
例如二进制小数0.1101转换为十进制小数的表达式为
[0.1101]2=1 ×2-1 + 1 ×2-2 + 0 × 2-3 + 1 × 2-4 = 0.5 + 0.25 + 0 + 0.0625=0.8125
同理,如果二进制包含整数和小数部分,则分开进行转换即可。
1.2.3 二进制和八进制、十六进制之间的转换
虽然二进制是计算机内部的数据表达形式,但是由于二进制基数太少,则导致数字比较长,为了简化数字的书写,就创建了八进制和十六进制。八进制和十六进制就是对二进制的简化,所以二进制到八进制和十六进制的转换非常简单。
二进制整数转换为八进制的方法是“三位一并“,也就是从右侧开始,每3位二进制数字转换为八进制的一位,依次类推,因为二进制的三位数字可以表达的区间是000-111,刚好和0-7重合。例如:
二进制的10111转换为8进制为:最后三位111转换为7,前面的数字10转换为2,则转换后得到的八进制数字为27。
二进制整数转换为十六进制的方法是“四位一并“,例如10111转换为十六进制是0111转换为7,1转换为1,则转换后得到的十六进制数字是17。
二进制小数转换为八进制的方法也是“三位一并“,只是转换时从小数的高位开始,也就是小数的左侧开始。例如0.10111转换为八进制是101转换为5,110转换为6,则转换得到的八进制小数为0.56。需要特别注意的是,小数最后如果不足三位,一定要在后续补零以后再进行转换。
二进制小数转换为十六进制的方法也是“四位一并”,只是转换时从小数的高位开始。例如二进制小数0.10111转换为十六进制小数为,1011转换为b,1000转换为8,则转换后得到的十六进制是0.b8。
如果二进制数包含整数和小数部分,则分开进行转换。
1.3 计算机内部的数据表达
计算机内部数据表达的总原则就是:把一切内容数值化、数字化。这个也是编程时处理数据的基本方式,对于编程理解的越深入,则将越认同该原则。
其实计算机也只能这样,因为计算机内部只能存储0和1两个数字,所以必须把指令、数据、图片、文本等各种各样的内容数字化成0和1进行存储、传输和显示。
1.3.1 整数的表达
整数有正负之分,但是计算机内部只能存储0和1,则计算机内部将符号数字化,用二进制码的最高位代表符号位,规定该位为0代表正,1代表负。这就是符号数字化的规定。
前面介绍过整数在计算机内部都是以二进制的形式保存的。但是为了计算方便,以及简化CPU的结构,所以在存储和运算时都采用补码的形式。
前面介绍的那些直接计算出来的二进制形式,都称作整数的原码。规定正数的原码、反码和补码都是自身。
而对于负数,仔细研究一下其组成格式。以8位机为例,也就是一个数字占计算机中的8位,也就是一个字节,用最高位存储符号位,其它的位存储数值。例如-8的原码是10001000,最高位的1代表负数,后续的7位代表数值。
负数的反码是指符号位不变,其他的位取反,也就是0变1,1变0,则-8的反码是11110111。
负数的补码是指在反码的数值位上加1,运算后得到的结果,只计算数值位,不改变符号位。则-8的补码是11111000,该次运算中,低位向高位进行了进位。
规律:补码的补码等于负数的原码。
也就是对负数的补码再求补,则得到的负数的原码。
熟悉整数的表达,对于后续理解数据的区间以及进行强制转换以后得到的数值很有帮助,也是进行位运算的基础。
备注:小数,编程语言中称浮点数,的存储形式和整数不同。
1.3.2 字符的表达
字符指计算机内部单个的符号,如标点符号、英文字母和汉字等等。因为这些字符种类各异,计算机无法直接表达,那么就采用了计算机编程中也常用的方式,对每个字符进行编号,例如规定a字符编号为97,b字符编号为98等等。
由于需要编号的字符很多,就专门规定了一系列字符和编号的对应规则,那么这些对应表就被称作字符集,常见的字符集有ASCII、GB2312、BIG5等。
在计算机内部存储、运算和传输时,都只需要使用该编号即可。
字符集比较完美的解决了字符的存储和传输的问题。
所以字符在程序内部可以参与运算,其实参与运算的就是这个字符的编号,字符集规律是很多字符变换逻辑实现的基础。
备注:字符的显示则通过专门的字符显示码实现。
1.3.3 总结
其实计算机内部所有的东西都是以数字的形式存储的,这里只是希望通过这两种简单的结构,使大家了解将数据数字化的思想,这是编程时常用的思想之一。
1.4 网络编程基础
对于初学者,或者没有接触过网络编程的程序员,会觉得网络编程涉及的知识很高深,很难,其实这是一种误解,当你的语法熟悉以后,其实基本的网络编程现在已经被实现的异常简单了。
1.4.1 网络编程是什么?
网络编程的本质是两个设备之间的数据交换,当然,在计算机网络中,设备主要指计算机。数据传递本身没有多大的难度,不就是把一个设备中的数据发送给两外一个设备,然后接受另外一个设备反馈的数据。
现在的网络编程基本上都是基于请求/响应方式的,也就是一个设备发送请求数据给另外一个,然后接收另一个设备的反馈。
在网络编程中,发起连接程序,也就是发送第一次请求的程序,被称作客户端(Client),等待其他程序连接的程序被称作服务器(Server)。客户端程序可以在需要的时候启动,而服务器为了能够时刻相应连接,则需要一直启动。例如以打电话为例,首先拨号的人类似于客户端,接听电话的人必须保持电话畅通类似于服务器。
连接一旦建立以后,就客户端和服务器端就可以进行数据传递了,而且两者的身份是等价的。
在一些程序中,程序既有客户端功能也有服务器端功能,最常见的软件就是BT、emule这类软件了。
下面来谈一下如何建立连接以及如何发送数据。
1.4.2 IP地址和域名
在现实生活中,如果要打电话则需要知道对应人的电话号码,如果要寄信则需要知道收信人的地址。在网络中也是这样,需要知道一个设备的位置,则需要使用该设备的IP地址,具体的连接过程由硬件实现,程序员不需要过多的关心。
IP地址是一个规定,现在使用的是IPv4,既由4个0-255之间的数字组成,在计算机内部存储时只需要4个字节即可。在计算机中,IP地址是分配给网卡的,每个网卡有一个唯一的IP地址,如果一个计算机有多个网卡,则该台计算机则拥有多个不同的IP地址,在同一个网络内部,IP地址不能相同。IP地址的概念类似于电话号码、身份证这样的概念。
由于IP地址不方便记忆,所以有专门创造了域名(Domain Name)的概念,其实就是给IP取一个字符的名字,例如163.com、sina.com等。IP和域名之间存在一定的对应关系。如果把IP地址类比成身份证号的话,那么域名就是你的姓名。
其实在网络中只能使用IP地址进行数据传输,所以在传输以前,需要把域名转换为IP,这个由称作DNS的服务器专门来完成。
所以在网络编程中,可以使用IP或域名来标识网络上的一台设备。
1.4.3 端口的概念
为了在一台设备上可以运行多个程序,人为的设计了端口(Port)的概念,类似的例子是公司内部的分机号码。
规定一个设备有216个,也就是65536个端口,每个端口对应一个唯一的程序。每个网络程序,无论是客户端还是服务器端,都对应一个或多个特定的端口号。由于0-1024之间多被操作系统占用,所以实际编程时一般采用1024以后的端口号。
使用端口号,可以找到一台设备上唯一的一个程序。
所以如果需要和某台计算机建立连接的话,只需要知道IP地址或域名即可,但是如果想和该台计算机上的某个程序交换数据的话,还必须知道该程序使用的端口号。
1.4.4 数据传输方式
知道了如何建立连接,下面就是如何传输数据了,先来看一下数据传输的方式。
在网络上,不管是有线传输还是无线传输,数据传输的方式有两种:
<!--[if !supportLists]-->l <!--[endif]-->TCP(Transfer Control Protocol)
传输控制协议方式,该传输方式是一种稳定可靠的传送方式,类似于显示中的打电话。只需要建立一次连接,就可以多次传输数据。就像电话只需要拨一次号,就可以实现一直通话一样,如果你说的话不清楚,对方会要求你重复,保证传输的数据可靠。
使用该种方式的优点是稳定可靠,缺点是建立连接和维持连接的代价高,传输速度不快。
<!--[if !supportLists]-->l <!--[endif]-->UDP(User Datagram Protocol)
用户数据报协议方式,该传输方式不建立稳定的连接,类似于发短信息。每次发送数据都直接发送。发送多条短信,就需要多次输入对方的号码。该传输方式不可靠,数据有可能收不到,系统只保证尽力发送。
使用该种方式的优点是开销小,传输速度快,缺点是数据有可能会丢失。
在实际的网络编程中,大家可以根据需要选择任何一种传输方式,或组合使用这两种方式实现数据的传递。
1.4.5 协议的概念
协议(Protocol)是网络编程中一个非常重要的概念,指的是传输数据的格式。因为大家在网络中需要传输各种各样的信息,在程序中获得到的都是一组数值,如何阅读这些数值呢,就需要提前规定好这组数据的格式,在客户端按照该格式生成发送数据,服务器端按照该格式阅读该数据,然后在按照一定的格式生成数据反馈给客户端,客户端再按照该格式阅读数据。现实中类似的例子就是电报编码,每个数字都是用特定的数据表达。
一般程序的协议都分成客户端发送的数据格式,以及服务器端反馈的数据格式,客户端和服务器端都遵循该格式生成或处理数据,实现两者之间的复杂数据交换。
1.4.6 小结
网络编程就是使用IP地址,或域名,和端口连接到另一台计算机上对应的程序,按照规定的协议(数据格式)来交换数据,实际编程中建立连接和发送、接收数据在语言级已经实现,做的更多的工作是设计协议,以及编写生成和解析数据的代码罢了,然后把数据转换成逻辑的结构显示或控制逻辑即可。
需要了解更多的网络编程的知识,建议阅读《JAVA2网络协议内幕》一书。
1.5 Java语言简介
Java语言现在是编程领域主流的开发语言之一,在这里就简单的介绍一下和Java语言相关的一些知识。
1.5.1 Java语言历史
1991年,美国的SUN(太阳微系统)公司为了在消费类电子设备(现在称作智能家电)方面进行前沿研究,建立了以James Gosling领导的Green小组进行软件方面的研究,该小组一开始选择当时已经很成熟的C++语言进行设计和开发,但是却发现执行C++程序需要很多的设备内存,这样将增加硬件的成本,不利于市场竞争,所以该小组在C++语言的基础上,创建了一种新的语言,由于该小组的领导很喜欢自己办公室窗外的一棵橡树,所以把该语言的名字叫做Oak,中文意思是橡树,这就是Java语言的前身。
但是这个科研小组的成果最终没有转变成SUN公司的产品,也没有为SUN公司带来什么收益,像很多企业的科研项目一样,Oak面临夭折的危险。
但是天无绝人之路,当上帝关上门的时候,同时会为你打开一扇窗户,由于Oak专门为内存有限的消费类电子设备进行设计,使其执行环境以及程序体积都很小,所以在1994年Internet的大潮中,找到了自己的位置。为了证明Java语言的强大的开发能力,Sun公司还专门使用Java语言开发了一个专门的浏览器软件——HotJava。
随着互联网的发展,以及Oak语言和浏览器的融合,产生了一种称作Applet技术,当然,现在该技术已经被Flash击败,Applet是一种将小程序嵌入到网页中进行执行的技术,是互联网从静态网页过渡成动态网页,也使SUN公司的该研发小组获得了新生。
1995年3月,SUN公司正式向外界发布Java语言,Java语言正式诞生。
Java语言大事记:
<!--[if !supportLists]-->l <!--[endif]-->1995年3月,SUN公司发布Java,Java语言诞生
<!--[if !supportLists]-->l <!--[endif]-->1996年1月,JDK1.0发布
<!--[if !supportLists]-->l <!--[endif]-->1997年2月,JDK1.1发布
<!--[if !supportLists]-->l <!--[endif]-->1998年12月,JDK1.2发布,这是Java语言的里程碑,Java也被首次划分为J2SE\J2EE\J2ME三个开发技术。不久SUN公司将Java改称Java 2,Java语言也开始被国内开发者学习和使用。
<!--[if !supportLists]-->l <!--[endif]-->2000年5月,JDK1.3发布
<!--[if !supportLists]-->l <!--[endif]-->2002年2月,JDK1.4发布
<!--[if !supportLists]-->l <!--[endif]-->2004年10月,JDK1.5发布,同时SUN公司将JDK1.5改名为J2SE5.0
<!--[if !supportLists]-->l <!--[endif]-->2006年6月,JDK1.6发布,也称Java SE6.0,同时Java的各版本去掉2的称号,J2EE改称Java EE,J2SE改称Java SE,J2ME改称Java ME。
1.5.2 Java程序开发过程
众所周知,程序分解释程序和编译程序,解释程序就是运行环境一句一句的读取源代码,然后执行,类似于新闻发布会中的翻译,演讲者讲一句,就翻译一句,另外一种是编译程序,就是将源代码一次翻译成计算机操作系统可以直接执行的机器指令的文件,例如Windows中的exe文件,以后只需要执行编译以后生成的文件即可。
而Java语言是一种特殊的语言,它采用的形式为先编译,再解释的执行方式。也就是先把Java语言的源代码编译成中间代码class文件,然后在运行时根据class文件的内容解释执行。所以严格意义上说,Java语言是一种半编译半解释的语言。
则一般Java程序的开发步骤如下:
<!--[if !supportLists]-->1、 <!--[endif]-->编写源代码
<!--[if !supportLists]-->2、 <!--[endif]-->编译源代码,如果有语法错误,则返回步骤1。
<!--[if !supportLists]-->3、 <!--[endif]-->执行编译以后的class文件,如果有逻辑错误(功能错误),则返回步骤1
<!--[if !supportLists]-->4、 <!--[endif]-->生成部署文件
1.5.3 Java虚拟机介绍
Java语言有很多的特点,再这里就不一一列举了,其实列举了对于初学者来说也意义不大,但是Java语言中最特殊的一个特点,却不能不提,这个特性就是——跨平台性,或者叫平台无关性。
这里的平台(platform)指的是操作系统等执行程序的平台,也就是说Java语言的执行程序,也就是class文件,可以不经过修改,就可以直接在各个环境中执行。
这个特性将极大的降低多平台程序的开发版本,那么在国内为什么就感觉不到这个特性呢?因为盗版的缘故,获得操作系统的成本太低,如果每个操作系统都需要使用正版的话,相信公司或者个人的操作系统肯定种类各异,这样Java语言就只需要开发一套程序,就可以在多种操作系统中执行了。
平台无关性是通过Java虚拟机(Java Virtual Machine)这个概念实现的。其实这个概念就类似于PC机上的街机模拟器,或者是PC机上的操作系统模拟器,如VM等软件。虚拟机其实就是一个翻译,跟现实中的翻译一样,它将Java语言的执行文件class文件翻译成操作系统可以识别的指令格式,这样Java就可以在该操作系统上执行,如果需要在某个操作系统上执行Java程序,只需要安装该操作系统支持的Java虚拟机即可。
其实更准确的说,是安装实现了Java虚拟机规范的软件——JRE(Java Runtime Environment),Java运行时环境即可。
1.5.4 其他
<!--[if !supportLists]-->l <!--[endif]-->JavaScript和Java的关系
JavaScript是一种嵌入到网页中执行的脚本语言,该语言除了和Java语言的名字相似以后,没有任何的关系。
<!--[if !supportLists]-->l <!--[endif]-->Java语言是免费的
Java语言的基础开发工具、Java语言规范、Java语言的文档都是免费的,但是如果厂商需要生产SUN公司认证的Java软件,则需要支付许可证费用,类似现在的什么315认证之类。
Java语言有很多的特点,再这里就不一一列举了,其实列举了对于初学者来说也意义不大,但是Java语言中最特殊的一个特点,却不能不提,这个特性就是——跨平台性,或者叫平台无关性。
这里的平台(platform)指的是操作系统等执行程序的平台,也就是说Java语言的执行程序,也就是class文件,可以不经过修改,就可以直接在各个环境中执行。
这个特性将极大的降低多平台程序的开发版本,那么在国内为什么就感觉不到这个特性呢?因为盗版的缘故,获得操作系统的成本太低,如果每个操作系统都需要使用正版的话,相信公司或者个人的操作系统肯定种类各异,这样Java语言就只需要开发一套程序,就可以在多种操作系统中执行了。
平台无关性是通过Java虚拟机(Java Virtual Machine)这个概念实现的。其实这个概念就类似于PC机上的街机模拟器,或者是PC机上的操作系统模拟器,如VM等软件。虚拟机其实就是一个翻译,跟现实中的翻译一样,它将Java语言的执行文件class文件翻译成操作系统可以识别的指令格式,这样Java就可以在该操作系统上执行,如果需要在某个操作系统上执行Java程序,只需要安装该操作系统支持的Java虚拟机即可。
其实更准确的说,是安装实现了Java虚拟机规范的软件——JRE(Java Runtime Environment),Java运行时环境即可。
1.5.4 其他
<!--[if !supportLists]-->l <!--[endif]-->JavaScript和Java的关系
JavaScript是一种嵌入到网页中执行的脚本语言,该语言除了和Java语言的名字相似以后,没有任何的关系。
<!--[if !supportLists]-->l <!--[endif]-->Java语言是免费的
Java语言的基础开发工具、Java语言规范、Java语言的文档都是免费的,但是如果厂商需要生产SUN公司认证的Java软件,则需要支付许可证费用,类似现在的什么315认证之类。
第二章 建立开发环境
“工欲善其事,必先利其器”。
进行程序开发,首先要安装开发相关的软件,并且熟悉这些工具软件的基本使用。本章介绍一下两类开发工具的使用:
l 基础开发工具
基础开发工具是进行程序设计的基础,包含开发中需要的一些基本功能,例如编译、运行等,是其它开发工具的基础。
Java语言的基本开发工具是SUN公司免费提供的JDK。
实际开发中,为了方便和程序开发的效率,一般不直接使用基础开发工具,所以对于很多基础开发工具,只需要掌握其基本的使用即可。
l 集成开发环境(IDE)
集成开发环境是指将程序设计需要的很多功能,例如代码编辑、代码调试、程序部署等等一系列功能都整合到一个程序内部,方便程序开发,并提高实际的开发效率,简化了程序设计中的很多操作。
Java语言的集成开发环境很多,常见的有Eclipse、JBuilder、NetBeans等等。
由于实际开发中,基本都是使用集成开发环境进行开发,所以在学习中必须熟练掌握该类工具的使用。
一般集成开发环境的使用都很类似,在学习时只要熟练掌握了其中一个的使用,其它的工具学习起来也很简单。
本文以Eclipse为例来介绍集成开发环境的基本使用。
2.1 JDK开发环境
JDK(Java Developer’s Kit),Java开发者工具包,也称J2SDK(Java 2 Software Development Kit),是SUN公司提供的基础Java语言开发工具,该工具软件包含Java语言的编译工具、运行工具以及执行程序的环境(即JRE)。
JDK现在是一个开源、免费的工具。
JDK是其它Java开发工具的基础,也就是说,在安装其它开发工具以前,必须首先安装JDK。
对于初学者来说,使用该开发工具进行学习,可以在学习的初期把精力放在Java语言语法的学习上,体会更多底层的知识,对于以后的程序开发很有帮助。
但是JDK未提供Java源代码的编写环境,这个是SUN提供的很多基础开发工具的通病,所以实际的代码编写还需要在其它的文本编辑器中进行。其实大部分程序设计语言的源代码都是一个文本文件,只是存储成了不同的后缀名罢了。
常见的适合Java的文本编辑器有很多,例如JCreator、Editplus、UltraEdit等。
下面依次来介绍JDK的下载、安装、配置和使用。
2.1.1 JDK的获得
如果需要获得最新版本的JDK,可以到SUN公司的官方网站上进行下载,下载地址为:
http://java.sun.com/javase/downloads/index.jsp
下载最新版本的“JDK 6 Update 6”,选择对应的操作系统,以及使用的语言即可。
在下载Windows版本时,有两个版本可供下载,,分别是:
l Windows Online Installation
在线安装版本,每次安装时都从网络上下载安装程序,在下载完成以后,进行实际的安装。
l Windows Offline Installation
离线安装版本,每次安装时直接进行本地安装。
通常情况下,一般下载离线安装版本。
其实如果不需要安装最新版本的话,也可以在国内主流的下载站点下载JDK的安装程序,只是这些程序的版本可能稍微老一些,这些对于初学者来说其实问题不大。
2.1.2 JDK的安装
Windows操作系统上的JDK安装程序是一个exe可执行程序,直接安装即可,在安装过程中可以选择安装路径以及安装的组件等,如果没有特殊要求,选择默认设置即可。程序默认的安装路径在C:\Program Files\Java目录下。
2.1.3 JDK的配置
JDK安装完成以后,可以不用设置就进行使用,但是为了使用方便,一般需要进行简单的配置。
由于JDK提供的编译和运行工具都是基于命令行的,所以需要进行一下DOS下面的一个设定,把JDK安装目录下bin目录中的可执行文件都添加到DOS的外部命令中,这样就可以在任意路径下直接使用bin目录下的exe程序了。
配置的参数为操作系统中的path环境变量,该变量的用途是系统查找可执行程序所在的路径。
配置步骤为:
1、“开始”>“设置”>“控制面板”>“系统”
如果控制面板的设置不是经典方式,那么可以在控制面板的“性能和维护”中找到“系统”。
当然,也可以选择桌面上的“我的电脑”,点击鼠标右键,选择“属性”打开。
2、在“系统属性”窗口中,选择“高级”属性页中的“环境变量”按钮。
3、在“环境变量”窗口中,选择“系统变量”中变量名为“Path”的环境变量,双击该变量。
4、把JDK安装路径中bin目录的绝对路径,添加到Path变量的值中,并使用半角的分号和已有的路径进行分隔。
例如JDK的安装路径下的bin路径是C:\Program Files\Java\jdk1.6.0_04\bin,则把该路径添加到Path值的起始位置,则值为:C:\Program Files\Java\jdk1.6.0_04\bin;C:\Program Files\PC Connectivity Solution\;C:\Program Files\Java\jdk1.6.0_04\bin;C:\j2sdk1.4.2_11\bin;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem
以上路径在不同的计算机中可能不同。
配置完成以后,可以使用如下格式来测试配置是否成功:
1、 打开“开始”>“程序”>“附件”>“命令提示符”
2、 在“命令提示符”窗口中,输入javac,按回车执行
如果输出的内容是使用说明,则说明配置成功。
如果输出的内容是“’javac’不是内部或外部命令,也不是可执行的程序或批处理文件。”,则说明配置错误,需要重新进行配置。
常见的配置错误为:
1) 路径错误,路径应该类似C:\Program Files\Java\jdk1.6.0_04\bin。
2) 分隔的分号错误,例如错误的打成冒号或使用全角的分号。
2.1.4 第一个HelloWorld程序
对于初学者来说,第一个HelloWorld程序太神秘,也太难了,因为需要在第一个程序中学到很多的操作,而且在操作的过程中,即使是一个很小的错误,都可以让初学者束手无策,所以学习第一个HelloWorld程序,一点都不简单,也需要足够的细心和耐心。
在本节中,将通过第一个HelloWorld程序学习如下内容:
<!--[if !supportLists]-->l <!--[endif]-->如何编辑代码
<!--[if !supportLists]-->l <!--[endif]-->如何保存代码
<!--[if !supportLists]-->l <!--[endif]-->如何编译代码
<!--[if !supportLists]-->l <!--[endif]-->如何运行程序
2.1.4.1 如何编辑代码
由于JDK没有提供代码编辑环境,所以使用JDK进行Java程序开发时,还需要一个编辑代码的软件。
Java源代码可以在任意文本编辑中进行,例如Windows自带的记事本等,下面介绍如何在记事本编辑代码。
打开记事本程序的步骤如下:“开始”>“程序”>“附件”>“记事本”。
然后就可以在新打开的记事本内部进行代码编辑了。
下面是一个Java语言的HelloWorld程序:
public class HelloWorld{
public static void main(String[] args){
System.out.println("Hello world!");
}
}
在编辑代码时,注意以下问题:
<!--[if !supportLists]-->l <!--[endif]-->源代码中的字母区分大小写
<!--[if !supportLists]-->l <!--[endif]-->标点符号均为半角字符,即英文输入模式下的标点符号
<!--[if !supportLists]-->l <!--[endif]-->缩进只是为了美观,在实际编译时,会删除代码中每行开始和每行结束的所有空格
2.1.4.2 如何保存代码
Java语言的源文件,后缀名必须是java,不区分大小写,通常都作成小写。
对于源代码的文件名,如果源代码中声明时使用public,格式如下:
public class HelloWorld
则源代码的名称必须和class后面的单词相同,也就是必须为HelloWorld,这个名称区分大小写。也就是文件的全名必须为HelloWorld.java。
如果源代码声明时未使用public,格式如下:
class HelloWorld
则对于源代码的名称没有要求,但是后缀名还必须是java。
通常情况下,一个源代码只有一个声明,而且把这个声明作成public的。
在实际保存时,对于源代码的保存路径没有具体的要求,也就是可以保存到任意路径下,为了后续的说明方便,把该代码保存在d:\java\chapter2文件夹中,后续的说明和该路径保持一致。
保存以后,在对应路径下就将有该文件,具体现实的图标和计算机中安装的软件有关,但是这个图标不影响实际的使用。
则在记事本中保存代码的操作步骤如下:
<!--[if !supportLists]-->l <!--[endif]-->选择记事本的“文件”>“保存”
<!--[if !supportLists]-->l <!--[endif]-->选择d:\java\chapter2路径
<!--[if !supportLists]-->l <!--[endif]-->保存类型设置为“所有文件”
<!--[if !supportLists]-->l <!--[endif]-->文件名为“HelloWorld.java”
<!--[if !supportLists]-->l <!--[endif]-->选择“保存”按钮保存即可。
2.1.4.3 如何编译代码
源文件保存好以后,就可以使用JDK中的编译工具来编译程序了。
如前所述,编译是将程序的源代码转换成该程序的可执行文件。Java语言的可执行文件是后缀名为class的文件,称作类文件或字节码文件。
编译时使用JDK中bin目录下的javac.exe文件来实现。
在命令行下编译程序的步骤如下:
<!--[if !supportLists]-->l <!--[endif]-->打开命令提示符窗口
选择“开始”>“程序”>“附件”>“命令提示符”。
也可以选择“开始”>“运行”,则输入框中输入“cmd”命令打开该窗口。
<!--[if !supportLists]-->l <!--[endif]-->切换到源代码保存目录。
在命令行中输入d:,按回车键切换到d盘
然后输入cd java\chapter2切换到源代码的保存路径。
则命令提示符中的提示是:“D:\java\chapter2>”。
<!--[if !supportLists]-->l <!--[endif]-->输入编译命令。
命令格式为: javac 源文件全名
例如:javac HelloWorld.java
按回车键执行该命令,如果没有任何提示,则代表编译成功,在源代码所在目录下生成HelloWorld.class文件,如果有一系列提示,则代表有语法错误,可以仔细检查代码书写是否有错,以及文件名或者路径是否有错。
注意:源文件全名区分大小写。
编译正确以后,则得到HelloWorld.java对应的可执行文件HelloWorld.class文件。
说明:javac命令的参数说明,可以在命令提示符窗口中直接输入javac,按回车键查看。
小技巧:可以通过如下方式设置命令提示符窗口的默认路径:
<!--[if !supportLists]-->l <!--[endif]-->选择“开始”>“程序”>“附件”>“命令提示符”菜单,在“附件”的“命令提示符”图标点击右键,打开“属性”菜单。
<!--[if !supportLists]-->l <!--[endif]-->修改“命令提示符 属性”窗口中“快捷方式”属性页中“目标”的值为需要设置的路径即可。
这样,以后打开命令提示符时,直接就切换到了该路径下。
2.1.4.4 如何运行程序
编译成功以后,就可以使用JDK中bin下的运行命令java.exe,运行生成的程序了。运行时,执行的是class文件。
运行的步骤如下:
<!--[if !supportLists]-->1、 <!--[endif]-->切换到class文件所在的目录。
操作方式参看2.1.4.3中的说明
<!--[if !supportLists]-->2、 <!--[endif]-->输入运行命令
命令格式:java class文件名(不带后缀名)
例如: java HelloWorld
按回车键执行该命令即可。
注意:class文件名区分大小写,且不能带class后缀名。
这样,你就可以在命令提示符窗口中,看到HelloWorld程序的执行结果了,也就是在命令提示符窗口中输出一行字符:
Hello world!
这就是使用JDK进行HelloWorld程序开发的完整步骤。
2.2 Eclipse使用
Eclipse是一个开源、免费的集成开发工具。
Eclipse是实现开发中的主流开发工具之一,熟练使用该工具将在学习,以及以后的实际开发中让你如虎添翼。
如果把程序员类比成军队中的士兵的话,那么集成开发工具就是你手中的枪,你要对它足够的熟悉,并且足够熟练的使用它。
对于开发工具的学习,需要在学习中使用,在使用中学习。
2.2.1 Eclipse的获得
Eclipse的安装程序可以从其官方网站上免费下载,地址为:
http://www.eclipse.org
在下载时选择“Eclipse Classic”下载即可,其最新版本为3.3.2。
需要注意的是,在现在时一定要下载SDK,而且根据你的操作系统选择对应的版本,例如Windows平台上的文件默认是eclipse-SDK-3.3.2-win32.zip。
以下为Windows操作系统为例子来介绍Eclipse的安装。
2.2.2 Eclipse的安装
Eclipse是一个使用Java语言开发的工具软件,所以在安装Eclipse以前,一定要安装JDK,其中Eclipse3.3.2要求安装的JDK版本在1.5及以上。
Eclipse的安装很简单,只需要解压缩安装文件即可,解压缩的文件没有限制,可以根据实际使用的需要解压缩到任意路径下。
2.2.3 Eclipse基本使用
Eclipse安装完成以后,选择Eclipse安装目录下的eclipse.exe即可启动该软件。
2.2.3.1 工作空间设置
第一次启动Eclipse时,会弹出一个标题为“Workspace Launcher”的窗口,该窗口的功能是设置Eclipse的workspace(工作空间)。workspace是指Eclipse新建的内容默认的保存路径,以及Eclipse相关的个性设置信息。该窗口中“Workspace”输入框中是需要设置的路径,可以根据个人的需要进行设置,下面的“Use this as default and do not ask again”选择项的意思是:使用这个作为默认设置,以后不要再询问,选中以后的效果是:1、下次启动时不再弹出该窗口,2、把这个设置作为默认设置,不选中该选择项则每次启动时都弹出该窗口。设置完成以后,选择“OK”按钮,就可以启动Eclipse了。
2.2.3.2 显示主界面
Eclipse第一次启动起来以后,会显示一个欢迎界面,选择左上角“Welcome”右侧的“X”关闭欢迎界面,就可以看到Eclipse的主界面了。
欢迎界面只显示一次,以后只有在变更了工作空间以后才可能会再次显示。
关于Eclipse界面的布局方式,这里暂不介绍,因为介绍时不可避免的要用到一些后续要学到的专业术语,这里先简单介绍一下其使用方式,至于界面的布局方式这个在使用中逐渐去熟悉。
2.2.3.3 Eclipse基本使用
集成开发环境(IDE)的使用相对来说稍显繁琐,但是对于实际的项目开发来说却是非常实用的,在初次使用时,需要习惯和适应这种使用方式。
集成开发环境在使用前,需要首先建立Project(项目),Project是一个管理结构,管理一个项目内部的所有源代码和资源文件,并保存和项目相关的设置信息。
一个项目内部可以有任意多个源文件,以及任意多的资源。
使用Eclipse的基础步骤主要有如下这些:
l 新建项目
l 新建源文件
l 编辑和保存源文件
l 运行程序
2.2.3.3.1 新建项目
新建项目的步骤如下:
1、 选择菜单“File”>“New”>“Java Project”
2、 在“New Java Project”窗口中,进行新建项目的设定,例如输入Test
“Project Name”是必须输入的内容,代表项目名称,在硬盘上会转换成一个文件夹的名称。
“Content”设置项目的内容。
“JRE”部分设置项目使用的JDK版本。
“Project layout”部分设置项目文件内部的目录结构。
3、 选择“Finish”按钮完成设置
项目建立以后,可以到磁盘对应路径下观察一下项目文件夹的结构。
2.2.3.3.2 新建源文件
项目建立以后,或者打开项目以后,就可以新建源文件了。
一个项目中可以包含多个源文件,每个源文件都可以独立执行。
新建源文件的步骤为:
1、 选择菜单“File”>“New”>“Class”
2、 在“New Java Class”向导中,进行新建源文件的设定
“Source folder”代表源代码目录,例如“test/src”,如果该内容和项目保持一致则不需要修改,否则可以选择后续的“Browse…”按钮进行修改。
“Name”代表源文件的名称,例如输入Hello。
“public static void main(String[] args)”选项代表在生成的源代码中包含该代码,选中该设置。
3、 选择“Finish”按钮完成设置,则Eclipse将自动生成符合要求的源代码,并在Eclipse环境中打开。
生成的代码如下:
Public class Hello {
/**
*@paramargs
*/
Public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
2.2.3.3.3 编辑和保存源文件
可以把其中的用于说明的注释内容删除,并添加输出字符串的代码,则代表变为如下内容:
Public class Hello {
Public static void main(String[] args) {
System.out.println("Hello World!");
}
}
选择工具栏的保存按钮,或者按Ctrl+S组合键保存源文件,在源文件保存时,Eclipse会自动编译该代码,如果有语法错误,则以红色波浪线进行提示。
2.2.3.3.4 运行程序
运行程序的方法为:选择源代码空白处,点击右键,选择“Run as”>“1 Java Application”即可运行,当然,你也可以选择Eclipse左侧你需要运行的文件名,点击右键,也可以找到一样的菜单进行运行。
这样,程序的运行结果就显示在该界面中了。
2.2.4 Eclipse基础使用进阶
熟悉了Eclipse基本的使用以后,下面再补充一下Eclipse其它常见的操作,主要包含以下一些技巧:
l 分类项目目录
l 打开项目
l 添加JDK
l 添加源代码
l 快捷键速查
2.2.4.1 分类项目目录
在默认的Eclipse项目目录下,源代码和class文件都存储在项目根目录下,这样项目目录显得比较凌乱,可以通过以下设置来设置项目目录的结构:
菜单“Window”>“Preferences…”>“Java”>“Build Path”,选择右侧的“Folders”选项,设置“Source folder name”源代码目录名称为src,“Output folder name”输出目录名称为classes,选择“OK”按钮完成设置。
这样在新建eclipse项目时,将在项目目录下自动新建src和classes这两个文件夹,并将新建的源代码文件默认存储在项目目录下的src目录中,将生成的class文件默认存储在项目目录下的classes目录中。
2.2.4.2 打开项目
在日常的使用中,经常需要打开已有的项目,在eclipse中打开已有的eclipse项目的操作步骤为:
菜单“File”>“Import”>“General”>“Existing Projects into Workspace”,选择“Next”按钮打开导入窗口,选择“Select root directory”后面的“Browse…”按钮选择项目的根目录,此时项目名称会现在导入窗口下面的空白区域内,选择“Finish”按钮,即可完成打开项目的操作。
2.2.4.3 添加JDK
默认的Eclipse中只集成一个JDK,如果在新建项目时需要不同版本的JDK,则需要首先在Eclipse中集成对应的JDK,然后才可以在新建项目时进行选择。
添加新的JDK到Eclipse中的步骤如下:
菜单“Window”>“Preferences…”>“Java”>“Installed JREs”,选择右侧的“Add”按钮,然后选择弹出窗口中的“JRE home directory”后的“Browse…”按钮选择需要添加的JDK安装目录的根目录,例如C:\j2sdk1.4.2_11,选择“OK”完成添加即可。
2.2.4.4 添加源代码
将已有的java源代码添加到已有的项目中,这样才可以在eclipse中运行该程序,添加的步骤为:
1、 复制该文件,而非该文件的内容
2、 选择Eclipse界面左侧的项目列表中,对应项目的源代码目录,例如Hello项目下的src目录
3、 选择ctrl+v粘贴即可
2.2.4.5 快捷键速查
为了方便对于eclipse的操作,Eclipse提供了常见快捷键的功能和列表,可以选择菜单“Help”>“ Key Assist”查看。
2.3 总结
开发环境配置和使用部分就简单的介绍这么,其实只掌握这些还满足不了实际开发的要求,还需要在使用中大量的进行学习,并熟练这些工具软件的使用。
第三章 Java基础语法
学习一个程序语言,首先需要学习该语言的格式,这个格式就是语言的语法。语法,对于初学者来说,是学习程序最枯燥的地方,因为语法部分需要记忆的内容太多,而且需要理解很多的知识。
而对于曾经接触过其他程序设计语言的人来说,学习语法的速度特别快,主要是因为语法部分涉及的很多概念已经理解,而且大部分语言的语法格式比较类似。
本章就来详细介绍一下Java语言的基础语法,希望能够通过本章的学习掌握Java语言的语法格式。
3.1 如何学好Java语法
对于初学者来说,学习Java语法是一个长期、艰苦的过程,所以要做好吃苦的准备,而且语法的学习会随着对于语言理解的加深,而体会到更多设计的巧妙。
语法格式只是学习程序时最基础的知识,在实际的开发中,需要根据程序的需要,使用恰当的格式去实现程序逻辑,所以语法一定要熟练。
学习语法主要有三种学习方式:
l 在理解的基础上记忆
这个是最理想的学习语法的方式,通过这样学习语法会觉得很轻松,而且对于语法的使用也把握的很准确。
对于这种方式,需要在学习的初期深刻理解语法的功能,体会语法适用的场合,记忆语法的实现格式。
但是在实际的学习中,由于初学者未接触过开发,很多的概念无法深刻理解,所以很多人还无法实现使用该方式来进行学习。
l 在记忆的基础上理解
使用这种方式,首先需要记忆住该语法格式,然后在记忆以及后续的练习中逐步体会语法的用途,这也是大部分初学者学习语法的方式。
通过这种方式学习语法,也可以在一定的锻炼以后成为合格的程序员。
l 在未理解的基础上记忆
这种方式是学习语法是最笨拙的方式,也是很多很努力学习,但是无法理解语法格式的学生。
使用这种方式其实没有真正理解语法的格式,而只是生硬的去进行记忆,很多时候还无法灵活的去运用这些格式,所以学习的效果也打了一定的折扣。
学习语法时,主要需要学习和理解以下这些内容:
l 语法的格式
这个必须进行记忆,熟记以后可以提高写代码的速度。
l 语法概念
理解相关的语法概念,例如变量、方法、数组等等
l 语法的用途
语法的适用领域。
l 大量练习
通过大量的练习深刻体会语法的使用。
关于语法的学习就介绍这么多,下面就进入设计巧妙的Java语法世界开始正式的学习了
关于语法的学习就介绍这么多,下面就进入设计巧妙的Java语法世界开始正式的学习了。
3.2 代码框架
“罗马不是一天建成的”,所以想只学习几天的语法或者一两周的语法就能很熟练的编程,是不实际的想法。说个极端的例子,你的英语学了多少年了,能很流利的与人交流和书写文章吗?当然,就程序语法来说,比英语简单多了。
在开始学习Java时,不可能把所有的语法都一下子介绍清楚,但是如果需要把程序正确的运行起来,那么还必须不少的语法知识,为了在学习的初期可以让自己编写的代码编译通过,并且能够执行,所以特提供一个简单的代码框架,方便大家初期的练习。
代码框架的结构如下:
public class 文件名{
public static void main(String[] args){
你的代码
}
}
使用该代码框架时,只需要把“文件名”的位置换成自己的文件名,并且在“你的代码”的位置写自己的代码即可,使用示例:
public class Hello{
public static void main(String[] args){
System.out.println(“Hello world!”);
}
}
则在该示例代码中,“文件名”被替换成了Hello,“你的代码”被替换成了System.out.println(“Hello world!”);,在后续的示例中,给出的代码片段,除非特别说明,都是应该写在“你的代码”位置的代码,后面就不专门声明了。
3.3 关键字
关键字(keyword),也称保留字(reserved word),是指程序代码中规定用途的单词。也就是说,只要在程序代码内部出现该单词,编译程序就认为是某种固定的用途。
关键字列表及中文解释如下,格式为:关键字(中文解释):
abstract(抽象的) continue(继续) for(当…的时候) new(新建) switch(转换)
assert(断言) default(默认) if(如果) package(打包) synchronized(同步)
boolean(布尔) do(做) goto(跳转到) private(私有的) this(这个)
break(中断) double(双精度) implements(实现) protected(受保护的) throw(抛出,动词)
byte(字节) else(否则) import(引入) public(公共的) throws(抛出,介词)
case(情形) enum(枚举) instanceof(是…的实例) return(返回) transient(瞬时的)
catch(捕获) extends(继承) int(整数) short(短整数) try(尝试)
char(字符) final(最终的) interface(接口) static(静态的) void(空的)
class(类) finally(最终地) long(长整数) strictfp(精确浮点) volatile(易变的)
const(常量) float(单精度浮点) native(本地的) super(超级的) while(当…的时候)
说明:其中goto和const的用途被保留,在语法中未使用到这两个关键字。
在实际学习时,必须牢记关键字的意义以及拼写。
后续学习的语法知识,大部分都是使用关键字和一些符号组成的,关键字的意义基本上就代表了该种语法格式的用途。
3.4 标识符
标识符,也就是标识的符号,指程序中一切自己指定的名称,例如后续语法中涉及到的变量名称、常量名称、数组名称、方法名称、参数名称、类名、接口名、对象名等等。
其实程序中除了一些分隔符号,如空格、括号和标点符号以外,只有三类名称:
l 关键字
l 系统功能名称
如System.out.println中的System、out和println。
l 标识符
对于英语不好的学习者来说,第一类和第二类都是需要熟悉和记忆的,而第三类名称,也就是标识符,可以由程序开发者自己进行设定。
通常情况下,为了提高程序的可读性,一般标识符的名称和该标识符的作用保持一致。
标识符的命名规则主要有如下几个要求:
1、 不能是关键字
2、 只能以字母、下划线(_)和美元符号($)开头
需要特别注意的是,标识符不能以数字字符开头。
3、 不能包含特殊字符,例如空格、括号和标点符号等等。
通常情况下,标识符一般全部是字母,或者使用字母和数字的组合。
3.5 基本数据类型
程序中最核心的就是一系列的数据,或者叫程序状态,计算机为了方便的管理数据,就为数据设定了一组类型,这样在为数据分配内存以及操作数据时都比较方便,这就是数据类型的由来。其实现实生活中也存在各种数据类型,例如数字型,字符型等,数字型又可以划分为整数型和小数型,只是没有很可以的划分罢了。
在数据类型中,最常用也是最基础的数据类型,被称作基本数据类型。可以使用这些类型的值来代表一些简单的状态。
3.5.1 概述
学习数据类型的目的就是在需要代表一个数值时,能够选择合适的类型。当然,有些时候好几种类型都适合,那就要看个人习惯了。
学习数据类型需要掌握每种数据类型的特征,以及对应的细节知识,这样会有助于对类型的选择。所以在初次学习时,需要记忆很多的内容
Java语言的基本数据类型总共有以下8种,下面是按照用途划分出的4个类别:
l 整数型:byte(字节型)、short(短整型)、int(整型)、long(长整型)
l 小数型:float(单精度浮点型)、double(双精度浮点型)
l 字符型
l 布尔型
3.5.2 整数型
整数型是一类代表整数值的类型。当需要代表一个整数的值时,可以根据需要从4种类型中挑选合适的,如果没有特殊要求的话,一般选择int类型。4种整数型区别主要在每个数据在内存中占用的空间大小和代表的数值的范围。具体说明参看下表:
整数型参数表
类型名称
关键字
占用空间(字节)
取值范围
默认值
字节型
byte
1
-27—27-1
0
短整型
short
2
-215—215-1
0
整型
int
4
-231—231-1
0
长整型
long
8
-263—263-1
0
说明:1、Java中的整数都是有符号数,也就是有正有负。
2、默认值指在特定的情况下才自动初始化,具体的情况后续将有叙述。
3、程序中的整数数值默认是int以及int以下类型,如果需要书写long型的值,则需要在数值后面添加字母L,大小写均可。
4、程序中默认整数是十进制数字,八进制数字以数字字符0开头,例如016、034等,十六进制数字以数字字符0和字母x(不区分大小写)开头,例如0xaf、0X12等。
3.5.3 小数型
小数型是一类代表小数值的类型。当需要代表一个小数的值时,可以根据需要从以下2种类型中挑选合适的。如果没有特殊要求,一般选择double类型。
由于小数的存储方式和整数不同,所以小数都有一定的精度,所以在计算机中运算时不够精确。根据精度和存储区间的不同,设计了两种小数类型,具体见下表:
小数型参数表
类型名称
关键字
占用空间(字节)
取值范围
默认值
单精度浮点型
float
4
-3.4E+38—3.4E+38
0.0f
双精度浮点型
double
8
-1.7E+308—1.7E+308
0.0
说明:1、取值范围以科学计数法形式进行描述。
2、在程序中,小数的运算速度要低于整数运算。
3、float类型的小数,需要在小数后加字母f,不区分大小写,例如1.01f。
3.5.4 字符型
字符型代表特定的某个字符,按照前面介绍的知识,计算机中都是以字符集的形式来保存字符的,所以字符型的值实际只是字符集中的编号,而不是实际代表的字符,由计算机完成从编号转换成对应字符的工作。
Java语言中为了更加方便国际化,使用Unicode字符集作为默认的字符集,该字符集包含各种语言中常见的字符。
在程序代码中,字符使用一对单引号加上需要表达的字符来标识,例如’A’、’a’等,当然也可以直接使用字符编码,也就是一个非负整数进行表示。
字符型参数表
类型名称
关键字
占用空间(字节)
取值范围
默认值
字符型
char
2
0-216-1
0
说明:1、字符型的编号中不包含负数。
2、字符型由于存储的是编号的数值,所以可以参与数学运算。
3、字符型可以作为Java语言中的无符号整数使用。
4、字符型的默认值是编号为0的字符,而不是字符0
3.5.5 布尔型
布尔型代表逻辑中的成立和不成立。Java语言中使用关键字true代表成立,false代表不成立。布尔型是存储逻辑值的类型,其实很多程序中都有逻辑值的概念,Java把逻辑的值用布尔型来进行表达。
布尔型参数表
类型名称
关键字
占用空间(字节)
取值范围
默认值
布尔型
boolean
true或false
false
说明: 1、布尔型占用的空间取决于Java虚拟机(JVM)的实现,可能是1位也可能是1个字节。
3.5.6 小结
这里简单的介绍了8种基本数据类型的基本特征,在实际的程序设计中,可以根据需要选择对应的类型。
由于Java语言是一种强类型的语言,所以在使用数据类型是需要小心。
3.6 变量和常量
在程序中存在大量的数据来代表程序的状态,其中有些数据在程序的运行过程中值会发生改变,有些数据在程序运行过程中值不能发生改变,这些数据在程序中分别被叫做变量和常量。
在实际的程序中,可以根据数据在程序运行中是否发生改变,来选择应该是使用变量代表还是常量代表。
3.6.1 变量
变量代表程序的状态。程序通过改变变量的值来改变整个程序的状态,或者说得更大一些,也就是实现程序的功能逻辑。
为了方便的引用变量的值,在程序中需要为变量设定一个名称,这就是变量名。例如在2D游戏程序中,需要代表人物的位置,则需要2个变量,一个是x坐标,一个是y坐标,在程序运行过程中,这两个变量的值会发生改变。
由于Java语言是一种强类型的语言,所以变量在使用以前必须首先声明,在程序中声明变量的语法格式如下:
数据类型 变量名称;
例如:int x;
在该语法格式中,数据类型可以是Java语言中任意的类型,包括前面介绍到的基本数据类型以及后续将要介绍的复合数据类型。变量名称是该变量的标识符,需要符合标识符的命名规则,在实际使用中,该名称一般和变量的用途对应,这样便于程序的阅读。数据类型和变量名称之间使用空格进行间隔,空格的个数不限,但是至少需要1个。语句使用“;”作为结束。
也可以在声明变量的同时,设定该变量的值,语法格式如下:
数据类型 变量名称 = 值;
例如:int x = 10;
在该语法格式中,前面的语法和上面介绍的内容一致,后续的“=”代表赋值,其中的“值”代表具体的数据。在该语法格式中,要求值的类型需要和声明变量的数据类型一致。
也可以一次声明多个相同类型的变量,语法格式如下:
数据类型 变量名称1,变量名称2,…变量名称n;
例如:int x,y,z;
在该语法格式中,变量名之间使用“,”分隔,这里的变量名称可以有任意多个。
也可以在声明多个变量时对变量进行赋值,语法格式如下:
数据类型 变量名称1=值1,变量名称2=值2,…变量名称n=值n;
例如:int x = 10,y=20,z=40;
也可以在声明变量时,有选择的进行赋值,例如:int x,y=10,z;
以上语法格式中,如果同时声明多个变量,则要求这些变量的类型必须相同,如果声明的变量类型不同,则只需要分开声明即可,例如:
int n = 3;
boolean b = true;
char c;
在程序中,变量的值代表程序的状态,在程序中可以通过变量名称来引用变量中存储的值,也可以为变量重新赋值。例如:
int n = 5;
n = 10;
在实际开发过程中,需要声明什么类型的变量,需要声明多少个变量,需要为变量赋什么数值,都根据程序逻辑决定,这里列举的只是表达的格式而已。
3.6.2 常量
常量代表程序运行过程中不能改变的值。
常量在程序运行过程中主要有2个作用:
l 代表常数,便于程序的修改
l 增强程序的可读性
常量的语法格式和变量类型,只需要在变量的语法格式前面添加关键字final即可。在Java编码规范中,要求常量名必须大写。
则常量的语法格式如下:
final 数据类型 常量名称 = 值;
final 数据类型常量名称1 = 值1, 常量名称2 = 值2,……常量名称n = 值n;
例如:
final double PI = 3.14;
final char MALE=’M’,FEMALE=’F’;
在Java语法中,常量也可以首先声明,然后再进行赋值,但是只能赋值一次,示例代码如下:
final int UP;
UP = 1;
常量的两种用途对应的示例代码分别如下:
l 代表常数
final double PI = 3.14;
int r =5;
double l = 2 * PI * r;
double s = PI * r * r;
在该示例代码中,常量PI代表数学上的∏值,也就是圆周率,这个是数学上的常数,后续的变量r代表半径,l代表圆的周长,s代表圆的面积。
则如果需要增加程序计算时的精度,则只需要修改PI的值3.14为3.1415926,重新编译程序,则后续的数值自动发生改变,这样使代码容易修改,便于维护。
l 增强程序的可读性
int direction;
final int UP = 1;
final int DOWN = 2;
final int LEFT = 3;
final int RIGHT = 4;
在该示例代码中,变量direction代表方向的值,后续的四个常量UP、DOWN、LEFT和RIGHT分辨代表上下左右,其数值分别是1、2、3和4,这样在程序阅读时,可以提高程序的可读性。
3.6.3 语句块
在程序中,使用一对大括号{}包含的内容叫做语句块,语句块之间可以互相嵌套,嵌套的层次没有限制,例如:
{
int a;
}
语句块的嵌套:
{
int b;
{
char c;
}
}
以上代码只是演示语法,没有什么逻辑意义。在后续的语法介绍中,还会有语句块的概念,就不再重复介绍了。
3.6.4 变量的作用范围
每个变量都有特定的作用范围,也叫做有效范围或作用域,只能在该范围内使用该变量,否则将提示语法错误。通常情况下,在一个作用范围内部,不能声明名称相同的变量。
变量的作用范围指从变量声明的位置开始,一直到变量声明所在的语句块结束的大括号为止。例如以下代码:
{
{
int a = 10;
a = 2;
}
char c;
}
在该代码中,变量a的作用范围即从第三行到第五行,变量c的作用范围即从第六行到第七行。
3.6.5 常量的作用范围
常量的作用范围和变量的作用范围规则完全一致。
3.6.6 总结
对于变量和常量的内容就介绍这么,下面是一个完整的代码,可以在JDK或Eclipse中进行编译和运行。代码文件名为VariableAndConst.java,示例代码如下:
public class VariableAndConst{
public static void main(String[] args){
int n = 0;
char c = ‘A’;
System.out.println(n);
n = 10;
System.out.println(n);
System.out.println(c);
}
}
说明:在该代码中,System.out.println的功能是输出变量中存储的值。
3.7 数据类型转换
Java语言是一种强类型的语言。强类型的语言有以下几个要求:
l 变量或常量必须有类型
要求声明变量或常量时必须声明类型,而且只能在声明以后才能使用。
l 赋值时类型必须一致
值的类型必须和变量或常量的类型完全一致。
l 运算时类型必须一致
参与运算的数据类型必须一致才能运算。
但是在实际的使用中,经常需要在不同类型的值之间进行操作,这就需要一种新的语法来适应这种需要,这个语法就是数据类型转换。
在数值处理这部分,计算机和现实的逻辑不太一样,对于现实来说,1和1.0没有什么区别,但是对于计算机来说,1是整数类型,而1.0是小数类型,其在内存中的存储方式以及占用的空间都不一样,所以类型转换在计算机内部是必须的。Java语言中的数据类型转换有两种:
l 自动类型转换
编译器自动完成类型转换,不需要在程序中编写代码。
l 强制类型转换
强制编译器进行类型转换,必须在程序中编写代码。
由于基本数据类型中boolean类型不是数字型,所以基本数据类型的转换是出了boolean类型以外的其它7种类型之间的转换。下面来具体介绍两种类型转换的规则、适用场合以及使用时需要注意的问题。
3.7.1 自动类型转换
自动类型转换,也称隐式类型转换,是指不需要书写代码,由系统自动完成的类型转换。由于实际开发中这样的类型转换很多,所以Java语言在设计时,没有为该操作设计语法,而是由JVM自动完成。
l 转换规则
从存储范围小的类型到存储范围大的类型。
具体规则为:
byte→short(char)→int→long→float→double
也就是说byte类型的变量可以自动转换为short类型,示例代码:
byte b = 10;
short sh = b;
这里在赋值时,JVM首先将b的值转换为short类型,然后再赋值给sh。
在类型转换时可以跳跃。示例代码:
byte b1 = 100;
int n = b1;
l 注意问题
在整数之间进行类型转换时,数值不发生改变,而将整数类型,特别是比较大的整数类型转换成小数类型时,由于存储方式不同,有可能存在数据精度的损失。
3.7.2 强制类型转换
强制类型转换,也称显式类型转换,是指必须书写代码才能完成的类型转换。该类类型转换很可能存在精度的损失,所以必须书写相应的代码,并且能够忍受该种损失时才进行该类型的转换。
l 转换规则
从存储范围大的类型到存储范围小的类型。
具体规则为:
double→float→long→int→short(char)→byte
语法格式为:
(转换到的类型)需要转换的值
示例代码:
double d = 3.10;
int n = (int)d;
这里将double类型的变量d强制转换成int类型,然后赋值给变量n。需要说明的是小数强制转换为整数,采用的是“去1法”,也就是无条件的舍弃小数点的所有数字,则以上转换出的结果是3。整数强制转换为整数时取数字的低位,例如int类型的变量转换为byte类型时,则只去int类型的低8位(也就是最后一个字节)的值。
示例代码:
int n = 123;
byte b = (byte)n;
int m = 1234;
byte b1 = (byte)m;
则b的值还是123,而b1的值为-46。b1的计算方法如下:m的值转换为二进制是10011010010,取该数字低8位的值作为b1的值,则b1的二进制值是11010010,按照机器数的规定,最高位是符号位,1代表负数,在计算机中负数存储的是补码,则该负数的原码是10101110,该值就是十进制的-46。
l 注意问题
强制类型转换通常都会存储精度的损失,所以使用时需要谨慎。
3.7.2 其它
后续的复合数据类型,如类和接口等,也存在类似的转换。
3.8 空白
在前面的内容中,已经介绍了在编写代码中,单词和单词之间需要使用空格进行间隔,至于空格的数量则不限制。
而实际的编码中,为了使代码的结构清晰,一般需要在代码的前面加入一定数量的空格,例如如下格式:
public class Blank{
public static void main(String[] args){
int n;
{
n = 10;
}
System.out.println(n);
}
}
在该代码中,除了第一行和最后一行外,每行都包含一定数量的空格,这种编码的格式称为代码缩进。
在实际代码中,只要存在包含关系,则代码就应该缩进,语句块是最典型的包含关系之一。说明:在编译时,每行开始的空格都会被忽略掉。
3.9 语句结束
Java语法中,语句以“;”作为行结束符,例如:
int n = 2;
通常情况下,代码中每行只写一句代码,但是也可以写多句,例如:
int n = 2; byte b = 10;
但是一般为了代码结构清楚,只在一行中书写一句代码。
有些时候代码本身比较长,则也可以把一句代码写在多行,而代码语句结束的地方书写一个“;”即可。
在实际代码中,一般大括号开始和结束的地方,以及大部分小括号结束的地方都不需要书写“;”来进行结束。
3.10 注释
注释(comment)是对代码功能和用途的说明。在程序中编写适当的注释,将使程序代码更容易阅读,增强代码的可维护性。
注释在程序编译时都会被忽略,所以注释不会增加class文件的大小。
Java语言中注释的语法有三种:单行注释、多行注释和文档注释。
3.10.1 单行注释
单行注释指只能书写一行的注释。单行注释属于注释中最简单的语法格式,用于对于代码进行简单的说明,例如变量功能等。
单行注释的语法格式为:
//注释内容
注释以两个斜线开始,该行后续的内容都是注释的内容,注释内容不能换行。
单行注释一般书写在需要说明的代码的上一行,或者该行代码的结束处,示例结构如下:
//循环变量
int i = 0;
char sex; //性别
3.10.2 多行注释
多行注释指可以书写任意多行的注释。多行注释一般用于说明比较复杂的内容,例如程序逻辑或算法实现原理等。
多行注释的语法格式为:
/*
注释内容
*/
注释以一个斜线和一个星号开始,后续是注释内容,注释内容可以书写任意多行,最后注释以一个星号和一个斜线结尾。
很多多行注释在每行以星号开始,这个不是语法必需的。
3.10.3 文档注释
文档注释指可以被提取出来形成程序文档的注释格式,这是Java语言有特色的注释格式。一般对于程序程序的结构进行说明,例如类、属性、方法和构造方法进行说明,这些概念在后续将详细介绍。
文档注释的语法格式为:
/**
注释内容
*/
注释以一个斜线和两个星号开始,后续是注释内容,注释内容可以书写任意多行,最后注释以一个星号和一个斜线结尾。
在后续内容中还将完善该注释的语法格式。
3.10.4 其它
在规范的代码中,一般有10%-20%的注释,也就是每100行代码中包含10-20行注释的内容,但是国内很多开发公司都远远达不到这个要求。
另外需要特别注意的是,在实际的项目开发中,在修改代码后,一定要对应的修改注释的内容,保持代码和注释内容的同步。
第四章 运算符
计算机,顾名思义,就是计算的机器,所以在编程中,也要进行大量的计算(运算),运算的符号简称为运算符。
由于计算机可以进行各种运算,所以提供了很多的运算符号,这些运算符号一部分是现实里经常使用的,也有不少是计算机中新增的。
学习运算符,首先要掌握每种运算的运算规则,然后在适当的时候使用对应的运算符。这需要对于运算符最够的熟悉,并具备一定的计算机基础知识。
运算符的种类很多,为了方便学习,以下按照类别来进行介绍。
4.1 算术运算符
算术运算符,也称数学运算符,是指进行算术运算的符号,语法中对应的符号、功能以及说明参看下表
表4-1 算术运算符
符号
名称
功能说明
+
加
加法运算
-
减
减法运算
*
乘
乘法运算
/
除
除法运算
%
取余
求两个数字相除的余数
在算术运算符中,+、-、*和/的运算规则和数学基本相同,在四则运算中,乘除优先于加减,计算时按照从左向右的顺序计算,不同的地方在于:
l 程序中乘号不能省略,在数学上可以写y = 2x,但是程序中必须写成y=2 *x。
l 运算结果的类型和参与运算的类型中最高的类型一致,例如整数加整数还是整数。影响最大的是除法,整数除整数结果还是整数,例如10/3的结果是3,而不是3.333。
接着来说说取余运算符,%的功能是取两个数字相除的余数,例如10%3表示计算10除以3的余数,则结果应该是1。取余运算在编程中的用途也比较大,比较常见的用途有:控制规则变化,控制随机数字的区间等。
算术运算符基本使用的示例代码如下:
int n = 3 + 5;
int a = 10;
int b = 20;
int c = a * b;
double d = 100.2;
double d1 = d + a;
在算术运算符部分,需要特别注意的一个语法现象是“晋升”。晋升指低于int的3种数字类型(byte、short和char)进行算术运算后,结果会自动提升成int类型。示例代码如下:
byte b1 = 10;
byte b2 = 20;
byte b3 = b1 + b2; //语法错误,类型不匹配
int n = b1 + b2; //或者 byte b3 = (byte)(b1 + b2);
在程序中使用算术运算符实现程序中的数学运算,在运算时也可以加入小括号,和数学一样,在程序中也是先计算小括号内部的,然后再计算小括号外部的内容,示例代码如下:
int a = 1;
int b = 2;
int c = 3;
int d = c * (a + b) + c;
另外一个需要注意的就是,变量在计算时必须被赋值,否则直接报语法错误,例如:
int n;
int m = 2 * n;
4.2 比较运算符
比较运算符实现数据之间大小或相等的比较。
比较运算符运算的结果是一个boolean类型的值,如果比较结果成立则为true,否则为false。
Java语言中比较运算符的表示和功能见下表4-2。
表4-2 比较运算符
符号
名称
功能说明
>
大于
比较左侧数字是否大于右侧数字
<
小于
比较左侧数字是否小于右侧数字
>=
大于等于
比较左侧数字是否大于或等于右侧数字
<=
小于等于
比较左侧数字是否小于或等于右侧数字
==
等于
比较左侧数字是否等于右侧数字
!=
不等于
比较左侧数字是否不等于右侧数字
比较运算符的运算规则和现实中的规则一样。需要注意的问题主要有以下几个:
l boolean类型只能比较相等和不相等,不能比较大小。
l >=的意思是大于或等于,两者成立一个即可,所以5>=5成立。
l 在数学上表示的区间[1,10),也就是数字大于等于1同时小于10,在程序中不能写成如下格式:1<=n<10,这种书写在语法上是错误的,如果需要表达这种区间,则参看4.3逻辑运算符实现部分。
l 判断相等的符号是两个等号,而不是一个等号,这个需要特别小心。
比较运算使用的示例代码如下:
int a = 10;
boolean b = (a > 3); //该条件成立,则将值true赋值给变量b
boolean c = (b == true); //条件成立,结果为true
在实际代码中,数值、变量以及运算结果都可以直接参与比较,只是程序中为了增强可读性,有些时候需要将比较分开进行书写。
比较运算符是程序设计中实现数据比较的基础,也是很多逻辑实现的基础,在程序逻辑中,经常通过比较一定的条件,来判断后续的程序该如何执行。
4.3 逻辑运算符
逻辑运算符是指进行逻辑运算的符号。逻辑运算主要包括与(and)、或(or)和非(not)三种,在程序中主要用来连接多个条件,从而形成更加复杂的条件。
逻辑运算符的运算结果是boolean类型。
参与逻辑运算的数据也必须是boolean类型。
关于逻辑运算符的种类和说明参看表4-3。
表4-3 逻辑运算符
符号
名称
功能说明
&&
逻辑与
两个条件同时为true才为true,否则为false
||
逻辑或
两个条件有一个为true则为true,否则为false
!
逻辑非
只操作一个数据,对数据取反
逻辑运算符使用示例代码:
boolean b1 = true;
boolean b2 = false;
boolean b3 = b1 && b2; //则b3的值是false
b3 = b1 || b2; //则b3的值是true
b3 = !b1; //则b3的值是false
在实际程序中,可以根据逻辑的需要使用对应的逻辑运算符号。实际使用示例:
l 表示变量n是否属于[0,10)区间
int n = 4;
boolean b = (n >=0) && (n < 10);
对于变量n来说,只要n同时满足大于等于零,且小于10,则位于[0,10)区间,由于程序中无法书写0<=n<10这样的条件,则必须通过逻辑运算符进行连接。
l 表示变量n不属于[0,10)区间
一种写法是:
int n = 4;
boolean b = !((n >= 0) && (n < 10));
这里,对于属于该区间的条件取反,则可以获得不属于该区间的条件。
另一种写法是:
int n = 4;
boolean b = (n < 0) || (n >= 10);
这里做了一个简单的变通,如果变量n不属于该区间,则在数学上只需要满足n小于0或者n大于等于10的任何一个条件即可,这样的或者关系在程序中实现时使用逻辑或实现。
在程序设计中,根据逻辑需要,使用对应的逻辑运算符,可以实现相对比较复杂的组合条件,从而实现对应程序的功能。
最后说一下&&和&的区别,其实在进行逻辑与运算时,既可以使用&&也可以使用&,在功能上本身没有区别。两者区别的位置在,对于&来说,如果左侧条件为false,也会计算右侧条件的值,而对于&&来说,如果左侧的条件为false,则不计算右侧的条件,这种现象被称作短路现象。
示例代码:
int n = -1;
boolean b1 = (n >= 0) && (n < 10);
boolean b2 = (n >= 0) & (n < 10);
则对于第二行代码来说,两个条件都将被计算,而对于第三行代码来说,因为n >= 0这个条件不成立,则n < 10根本不会被执行。当然,两者得到的最终结果是一样的。
对于现在的代码来说,区别不大,但是如果后续的条件是一个方法(方法的概念后续将介绍到),则将影响程序逻辑。
验证&和&&功能的示例代码如下:
public class Test{
public static void main(String[] args){
int n = 10;
boolean b = (n < 8) && ((n = 1) != 0);
int m = 20;
boolean b1 = (m < 8) & ((m = 1) != 0);
System.out.println(n);
System.out.println(m);
}
}
4.4 赋值运算符
赋值运算符是指为变量或常量指定数值的符号。最基本的赋值运算符是“=”。
由于Java语言是强类型的语言,所以赋值时要求类型必须匹配,如果类型不匹配时需要能自动转换为对应的类型,否则将报语法错误。示例代码:
byte b = 12; //类型匹配,直接赋值
int n = 10; //类型匹配,直接赋值
double d = 100; //类型不匹配,系统首先自动将100转换成100.0,然后赋值
char c = -100; //类型不匹配,无法自动转换,语法错误
需要强调的是,只能为变量和常量赋值,不能为运算式赋值,例如:
int a = 10;
int b = 2;
a + b = 100; //不能为运算式a + b赋值,语法错误
常量只能赋值一次,否则也将出现语法错误,例如:
final int N = 10;
N = 20; //常量只能赋值一次,语法错误
在基本的赋值运算符基础上,可以组合算术运算符,以及后续将学习的位运算符,从而组成复合赋值运算符。赋值运算符和算术运算符组成的复合赋值运算符如下表4-4所示。
表4-4 复合赋值运算符
符号
名称
功能说明
+=
加等
把变量加上右侧的值然后再赋值给自身
-=
减等
把变量减去右侧的值然后再赋值给自身
*=
乘等
把变量乘以右侧的值然后再赋值给自身
/=
除等
把变量除以右侧的值然后再赋值给自身
%=
取余等
把变量和右侧的值取余然后再赋值给自身
实际使用示例:
int n = 2;
n += 3;
说明:计算以前n的值是2,也就是把n + 3的值,也就是5再赋值给n,经过运算以后n的值为5,因为该代码只执行一次,所以不会导致循环。
依次类推,其它的复合赋值运算符也是这样:
int n = 10;
n -= 2; //则n的值是8
n *= 3; //因为n的初值是8,则n运算后的结果是24
n /= 5; //因为n的初值是24,则n运算后的值是4
注意:复合赋值运算不会改变结果的类型,所以在有些时候运算在逻辑上会出现错误,但是符合计算中数值的表述。例如:
byte b = 127;
b += 1;
System.out.println(b);
根据前面的介绍,byte类型的取值区间是-128~127,由于复合赋值运算符不改变结果的类型,则导致结果是-128,而不是128。原因如下:
l byte类型值127的机器数是01111111,0表示正数,后续的数值表示127
l 该数值加1后,得到的数值是10000000,二进制加法
l 而10000000在byte类型中恰好是-128的机器数表示形式
其它类型的符合运算符也可能存在类似的情况,使用时需要注意。
4.5 二进制运算符
由于计算机内部的数据都以二进制的形式存在,所以在Java语言中提供了直接操作二进制的运算符,这就是下面要讲解的位运算符和移位运算符。
使用二进制的运算符,可以直接在二进制的基础上对数字进行操作,执行的效率比一般的数学运算符高的多,该类运算符大量适用于网络编程、硬件编程等领域。
二进制运算符在数学上的意义比较有限。
在Java代码中,直接书写和输出的数值默认是十进制,Java代码中无法直接书写二进制数值,但是可以书写八进制和十六进制数字,八进制以数字0开头,例如016,十六进制以数字0和x开头,例如0x12,0xaf等等。
在计算二进制运算时,Java语言的执行环境(JRE)首先将十进制的数字转换为二进制,然后进行运算。如果输出结果的值,则数字会被转换成十六进制进行输出。
需要注意的是:
1、正数的机器数是原码,负数的机器数是补码,计算时需要小心。关于二进制和补码的计算可以参看《Java编程那些事儿7——进制的概念》和《Java编程那些事儿8——计算机内部的数据表达》。
2、整数型的计算结果都是int型,而不管是对byte还是short进行二进制运算。
4.5.1 位运算符
Java语言中的位运算符主要有4种:&(位与)、|(位或)、^(异或)和~(按位取反),下面依次介绍运算规则和使用示例。
l &(AND)
运算规则:参与运算的数字,低位对齐,高位不足的补零,对应的二进制位都为1,则运算结果为1,否则为0。
适用场合:屏蔽数字中某一位或某些位。因为任何数和0与都是0。
示例代码:
int a = 4;
int b = 10;
int c = a & b;
计算过程:
4的二进制形式为0000 0000 0000 0000 0000 0000 0000 0100
10的二进制形式为0000 0000 0000 0000 0000 0000 0000 1010
按照计算规则,结果为0000 0000 0000 0000 0000 0000 0000 0000
这个数字转换为十进制就是数字0
l | (OR)
运算规则:参与运算的数字,低位对齐,高位不足的补零,对应的二进制位有一个为1则为1,否则为0。
适用场合:将数字中某一位或某些位修改成1。因为1和任何数或都是1。
示例代码:
int a = 4;
int b = -10;
int c = a | b;
计算过程:
4的二进制形式为0000 0000 0000 0000 0000 0000 0000 0100
-10的二进制形式为1111 1111 1111 1111 1111 1111 1111 0110
按照计算规则,结果为1111 1111 1111 1111 1111 1111 1111 0110
这个二进制数转换为十进制就是数字-10。
l ^(XOR)
运算规则:参与运算的数字,低位对齐,高位不足的补零,对应的二进制位相同为零,不相同为1。
适用场合:判断数字对应的位是否相同。
示例代码:
int a = 4;
int b = 10;
int c = a ^ b;
计算过程:
4的二进制形式为0000 0000 0000 0000 0000 0000 0000 0100
10的二进制形式为0000 0000 0000 0000 0000 0000 0000 1010
按照计算规则,结果为0000 0000 0000 0000 0000 0000 0000 1110
这个数字转换为十进制就是数字14
l ~(NOT)
运算规则:只操作一个数字,将该数字中为1的位变成0,为0的位变成1。
适用场合:反转数字的内容
示例代码:
int a = 4;
int c = ~a;
计算过程:
4的二进制形式为0000 0000 0000 0000 0000 0000 0000 0100
按照计算规则,结果为1111 1111 1111 1111 1111 1111 1111 1011
这个数字转换为十进制就是数字-5。
其实位运算和实际的应该实现保持一致,也就是提供的电路级运算符号,每种运算符都有对应的电路实现。
实际使用简单示例:
l 把任意数字转换为正数
假设n是一个任意的整数,则把n转换为正数的代码为:
int m = n & 0x7fffffff;
l 判断任意数字倒数第三位的值是否为1
假设n是一个任意的整数,则判断的代码为:
int m = n & 0x4;
boolean b = (m != 0);
l 将任意数字倒数第四位置为1
假设n是一个任意的整数,则代码为:
int m = n | 0x8;
4.5.2 移位运算符
移位运算符就是在二进制的基础上对数字进行平移。按照平移的方向和填充数字的规则分为三种:<<(左移)、>>(带符号右移)和>>>(无符号右移)。
在移位运算时,byte、short和char类型移位后的结果会变成int类型,对于byte、short、char和int进行移位时,规定实际移动的次数是移动次数和32的余数,也就是移位33次和移位1次得到的结果相同。移动long型的数值时,规定实际移动的次数是移动次数和64的余数,也就是移动66次和移动2次得到的结果相同。
三种移位运算符的移动规则和使用如下所示:
l <<
运算规则:
按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。
语法格式:
需要移位的数字 << 移位的次数
例如: 3 << 2,则是将数字3左移2位
计算过程:
3 << 2
首先把3转换为二进制数字0000 0000 0000 0000 0000 0000 0000 0011,然后把该数字高位(左侧)的两个零移出,其他的数字都朝左平移2位,最后在低位(右侧)的两个空位补零。则得到的最终结果是0000 0000 0000 0000 0000 0000 0000 1100,则转换为十进制是12。
数学意义:
在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方。
l >>
运算规则:
按二进制形式把所有的数字向右移动对应位数,低位移出(舍弃),高位的空位补符号位,即正数补零,负数补1。
语法格式:
需要移位的数字 >> 移位的次数
例如11 >> 2,则是将数字11右移2位
计算过程:
11的二进制形式为:0000 0000 0000 0000 0000 0000 0000 1011,然后把低位的最后两个数字移出,因为该数字是正数,所以在高位补零。则得到的最终结果是0000 0000 0000 0000 0000 0000 0000 0010。转换为十进制是3。
数学意义:
右移一位相当于除2,右移n位相当于除以2的n次方。
l >>>
运算规则:
按二进制形式把所有的数字向右移动对应巍峨位数,低位移出(舍弃),高位的空位补零。对于正数来说和带符号右移相同,对于负数来说不同。
其他结构和>>相似。
4.5.3 小结
二进制运算符,包括位运算符和移位运算符,使程序员可以在二进制基础上操作数字,可以更有效的进行运算,并且可以以二进制的形式存储和转换数据,是实现网络协议解析以及加密等算法的基础。
但是,在实际使用中,为了使代码可读性强,还是大量使用一般的算术运算符来进行数字运算。
4.6 其它运算符
对于无法归类,或者单独占一类的运算符,将在下面进行介绍。
l ++、--
这两个运算符是程序中的递增和递减运算符。其意义参照以下示例代码:
int n = 0;
n++; // n = n + 1
System.out.println(n);
n++的意义就是对原来变量n的值加1以后再赋值给自身,因为原来变量n的值是0,加1以后则变成1。
同理,递减运算符的意义也是这样,示例代码:
int m = 0;
m--;
System.out.println(m);
m—的意义就是对原来变量m的值减1以后再赋值给自身,则m的值变成-1。
需要注意的是++和—只能操作变量,而不能操作其他的内容,以下使用都是错误的:
int a = 0;
int b = 0;
(a + b)++; //错误
final int M = 1;
M++; //错误
5++; //错误
在实际书写时,++和—既可以写在变量的前面,也可以写在变量的后面,例如:
int k = 0;
k++;
++k;
同理,--也可以这样,那么这些在实际使用中有什么区别呢?其实对于变量的值来说,没有区别,也就是++无论写后面还是写前面,变量的值肯定增加1,--无论写在后面还是前面,变量的值都减1。其最大的区别在于整个式子的值,如n++,规则如下:
1)++或—写在变量前面,则该式子的值等于变量变化以后的值。
2)++或—写在变量后面,则该式子的值等于变量变化以前的值。
示例代码如下:
int n = 1;
int m= 1;
n++; //n的值变为2
++m; //m的值变为2
int k = n++; //n的初始值是2,则n++的值是2,结果n的值为3,k的值为2
int j = ++m; //m的初始值是2,则++m的值是3,结果m的值是3,j的值为3
同理,--也是这样。
下面是一个稍微综合点的示例:
int a = 0;
int b = 0;
a = b++; //a为0,b为1
a = ++b; //a为2,b为2
b = a++; //a为3,b为2
a = ++b; //a为3,b为3
说明:注释部分为对应行代码运行以后,a和b的值。
在程序开发中,可以使用该区别简化代码的书写,但是不推荐这样做,因为这样将增加阅读代码的难度。
l +、-
前面介绍过加减运算符,其实+、-还有另外一个意义,也就是代表正负,通常情况下正号可以省略,而负号可以和数值、变量以及运算式进行结合,示例代码如下:
int a = 0;
int b = 1;
int c = -5;
c = -a;
c = -(a + b);
l ? :
这个运算符称为条件运算符,其作用是根据判断的结果获得对应的值,语法格式如下:
条件式 ? 值1 : 值2
语法要求条件式部分必须是boolean类型,可以是boolean值,也可以是boolean变量,或者是关系运算符或逻辑运算符形成的式子,值1和值2必须能够转换成相同的类型。
功能说明:如果条件式的结果是true,则整个式子的值取值1的值,否则取值2的值。示例代码如下:
int x = 10;
int y = 20;
int max = x > y ? x : y; //因为x大于y,则取变量x的值,然后赋值给max
int a = -10;
int abs = a > 0 ? a : -a; //实现求绝对值得功能
l ()
括号,也是运算符的一种,作用是可以让括号内部的计算首先进行,这个和数学上一致,只是程序代码中可以使用这个组合任意的合法运算式。示例代码为:
int a = 1 + 2 * 3;
int a = (1 + 2) * 3; //和以上代码的运行结果不一致
其实每个运算符都有自己的优先级,使用括号可以提升对应式子的优先级。关于运算符优先级的概念,后续将进行介绍。
4.7 运算符优先级
在实际的开发中,可能在一个运算符中出现多个运算符,那么计算时,就按照优先级级别的高低进行计算,级别高的运算符先运算,级别低的运算符后计算,具体运算符的优先级见下表:
运算符优先级表
优先级
运算符
结合性
1
() [] .
从左到右
2
! +(正) -(负) ~ ++ --
从右向左
3
* / %
从左向右
4
+(加) -(减)
从左向右
5
<< >> >>>
从左向右
6
< <= > >= instanceof
从左向右
7
== !=
从左向右
8
&(按位与)
从左向右
9
^
从左向右
10
|
从左向右
11
&&
从左向右
12
||
从左向右
13
?:
从右向左
14
= += -= *= /= %= &= |= ^= ~= <<= >>= >>>=
从右向左
说明:
1、 该表中优先级按照从高到低的顺序书写,也就是优先级为1的优先级最高,优先级14的优先级最低。
2、 结合性是指运算符结合的顺序,通常都是从左到右。从右向左的运算符最典型的就是负号,例如3+-4,则意义为3加-4,符号首先和运算符右侧的内容结合。
3、 instanceof作用是判断对象是否为某个类或接口类型,后续有详细介绍。
4、 注意区分正负号和加减号,以及按位与和逻辑与的区别
其实在实际的开发中,不需要去记忆运算符的优先级别,也不要刻意的使用运算符的优先级别,对于不清楚优先级的地方使用小括号去进行替代,示例代码:
int m = 12;
int n = m << 1 + 2;
int n = m << (1 + 2); //这样更直观
这样书写代码,更方便编写代码,也便于代码的阅读和维护。
4.8 表达式
由运算符和变量、常数或常量组成的式子称为表达式。例如2+3,a*b等。表达式是组成程序的基本单位,也是程序运算时的基本单位。
在程序代码内部,每个表达式都有自己对应的数据类型,具体参看下表:
表达式结果类型
序号
运算符
结果类型
1
算术运算符
数字型
位运算符
移位运算符
递增、递减运算符
2
比较运算符
布尔型
逻辑运算符
3
赋值运算符
和变量类型一致
4
条件运算符
和两个值中类型高的一致
对于序号1和2的运算符组成的表达式,由于比较直观,就不再单独举例了,对于3和4说明如下:
int n = 10;
int m = 10;
n =( m = 10); //则表达式m=10的类型是变量m的类型,也是m的值
boolean b = false;
boolean b1 = true;
b = (b1 = true); //则表达式b1 = true的类型是布尔型,值是true
double d;
d = 10 > 0 ? 1.0 : 2; //由于1.0是double型,2是整数型,则表达式的类型是double
对于由多个运算符组成的表达式,其最终的类型由最后一个运算符决定。
在实际的程序代码中,大部分的表达式不能单独成为代码中的一行,否则程序会提示语法错误,例如:
int a = 10;
int b = 20;
a + b; //不能单独成行
在表达式中,能够单独成行的运算符包括赋值运算符和递增、递减运算符。
4.9 总结
本部分系统的讲解了Java语言中运算符的知识,并且介绍了实际使用过程中需要注意的问题,在学习时需要熟记每种运算符的作用,然后在实际项目中根据需要使用对应的运算符来实现程序的功能。
第五章 流程控制
流程就是指程序执行的顺序,流程控制就是指通过控制程序执行的顺序实现要求的功能。流程控制部分是程序中语法和逻辑的结合,也是程序中最灵活的部分,是判断一个程序员能力的主要方面。
众所周知,算法是程序逻辑的核心,而算法的绝大部分代码都是流程控制实现的。
流程控制就是将程序员解决问题的思路,也就是解决问题的步骤,使用程序设计语言的语法格式描述出来的过程。
5.1 流程控制基础
流程控制在程序语言实现时,通过三类控制语句进行实现:
l 顺序语句
顺序语句是一种自然的语句,没有特定的语法格式,总体的执行流程就是先写的代码先执行,后写的代码后执行。
使用顺序语句时,需要特别注意代码的执行顺序。
l 条件语句
条件语句,又称判断语句或分支语句,在程序中有对应的语法格式,执行流程是根据条件是否成立,决定代码是否执行。如果条件成立,也就是true,则执行对应的代码,否则不执行。
使用条件语句时,需要特别注意条件是否准确以及功能部分的书写。
l 循环语句
循环语句是一种计算机内部特有的语句,指重复执行的代码,在程序中有对应的语法格式,执行的流程是每次判断条件是否成立,然后决定是否重复执行。循环语句是流程控制部分最灵活、最复杂,也是功能最强大的一类语句。
使用循环语句时,需要注意循环条件以及循环功能部分的书写。
在程序中,任意复杂的流程,都只通过以上三类语句的组合、嵌套来进行实现,所以在学习流程控制时,首先需要对于三类语句有个基础的认识,然后熟悉相关的语法,进行针对的练习,最后灵活使用这三类语句解决实际的问题。、
另外,需要强调的是,根据逻辑的需要,各种语句可以任意进行嵌套,也就是在一个语句的内部书写其它的语句,这样可以实现更加复杂的逻辑。
后续的讲解也按照该顺序进行,本部分会附带部分流程控制的练习。
5.2 顺序语句
顺序语句是流程控制语句中最简单的一类语句,在代码中没有语法格式,只需要根据逻辑的先后顺序依次书写即可,所以在实际书写以前,首先要思考清楚对应的逻辑顺序,然后再开始对应的书写。
需要注意的是,在实际的代码中,有些时候代码书写的先后会影响程序的逻辑,例如如下输出的代码。
示例代码1:
int n = 10;
n += 2;
System.out.println(n);
示例代码2:
int n = 10;
System.out.println(n);
n += 2;
则由于代码书写的顺序不同,即使相同的代码,示例代码1中输出的值是12,而示例代码2中输出的值是10。类似的逻辑在实际的项目中也有很多。
5.3 条件语句
条件语句,是程序中根据条件是否成立进行选择执行的一类语句,这类语句在实际使用中,难点在于如何准确的抽象条件。例如实现程序登录功能时,如果用户名和密码正确,则进入系统,否则弹出“密码错误”这样的提示框等。
本部分对于条件语句的介绍,重点在于语法讲解和基本的使用,更详细的使用参看后续的综合示例部分。
在Java语言中,条件语句主要有两类语法:if语句和switch语句。
5.3.1 if语句
if关键字中文意思是如果,其细致的语法归纳来说总共有三种:if语句、if-else语句和if-else if-else语句,下面分别进行介绍。
5.3.1.1 if语句
该类语句的语法格式为:
if(条件表达式)
功能代码;
语法说明:if是该语句中的关键字,后续紧跟一对小括号,该对小括号任何时候不能省略,小括号的内部是具体的条件,语法上要求该表达式结果为boolean类型。后续为功能的代码,也就是当条件成立时执行的代码,在程序书写时,一般为了直观的表达包含关系,功能代码一般需要缩进。
需要特别注意的是:
1、 这里的功能代码只能是一行,关于多行结构的功能代码,后续将说明。
2、 if(条件表达式)后续一般不书写分号
if语句的执行流程为:如果条件表达式成立,则执行功能代码,如果条件表达式不成立,则不执行后续的功能代码。
示例代码:
int a = 10;
if(a >= 0)
System.out.println(“a是正数”);
if( a % 2 == 0)
System.out.println(“a是偶数”);
在该示例代码中,第一个条件是判断变量a的值是否大于等于零,如果该条件成立则执行输出,第二个条件是判断变量a是否为偶数,如果成立也输出。
注意以下代码的执行流程:
int m = 20;
if( m > 20)
m += 20;
System.out.println(m);
按照前面的语法格式说明,只有m+=20;这行代码属于功能代码,而后续的输出语句和前面的条件形成顺序结构,所以该程序执行以后输出的结果为20。
如果当条件成立时,需要执行的语句有多句,则可以使用语句块来进行表述,语法格式如下:
if(条件表达式){
功能代码块;
}
使用这种语法格式,使用一个代码块来代替前面的功能代码,这样可以在代码块内部书写任意多行的代码,而且也使整个程序的逻辑比较清楚,所以在实际的代码编写中推荐使用该种逻辑。
5.3.1.2 if-else语句
if-else语句实现了封闭的条件,在程序中使用的更加常见。其中else关键字的作用是“否则”,即条件不成立的情况。
if-else语句的语法格式如下:
if(条件表达式)
功能代码1;
else
功能代码2;
语法说明:其中前面的部分和if语句一样,else部分后面是功能的代码,按照该语法格式,功能代码只能有一句。
执行顺序:如果条件成立,则执行if语句中的功能代码1,否则执行else中的功能代码2。
示例代码为:
int n = 12;
if(n % 2 != 0)
System.out.println(“n是奇数”);
else
System.out.println(“n不是奇数”);
则因为n%2的值是0,条件不成立,则执行else语句的代码,程序输出“n不是奇数”。
在实际使用时,为了结构清楚,以及可以在功能代码部分书写多行代码,一般把功能代码部分使用代码块,则语法格式为:
if(条件表达式){
功能代码块
}else{
功能代码块
}
当程序中有多个if时,else语句和最近的if匹配。示例代码:
if(条件1)
功能代码1;
if(条件2)
功能代码2;
else
功能代码3;
则这里的else语句和条件2对应的if语句匹配,前面的条件1是一个独立的语句。在实际代码中,可以使用大括号使整个程序的结构更加清楚。
对于if-else语句来说,因为if的条件和else的条件是互斥的,所以在实际执行中,只有一个语句中的功能代码会得到执行。
在实际开发中,有些公司在书写条件时,即使else语句中不书写代码,也要求必须书写else,这样可以让条件封闭。这个不是语法上必须的。
5.3.1.3 if-else if-else语句
在现实中,有些时候的条件不是一个,而是一组相关的条件,例如将阿拉伯数字转换为中文大写,根据分数转换为对应的等级等,都是多条件的结构,在程序中为了避免写多个if语句的结构,提供了一类专门的多分支语句,这就是if-else if-else语句。
if-else if-else语句的语法格式为:
if(条件1)
功能代码1;
else if(条件2)
功能代码2;
else if(条件3)
功能代码3;
……
else
功能代码;
语法说明:
1、 else if是else和if两个关键字,中间使用空格进行间隔。
2、 条件1到条件n都是boolean类型
3、 else if语句可以有任意多句
4、 最后的else语句为可选
5、 如果功能代码部分不是语句块,也就是不用大括号,就只能写一句。
执行流程:当条件1成立时,则执行功能代码1;当条件1不成立且条件2成立时,则执行功能代码2;如果条件1、条件2都不成立且条件3成立,则执行功能代码3,依次类推,如果所有条件都不成立,则执行else语句的功能代码。其执行流程的流程图如上所示。
下面是一个实现根据月份的值,输出该月份包含的日期数,2月份全部输出28,不考虑闰年的示例代码:
int month = 3;
int days = 0; //日期数
if(month == 1){
days = 31;
}else if(month == 2){
days = 28;
} else if(month == 3){
days = 31;
} else if(month == 4){
days = 30;
} else if(month == 5){
days = 31;
} else if(month == 6){
days = 30;
} else if(month == 7){
days = 31;
} else if(month == 8){
days = 31;
} else if(month == 9){
days = 30;
} else if(month == 10){
days = 31;
} else if(month == 11){
days = 30;
} else if(month == 12){
days = 31;
}
System.out.println(days);
再来看一个示例代码,该代码的功能是实现将百分制的成绩转换为A、B、C、D和E,代码如下:
int score = 87;
if(score >= 90){
System.out.println(‘A’);
} else if(score >= 80){
System.out.println(‘B’);
} else if(score >= 70){
System.out.println(‘C’);
} else if(score >= 60){
System.out.println(‘D’);
} else{
System.out.println(‘E’);
}
从该代码中可知,每个else if语句在书写时是有顺序的,在实际书写时,必须按照逻辑上的顺序进行书写,否则将出现逻辑错误。
if-else if-else语句是Java语言中提供的一个多分支条件语句,但是在判断某些问题时,会书写的比较麻烦,所以在语法中提供了另外一个语句——switch语句来更好的实现多分支语句的判别。
5.3.2 switch语句
switch关键字的中文意思是开关、转换的意思,switch语句在条件语句中特别适合做一组变量相等的判断,在结构上比if语句要清晰很多。
switch语句的语法格式为:
switch(表达式){
case 值1:
功能代码1;
[break;]
case 值2:
功能代码2;
[break;]
……
default:
功能代码1;
[break;]
}
语法说明:
1、 表达式的类型只能为byte、short、char和int这4种之一。
2、 值1、值2…值n只能为常数或常量,不能为变量。
3、 功能代码部分可以写任意多句。
4、 break关键字的意思是中断,指结束switch语句,break语句为可选。
5、 case语句可以有任意多句,是标号语句。
6、 default语句可以写在switch语句中的任意位置,功能类似于if语句中的else。
执行流程:当表达式的值和对应case语句后的值相同时,既从该位置开始向下执行,一直执行到switch语句的结束,在执行中,如果遇到break语句,则结束switch语句的执行。
则在if-else if-else语句中,根据月份获得每个月的天数,不考虑闰年,的示例代码如下:
int month = 10;
int days = 0;
switch(month){
case 1:
days = 31;
break;
case 2:
days = 28;
break;
case 3:
days = 31;
break;
case 4:
days = 30;
break;
case 5:
days = 31;
break;
case 6:
days = 30;
break;
case 7:
days = 31;
break;
case 8:
days = 31;
break;
case 9:
days = 30;
break;
case 10:
days = 31;
break;
case 11:
days = 30;
break;
case 12:
days = 31;
break;
}
System.out.println(days);
根据switch语句的语法,该代码也可以简化为如下格式:
int month = 10;
int days = 0;
switch(month){
case 2:
days = 28;
break;
case 4:
case 6:
case 9:
case 11:
days = 30;
break;
default:
days = 31;
}
System.out.println(days);
代码说明:因为switch语句每次比较的是相等关系,所以可以把功能相同的case语句合并起来,而且可以把其他的条件合并到default语句中,这样可以简化case语句的书写。该代码的结构比最初的代码简洁很多了。
虽然在语法上switch只能比较相等的结构,其实某些区间的判别也可以通过一定的变换使用switch语句进行实现。例如if-else if-else语句示例中的分数转换的示例,则分数的区间位于0-100之间,如果一个一个的去比较,case语句的数量会比较多,所以可以做一个简单的数字变换,只比较分数的十位及以上数字,这样数字的区间就缩小到了0-10,则实现的代码如下:
int score = 87;
switch(score / 10){
case 10:
case 9:
System.out.println(‘A’);
break;
case 8:
System.out.println(‘B’);
break;
case 7:
System.out.println(‘C’);
break;
case 6:
System.out.println(‘D’);
break;
default:
System.out.println(‘E’);
}
当然,switch语句不是很适合进行区间的判别,更多的区间判别一般还是使用if-else if-else语句进行实现。
5.3.3 小结
if语句可以实现程序中所有的条件,switch语句特别适合一系列点相等的判别,结构显得比较清晰,而且执行速度比if语句要稍微快一些,在实际的代码中,可以根据需要来使用对应的语句实现程序要求的逻辑功能。
5.4 循环语句
循环语句在程序设计中用来描述有规则重复的流程。在实际的程序中,存在很多需要重复执行的流程,为了简化这些重复的执行流程,在程序设计语言中新增了该类语句。
在学习循环语句时,最重要的就是发现流程的规律,然后再用程序设计语言将该规律描述出来,从来实现程序要求的流程。
循环语句是流程控制中最复杂,也是最有用、最难掌握的语句,在最初接触时,首先要熟悉基本的语法,然后需要能够快速观察出流程的规律,这个观察能力需要依靠大量的阅读和编写程序进行培养,这就是基本的逻辑思维,然后将该规律描述出来即可。所以在学习循环语句时,学习语法只是基本的内容,更多的是培养自己观察规律的能力,这个才是真正学习循环语句时的难点,也是重点。
本节主要讲述循环语句的三种语法格式:while语句、do-while语句和for语句。
5.4.1 while语句
while关键字的中文意思是“当……的时候”,也就是当条件成立时循环执行对应的代码。while语句是循环语句中基本的结构,语法格式比较简单。
while语句语法格式:
while(循环条件)
循环体;
为了结构清楚,并且使循环体部分可以书写多行代码,一般把循环体处理成代码块,则语法格式变为:
while(循环条件){
循环体;
}
语法说明:和if语句类似,如果不是用代码块的结构,则只有while后面的第一个语句是循环体语句。在该语法中,要求循环条件的类型为boolean类型,指循环成立的条件,循环体部分则是需要重复执行的代码。
执行流程:在执行while语句时,首先判断循环条件,如果循环条件为false,则直接执行while语句后续的代码,如果循环条件为true,则执行循环体代码,然后再判断循环条件,一直到循环条件不成立为止。
下面结合具体的示例来演示一下while语句的基本使用。首先我们来实现一个无限循环,也称死循环,具体代码如下:
while(true){
System.out.println(‘a’);
}
下面讲解一下该while语句的执行顺序,首先判断while语句的循环条件,条件成立,则执行循环体的代码,输出字符a,然后再判别循环条件,条件成立,继续执行循环体代码,输出a,再判断循环条件……,依次类推,因为循环条件一直成立,所以该程序的功能是一直输出a,永不停止。
说明:在控制台下执行死循环的程序,可以按Ctrl+C结束循环,在Eclipse中运行死循环的程序,可以选择执行窗口中的红色按钮“Terminate”结束程序。
下面是使用while语句输出0-9这10个数字,程序实现的原理是使用一个变量代表0-9之间的数字,每次输出该变量的值,每次对该变量的值加1。变量的值从0开始,只要小于数字10就执行该循环。具体的示例代码如下:
int i = 0;
while(i < 10){
System.out.println(i); //输出变量的值
i++; //变量的值增加1
}
其执行流程为:
1、 执行int I = 0;
2、 判断i<10是否成立,如果不成立则结束,否则执行下一步
3、 输出变量i的值
4、 i的值增加1
5、 跳转到步骤2继续执行
需要注意的是,首先while语句中的条件是循环成立的条件,也就是该条件成立则继续循环,所以在书写时注意。另外,内部代码的书写有顺序,同样是上面的代码,如果调整内部代码的顺序,如下所示:
int i = 0;
while(i < 10){
i++; //变量的值增加1
System.out.println(i); //输出变量的值
}
则程序的执行结果将变为输出数字1-10。所以在循环语句中,代码之间的顺序也影响整个程序的逻辑。
下面是用循环实现一个简单的数学逻辑,求1-10这10个数字的和。程序的原理是这样:声明一个变量i,从1变化到10,在声明一个变量sum,每次和i的值相加以后赋值给自身,下次再使用sum是变化以后的i相加,循环结束以后,得到的结果就是数字1-10之间所有数字的和。
示例代码如下:
int i = 1; //循环变量
int sum = 0; //数字和
while(i <= 10){
sum += i; //和当前的i值相加
i++; //变量i增加1
}
这样,第一次循环是把和1相加的结果赋值给sum,然后再使用sum的值和2相加再赋值给sum,依次类推,则得到1-10之间所有变量的和。
在使用循环语句时,发现规律需要的时间比编写和调试代码需要的时间多得多,所以要善于发现规律,善于思考。
5.4.2 do-while语句
do-while语句由关键字do和while组成,是循环语句中最典型的“先循环再判断”的流程控制结构,这个和其它2个循环语句都不相同。
do-while语句的语法格式为:
do{
循环体;
}while(循环条件);
语法说明:在do-while语句中,循环体部分是重复执行的代码部分,循环条件指循环成立的条件,要求循环条件是boolean类型,值为true时循环执行,否则循环结束,最后整个语句以分号结束。
执行流程:当执行到do-while语句时,首先执行循环体,然后再判断循环条件,如果循环条件不成立,则循环结束,如果循环条件成立,则继续执行循环体,循环体执行完成以后再判断循环条件,依次类推。
实现和while语句实现的类似的示例,则死循环的示例代码为:
do{
System.out.println(‘a’);
}while(true);
实现输出0-9这10个数字的循环为:
int i = 0;
do{
System.out.println(i); //输出变量的值
i++; //变量增加1
}while(i < 10);
实现求1-10这10个数字的和的代码为:
int i = 1;
int sum = 0;
do{
sum += i; //求和
i++; //变量增加1
}while(i < 10);
System.out.println(sum); //输出和
同理,实现求5的阶乘的代码类似,在数学上5的阶乘指1×2×3×4×5,数学上规定0的阶乘等于1,在实际计算时,阶乘的值增加的非常快,所以需要注意结果不能溢出。其具体代码为:
int i = 1;
int result = 1;
do{
result *= i;
i++;
}while(i <= 5);
System.out.println(result);
在实际的程序中,do-while的优势在于实现那些先循环再判断的逻辑,这个可以在一定程度上减少代码的重复,但是总体来说,do-while语句使用的频率没有其他的循环语句高。
5.4.3 for语句
for关键字的意思是“当…的时候”,是实际开发中比较常用的循环语句,其语法格式相对于前面的循环语句来说稍显复杂,但是在熟悉以后,将会发现其语法安排的比较条理,把循环控制和循环体很清晰的分开。
for语句的语法格式为:
for(初始化语句;循环条件;迭代语句){
循环体;
}
语法说明:
1、 和其它流程控制语句一样,语句中的大括号不是语法必须的,但是为了结构清楚以及在循环体部分可以书写多行代码,一般使用大括号。
2、 初始化语句作用是在循环开始以前执行,一般书写变量初始化的代码,例如循环变量的声明、赋值等。该语句可以为空。
3、 循环条件是循环成立的条件,要求必须为boolean类型,如果该条件为空,则默认为true,即条件成立。
4、 迭代语句是指循环变量变化的语句,一般书写i++、i—这样的结构,当然,该语句也可以为空
5、 循环体指循环重复执行的功能代码。
执行流程:
1、 执行初始化语句
2、 判断循环条件,如果循环条件为false,则结束循环,否则执行下一步
3、 执行循环体
4、 执行迭代语句
5、 跳转到步骤2重复执行
需要注意的是:for语句中的各个语句都可以为空,初始化语句在for语句执行时执行且只执行一次。
依据for语句的语法格式,则最简单的for语句是如下格式:
for(;;);
由于循环条件为空时,默认为true,则循环条件恒成立,该循环的循环体即最后的一个分号,这样的语句称作空语句,则该循环是一个死循环,循环体是空语句。
在实际书写代码时,一般把循环控制部分都写在for语句的小括号内部,而循环体只书写和逻辑相关的代码,这种结构使逻辑显得很清晰。
使用for语句输出的0-9之间数字的代码如下:
for(int i = 0;i < 10;i++){
System.out.println(i);
}
则该语句的执行流程为:
1、 执行int i = 0;
2、 判断i<10,如果条件不成立则结束,否则继续执行下一步
3、 执行System.out.println(i);
4、 执行i++
5、 跳转到步骤2继续执行
类似的示例代码,实现求1-100之间数字的和,代码如下:
int sum = 0;
for(int i = 1;i <= 100;i++){
sum +=i;
}
System.out.println(sum);
这些是一些基本的for语句的使用,在通常情况下,for语句和while语句之间可以实现很简单的转换,例如下面是一个使用for语句书写的while格式的代码:
int i = 0;
for(;i < 10;){
System.out.println(i);
i++;
}
关于for语句的深入使用请参看后续的综合示例部分的讲解。
5.4.4.小结
这里介绍了基本的循环控制语句的语法格式,在程序设计时,必须理解每种语句的语法格式和对应的特点,才能在实际使用时依据自己的逻辑进行灵活运用。
和前面的条件语句一样,在实际使用时,循环控制语句之间也可以进行相互的嵌套来解决复杂的逻辑,在语法上对于嵌套的层次没有限制。
while语句和for语句在循环语句中属于“先判断再循环”的结构,而do-while语句属于“先循环再判断”的结构,所以从语法角度来看,do-while语句的循环体至少会执行一次,在实际使用时while语句和for语句之间可以进行很方便的替换。
5.5 break和continue语句
break和continue语句是和循环语句紧密相关的两种语句。其中break关键字的意思是中断、打断,continue关键字的意思是继续。使用这两个关键字可以调节循环的执行。
5.5.1 break语句
break语句在前面的switch语句中已经介绍过,功能的话是中断switch语句的执行,在循环语句中,break语句的作用也是中断循环语句,也就是结束循环语句的执行。
break语句可以用在三种循环语句的内部,功能完全相同。下面以while语句为例来说明break语句的基本使用及其功能。
示例代码:
int i = 0;
while(i < 10){
i++;
if(i == 5){
break;
}
}
则该循环在变量i的值等于5时,满足条件,然后执行break语句,结束整个循环,接着执行循环后续的代码。
在循环语句中,可以使用break语句中断正在执行的循环。
在实际的代码中,结构往往会因为逻辑比较复杂,而存在循环语句的嵌套,如果break语句出现在循环嵌套的内部时,则只结束break语句所在的循环,对于其它的循环没有影响,示例代码如下:
for(int i = 0;i < 10;i++){
for(int j = 0;j < 5;j++){
System.out.println(j);
if(j == 3){
break;
}
}
}
则该break语句因为出现在循环变量为j的循环内部,则执行到break语句时,只中断循环变量为j的循环,而对循环变量为i的循环没有影响。
在上面的示例代码中,如果需要中断外部的循环,则可以使用语法提供的标签语句来标识循环的位置,然后跳出标签对应的循环。示例代码如下:
lable1:
for(int i = 0;i < 10;i++){
for(int j = 0;j < 5;j++){
System.out.println(j);
if(j == 3){
break label1;
}
}
}
说明:这里的label1是标签的名称,可以为Java语言中任意合法的标识符,标签语句必须和循环匹配使用,使用时书写在对应的循环语句的上面,标签语句以冒号结束。如果需要中断标签语句对应的循环时,采用break后面跟标签名的方式中断对应的循环。则在该示例代码中break语句中断的即循环变量为i的循环。
同样的功能也可以使用如下的逻辑实现:
boolean b = false;
for(int i = 0;i < 10;i++){
for(int j = 0;j < 5;j++){
System.out.println(j);
if(j == 3){
b = true;
break;
}
}
if(b){
break;
}
}
该示例代码中,通过组合使用2个break以及一个标识变量,实现跳出外部的循环结构。
5.5.2 continue语句
continue语句只能使用在循环语句内部,功能是跳过该次循环,继续执行下一次循环结构。在while和do-while语句中continue语句跳转到循环条件处开始继续执行,而在for语句中continue语句跳转到迭代语句处开始继续执行。
下面以while语句为例,来说明continue语句的功能,示例代码如下:
int i = 0;
while(i < 4){
i++;
if(i == 2){
continue;
}
System.out.println(i);
}
则该代码的执行结果是:
1
3
4
在变量i的值等于2时,执行continue语句,则后续未执行完成的循环体将被跳过,而直接进入下一次循环。
在实际的代码中,可以使用continue语句跳过循环中的某些内容。
和前面介绍的break语句类似,continue语句使用在循环嵌套的内部时,也只是跳过所在循环的结构,如果需要跳过外部的循环,则需要使用标签语句标识对应的循环结构。示例代码如下:
lable1:
for(int i = 0;i < 10;i++){
for(int j = 0;j < 5;j++){
System.out.println(j);
if(j == 3){
continue label1;
}
}
}
这样在执行continue语句时,就不再是跳转到j++语句,而是直接跳转到i++语句。
5.5.3 小结
在实际的代码中,可以根据需要使用break和continue语句调整循环语句的执行,break语句的功能是结束所在的循环,而continue语句的功能是跳过当次循环未执行的代码,直接执行下一次循环。
5.6 综合示例
在一般的学校学习流程控制时,重点是放在流程控制的相关语法,其实为了能成为一个合格的程序员,仅仅学好语法是远远不够的,还需要通过大量的练习来适应程序设计语言的思维方式,并且熟练地把自己的解决问题的步骤形成代码,这些都需要通过大量的阅读代码和编写代码来实现。
所以在学习流程控制时,重点是解决实际的问题,而不是仅仅停留在语法层面上,这个是很多在校学生学习程序时最突出的一个问题。
在遇到一个实际问题时,首先要能够思考出解决这个问题的数学步骤或逻辑步骤,然后才能编写对应的代码,所以遇到实际问题是,一定要积极思考,并且善于思考,对于一个相同的问题,不同的逻辑就可以写出不同的代码,所以在思考解决问题的方式时,需要进行发散性的思维,而这些理性的思维很多都是建立在数学基础以及对语法的熟悉基础之上。
下面,通过一系列的实际问题,来说明解决实际问题的步骤以及书写的对应的代码。
5.6.1 示例讲解
5.6.1.1 最大公约数
问题:求两个自然数的最大公约数。
这两个都是基础的数学问题,最大公约数指两个数字公共的约数中最大的,例如数字6的约数有1、2、3、6,数字9的约数有1、3、9,则数字6和数字9的公共约数有1和3,其中3是最大的公约数。
第一种思路:从1开始循环,每次把符合要求(即同时是两个数字的约数)的值都存储起来,那么最后一个存储起来的就是最大的约数。
则实现的代码如下:
int n = 6;
int m = 9;
int result = 1;
for(int i = 1;i <= n;i++){
if((n % i == 0) && (m % i == 0)){
result = i;
}
}
System.out.println(result);
使用该思路,每次都存储得到的公共约数,那么最后一个存储的就是两个数字的最大公约数。
第二种思路:从两个数字中最小的数字开始循环,每次减1,那么第一次得到的公共约数就是所求的最大公约数。
则实现的代码如下:
int n = 6;
int m = 9;
int result = n > m ?m : n;
for(int i = result;i >= 1;i--){
if((n % i == 0) && (m % i == 0)){
result = i;
break; //结束循环
}
}
System.out.println(result);
当然,解决这个问题,还有很多其它的方法,这里演示的这两种实现只是最自然的实现而已,采用类似的原理也可以求两个数字的最小公倍数的结构。
5.6.1.2 百元百鸡问题
问题描述:每只母鸡3元,每只公鸡4元,每只小鸡0.5元,如果花100元钱买100只鸡,请问有哪些可能?说明:每种鸡的数量都可以为零。
其实这个问题是数学上的组合问题,只需要把所有的情况列举出来,然后来判断是否符合要求即可。这样的重复列举的问题,在程序上可以使用循环进行解决。
第一种思路:当母鸡的数量为0时,公鸡的数量从0-100,当公鸡的数量每变化一次,小鸡的数量就从0变化到100,使用如下数值组合来描述这个思路:
母鸡数量 公鸡数量 小鸡数量
0 0 从0变化到100
0 1 从0变化到100
0 2 从0变化到100
……
1 0 从0变化到100
1 1 从0变化到100
……
100 100 100
上面列举出了所有公鸡、母鸡和小鸡的数量都是0-100时的所有组合,总计是101的三次方种,这样的穷举结构直接存在嵌套,在程序实际实现时,通过循环之间的嵌套就可以实现,则实现的代码如下:
for(int i = 0;i <= 100;i++){ //母鸡数量
for(int j = 0;j <= 100;j++){ //公鸡数量
for(int k = 0;k <= 100;k++){ //小鸡数量
//判断数量是否为100,以及金额是否为100
if((i +j + k == 100) && (i * 3 + j * 4 + k * 0.5 == 100)){
System.out.println(“母鸡数量:” + i + “ 公鸡数量:” + j + “ 小鸡数量” + k);
}
}
}
}
按照for语句的执行流程,循环变量变化1,则内部的循环执行一次,而在循环嵌套时,循环体又是一个新的循环,则该循环执行完成的一组循环。这里通过循环的嵌套实现了所有数值的穷举。在循环的内部,只需要按照题目要求判断一下数量和金额是否符合要求即可。
但是这样的代码效率比较差,可以通过简单的优化来提高程序的执行效率。
第二种思路:由于母鸡每只的金额是3元,所以100元最多购买的母鸡数量是100/3=33只,同理100元最多购买的公鸡数量是25只,而按照100元100只的要求,小鸡的数量应该为100减去公鸡和母鸡的数量,这样代码就可以简化为如下的结构:
for(int i = 0;i <= 33;i++){ //母鸡数量
for(int j = 0;j <= 25;j++){ //公鸡数量
int k = 100 –i – j; //小鸡数量
//判断金额是否为100
if (i * 3 + j * 4 + k * 0.5 == 100){
System.out.println(“母鸡数量:” + i + “ 公鸡数量:” + j + “ 小鸡数量” + k);
}
}
}
}
这样,就可以大幅提高程序的执行效率,从而提高程序的执行速度。当然该代码还可以继续进行优化,那样可以再次提供程序的执行效率。
5.6.1.3 喝汽水问题
问题:共有1000瓶汽水,每喝完后一瓶得到的一个空瓶子,每3个空瓶子又能换1瓶汽水,喝掉以后又得到一个空瓶子,问总共能喝多少瓶汽水,最后还剩余多少个空瓶子?
这个问题其实是个比较典型的递推问题,每3个空瓶都可以再换1瓶新的汽水,这样一直递推下去,直到最后不能换到汽水为止。
第一种思路:每次喝一瓶,每有三个空瓶子就去换一瓶新的汽水,直到最后没有汽水可以喝为止。在程序中记忆汽水的数量和空瓶子的数量即可。
则实现的代码如下:
int num = 1000; //汽水数量
int drinkNum = 0; //喝掉的汽水数量
int emptyNum = 0; //空瓶子的数量
while(num > 0){ //有汽水可以喝
num--; //喝掉一瓶
emptyNum++; //空瓶子数量增加1
drinkNum++; //喝掉的汽水数量增加1
if(emptyNum == 3){ //有3个空瓶子,则去换
num++; //汽水数量增加1
emptyNum = 0; //空瓶子数量清零
}
}
System.out.println(“总共喝掉瓶数:” + drinkNum);
System.out.println(“剩余空瓶子数:” + emptyNum);
执行该程序,输出结果如下:
总共喝掉瓶数:1499
剩余空瓶子数:2
在该代码中,每次循环喝掉一瓶汽水,则汽水数量减少1,空瓶子数增加1,喝掉的总汽水瓶数增加1,每次判断空瓶子的数量是否达到3,如果达到3则换1瓶汽水,同时空瓶子的数量变为零。这种思路比较直观,但是循环的次数比较多,所以就有了下面的逻辑实现。
第二种思路:一次把所有的汽水喝完,获得所有的空瓶子,再全部换成汽水,然后再一次全部喝完,再获得所有的空瓶子,依次类推,直到没有汽水可喝为止。
则实现的代码如下:
int num = 1000; //汽水数量
int drinkNum = 0; //喝掉的汽水数量
int emptyNum = 0; //空瓶子的数量
while(num > 0){ //有汽水可以喝
drinkNum += num; //喝掉所有的汽水
emptyNum += num; //空瓶子数量等于上次剩余的加上这次喝掉的数量
num = emptyNum / 3; //兑换的汽水数量
emptyNum -= num * 3; //本次兑换剩余的空瓶子数量
}
System.out.println(“总共喝掉瓶数:” + drinkNum);
System.out.println(“剩余空瓶子数:” + emptyNum);
在该代码中,每次喝掉所有的汽水,也就是num瓶,则喝掉的总瓶数每次增加num,因为每次都可能剩余空瓶子(不足3个的),则总的空瓶子数量是上次空瓶子数量加上本次喝掉的num瓶。接着是对话汽水,则每次可以兑换的汽水数量是空瓶子的数量的1/3,注意这里是整数除法,而本次兑换剩余的空瓶子数量是原来的空瓶子数量减去兑换得到汽水数量的3倍,这就是一次循环所完成的功能,依次类推即可解决该问题。
5.6.1.4水仙花数
问题:水仙花数指三位数中,每个数字的立方和和自身相等的数字,例如370,3 × 3 × 3 + 7 × 7 × 7 + 0 × 0 × 0 =370,请输出所有的水仙花数。
该问题中体现了一个基本的算法——数字拆分,需要把一个数中每位的数字拆分出来,然后才可以实现该逻辑。
实现思路:循环所有的三位数,拆分出三位数字的个位、十位和百位数字,判断3个数字的立方和是否等于自身。
则实现的代码如下所示:
for(int i = 100;i < 1000;i++){ //循环所有三位数
int a = i % 10; //个位数字
int b = (i / 10) % 10; //十位数字
int c = i / 100; //百位数字
//判断立方和等于自身
if(a * a * a + b * b * b + c * c * c == i){
System.out.println(i);
}
}
在该代码中,拆分个位数字使用i和10取余即可,拆分十位数字时首先用i除以十,去掉个位数字,并使原来的十位数字变成个位,然后和10取余即可,因为i是一个三位数,所以i除以100即可得百位数字,因为这里都是整数除法,不存在小数的问题。然后只需要判断立方和是否等于自身即可。
注意:因为i是循环变量,这里不能改变i的值,不然可能造成死循环。
5.6.2 综合练习
本部分是一些整理的关于流程控制部分的综合练习,可以通过这些练习熟悉Java语言的基本语法,以及锻炼逻辑思维能力。
练习题:
1、 计算数字12和18的最小公倍数。
2、 如果苹果 1元/个, 桔子 2 元/个, 芒果 4元/个,若是用10元去买,有几种组合呢?
3、 一只猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个,第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个,第10天早上想再吃时,发现只剩下一个桃子了。请问猴子第一天一共摘了多少个桃子?
4、 计算30的阶乘。
5、 一个农场有头母牛,现在母牛才一岁,要到四岁才能生小牛,四岁之后,每年生一头小牛。假设每次生的都是母牛,并且也遵守4年才生育并生母牛的原则,并且无死亡,请问n年后共有多少头牛?
6、 角谷猜想问题:日本一位中学生发现一个奇妙的“定理”,请角谷教授证明,而教授无能为力,于是产生角谷猜想。猜想的内容是:任给一个自然数,若为偶数除以2,若为奇数则乘3加1,得到一个新的自然数后按照上面的法则继续演算,若干次后得到的结果必然为1。试编写代码验证该猜想是否正确。
7、 输出20个如下规律的数列: 1 1 2 3 5 8 13……
8、 输出30个如下规律的数列: 1 3 6 10 15 21 ……
9、 输出任意一个三位数中的个位数字和百位数字对调的数值,例如如果三位数是235,则输出532。
10、 求100以内所有质数的和。
5.6.1.5 99乘法表
问题:在控制台打印数学上的99乘法表
该类问题是发现数字的规律,然后将数值的规律用程序描述出来。实际实现时,可能需要耐心的进行调试。在这里,需要实现数字的多行输出,前面使用的System.out.println是输出内容并换行,后续再输出的内容就再下一行显示,如果需要在输出时不换行,则可以使用System.out.print进行输出。
99乘法表的规则是总计9行,每行单独输出,第一行有1个数字,第二行有2个数字,依次类推,数字的值为行号和列号的乘积。
实现思路:使用一个循环控制打印9行,在该循环的循环体中输出该行的内容,一行中输出的数字个数等于行号,数字的值等于行号和列号的成绩。
实现代码如下:
for(int row = 1;row <= 9;row++){ //循环行
for(int col = 1;col <= row;col++){ //循环列
System.out.print(row * col); //输出数值
System.out.print(' '); //输出数字之间的间隔空格
}
System.out.println(); //一行输出结束,换行
}
该程序的输出为:
1
2 4
3 6 9
4 8 12 16
5 10 15 20 25
6 12 18 24 30 36
7 14 21 28 35 42 49
8 16 24 32 40 48 56 64
9 18 27 36 45 54 63 72 81
在该输出中,数字之间的对齐有些问题,第四行和第五行的对齐就很明显。那么如果在输出时想让数字对齐,那么就要首先思考数字为什么不能对齐?则问题直观的出现在有些数字是一位数有些是两位数,发现了原因就可以着手解决了,如果想实现数字的左对齐,则在一位数字的后续多输出一个空格,如果想实现数字的右对齐,则只需要在一位数字的前面输出一个空格即可。
以下代码实现了数字的右对齐:
for(int row = 1;row <= 9;row++){ //循环行
for(int col = 1;col <= row;col++){ //循环列
if(row * col < 10){ //一位数
System.out.print(' ');
}
System.out.print(row * col); //输出数值
System.out.print(' '); //输出数字之间的间隔空格
}
System.out.println(); //一行输出结束,换行
}
所以在实际书写代码时,代码的位置对于程序逻辑的影响很大,在编写代码时,需要认真考虑代码书写的位置。
5.6.1.6 打印图形
问题:在控制台中打印如下格式的图形
*
***
*****
*******
*********
由于受到控制台输出的限制,只能按照行的上下,依次进行输出,所以解决打印图形的问题时,只能按照从上到下依次输出每行的内容,关键是仔细观察,发现图形的规律。
第一种思路:外部循环循环5次打印5行,每行的内容分为两部分:空格和星号,每行空格的数量是5减去行号个,每行星号的数量是行号的2倍减1个,在外部循环内部先打印空格,再打印星号,每个都只打印1个,使用数量控制对应的打印次数,打印完星号以后换行。
则实现的代码如下:
for(int row = 1;row <= 5;row++){ //循环行
//打印空格
for(int c1 = 0;c1 < 5 - row;c1++){
System.out.print(' ');
}
//打印星号
for(int c2 = 0;c2 < 2 * row - 1;c2++){
System.out.print('*');
}
//换行
System.out.println();
}
该代码中row的循环用于控制打印的行数,row变量的值代表行号,内部的循环体分为三部分:打印空格,打印星号和换行,打印的数量参看图形的规律部分。
第二种思路:外部循环循环5次打印5行,内部每行打印的总字符数量是4加行号个,其中前5-行号个字符是空格,后续的字符是星号,所有字符打印完成以后换行。
则实现的代码如下:
for(int row = 1;row <= 5;row++){ //循环行
//循环总的字符数
for(int col = 0; col < 4 + row;col++){
if(col < 5 - row){ //打印空格
System.out.print(' ');
}else{ //打印星号
System.out.print('*');
}
}
//换行
System.out.println();
}
该代码的总体思路和第一种思路一样,都是按行打印,只是在考虑问题时首先考虑字符总的数量,把这个数量作为循环次数,内部控制那些该输出字符那些该输出星号即可。
5.6.1.7 质数判断
问题:判断一个自然数是否是质数。
质数指只能被1和自身整除自然数,也称素数,最小的质数是2。对于自然数来说,任何一个数字都可以被1和自身整除。
实现思路:利用数学上的反证法进行判断。则问题转换为只需要判断不能被1和自身以外的任何一个数字整除即可。则假设判断的数字是n的话,则这些数字的区间是[2,n-1]和大于n的所有数字。在数学上n不可能被大于n的数字整除,所以程序只需要判断[2,n-1]之间的数字即可,如果被该区间的任何一个数字整除了,则说明不是质数。
则实现的代码如下:
int n = 23;
boolean b = true; //存储是否是质数,假设是质数
for(int i = 2;i < n;i++){
//如果整除,则不是质数
if(n % i == 0){
b = false;
break; //后续比较没有意义,结束循环
}
}
//输出是否是质数
if(b){
System.out.println("是质数");
}else{
System.out.println("不是质数");
}
该代码是最容易思考出来的一种实现,其实在数学上只需要判断n是否可以被2到n的二次方根之间的数字即可。则实现的代码变为如下:
int n = 23;
boolean b = true; //存储是否是质数,假设是质数
for(int i = 2;i <= Math.sqrt(n);i++){
//如果整除,则不是质数
if(n % i == 0){
b = false;
break; //后续比较没有意义,结束循环
}
}
//输出是否是质数
if(b){
System.out.println("是质数");
}else{
System.out.println("不是质数");
}
通过缩小判断数字的区间,可以显著提高程序的执行效率。说明:这里的Math.sqrt的功能是计算n的二次方根。
关于流程控制的综合示例部分就介绍这么多,下面将整理一下流程控制的综合练习。
这里只列举了部分流程控制的练习,希望大家可以积极补充一些经典的流程控制练习习题,大家一起提高。
本文来自CSDN博客,转载请标明出处:file:///C:/Documents%20and%20Settings/Administrator/桌面/Java编程那些事儿(整理)%20-%20娃娃鸭的专栏%20-%20CSDN博客.htm
posted on 2009-10-28 22:47
大鸟 阅读(1809)
评论(3) 编辑 收藏