Java从入门到精通(第2版)
上QQ阅读APP看书,第一时间看更新

第3章 初识庐山真面目—Java程序要素概览

本章视频教学录像:1 小时33分钟

麻雀虽小,五脏俱全。本章的实例虽然非常简单,但基本涵盖了本篇所讲的内容。可以通过本章来概览Java程序的组成及内部部件(如Java中的标识符、关键字、变量、注释等)。同时,本章还涉及到Java程序错误的检测及Java编程风格的注意事项。

本章要点(已掌握的在方框中打钩)

□ 掌握Java程序的组成

□ 掌握Java 程序注释的使用

□ 掌握Java 中的标识符和关键字

□ 了解Java 中的变量及其设置

□ 了解程序的检测

□ 掌握提高程序可读性的方法

3.1 一个简单的例子

本节视频教学录像:15分钟

从本章开始,我们正式学习Java语言的程序设计。除了认识程序的架构外,本章还将介绍修饰符、关键字以及一些基本的数据类型。通过简单的范例,让读者了解到检测与提高程序可读性的方法,以培养读者良好的编程风格和正确的程序编写习惯。

下面来看一个简单的Java程序。在介绍程序之前,读者先简单回顾一下第1章讲解的例子,之后再来看下面的这个程序,在此基础上理解此程序的主要功能。

【范例3-1】 Java程序简单范例(代码TestJava3 1.java)。

        01  /**
        02
        03  *@ClassName:TestJava3_1
        04
        05  *@Description:这是Java的一个简单范例
        06
        07  *@author:YuHong&Kerl
        08
        09  *@date:2014年1月14日下午9:40:39
        10
        11  */
        12   public class TestJava3_1
        13   {
        14     public static void main(String args[])
        15     {
        16       int num;         //声明一个整型变量num
        17       num=5;        //将整型变量赋值为3
        18       //输出字符串,这里用"+"号连接变量
        19       System.out.println("这是数字"+num);
        20       System.out.println("我有"+num+"本书!");
        21     }
        22   }

【运行结果】

保存并运行程序,结果如下图所示,注意该图由Eclipse软件输出的界面,Eclipse软件的使用方法读者参阅第2章。

如果读者现在暂时看不懂上面的这个程序,也没有关系,先把这些Java代码在任意文本编辑器里(Eclipse编辑器、微软的写字板、Notepad++等均可)手工敲出来(尽量不要用拷贝+复制的模式来完成代码输入,每一次编程上的犯错,纠正之后,都是进步!),然后存盘、编译、运行,就可以看到输出结果。

【代码详解】

首先说明的是,例3-1中的行号是为了让读者(程序员)便于理解而人为添加的,真正Java源代码是没有这些行号的。

第1-11行为程序的注释, 会被编译器自动过滤。但通过注释可以提高Java源码的可读性,使得Java程序条理清晰。需要说明的是,第1-11行有部分空白行,空白行同样会被编译器过滤,在这里的主要功能是为了代码的美观。编写程序到了一定的境界,程序员不仅要追求程序功能的实现,还要追求源代码外在的“美”。这是一种编程风格,“美”的定义和理解不同,编程风格也各异。

第12行 public class TestJava3_1中的public与class是Java的关键字,class为“类”的意思,后面接上类名称,在本程序中取名为TestJava3_1。public则是用来表示该类为公有,也就是在整个程序里都可以访问到它。

需要特别注意的是:如果将一个类声明成public,那么需保证文件名称和这个类名称相同,如下图所示。本例中的文件名为TestJava3_1.java,而public之后所接的类名称也为TestJava3_1。也就是说,在一个Java文件里,最多只能有一个public类,否则.java的文件便无法命名。

第14行public static void main(String args[ ])为程序运行的起点。第15~21行({}花括号之内)的功能类似于一般程序语言中的函数(function),但在Java中称之为method(方法)。因此C语言里的main()函数(主函数),在Java中则被称为main()方法(主方法)。

main()方法的主体(body)从第15行的左大括号“{”到第21行的右大括号“}”为止。每一个独立的Java程序一定要有main()方法才能运行,因为它是程序开始运行的起点。

第16行“int num”的目的是声明num为一个整数类型的变量。在使用变量之前,必须先声明其数据类型。

第17行“num=3”为一赋值语句,即把整数3赋给存放整数的变量num。

第19行的语句如下。

        19   System.out.println("这是数字"+num);

