变量

本文深入探讨Java中的变量类型,包括实例变量、类变量、局部变量等,同时讲解了基本数据类型、默认值及数组的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
在前一章中讲过,对象把它的状态存储在字段(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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值