正如我们上一节中所学,对象将它的状态存储在"字段(fields)"中
int cadence = 0; int speed = 0; int gear = 1; 复制代码
"何为对象?"中向您介绍了“字段(fields)”,但是您可能会存在一些疑问,例如:命名"字段(fields)"的规则和约定是什么?除了int,还有一些什么数据类型?字段声明时必须初始化吗?如果没有显示初始化,它们具有默认的值吗?本节课我们将探索这些问题的答案,但是在这之前,下面是您必须首先注意的一些技术区别。在Java编程语言中,术语“字段(fields)”和“变量(Variables)”都被采用,对于新开发人员来说,这是一个常见的困惑源,它们通常指代的似乎是同一个事物。
Java编程语言定义了以下几种变量:
实例变量(非静态字段):技术上来说,对象将各自的状态存储在“非静态字段”中,也就是说,声明字段时没有指定static关键字。非静态字段也称为实例变量,因为他们的值在每个类的实例中(或者说:在每个对象中)都是不一样的。一辆自行车的当前速度与另一辆自行车的当前速度无关。
类变量(静态字段):使用静态修饰符(static)声明的任何字段称为类变量;这告诉编译器,不管这个类实例化了多少次,该变量都仅存在一个副本。对于特定类型自行车齿轮数的字段可以声明为static,这将赋予所有实例相同的齿轮数。代码段:static int numGears = 6;将创建一个静态字段,此外,可以添加关键字final来表示齿轮的数量永远不会改变。
局部变量:与对象将状态存储在字段中相似,方法经常将其暂时的状态存在局部变量中。声明局部变量的语法类似于声明字段(int count = 0;)。没有特殊的关键字指定其为局部变量;这完全取决于变量声明的位置——方法的开括号和闭括号之间;因此,局部变量只对声明它们的方法可见;其他类无法访问它们。
参数: 在自行车类和“Hello world!”应用中您已经看到过参数的示例。回想一下main方法:public static void main(String[] args),这里,args是这个方法的参数。重要的是要记住参数总是被分类为“变量”而不是“字段”。这也适用于其他接受参数的构造方法(如构造函数和异常处理器),您将在本教程的后面学习这些
话虽如此,如果我们谈论的是“一般的字段”(不包括局部变量和参数),我们可以简单地说“字段”。如果讨论适用于“以上所有”,我们可以简单地说“变量”。如果上下文需要区别,我们将使用特定的术语(静态字段、局部变量等)。你可能会经常看到"成员"这个术语。字段、方法和嵌套类型统称为其成员。
每种编程语言对于允许使用的命名方式都有自己的一套规则和约定,Java编程语言也不例外,变量命名的规则和约定可以总结如下:
变量名区分大小写。变量的名称可以是任何合法的标识符——不限长度的Unicode字母和数字序列,以字母,“$”符,或下划线“_”开头。然而,习惯上,变量名总是以字母开头,而不是“$”或“_”。此外,根据惯例,美元符号字符从未使用过。您可能会发现在某些情况下,自动生成的名称将包含美元符号,但是变量命名时应该避免使用。下划线字符也有类似的约定;虽然从技术上讲,以“_”作为变量名的开头是合法的,但是不鼓励这样做。空格是不被允许使用的。
后续字符可以是字母、数字、美元符号或下划线字符。当为变量选择名称时,使用完整的单词而不是模糊的缩写。这样做将使您的代码更容易阅读和理解。在许多情况下,它还会使您的代码更直观;例如,名为cadence、speed和gear的字段比缩写版本(如s、c和g)更直观。还要记住,您选择的名称不能是关键字或保留字
如果您选择的名称只包含一个单词,请将该单词全部用小写字母拼写。如果由一个以上的单词组成,则将后面每个单词的第一个字母大写。如:gearRatio、currentGear。对于static final int NUM_GEARS = 6,约定有少许变化,将每个字母大写,然后用下划线分隔后面的单词。按照惯例,下划线字符永远不会在其他地方使用
Java编程语言是静态类型语言,这意味着所有的变量在使用前必须先声明。包括声明变量的类型和名称,正如你已经见过的:
int gear = 1; 复制代码
这样做会告诉程序存在一个名为“gear”的字段,该字段保存数值数据,初始值为“1”。 变量的数据类型决定了它可能包含的值,以及可能对其执行的操作。除了int,Java程序语言还支持其他7种基本数据类型。基本类型由语言预定义,并由保留关键字命名。不同基本数据类型之间的值并不共享。Java支持的8种基本数据类型分别为:
byte:byte数据类型是8-bit补码表示的有符号整数。最小值为-128,最大值为127(含)。byte数据类型在大数组中节省内存时显得十分有用,在大数组中,内存的节省实际上非常重要。范围限制有助于阐明代码时可以用其代替int;变量的范围有限这一事实可以作为文档的一种形式。
short:short数据类型是16-bit补码表示的有符号整数。最小值为-32768,最大值为32767(含)。与byte一样,同样的指导原则也适用:在实际需要节省内存的情况下,您可以使用short来在大数组中节省内存。
int:默认情况下,int数据类型是一个32-bit补码表示的有符号整数,最小值为-2^31,最大值为2^31-1。在Java SE 8及更高版本中,可以使用int数据类型表示范围为0到2^32-1的无符号32-bit整数。使用Integer类将int数据类型用作无符号整数,更多信息见The Number Classes一节,在Integer类中添加了compareUnsigned、divideUnsigned等静态方法来支持无符号整数的算术运算。
long:long数据类型是一个64-bit补码表示的有符号整数,最小值为-2^64,最大值为2^64-1。在Java SE 8及更高版本中,您可以使用long数据类型来表示范围为0到2^64-1的无符号64-bit整数。当您需要比int提供的值范围更大的值时,可以使用这种数据类型。Long类还包含compareUnsigned、divideUnsigned等方法来支持无符号long的算术操作
float:float数据类型是一个单精度的32位IEEE 754浮点数,它的取值区间超出了我们的讨论范围,但是在 Floating-Point Types, Formats, and Values 一节中有详细说明。与byte和short的建议一样,如果需要在浮点数的大数组中节省内存,请使用float(而不是double)。这种数据类型不应该用于存储精确的值,比如货币。为此,您需要使用java.math.BigDecimal类。Numbers and Strings中涵盖了BigDecimal和其它Java平台提供的有用的类。
double:double数据类型是一个双精度的64位IEEE 754浮点数,它的取值区间超出了我们的讨论范围,但是在 Floating-Point Types, Formats, and Values 一节中有详细说明。对于包含小数的值,这种数据类型通常是默认选择。如上所述,这种数据类型永远不应该用于精确的值,比如货币。
boolean:boolean数据类型只有两个可能的值:true和false。使用此数据类型存储"真/假"条件。这个数据类型表示1-bit的信息,但是它实际占用内存的“大小”并不是精确定义的。
char: char数据类型是一个16位Unicode字符。 最小值为 '/u0000' (0),最大值为 '/uffff' (65535)
除了上面列出的八种基本数据类型之外,Java编程语言还通过Java .lang.String提供了对字符串的特殊支持。将字符串括在双引号内将自动创建一个新的字符串对象;例如: String s = "this is a string";String对象是不可变的,这意味着一旦创建,它们的值就不能更改。String类在技术上不是原始数据类型,但是考虑到该语言对它的特殊支持,您可能会这样认为。您将在 Numbers and Strings 中了解关于String类的更多信息
声明字段时并不总是需要赋值,编译器将为已经声明但未初始化的字段设置合理的默认值。一般来说,根据数据类型的不同,这个默认值将是零或null。然而,依赖这些默认值通常被认为是糟糕的编程风格
下表总结了上述数据类型的默认值:
局部变量略有不同;编译器从不将默认值分配给未初始化的局部变量。如果无法在声明局部变量的地方初始化该变量,请确保在尝试使用它之前为其赋值。访问未初始化的局部变量将导致编译时错误。
您可能已经注意到,在初始化基本类型的变量时不使用new关键字。基本类型是构建在语言中的特殊数据类型;它们不是通过类创建的对象。字面量是值的源代码表示,直接在代码中表示,不需要计算。如下所示,可以将字面量赋值给基本类型的变量:
boolean result = true; char capitalC = 'C'; byte b = 100; short s = 10000; int i = 100000; 复制代码
如果以字母L或l结尾,整数字面量的类型为long;否则它是int类型的。建议使用大写字母L,因为小写字母l很难与数字1区分.整数类型byte、short、int和long的值可以从int字面量创建。long类型的值超出int的范围,可以从long字面量创建。整数字面值可以由这些数字系统表示:
对于一般编程,十进制系统可能是您将使用的唯一数字系统。但是,如果需要使用另一个数字系统,下面的示例显示了正确的语法。前缀0x表示十六进制,0b表示二进制:
// The number 26, in decimal int decVal = 26; // The number 26, in hexadecimal int hexVal = 0x1a; // The number 26, in binary int binVal = 0b11010; 复制代码
如果以字母F或f结尾,浮点字面量的类型为float;否则,它的类型是double,并且可以选择以字母D或d结尾。浮点类型(float和double)也可以使用E或e(用于科学表示法)、F或f(32位float字面量)和D或d(64位double字面量;这是默认值,按惯例可省略)来表示。
double d1 = 123.4; // same value as d1, but in scientific notation double d2 = 1.234e2; float f1 = 123.4f; 复制代码
字符和字符串类型的字面量可以包含任何Unicode (UTF-16)字符。如果编辑器和文件系统允许,可以在代码中直接使用这些字符。如果不允许,您可以使用“Unicode转义”,比如“/u0108”(大写的C)或"S/u00ED Se/u00F1or"(西班牙语中的Sí Señor)。对于char字面量,请始终使用'单引号';对于String字面量,请使用“双引号”。Unicode转义序列可以在程序的其他地方使用(例如在字段名中),而不仅仅是在字符或字符串字面量中。Java编程语言还支持一些用于字符和字符串字面量的特殊转义序列:/b(退格)、/t(制表符)、/n(换行)、/f(表格换行)、/r(回车)、/"(双引号)、/'(单引号)和//(反斜杠)
还有一个特殊的null字面量,可以用作任何引用类型的值。除基本类型的变量外,可以将null赋给任何变量。除了测试null值的存在性之外,您对null值几乎无能为力。因此,在程序中经常使用null作为标记来指示某些对象不可用。
最后,还有一种特殊的字面量,叫做类字面量,它是由一个类型名加上“.class”组成的;例如String.class。这表示对象本身的类型(类的类型)。
在Java SE 7和更高版本中,任何下划线字符(_)都可以出现在数字字面量中数字之间的任何位置。这个特性支持您将数字字面量进行分隔,以提高代码的可读性。 例如,如果您的代码包含很多位数字,您可以使用下划线将数字分成三组,类似于使用逗号或空格等标点符号作为分隔符。 下面的示例展示了在数字字面量中使用下划线的方法:
long creditCardNumber = 1234_5678_9012_3456L; long socialSecurityNumber = 999_99_9999L; float pi = 3.14_15F; long hexBytes = 0xFF_EC_DE_5E; long hexWords = 0xCAFE_BABE; long maxLong = 0x7fff_ffff_ffff_ffffL; byte nybbles = 0b0010_0101; long bytes = 0b11010010_01101001_10010100_10010010; 复制代码
只能在数字之间放置下划线;不能在以下位置放置下划线:
下面的例子演示了数字字面量中有效和无效的下划线位置:
// Invalid: cannot put underscores // 小数点相邻位置 float pi1 = 3_.1415F; // Invalid: cannot put underscores // 小数点相邻位置 float pi2 = 3._1415F; // Invalid: cannot put underscores // 在L后缀之前 long socialSecurityNumber1 = 999_99_9999_L; // OK (decimal literal) int x1 = 5_2; // Invalid: cannot put underscores // 字面量的开头或结尾 int x2 = 52_; // OK (decimal literal) int x3 = 5_______2; // Invalid: cannot put underscores // 在0x前缀中 int x4 = 0_x52; // Invalid: cannot put underscores // 在数字的开头 int x5 = 0x_52; // OK (hexadecimal literal) int x6 = 0x5_2; // Invalid: cannot put underscores // 数字的结尾 int x7 = 0x52_; 复制代码
数组是一个容器对象,它存储固定数量的单一类型值。数组的长度是在创建数组时确定的。创建之后,它的长度就是固定的。您已经在“Hello World!”应用程序的主方法中看到了一个数组示例(main(String[] args))。本节更详细地讨论数组:
数组中的每一项都称为一个元素,每个元素都由其数值索引访问。如上图所示,编号从0开始。例如,第9个元素将在索引8处访问。
下面的程序ArrayDemo创建一个整数数组,在数组中放入一些值,并将每个值打印到标准输出:
class ArrayDemo { public static void main(String[] args) { // declares an array of integers int[] anArray; // allocates memory for 10 integers anArray = new int[10]; // initialize first element anArray[0] = 100; // initialize second element anArray[1] = 200; // and so forth anArray[2] = 300; 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 复制代码
在实际的编程环境中,您可能会使用受支持的循环结构来遍历数组的每个元素,而不是像前面的示例那样单独地编写每一行。然而,这个例子清楚地说明了数组语法。在 "控制流(Control Flow)"一节中您将了解各种循环结构(for、while和do-while)。
前面的程序使用以下代码行声明一个数组(名为anArray):
// declares an array of integers int[] anArray; 复制代码
与其他类型变量的声明一样,数组声明有两个组件:数组的类型和数组的名称。数组的类型被写成type[],其中type是所包含元素的数据类型;[]是一些特殊的符号,表示这个变量包含一个数组。数组的大小不是其类型的一部分(这就是为什么[]是空的)。数组的名称可以是任何您想要的名称,只要它遵循前面在命名部分中讨论的规则和约定即可。与其他类型的变量一样,声明实际上并不创建数组;它只是告诉编译器该变量将保存指定类型的数组。
类似地,您可以声明其他类型的数组:
byte[] anArrayOfBytes; short[] anArrayOfShorts; long[] anArrayOfLongs; float[] anArrayOfFloats; double[] anArrayOfDoubles; boolean[] anArrayOfBooleans; char[] anArrayOfChars; String[] anArrayOfStrings; 复制代码
你也可以把括号放在数组的名字后面:
// this form is discouraged float anArrayOfFloats[]; 复制代码
然而,惯例不鼓励这种形式;方括号标识数组类型,并应与类型指定一起出现。
创建数组的一种方法是使用new操作符。下面ArrayDemo程序中的语句申请一个足够容纳10个整数的数组,并将数组分配给anArray变量:
// create an array of integers anArray = new int[10]; 复制代码
如果缺少该语句,编译器将打印如下错误,编译失败:
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; // and so forth 复制代码
每个数组元素都由其数值索引访问:
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 }; 复制代码
数组的长度由大括号中逗号分隔的值的数量决定。
您还可以使用两个或多个括号(如String[][]名称)声明数组的数组(也称为多维数组)。因此,每个元素必须由相应数量的索引值访问。
在Java编程语言中,多维数组是一个数组,其组件本身就是数组。这与C或Fortran中的数组不同。其结果是允许行长度变化,如下面的MultiDimArrayDemo程序所示:
class MultiDimArrayDemo { public static void main(String[] args) { String[][] names = { {"Mr. ", "Mrs. ", "Ms. "}, {"Smith", "Jones"} }; // Mr. Smith System.out.println(names[0][0] + names[1][0]); // Ms. Jones System.out.println(names[0][2] + names[1][1]); } } 复制代码
程序输出为:
Mr. Smith Ms. Jones 复制代码
最后,您可以使用内置的length属性来确定任何数组的大小。以下代码将数组的大小打印到标准输出:
System.out.println(anArray.length); 复制代码
System类有一个arraycopy方法,您可以使用它来有效地将数据从一个数组复制到另一个数组:
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 复制代码
这两个Object参数指定源数组和要目的数组,三个int参数指定源数组中的起始位置、目标数组中的起始位置和要复制的数组元素的数量。
下面的程序ArrayCopyDemo声明了一个char元素数组,存储单词“decaffeated”。它使用System.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 复制代码
数组是编程中使用的一个强大而有用的概念。Java SE提供了一些方法来执行与数组相关的一些最常见的操作。例如,ArrayCopyDemo示例使用系统类的arraycopy方法,而不是手动遍历源数组的元素并将每个元素放入目标数组。这是在后台执行的,允许开发人员只使用一行代码来调用方法。
为了方便起见,Java SE在java.util.Arrays 类中提供了几种执行数组操作的方法(常见的任务:如复制、排序和搜索)。例如,可以修改前面的示例,使用java.util.Arrays类的copyOfRange方法。正如您在ArrayCopyOfDemo示例中所看到的。不同之处在于,使用copyOfRange方法不需要在调用该方法之前创建目标数组,因为该方法返回目标数组。
class ArrayCopyOfDemo { public static void main(String[] args) { char[] copyFrom = {'d', 'e', 'c', 'a', 'f', 'f', 'e', 'i', 'n', 'a', 't', 'e', 'd'}; char[] copyTo = java.util.Arrays.copyOfRange(copyFrom, 2, 9); System.out.println(new String(copyTo)); } } 复制代码
正如您所看到的,尽管这个程序的代码更少,但是输出却是相同的(caffein)。注意,copyOfRange方法的第二个参数是要复制的范围的初始索引,而第三个参数是要复制的范围的最终索引。在本例中,要复制的范围不包括索引9处的数组元素('a')。
下面例举java.util.Arrays类中提供的一些其他有用的方法: