首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

3.1  变量

在前一章中讲过,对象把它的状态存储在字段(field)中:

int cadence = 0;

int speed = 0;

int gear = 1;

2.1节介绍了字段,但是你可能还有几个问题,比如:命名字段的规则和惯例是什么?除了int之外,还有什么其他数据类型?声明的时候必须对字段进行初始化吗?如果没有明确地初始化,那么会给字段分配默认值吗?我们将在本章中回答这些问题,但是,首先你必须了解几个技术上的区别。在Java编程语言中,“字段”和“变量”这两个术语都用到了;这是编程新手经常混淆的地方,因为这两个术语似乎指的是同一种东西。

Java编程语言定义下面这些变量类型。

l 实例变量(非静态字段)。从技术上讲,对象把它们的各个状态存储在“非静态字段”中,这就是说,不使用关键字static声明字段。非静态字段也被称为实例变量(instance variable),因为它们的值对类的每个实例(instance)是唯一的(换句话说,就是对每个对象是唯一的);一辆自行车的currentSpeed独立于另一辆自行车的currentSpeed。

l 类变量(静态字段)。类变量(class variable)是使用static修饰符声明的任何字段;这就通知编译器,不管这个类被实例化了多少次,都只存在这个变量的一个副本。定义特定类型自行车的挡位数量的字段就可以被标记为static,因为在概念上所有实例都使用相同数量的挡位。代码static int numGears = 6;就创建了这样的静态字段。另外,可以添加关键字final,表示永远都不能改动挡位数量。

l 局部变量。和对象把其状态存储在字段中类似,方法经常把其临时状态存储在局部变量(local variable)中。声明局部变量的语法和声明字段的语法类似(例如int count = 0;)。没有专门关键字用于说明变量是局部变量;确定局部变量的方式完全取决于声明变量的位置——在方法的前后括号之间。这样,局部变量只对声明它的方法是可见的;不能从类的其他部分访问局部变量。

l 参数。在Bicycle类和应用程序“Hello World!”的main方法中,你已经见到了参数的例子。main方法的签名是public static void main(String[] args)。这里,变量args是这个方法的参数。要牢记,这些参数总是被归类为“变量”,而不是“字段”,这很重要。这也适用于其他接受参数的结构(比如构造器和异常处理代码),你将在本书后面学到它们。

当本书的其余部分讨论到字段和变量时,遵循如下总的指导方针。如果我们谈到“一般字段”(不包括局部变量和参数),我们可能简单地说“字段”。如果讨论到“前面所有内容”,我们可能简单地说“变量”。如果上下文要求作出区分,我们会适当地使用特定术语(静态字段、局部变量,等等)。有时候你可能会看到“成员”这个术语。类型的字段、方法和嵌套类型被统称为这个类型的成员(member)。

3.1.1  命名

每种编程语言都有其自己的一套规则和惯例,用于规定允许使用的名称类型,Java编程语言也不例外。下面总结了命名变量的规则和惯例:

l 变量名称是区分大小写的。变量名称可以是任何合法的标识符——长度不限的Unicode字母和数字的序列,以字母、美元符号“$”或者下划线字符“_”开头。但是,按照惯例,总是以字母开始变量名称,而不使用“$”或者“_”。另外,按照惯例,不要使用美元符号。你可能在一些自动生成的名称中看到美元符号,但是在你自己的变量名称中应该避免使用它。对下划线字符也有类似的惯例;虽然从技术上说,以“_”开头的变量名称是合法的,但是不推荐这样做。空白是不允许使用的。

l 后续字符可以是字母、数字、美元符号或者下划线字符。当选择变量的名称时,使用完整的单词,而不是难以理解的简写。这样做可以使你的代码容易阅读和理解。很多情况下,也使你的代码能够自我解释;例如,和简写的s、c和g相比,名为cadence、speed和gear的字段显得直观得多。还要牢记,你选择的名称必须不是关键字或者保留字。请参见附录A。

