语言的基本特性,包括变量,基本数据类型,数组,运算符和控制流。
变量(Variables)
一个对象将状态存储在域(字段)中,在Java中也会使用术语“变量”来表示。
int cadence = 0;
int speed = 0;
int gear = 1;
学习对象的概念时,介绍了字段。但您可能还有几个问题,例如:
- 命名字段有什么规则和约定?
- 除了int,还有什么其他数据类型?
- 是否必须在声明时初始化字段?
- 如果字段未显式初始化,字段是否会被分配了默认值?
注意:filed翻译成域或者字段,variables翻译成变量,在Java编程语言中,“字段”和“变量”这两个术语很容易被混淆,因为它们通常似乎指的是同一个东西(后面讲两者该如何区分)。
Java编程语言定义了以下几种变量:
-
Instance Variables (Non-Static Fields)实例变量(非静态字段)
从技术上讲,非静态字段就是没有声明static关键字的字段。非静态字段也称为实例变量,因为它们的值对于类的每个实例(每个对象)是唯一的;一辆自行车的currentSpeed与另一辆自行车的currentSpeed是独立不相关的。 -
Class Variables (Static Fields)类变量(静态字段)
类变量是使用了static修饰符声明的字段;它告诉编译器,不管这个类被实例化多少次,这个变量只有一份。如果某种特定种类的自行车的齿轮数量是相同的,那么这个自行车类表示齿轮数量的字段可以被标记为静态字段:static int numGears = 6。另外,可以添加关键字final来表示齿轮的数量将保持不变。 -
Local Variables局部变量
类的方法通常将其临时状态存储在局部变量中。声明局部变量的语法与声明字段一样(例如,int count = 0;),没有特殊的关键字将变量指定为局部变量。一个变量是不是局部变量,看这个变量被声明的位置是不是在类的方法。因此,局部变量仅对它们被声明的方法可见;类的其余部分不能访问它们。 -
Parameters参数
您已经在Bicycle类的main()方法中看到过参数的示例:public static void main(String [] args)。这里,args变量是此方法的参数。注意,参数总是被分类为“变量”而不是“字段”。这也适用于其他参数接收结构(例如构造函数和异常处理程序)。
字段和变量这两个术语很容易混淆,在讨论字段和变量时使用以下准则:
如果我们谈论“一般的字段”(不包括局部变量和参数,即上面4种变量的前两种),我们可以说“字段”。 如果讨论适用于“上述所有”,我们说“变量”。 如果上下文要求区分,我们可以适当地使用特定的术语(静态字段,局部变量等)。
你可能有时还会看到“成员”member一词。 字段,方法和嵌套类型统称为类的成员。
变量的命名
Java命名变量的规则和约定可以总结如下:
变量名称区分大小写,长度不限。
不允许使用空格。
命名名称不能是关键字或保留字。
-
变量名的首字符可以是:
- 字母
- 美元符号“$”
- 下划线字符“_”
我们约定始终以字母作为您的变量名的开头,而不是“$”或“_”。一般来说美元符号字符在命名中从未使用过。您可能会发现在某些情况下,自动生成的名称可能会包含美元符号,但您自己定义的变量名称应该避免使用它。下划线字符也是类似的。用“$”和“_”开始的变量名是合法的,但不鼓励这种做法。
后续字符可以是字母,美元符号,下划线字符,数字。变量名称使用全词,不要用隐晦的缩写,这样可以使代码更容易阅读和理解。例如,cadence, speed和gear比缩写s,c和g更直观。
如果您的变量命名的名称只包含一个词,请用小写字母拼写整个单词,例如cadence和speed;如果由多个单词组成,则第一个词全小写,后面每个词的首字母大写,例如gearRatio和currentGear;如果变量存储一个常量值,大写每个字母,并用下划线字符分隔后续单词,例如static final int NUM_GEARS = 6。按照前面的约定,下划线字符不在其他地方使用。
基本数据类型(Primitive Data Types)
A primitive type is predefined by the language and is named by a reserved keyword. Primitive values do not share state with other primitive values.
Java编程语言是静态的,这意味着所有变量必须先声明,然后才能使用,包括声明变量的类型和名称,如您所见:
int gear = 1;
这行代码会告诉程序,一个名为“gear”的字段用于保存的数据类型是数字,并且初始值为“1”。 变量的数据类型决定了其可能包含的值,以及可对其执行的操作。
Java编程语言支持八种基本数据类型:
byte
short
int
long
float
double
boolean
char
除了上面的8种基本数据类型之外,Java还通过java.lang.String类提供对字符串的特殊支持。 将字符串括在双引号中将自动创建一个String对象;例如,String s =“this is a string”; String对象是不可变的,String一旦被创建,它的值不能被改变。
String类在不是原始数据类型
默认值
在声明字段时,不是必须要分配值。 声明但未初始化的字段将由编译器设置一个默认值。 一般来说,默认值为零或null(取决于数据类型)。 但是,依赖编译器赋予字段默认值,通常被认为是坏的编程风格。
Data Type | Default Value |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
boolean | false |
char | '\u0000' |
String(or any object) | null |
局部变量略有不同; 编译器不会为未初始化的局部变量分配一个默认值。 如果您无法在声明它的地方初始化局部变量,请确保在使用它之前为其分配一个值。 访问未初始化的局部变量将导致编译时报告错误。
字面量
您可能已经注意到,在初始化基本数据类型的变量时,不使用new关键字。 基本数据类型是内置于语言中的特殊数据类型; 它们不是从类创建的对象。 直接为一个基本类型的变量赋值:
boolean result = true;
char capitalC = 'C';
byte b = 100;
short s = 10000;
int i = 100000;
整数字面量
整数数据类型,如果以字母L或l结尾,数据类型是long,否则是int类型; long类型结尾建议使用大写字母L,因为小写字母l难以与数字1区分开。
整数类型byte,short,int和long的值可以从int类型数据创建。 数据值超过int范围能表示的整数类型可以从long类型数据创建。整数可以用这些进制表示:
- 十进制:其数字由数字0至9组成; 我们日常用的计数就是十进制计数
- 十六进制:其数字由数字0至9和字母A至F组成
- 二进制:其数字由数字0和1组成(您可以在Java SE 7和更高版本中创建二进制数据)
前缀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位)和D或d(64位双精度;默认浮点类型) 来表示。
double d1 = 123.4;
// same value as d1, but in scientific notation
double d2 = 1.234e2;
float f1 = 123.4f;
字符(串)字面量
字符串类型char和String可以包含任何Unicode(UTF-16)字符。如果您的编辑器和文件系统允许,您可以直接在代码中使用这样的字符。如果不是,您可以使用“Unicode转义”,例如'\ u0108'(大写C 顶上有个^符号)或“S \ u00ED Se \ u00F1or”(西班牙语SíSeñor)。
字符使用‘单引号’
字符串使用“双引号”
Java编程语言支持char和String的一些特殊转义序列:\ b(退格),\ t(制表),\ n(换行),\ f(换页),\ r(回车),\“(双引号),'(单引号)和\(反斜杠)。
有一个特殊字面量null,null可以作为任何类型的值,可以分配给任何变量(除了基本类型的变量)。null通常在程序中用来指示某些对象不可用。
最后,还有一种特殊类型的字面量称为类字面量,由类型名称和“.class”构成。例如,String.class。类字面量是 表示该类型本身的对象(这个对象的类型是Class)。
java中任何一个类,都会有一个Class对象与这个类对应,在这个Class对象中,保存着实例化该类时所需要的基本信息,A.class 返回的就是类A的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;
您只能在数字之间使用下划线; 不能将下划线放在以下位置:
- 在数字的开始或结束
- 与浮点数字中的小数点相邻
- 在F或L后缀之前
- 在字符串里的数字之间的位置
Arrays(数组)
数组是一个容器对象,容纳一定数量的一种数据类型的值。 数组的长度在创建数组时确定;创建后,其长度是固定的。
数组中的每个条目都称为元素,每个元素通过其数字索引访问。 如上图所示,数字索引的编号从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
在实际编程中,您可能会使用循环结构来循环遍历数组的每个元素,而不是像上面的示例一样每一个元素都用一行来单独访问。 但是,该示例清楚地说明了数组的语法。 您将在“控制流”章节了解各种循环结构(for,while和do-while)。
声明一个数组
// 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操作符
// 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[][] names。和C,Fortran语言中的数组不一样的是,java中的多位数组的元素本身也是数组,比如二维数组实际上是一个数组,它的每个元素都是一个一维数组。数组x的长度是数组中元素的个数,可以用x.length获取该值;元素x[0],x[1],x[2],x[3],...,,x[x.length-1]也是数组,可以使用x[0].length,x[1].length,...,x[x.length-1].length获取它们的长度。
下面是一个多维数组的例子:
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
二维数组就是将一维数组当成元素,放到一个一维数组里去;三维数组就是将二维数组当成元素放到一个一维数组里去:
一维数组{1,2,3},
二维数组{{1,2,3},{4,5,6}},
三维数组{{{1,2},{3,4}},{{5,6},{7,8}}},
你可以使用内建方法得到数组的大小:
System.out.println(anArray.length);
数组的复制
系统提供了数组复制的方法,可以让用户将数据从一个数组复制到另一个数组:
public static void arraycopy(Object src, int srcPos,
Object dest, int destPos, int length)
Object src是源数组,Object dest是目标数组;将源数组的值复制到目标数组;srcPos是源数组复制的起始位置,destPos是目标数组复制的起始位置,length是复制的元素的个数:
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提供了一些常用的数组操作方法,比如上面的例子用了System类中的方法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类还包括了一些其他有用的数组操作:
- 在数组中搜索一个给定的值,返回值的索引位置 (binarySearch).
- 比较两个数组是否相等 (equals).
- 将一个值填到数组的每个元素位置(fill).
- 按升序对数组排序,用sort方法或者用Java SE 8中引入的parallelSort方法执行。在多处理器系统上对大型数组进行并行排序比顺序排序要更快。
总结
Java编程语言使用“字段”和“变量”作为其术语的一部分。实例变量(非静态字段)对每个对象是唯一的。类变量(静态字段)是使用static修饰符声明的字段;一个类只有一份,而不管该类被实例化的次数。局部变量是在方法中存储临时状态的变量。参数是为方法提供额外信息的变量;局部变量和参数总是被分类为“变量”(而不是“字段”)。在命名字段或变量时,您应该必须命名的规则和约定。
八种基本数据类型:byte,short,int,long,float,double,boolean和char。 java.lang.String类表示字符串。编译器将为上述类型的字段分配一个默认值;对于局部变量,永远不会分配默认值。数组是容纳单个类型的固定数量的值的容器对象;数组的长度在创建数组时建立;创建后,其长度是固定不变的。