程序运行时会在显示器上输出引号(")内所包含的内容,包括“这是数字”和整数变量num所存放的值。

System.out是指标准输出,通常与计算机的接口设备有关,如打印机、显示器等。其后所续的println,是由print与line所组成的,意思是将后面括号中的内容打印在标准输出设备——显示器上。因此第19行的语句执行完后会换行,也就是把光标移到下一行的开头继续输出。类似的方法还有System.out.print(),同System.out.println()相比,System.out.print()输出指定内容,但不换行。读者可以把System.out.println()改成System.out.print(),看一下换行与不换行的区别。

第21行的右大括号告诉编译器main() method到这儿结束。

第22行的右大括号告诉编译器class TestJava3_1到这儿结束。

这里只是简单地介绍了一下TestJava3_1这个程序,相信读者已经对Java语言有了一个初步的了解。TestJava3_1程序虽然很短,却是一个相当完整的Java程序。在后面的章节中,将会对Java语言的细节部分做详细的讨论。

3.2 感性认识Java程序

在本节,我们将探讨Java语言的一些基本规则及用法。

本节视频教学录像:54分钟

3.2.1 认识Java程序的框架

1. 大括号、段及主体

将类名称定出之后,就可以开始编写类的内容。左大括号“{”为类的主体开始标记,而整个类的主体至右大括号“}”结束。每个命令语句结束时,都必须以分号“;”做结尾。当某个命令的语句不止一行时,必须以一对大括号“{}”将这些语句包括起来,形成一个程序段(segment)或是块(block)。

下面以一个简单的程序为例来说明什么是段与主体(body)。若暂时看不懂TestJava3_2这个程序,也不用担心,以后会讲到该程序中所用到的命令。

【范例3-2】 简单的Java程序(TestJava3 2.java)。

        01 //TestJava3_2,简单的Java程序
        02 public class TestJava3_2
        03 {
        04   public static void main(String args[])
        05   {
        06     int x;
        07     for(x=1;x<3;x++)
        08     {
        09       System.out.println(x+"*"+x+"="+x*x);
        10     }
        11   }
        12 }

【运行结果】

运行程序并保存,结果如下图所示。

【范例分析】

在上面的程序中,可以看到main() 方法的主体以左右大括号{}包围起来;for循环中的语句不止一行,所以使用左右大括号{}将属于for循环的段内容包围起来;类TestJava3_2的内容又被第3行与第12行的左右大括号{}包围,这个块属于public类TestJava3_2所有。此外,应注意到每个语句结束时,都是以分号“;”作为结尾。

2. 程序运行的起始点 —— main() 方法

Java程序是由一个或一个以上的类组合而成,程序起始的主体也是被包含在类之中。这个起始的地方称为main(),用左右大括号将属于main()段的内容包围起来,称之为“方法”(method)。

main()方法为程序的主方法,如同中国的古话“家有千口,主事一人”。类似的,在一个Java程序中,不论它有千万行,执行的入口只能有一个,而这个执行的入口就是main()方法,它有且仅有一个。通常看到的main() 方法如下面的语句片段所示。

        public static void main(String args[])  //main()方法,主程序开始
        {
        …
        }

如前一节所述,main() 方法之前必须加上public static void 这3个标识符。public代表main()公有的方法;static表示main()是个静态方法,可以不依赖对象而存在,也就是说,在没有创建类的对象情况下,仍然可以执行;void英文本意为空的,这里表示main()方法没有返回值。main后面括号()中的参数String args[]表示运行该程序时所需要的参数,这是固定的用法,我们会在以后的章节介绍这个参数的使用细节。

我们在学习Java中经常遇到“方法”这个概念,而在学习C语言或者C++中,又会遇到“函数”概念,语言都是相通的,那二者又有什么区别和联系呢?

⑴ “函数”是一段实现某种“功能”的代码,函数的操作是对输入数据的处理。函数的英文“function”恰恰有“功能”的含义,可以达到“见名知意”。通过函数的名称来实施函数调用。它能将一些数据(也就是参数)传递进去进行处理,然后返回一些数据(即函数的返回值),也可以没有返回值。所有传递给函数的数据都是显式传递的。而方法的参数传递通常是隐式的,它可以直接操作类内部的数据。

⑵ “方法”也是一段完成某项功能的代码,也通过名字来进行调用,但它依赖于某个特定的对象,例如,我们可以说“调用对象X的Y方法”,而不能“调用Y方法”。简单来讲, 方法和对象相关;而函数和对象无关。因为Java是一门完全面向对象编程的语言,所以在Java里只有方法。

C语言是面向过程的编程语言,所以在C语言中只有函数。C++是C语言的超集,既支持面向过程编程,又支持面向对象编程,所以在C++中,如果一个函数独立于类之外,那它就是函数,如果它存在于一个类中,那它就是方法,所不同的是,C++给这种特殊的方法取了一个新名称——成员函数(member function)。

3.2.2 认识标识符

Java中的包(package)、类、方法、参数和变量的名称,可由任意顺序的大小写字母、数字、下划线(_)和美元符号($)等组成,但这些名称的标识符不能以数字开头,也不能是Java中保留的关键字。

下面是合法的标识符。

        yourname    your_name    _yourname    $yourname

比如,下面的4个标识符是非法的。

        class    6num23    abc@sina             x+y

非法的原因分别是:class是Java的保留关键字;6num23的首字母为数字;abc@sina中不能包含@等特殊字符;x+y不能包含运算符。此外,读者应该注意,在Java中,标识符是区分大小写的,也就是说,A123和a123是两个完全不同的标识符。

注意

标识符的命名规则属于强制性的,不然编译时会报错。一些刚接触编程语言的读者可能会觉得记住上面的规则很麻烦,所以在这里提醒读者,标识符最好永远用字母开头,而且尽量不要包含其他的符号。规则是必须遵守的,而规范是约定俗成的,鼓励大家都遵守的。

3.2.3 认识关键字

和其他语言一样,Java中也有许多关键字(Keywords,也叫保留字),如public、static、int等,这些关键字不能当做标识符使用。下表列出了Java中的关键字。在程序开发中一旦使用了这些关键字做标识符,编译器在编译时就会报错,而对智能的编辑器(如Eclipse等)会在编写代码时自动提示这些语法错误。在后续的章节中,我们会慢慢学习它们的内涵和用法。

注:*表示该关键字尚未启用;**表示在Java 1.2添加的;***表示在Java 1.3添加的;****表示在Java 5.0添加的。

注意

虽然goto、const在Java中并没有任何意义,却也是保留的关键字,与其他的关键字一样,在程序里不能用来作为自定义的标识符,true、false、null等看起来像关键词,实际上在Java中,它们仅仅是普通的字符串。Java中的所有关键字均由小写字母构成。

3.2.4 认识注释

注释在源代码中的地位非常重要,虽然注释在编译时被编译器自动过滤掉,但为程序添加注释可以解释程序的某些语句的作用和功能,提高程序的可读性。特别当编写大型程序时,多人团队合作,A程序员写的程序B程序员可能很难看懂,而注释能起到非常重要的沟通作用。所以本书强力建议读者朋友养成写注释的好习惯。

Java里的注释根据不同的用途分为以下3种类型。

⑴ 单行注释。

⑵ 多行注释。

⑶ 文档注释。

单行注释,就是在注释内容的前面加双斜线(//),Java编译器会忽略这部分信息。如下所示。

        int num;    //定义一个整数

多行注释,就是在注释内容的前面以单斜线加一个星形标记(/*)开头,并在注释内容末尾以一个星形标记加单斜线(*/)结束。当注释内容超过一行时,一般可使用这种方法,如下所示。

        /*
        int c = 10 ;
        int x = 5 ;
        */

文档注释,是以单斜线加两个星形标记(/**)开头,并以一个星形标记加单斜线(*/)结束。用这种方法注释的内容会被解释成程序的正式文档,并能包含进如javadoc之类的工具生成的文档里,用以说明该程序的层次结构及其方法。范例3-1中的第1-11行对源代码的注释属于类别(3),通常在程序开头加入作者,时间,版本,要实现的功能等内容注释,方便后来的维护以及程序员的交流。本质上注释类别(3)是(2)的一个特例,(3)中的第2个星号*可看作注释的一部分。由于文档注释比较费篇幅,在后面的范例中,我们不再给出此类注释,读者可在随书配备的光盘中看到注释更为全面的源代码。

提示

还可以用程序注释来暂时屏蔽某些程序语句,让编译器暂时不要处理这部分语句,等到需要处理的时候,只需把注释标记取消即可。这也是程序调试一种技巧。注释在调试和后期的维护过程中也有重要作用,请读者务必重视。

需要注意的是,第3至第6行的注释中,每一行前都有一个*,其实这不是必需的,它们是注释区域的一个“普通”字符而已,仅仅是为了注释部分看起来更加美观。前文我们已提到,实现一个程序的基本功能是不够的,优秀的程序员还会让自己的代码看起来很“美”。

提示

撰写注释部分,看起来比较繁琐。其实完全可借助Eclipse方便地生成漂亮的注释。在Eclipse中选择【窗口】【首选项】【Java】【代码样式】【代码模板】,设置好一些固定格式的注释模板后,按Alt+ Shift+ J 组合键便可方便插入注释。

3.2.5 认识变量

在Java程序设计中,变量(Variable)在程序语言中扮演着最基本的角色,它是存储数据的载体。计算机中的变量是实际存在的数据。变量的数值可以被读取和修改,它是一切计算的基础。

与变量相对应的就是常量(Constant),顾名思义,常量是固定不变的量,其一旦被定义并赋初值后,它的值就不能再被改变。有关常量的知识介绍,请读者参阅第4章的有关内容。

本节主要关注的是对变量的认知,接下来看看Java中变量的使用规则。Java变量使用和其他高级计算机语言一样:先声明,后使用。即必须实现声明它想要保存数据的类型。

1. 变量的声明

声明一个变量的基本方式为:

        数据类型 变量名;

另外,在定义变量的同时,给予该变量初始化,建议读者使用这种声明变量的风格。

        数据类型 变量名 = 数值或表达式;

举例来说,想在程序中声明一个可存放整型的变量,这个变量的名称为num。在程序中即可写出如下所示的语句。

        int num;    //声明num为整数变量

int为Java的关键字,代表基本数据类型——整型(Integer)。若要同时声明多个整型的变量,可以像上面的语句一样分别声明它们,也可以把它们都写在同一个语句中,每个变量之间以逗号分开。下面的变量声明的方式都是合法的。

        int num1,num2,num3;  //声明三个变量num1,num2,num3,彼此用英文逗号“,”隔开
        int num1;int num2;int num3; //用三个语句声明上面的三个变量,彼此用英文分号“;”隔开

虽然上面两种定义多个变量的语句都是合法的,但对它们添加注释不甚方便,特别是对后一种同一行有多个语句,由于可读性不好,建议读者不要采纳此种编程风格。

2. 变量名称

读者可以依据个人的喜好来决定变量的名称,但这些变量的名称不能使用Java的关键字。通常会以变量所代表的意义来取名。当然也可以使用a、b、c等简单的英文字母代表变量,但是当程序很大时,需要的变量数量会很多,这些简单名称所代表的意义就比较容易忘记,必然会增加阅读及调试程序的困难度。变量的命名之美在于:在符合变量命名规则的前提下,尽量对变量做到“见名知意”,如:用num 表示数字,用length 表示高度等。

3. 变量的设置

给所声明的变量赋予一个属于它的值,用赋值运算符(=)来实现。具体可使用如下所示的3种方法进行设置。

⑴ 在声明变量时设置。

举例来说,在程序中声明一个整数的变量num,并直接把这个变量赋值为2 ,可以在程序中写出如下的语句。

        int num=2; //声明变量,并直接设置

⑵ 声明后再设置。

一般来说也可以在声明后再给变量赋值。举例来说,在程序中声明整型变量num1、num2及字符变量ch,并且给它们分别赋值,在程序中即可写出下面的语句。

        int num1,num2;      //声明两个整型变量num1和num2
        char ch;           //声明一个字符变量ch
        num1=2;          //将变量num1赋值为2
        num2=3;          //将变量num2赋值为3
        ch='z';            //将字符变量ch赋值为字母z

⑶ 在程序的任何位置声明并设置。

以声明一个整数的变量num为例,可以等到要使用这个变量时再给它赋值。

        int num;    //声明整型变量num
        …
        num=2;   //用到变量num时,再赋值

3.2.6 认识数据类型

除了整数类型之外,Java还提供有多种数据类型。Java的变量类型可以是整型(int)、长整型(long)、短整型(short)、浮点型(float)、双精度浮点型(double),或者字符型(char)和字符串型(String)等。下面对Java中的基本数据类型给予简要介绍,读者可参阅相关章节获得更为详细的介绍,基本数据类型的使用可参阅第5章;用户自定义数据类型(class)的使用可参阅第9章;String类型的使用可参阅第16章。

整型是取值为不含小数的数据类型,包括byte类型、short类型、int类型及long类型,默认情况下为int类型,可用八进制、十进制及十六进制来表示。另一种存储实数的类型是浮点型数据,主要包括float型(单精度浮点型,占4个字节)和double型(双精度浮点型,占8个字节)。用来表示含有小数点的数据,必须声明为浮点型。在默认情况下,浮点数是double型的。如果需要将某个包括小数点的实数声明为单精度,则需要在该数值后加字母‘F’(或小写字母‘f’)。

下面的语句是主要数据类型的定义说明。

        01  int num1=10;//定义4字节大小的整型变量num1,并赋初值为10
        02  byte age=20; //定义1个字节型变量age,并赋初值为20
        03  //byte age2=129; //错误:超出了byte类型表示的最大范围(-128~127)
        04  float price=12.5f; //定义4字节的单精度float型变量price,并赋初值为12.5
        05  float price=12.5; //错误:类型不匹配
        06  double weight=12.5;//定义8字节的单精度float型变量weight,并赋初值为12.5

定义数据类型时,要注意两点。

⑴ 在定义变量后,对变量赋值时,赋值大小不能超过所定义变量的表示范围。例如,在本例第3行是错误的,给age2赋值129,已超出了byte类型(一个字节)所能表示的最大范围(-128~127)。这好比某人在宾馆定了一个单人间(声明byte类型变量),等入住的时候,说自己太胖了,要住双人间,这时宾馆服务员(编译器)是不会答应的。解决的办法很简单,需要重新在一开始就定双人间(重新声明age2为short类型)。

⑵ 在定义变量后,对变量赋值时,运算符(=)左右两边的类型要一致。例如,在本例第5行是错误的,因为在默认情况下,包含小数点的数(12.5)是双精度double类型的,而“=”左边定义的变量price是单精度float类型的,二者类型不匹配!这好比一个原本定了双人间的顾客,非要去住单人间,刻板的宾馆服务员(编译器)一般也是不答应的,因为单人间可能放不下双人间客人的那么多行李,丢了谁负责呢?如果住双人间的顾客非要去住单人间,那需要双人间的顾客显式声明,我确保我的行李在单人间可以放得下,或即使丢失点行李,我也是可以承受的(即强制类型转换)。下面的两个语句都是合法的。

        float price=12.5f;  //中规中矩的定义,一开始就保证“=”左右类型匹配
        float price=(float)12.5; //一开始“=”右边不匹配,通过强制类型转换后,“=”左右类型匹配

3.2.7 认识运算符和表达式

计算机的英文为Computer,顾名思义,它存在的目的就是用来做计算(Compute)的。而要运算就要使用各种运算符。最简单的算术运算符是加(“+”)、减(“-”、)、乘(“*”)、除(“/”)、取余(“%”)等。

表达式则是由操作数与运算符所组成,操作数可以是常量、变量,甚至可以是方法。下面的语句说明了这些概念的使用。

        int result=1+2;

在这个语句中,1+2 为表达式,操作符为“+”,计算结果为 3,通过“=”赋值给整型变量result对于表达式,由于操作符是有优先级的,所以即使有计算相同的操作数和相同的操作符,其结果也是有可能不一样的,例如:

        c = a + b /100; //假设变量a,b,c为已定义的变量

在上述的表达式中,由于除法操作符“/”的优先级比加法操作符“+”高,所以c的值为b除以100后的值再加上a之和,这可能不是程序员的本意,如果希望是a+b之和一起除以100,那么就需要加上括号()来消除这种模糊性,如下所示:

        c = (a + b) /100; // a+b之和除以100

3.2.8 认识类

Java程序是由类(class)所组成。类的概念在以后会详细讲解,读者只要记住,类是一种用户自定义的类型就可以了,所有的Java程序都是由类组成的。下面的程序片段即为定义类的典型范例。

        public class Test            //定义public类—Test
        {
        …
        }

程序定义了一个新的public类Test,这个类的原始程序的文件名称应取名为Test.java。类Test的范围由一对大括号所包含。public是Java的关键字,指的是对于该类的访问方式为公有。

需要注意的是:由于Java程序是由类所组成,因此在一个完整的Java程序里,至少需要有一个类。在第1章中,我们曾提到,在Java程序的文件名不能随意命名,必须和public类名称一样(大小写也必须保存一致)。因此,在一个独立的源码程序里,只能有一个public类,却可以有许多non-public(非公有)类。若是在一个Java程序中没有一个类是public,那么对该Java程序的文件名就可以随意命名了。

Java提供了一系列的访问控制符来设置基于类(class)、变量(variable)、方法(method)及构造方法(constructor)等不同等级的访问权限。Java的访问权限主要有4类:默认模式(default)、private(私有)、public(公有)和protected(保护)。对类和接口概念的深度理解,读者可分别参阅第9章和第13章,这里仅需读者了解,在Java中有这么4种访问控制方式即可。

3.2.9 输入与输出

在Java中,流(Stream)是一个重要的但初学者很容易困惑的概念。输入/输出(I/O)是指程序与外部设备或其他计算机进行交互的操作。几乎所有的程序都具有输入与输出功能,如从键盘上读取数据,向屏幕或文件输出数据等。通过输入和输出操作可以从外界接收信息,或者把信息传递给外界。Java把这些输入与输出操作用流的模式来实现,通过统一的接口来表示,从而使程序设计更为简单。

流(stream)的概念最早源于UNIX操作系统中管道(pipe)的概念。在UNIX中,管道是一条不间断的字节流,用来实现进程或程序间的通信,或读写外围设备、外部文件之间的信息交互等。一个流必须有起源端和目的端,它们可以是计算机内存的某部分区域,也可以是某个磁盘文件,甚至可以是Internet上的某个URL。

为了便于理解,读者可以将Java中流想象成一种“数据流通的管道”,文件和程序之间一旦有了数据请求,二者之间就会建立某种形式的连接,从而形成了一个数据流。

就如在真正的管道中,可以传输不同形态的东西,既可以是水,也可以是天然气。类似的, Java中的数据流也有不同的形式,既可以是基于二进制的字节流,也可以是基于某种编码方式(如Unicode、UTF-8等)的字符流。

流的方向是重要的,根据流的方向,流可大致分为两类:输入流和输出流。当程序需要读取数据时,就会开启一个通向数据源的管道,程序可从该管道中提取数据,这种模式下的流称之为输入流。类似地,如果程序想要输出数据,也会建立一个通往输出目的地管道,一旦建立后,程序就可以经由此管道输出数据,这种模式的流称之为输出流。关于数据流,它们的输入、输出关系可用下图来说明。

作为程序输入数据源,其对象可以是键盘,也可以是某个文件或磁盘。作为程序输出数据源,其对象可以是显示器,也可以是某个文件或磁盘。为了方便一些频繁的设备交互,Java语言系统预定了3个可以直接使用的流对象,它们分别是:

⑴ System.in(标准输入),通常代表键盘输入。

⑵ System.out(标准输出):通常输出到终端显示器。

⑶ System.err(标准错误输出):通常将错误信息输出到终端显示器。

System.in是个标准的输入流(InputStream )对象,它主要是用来连接控制台程序和键盘的输入。目前System.in并不经常用到,这是因为Java命令行应用程序主要是通过命令行参数或配置文件来传递数据的,对于图形用户界面(GUI)程序,则主要是用图形界面来接纳用户的键盘输入。

System.out是打印输出流(PrintStream)对象,主要向控制台输出数据,这个对象经常会被面向控制台的程序使用到,它也常被程序员用作程序调试输出的工具。

System.err也是打印输出流(PrintStream)对象,除了其主要用于错误输出之外,它和System.out没有太大区别。有些应用程序(如Java程序员常用的Eclipse)用System.err以红色字体来输出错误信息。它可以看作一个专门用于输出错误信息的System.out。

数据的输入、输出属于I/O操作部分,Java把处理输入/输出相关的类放在java.io包中,这个包不属于java.lang包,没有被默认加载,所以如果需要使用与I/O相关的类和对象时,需要把java.io显式的导入(import)到相应的程序中。下面用范例3-3来说明标准输入和输出的使用。

【范例3-3】 简单的输入、输出流使用范例(StreamInTest3 3.java)。

        01 //Syetem.in、Syetem.out、Syetem.err的使用范例
        02  import java.io.*;    //导入Java中支持I/O类的包
        03  public class StreamInTest3_3
        04  {
        05     public static void main(String args[])
        06     {
        07      String str;
        08      //创建标准输入流对象stdin来接收键盘System.in的输入
        09      InputStreamReader stdin=new InputStreamReader(System.in);
        10      //以缓冲流模式来接收stdin
        11      BufferedReader bufin=new BufferedReader(stdin);
        12      //使用try和catch机制来处理输入的异常
        13      try{
        14        System.out.print ("请输入字符:");
        15        //用str字符串对象来接收键盘输入的一行数据
        16        str=bufin.readLine();18
        17        System.out.println ("你输入的字符为:"+str);
        18        }catch(Exception e){
        19          //System.out.println("发生I/O错误!!!");
        20          System.err.println("发生I/O错误!!!");
        21          e.printStackTrace();
        22      }
        23    }
        24  }

【运行结果】

运行程序并保存,结果如下图所示。

【代码详解】

程序第2行,如果要完全使用Java输入、输出流类模块,就必须导入相应的包库(java.io.*)支持。java.io.*中的字母“i”表示input(输入),字母“o”表示output(输出),“*”是代表任何含义的通配符,它表示输入/输出(io)下所有模块。在Java中,“.”表示对象与组件之间的所有关系,为了方便起见,读者可以直接用中文“的”来代替之。譬如:

        import java.io.*;

我们可以直接读成“导入java“的”io包库下“的”所有模块”。

程序的第9行表示创建标准输入流对象stdin ,来接收键盘System.in的输入。

程序的第11行表示以缓冲流模式来接收stdin。缓冲机制主要是为了处理应用程序和外设(输入/输出)之间速度不匹配的问题。

程序的第15和19行,使用的是标准输出对象System.out,这在前面的范例中频繁使用。

程序第20行用System.err对象输出程序的错误信息。

其实程序20行的功能完全可以用被注释掉的第19行代替,二者均可输出程序的错误信息,二者在此处可通用,所不同的是,使用System.err代码的可读性更好一点,其中err表示error(错误)。System.err可以理解为专用于输出错误信息的System.out特例。

程序的13行到22行,使用了Java的try-catch错误捕捉处理机制。正如“天有不测之风云”一样,在Java程序设计过程中,程序员虽已尽量考虑全面以避免各种错误,但从软件工程角度来看,在程序运行过程中还是不可避免的发生各种不可预测的异常情况,程序越大,此类情况越是明显。对此,一个良好的程序需要有一定的容错机制,即不能因为程序成员的某个潜在的设计缺陷或用户可能的操作失误(如输入信息出错),而导致整个程序崩溃。因此,需要添加相应的代码来处理程序中异常(Exception)错误。try-catch机制对于程序的健壮性(Robust)非常重要,我们会在第19章讲到这方面的知识,这里读者只需有个感性认识即可。

3.3 程序的检测

本节视频教学录像:6分钟

学习到本节,相信读者大概可以依照前面的例子“照猫画虎”,写出几个类似的程序了。而在编写程序时,不可避免的会遇到各种编译时(语法上的)或运行时(逻辑上的)的错误,接下来我们做一些小检测,看看读者能否准确地找出下面的程序中存在的错误。

3.3.1 语法错误

通过下面的范例应学会怎样找出程序中的语法错误。

【范例3-4】 找出下面程序中的语法错误(代码TestJava3 4.java)。

        01 //下面程序的错误属于语法错误,在编译的时候会自动检测到
        02 public class TestJava3_4
        03 {
        04   public static void main(String args[])
        05   {
        06    int num1=2;//声明整数变量num1,并赋值为2
        07    int num2=3;声明整数变量num2,并赋值为3
        08
        09    System.out.println("我有"+num1"本书!");
        10    System.out.println("你有"+num2+"本书!")
        11   )
        12 }

【范例分析】

程序TestJava3_4在语法上犯了几个错误,若是通过编译器编译,便可以把这些错误找出来。事实上,在Eclipse中,语法错误的部分都会显示红色的下划线,对应的行号处会有红色小×。

首先,可以看到第4行,main() 方法的主体以左大括号开始,应以右大括号结束。所有括号的出现都是成双成对的,因此第11行main() 方法主体结束时应以右大括号做结尾,而TestJava2_4中却以右括号“)”结束。

注释的符号为“//”,但是在第7行的注释中,没有加上“//”。在第9行,字符串的连接中少了一个“+”号。最后,还可以看到在第10行的语句结束时,少了分号“;”作为结束。

上述的3个错误均属于语法错误。当编译程序发现程序语法有错误时,会把这些错误的位置指出来,并告诉程序设计者错误的类型,程序设计者可根据编译程序所给予的信息加以更正。比如,在没有纠正第7行的错误时,编译器会显示下图所示的错误信息。

将程序第7行的错误更改后,重新编译,若还是有错误,再依照上述的方法重复测试,这些错误就会被一一改正,直到没有错误为止。纠正上述错误后,保存并运行程序,正确的运行结果如下图所示。

3.3.2 语义错误

若程序本身的语法都没有错误,但是运行后的结果却不符合程序设计者的要求,此时可能犯了语义错误,也就是程序逻辑上的错误。读者会发现,想要找出语义错误比找出语法错误更加困难。因为人都是有思维盲点的,如果在编写程序时,一旦陷入某个错误的思维当中,有时很难跳出来。排除一个逻辑上的语义错误,糟糕时可能需要经历一两天,才突然“顿悟”错误在哪里。

举例来说,想在程序中声明一个可以存放整数的变量,这个变量的名称为num。在程序中即可写出如下所示的语句。

【范例3-5】 程序语义错误的检测(TestJava3 5.java)。

        01 //下面这段程序原本是要计算一共有多少本书,但是由于错把加号写成了减号,
            //所以造成了输出结果不正确,这属于语义错误
        02 public class TestJava3_5
        03 {
        04   public static void main(String args[])
        05     {
        06     int num1=4;      //声明一整型变量num1
        07     int num2=5;      //声明一整型变量num2
        08
        09      System.out.println("我有"+num1+"本书!");
        10      System.out.println("你有"+num2+"本书!");
        11      //输出num1-num2的值s
        12      System.out.println("我们一共有"+(num1-num2)+"本书!");
        13   }
        14 }

【范例分析】

可以发现,在程序编译过程中并没有发现错误,但是运行后的结果却不正确,这种错误就是语义错误。在第12行中,因失误将“num1+num2”写成了“num1-num2”,虽然语法是正确的,但是却不符合程序设计的要求,只要将错误更正后,程序的运行结果就是想要的了。

【运行结果】

保存并运行程序,结果如下图(左)所示,显然不符合设计的要求。在纠正12行的语义错误之后的输出结果如下图(右)所示。

范例3-5的输出结果图

纠正语义错误的输出结果

3.4 提高程序的可读性

本节视频教学录像:3分钟

能够写出一个功能正确的程序,的确很让人兴奋。但如果这个程序除了本人之外,其他人都很难读懂,那这就不算是一个好的程序。所以程序的设计者在设计程序的时候,除了完成程序必需的功能,也要学习程序设计的规范格式。除了前面所说的加上注释之外,还应当保持适当的缩进,保证程序的逻辑层次清楚。我们前面的范例程序都是按缩进的方法来编写的,“美的东西都是丑的东西衬托出来的”。读者可以比较下面的两个范例,相信看完之后,就会明白程序中使用缩进的好处了。

【范例3-6】 缩进格式的程序(TestJava3 6.java)。

        01 //以下这段程序是有缩进的样例,可以发现这样的程序看起来比较清楚
        02 public class TestJava3_6
        03 {
        04   public static void main(String args[])
        05   {
        06     int x;
        07
        08     for(x=1;x<=3;x++)
        09     {
        10       System.out.print("x="+x+",");
        11       System.out.println("x*x="+(x*x));
        12     }
        13    }
        14 }

【运行结果】

保存并运行程序,范例3-6结果如下图所示。

【范例3-7】 非缩进格式的程序(代码TestJava3 7)。

        01 //下面的程序与前面程序的输出结果是一样的,但不同的是,
        02 //这个程序没有采用任何缩进,代码阅读者看起来很累,代码逻辑关系不明显,不易排错
        03 public class TestJava3_7{
        04 public static void main(String args[]){
        05 int x;for(x=1;x<=3;x++){
        06 System.out.print("x="+x+",");
        07 System.out.println("x*x="+(x*x));}}}

【范例分析】

TestJava3_7这个例子虽然简短,而且也没有语法错误,但是因为编写风格(Programming Style)的关系,阅读起来肯定没有TestJava3_6这个程序好读,所以建议读者尽量使用缩进,养成良好的编程习惯。良好代码编写风格是优秀程序员需要达到的境界,在满足程序基本的功能和性能目标前提下,还要满足代码的“雅致”之美。良好编程风格可增强代码的可读性、可移植性,更加有利于团队合作(teamwork)。只有程序设计者自己能明白的代码注定是“孤单的”。

3.5 高手点拨

本节视频教学录像:5分钟

注意Java源代码中的字符半角和全角之分

Java继承了C/C++的很多语法,其中一个语法就是每个语句(Statement)后加一个分号“;”作为本语句的结束。这本是一个很简单的语法要求,但是对于东方国家的初学者来说,却是一个极容易犯错的问题。其主要原因在于混淆了半角“;”和全角“;”的区别。那么什么是字符的半角和全角呢?

传统上,在西欧语系使用的计算机系统中,每一个字母或符号使用1个字节的空间,而1个字节由8比特(bit)组成,因此共有28=256个编码空间,这对西欧语系的字符足够用了;但是对于汉语、日语及韩语文字,由于其数量远远超过256个,故惯常使用两字节来储存一个字符,为了使字体看起来齐整,英文字母、数字及其他符号,也由原来只占1个字空间,改为用2个字的空间来显示,并用2个字节来储存。所以,中、日、韩等文字没有半角之说,统一为全角字符。相比起来,英文字母、数字及其他西欧符号用1个字节表示就是半角,用2个字节表示就是全角,如下图所示。

Java的初学者,在使用中文输入法输入英文字符时,很容易在中文输入模式输入全角的分号“;”、左右花括号“{”、“}”、左右括号“(”、“)”及逗号“,”,而Java的编译器在语法识别上仅仅识别这些字符对应的半角。因此建议初学者在输入Java语句(不包括注释和字符串的内容)时,在中文输入模式下,要么按“Ctrl+Shift”组合键切换至英文模式,要么将中文输入法转变为半角模式,如下图所示,中文输入法中的“太阳”标记为全角模式,单击该图标可以切换为“月亮”标记——半角模式。

3.6 实战练习

本节视频教学录像:10分钟

1.分析下面程序代码的运行结果,运行程序并查看实际结果,分析产生结果的原因。

        01 public class Exercise3-1
        02 {
        03   public static void main(String args[])
        04   {
        05     int x=10;
        06     int y=3;
        07     System.out.println(x/y);
        08   }
        09 }

2. 用float型定义变量:float f = 3.14; 是否正确? (Java面试题)

解析:不正确。赋值运算符(=)左右两边的精度类型不匹配。在默认情况下,包含小数点的实数,如本题中的3.14,被存储为double类型(即双精度),而float类型定义的变量,如本题中的f,是单精度的。如果想让上面的语句编译正确,应该对赋值运算符(=)右边的值做强制类型转换,即把常量3.14强制转换为单精度(即float类型),如下所示:

        float f=(float)3.14;  //正确

或者,一开始就把3.14存储为单精度类型,在3.14后加上小写字母‘f’或大写字母‘F’,如下所示:

        float f=3.14f;  //正确
        float f=3.14F;  //正确