l 如果你选择的名称只有一个单词,那么就全使用小写字母。如果它包含多个单词,那么就把每个后续单词的第一个字母大写。名称gearRatio和currentGear是很好的例子。如果你的变量用于存储常量值,比如static final int NUM_GEARS = 6,那么惯例就稍有变化,每个字母都大写,并且使用下划线分隔后续的单词。按照惯例,其他情况下不使用下划线字符。

3.1.2  基本数据类型

Java编程语言是类型严格(即“强类型”)的语言,这就是说所有变量在使用之前都必须声明。这涉及到声明变量的类型和名称,就像你看到过的:

int gear = 1;

这告诉程序,有一个名为“gear”的字段,它保存数值数据,并且其初始值为1。变量的数据类型决定它可能包含的值,以及可能对它执行的操作。除了int之外,Java编程语言还支持另外7种基本数据类型(primitive data type)。原始类型是语言预定义的,并且以保留关键字命名。原始值不和其他原始值共享状态。Java编程语言支持的8种基本数据类型如下:

l byte。byte数据类型是8位带符号二进制补码整数。其最小值为-128;最大值为127(含)。在内存紧张的情况下,将byte数据类型用于大型数组对节省内存很有帮助。我们将在3.1.3节讲解数组。也可以使用byte替换int,这样它们的限制能够帮助说明你的代码;变量的范围受到限制的事实可以作为文档的一种形式。

l short。short数据类型是16位带符号二进制补码整数。其最小值为-32 768;最大值为32 767(含)。它具有和byte一样的指导方针:你可以在内存紧张的情况下,将short用于大型数组以便节省内存。

l int。int数据类型是32位带符号二进制补码整数。其最小值为-2 147 483 648;最大值为2 147 483 647(含)。对于整数值,这个数据类型一般是默认选择,除非出于某种原因(比如节省内存)而选择其他类型。对你的程序要使用的数字来说,这种数据类型很可能已经足够大了,但是如果你的值需要更大范围,就使用long类型。

l long。long数据类型是64位带符号二进制补码整数。其最小值为-9 223 372 036 854 775 808;最大值为9 223 372 036 854 775 807(含)。当你的值需要的范围超过int提供的范围时,可以使用这个数据类型。

l float。float数据类型是单精度32位IEEE 754浮点数。对它的详细介绍超出了这里的讨论范围,但是在Java语言规范(Java Language Specification)的4.2.3节中作出了规定。和对byte以及short类型的建议一样,如果在使用大型的浮点数数组时需要节省内存,就要使用float(而不是double)。永远都不要把这种数据类型用于精确值,比如货币。对于精确值,你需要使用java.math.BigDecimal类。第8章讲解BigDecimal和Java平台提供的其他有用的类。

l double。double数据类型是双精度64位IEEE 754浮点数。对它的详细介绍超出了这里的讨论范围,但是在Java语言规范(Java Language Specification)的4.2.3节中作出了规定。对于小数,这个数据类型是一般的默认选择。和前面讲的一样,永远不要把这种数据类型用于精确值,比如货币。

l boolean。boolean数据类型只有两个可能值:true和false。这种数据类型被用作跟踪真/假条件的简单标志。这种数据类型表示1位信息,但是它的“长度”没有确切地定义。

l char。char数据类型是单一16位Unicode字符。其最小值为'\u0000'(即0);最大值为'\uffff'(即65 535(含))。

除了前面列出的8种基本数据类型之外,Java编程语言还通过java.lang.String类,提供对字符串的专门支持。把字符串包围在双引号中,就会自动地创建新的String对象;例如String s = "this is a string";。String对象是不可变的(immutable),这就是说创建之后,就不能改变它们的值。从技术上说,String类不是基本数据类型,但是考虑到语言给它提供的专门支持,你也可以把它看作基本数据类型。在第8章中将更多地介绍String类。

1. 默认值

声明字段时,不必非要为其赋值。被声明但是没有初始化的字段会被编译器设置为合理的默认值。总的来说,根据数据类型,这个默认值是0或者null。但是,依靠这样的默认值一般被认为是不良的编程方式。

表3-1总结了前述数据类型的默认值。

表3-1  数据类型及其默认值

数据类型

默认值(用于字段)

byte

0

short

0

int

0

long

0L

float

0.0f

double

0.0d

char

'\u0000'

String(或者任何对象)

null

boolean

false

局部变量稍有不同;编译器永远都不会给未初始化的局部变量分配默认值。如果在声明局部变量时不能初始化它,就要确保在使用之前为其赋值。访问未经初始化的局部变量将导致编译时错误。

2. 字面量

你或许会注意到,当初始化原始类型的变量时,没有使用关键字new。原始类型是语言内置的特殊数据类型;它们不是从类创建的对象。字面量(literal)是固定值的源代码表现形式;字面量直接出现在代码中,无需计算。如下所示,可以为基本数据类型的变量分配字面量。

boolean result = true;

char capitalC = 'C';

byte b = 100;

short s = 10000;

int i = 100000;

整数类型(byte、short、int和long)使用十进制、八进制或者十六进制计数系统。十进制是你每天都使用的计数系统;它的基数为10,使用的数字从0到9。八进制计数系统的基数为8,由数字0到7构成。十六进制计数系统的基数为16,使用数字0到9和字母A到F。对于通用程序设计来说,十进制系统可能是你将使用的唯一一种计数系统。但是,如果你需要八进制或者十六进制,下面的例子演示了正确的语法。前缀0表示八进制,而前缀0x表示十六进制。

int decVal = 26;    // The number 26, in decimal

int octVal = 032;   // The number 26, in octal

int hexVal = 0x1a;  // The number 26, in hexadecimal

浮点数类型(float和double)也可以用E或者e(用于科学记数法)、F或者f(32位浮点字面量)和D或者d(64位双精度字面量;这是默认的,并且按照惯例可以省略)表示。

double d1 = 123.4;

double d2 = 1.234e2; // same value as d1,

                         // but in scientific notation

float f1  = 123.4f;

char和String类型的字面量可能包含任意Unicode(UTF-16)字符。如果你的编辑器和文件系统允许,就可以在代码中直接使用这样的字符。如果不允许,可以使用“Unicode转义符”,比如'\u0108'(带有升调符号的大写C),或者"S\u00ED se\u00F1or"(西班牙语的Sí Señor)。对于char类型的字面量,总要使用单引号(');对于String类型的字面量,总要使用双引号(")。Unicode转义序列可以用在程序中的其他位置(比如字段名称中),并非只能在char和String字面量中使用。

Java编程语言还支持几个特殊的char和String字面量的转义序列:\b(退格)、\t(制表符)、\n(换行)、\f(换页)、\r(回车)、\"(双引号)、\'(单引号)和\\(反斜线)。

还有一个特殊的null字面量,它可以用作任何引用类型的值。null可以赋值给除属于原始类型的变量之外的任何变量。除了测试其存在之外,对null值没有什么可做的操作。因此,在程序中经常使用null作为标识,表示某个对象不可用。

最后,还有一种特殊类型的字面量,称为类字面量(class literal),它的构成方式是类名称加后缀“.class”,比如String.class。这引用表示类型本身的对象(类型为Class)。

3.1.3  数组

数组(array)是存储数量固定的单一类型值的容器对象。数组的长度在创建数组时就固定下来了。在创建之后,数组的长度是不可变的。你在应用程序“Hello World!”的main方法中看到过数组的例子。这一节更加详细地讨论数组。

数组中的每个项称为元素(element),通过每个元素的数字索引(也称下标)访问元素。如图3-1所示,索引的数字从0开始。因此,在索引8的位置访问第9个元素。

数组长度为10

 

索引

 

元素
(位于索引8)

 

第一个索引

 

图3-1  具有10个元素的数组

下面的程序ArrayDemo创建一个由整数构成的数组,它把一些值存放到数组中,然后把每个值发送到标准输出。

class ArrayDemo {

  public static void main(String[] args) {

    int[] anArray;                  // declares an array of integers

    anArray = new int[10];    // allocates memory for 10 integers

    anArray[0] = 100; // initialize first element

    anArray[1] = 200; // initialize second element

    anArray[2] = 300; // etc.

    anArray[3] = 400;

    anArray[4] = 500;

    anArray[5] = 600;

    anArray[6] = 700;

    anArray[7] = 800;

    anArray[8] = 900;

    anArray[9] = 1000;

    System.out.println("Element at index 0: " + anArray[0]);

    System.out.println("Element at index 1: " + anArray[1]);

    System.out.println("Element at index 2: " + anArray[2]);

    System.out.println("Element at index 3: " + anArray[3]);

    System.out.println("Element at index 4: " + anArray[4]);

    System.out.println("Element at index 5: " + anArray[5]);

    System.out.println("Element at index 6: " + anArray[6]);

    System.out.println("Element at index 7: " + anArray[7]);

    System.out.println("Element at index 8: " + anArray[8]);

    System.out.println("Element at index 9: " + anArray[9]);

  }

}

这个程序的输出是:

Element at index 0: 100

Element at index 1: 200

Element at index 2: 300

Element at index 3: 400

Element at index 4: 500

Element at index 5: 600

Element at index 6: 700

Element at index 7: 800

Element at index 8: 900

Element at index 9: 1000

 

在现实的编程工作中,可以使用一种循环结构(looping construct)迭代处理数组的每个元素,而不是像前面这样单独地编写每一行。但是,这个示例清楚地演示了数组的语法。你将在3.4节中学习各种循环结构(for、while和do-while)。

1. 声明引用数组的变量

前面的程序使用下面这行代码声明anArray:

int[] anArray;         // declares an array of integers

和其他类型变量的声明一样,数组的声明由两个部分构成:数组类型和数组名称。数组类型写作type[],其中type是被包含元素的数据类型;方括号是指示这个变量包含数组的专用符号。数组的长度不是其类型的一部分(这就是括号中为空白的原因)。数组的名称可以是你想使用的任何名称,只要名称遵循前面3.1.1节中讨论的规则和惯例即可。和其他类型的变量一样,声明不会实际创建一个数组——它只是通知编译器,这个变量将包含指定类型的数组。

类似的,你可以声明其他类型的数组:

byte[] anArrayOfBytes;

short[] anArrayOfShorts;

long[] anArrayOfLongs;

float[] anArrayOfFloats;

double[] anArrayOfDoubles;

boolean[] anArrayOfBooleans;

char[] anArrayOfChars;

String[] anArrayOfStrings;

你也可以把方括号放在数组名称的后面:

float anArrayOfFloats[];  // this form is discouraged

但是,按照惯例不推荐这样做;括号标识数组类型,应该和类型签名一起出现。

2. 创建、初始化和访问数组

创建数组的一个途径是使用new操作符。ArrayDemo程序中的下列语句为包含10个整数元素的数组分配足够的内存,并且把该数组赋值给anArray变量。

anArray = new int[10];  // create an array of integers

如果遗漏了这行语句,编译器就会输出如下错误消息,并且编译会失败:

ArrayDemo.java:4: Variable anArray may

                        not have been initialized.

下面这几行代码为数组的每个元素赋值:

anArray[0] = 100; // initialize first element

anArray[1] = 200; // initialize second element

anArray[2] = 300; // etc.

可以通过每个数组元素的数字索引访问它们:

System.out.println("Element 1 at index 0: " + anArray[0]);

System.out.println("Element 2 at index 1: " + anArray[1]);

System.out.println("Element 3 at index 2: " + anArray[2]);

另外,也可以使用简捷语法创建和初始化数组:

int[] anArray = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};

其中,数组的长度由{和}之间值的数量确定。

也可以声明由数组构成的数组[也称为多维(multidimensional)数组],声明方法是使用两组或者两组以上的方括号,比如String[][] names。因此,必须通过对应的数字索引值访问每个元素。

在Java编程语言中,多维数组仅仅是其组成元素本身也是数组的数组。这和C或者Fortran语言中的数组不同。其结果是允许行的长度可变,如下面的程序MultiDimArrayDemo所示:

class MultiDimArrayDemo {

  public static void main(String[] args) {

    String[][] names = {{"Mr. ", "Mrs. ", "Ms. "},

                            {"Smith", "Jones"}};

    System.out.println(names[0][0] + names[1][0]); // Mr. Smith

    System.out.println(names[0][2] + names[1][1]); // Ms. Jones

  }

}

这个程序的输出如下:

Mr. Smith

Ms. Jones

最后,你可以使用内置的length属性确定数组的长度。代码

System.out.println(anArray.length);

将把数组长度发送到标准输出。

3. 复制数组

System类有一个arraycopy方法,可以使用它把数据从一个数组复制到另一个数组:

public static void arraycopy(Object src,

                                 int srcPos,

                                 Object dest,

                                 int destPos,

                                 int length)

两个Object参数分别指定被复制的数组和复制到的数组。三个int参数分别指定源数组中的开始位置、目的数组中的开始位置和要复制的数组元素的数量。

下面的程序ArrayCopyDemo声明一个包含char元素的数组,这些字符拼写出单词“decaffeinated”。它使用arraycopy把数组元素的子序列复制到第二个数组:

class ArrayCopyDemo {

  public static void main(String[] args) {

    char[] copyFrom = { 'd', 'e', 'c', 'a', 'f', 'f', 'e',

                            'i', 'n', 'a', 't', 'e', 'd' };

    char[] copyTo = new char[7];

    System.arraycopy(copyFrom, 2, copyTo, 0, 7);

    System.out.println(new String(copyTo));

  }

}

这个程序的输出是:

caffein

3.1.4  变量小结

“字段”和“变量”都是Java语言中的术语。实例变量(非静态字段)对于类的每个实例是唯一的。类变量(静态字段)使用修饰符static声明;不管类被实例化了多少次,类变量严格地都只有一个副本。局部变量存储方法内部的临时状态。参数是为方法提供额外信息的变量;局部变量和参数总被分类为“变量”(而不是“字段”)。命名字段或者变量时,应该(或者必须)遵守一些规则和惯例。

8种基本数据类型是:byte、short、int、long、float、double、boolean和char。java.lang.String类代表字符串。编译器会为上述类型的字段分配合理的默认值;对于局部变量,不会为其分配默认值。字面量是固定值的源代码表现形式。数组是存储数量固定的单一类型值的容器对象。在创建数组时设置数组的长度。创建之后,数组长度是固定不变的。

问题和练习:变量

问题

1.   术语“实例变量”是___的另一个名称。

2.   术语“类变量”是___的另一个名称。

3.   局部变量存储临时状态;在___内声明它。

4.   在方法签名的前后括号中声明的变量被称为___。

5.   Java编程语言支持的8种基本数据类型是什么?

6.   字符串由___类表示。

7.   ___是存储数量固定的单一类型值的容器对象。

练习

1.   创建一个定义一些字段的小程序。尝试创建一些非法字段名称,然后查看编译器产生了什么样的错误消息。使用命名规则和惯例作为指导方针。

2.   在练习1创建的程序中,不初始化字段,然后输出它们的值。对局部变量进行同样的操作,看看编译器会产生什么样的错误消息。熟悉常见的编译器错误,这能够帮助你更容易地识别出代码中的错误。

答案

可以在以下位置找到“问题”和“练习”的答案:

tutorial/java/nutsandbolts/QandE/answers_variables.html

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论