Java实用教程
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.1 常量和变量

Java程序运行时值不可修改的数据称为常量,分为字面常量(即常数)与标识符常量两种。变量是程序运行时值发生改变的量。

2.1.1 数据类型

Java是一种强类型的语言,这意味着所有变量都必须先明确定义其数据类型,然后才能使用。Java语言的数据类型只有两类:基本数据类型与引用类型。基本数据类型包括:boolean(布尔型)、byte(字节型)、short(短整型)、int(整型)、long(长整型)、char(字符型)、float(单精度浮点数型)、double(双精度浮点数型)共八种。其他为引用类型,例如,数组、字符串、类、接口、用户自定义类型等。所有基本数据类型的大小(所占用的字节数)都已明确规定好,在各种不同的平台上都保持一致,这一特性有助于提高Java程序的可移植性。各个基本数据类型的大小及取值范围如表2-1所示。

表2-1 基本数据类型的属性

2.1.2 标识符和关键字

标识符是指程序中包、类、接口、变量或方法的名字的字符序列。Java语言要求标识符必须符合以下命名规则。

标识符的首字符必须是字母、下划线“_”、美元符号“$”。

标识符由数字(0~9)、大写字母(A~Z)、小写字母(a~z)、下划线“_”以及美元符号“$”等组成,并且标识符的长度不受限制。

不能把关键字和保留字作为标识符。

标识符是大小写敏感的,例如,hello与Hello是两个不同的标识符。

表2-2是一个标识符正误对照表,列举了一些合法的标识符和非法标识符。

表2-2 标识符正误对照表

在命名Java标识符时,应注意“见名知意”。Java中一些被赋予特定含义,用做专门用途的字符序列称为关键字,包括:

● 数据类型:boolean、byte、short、int、long、double、char、float、double。

● 包引入和包声明:import、package。

● 用于类和接口的声明:class、extends、implements、interface。

● 流程控制:if、else、switch、do、while、case、break、continue、return、default、while、for。

● 异常处理:try、catch、finally、throw、throws。

● 修饰符:abstract、final、native、private、protected、public、static、synchronized、transient、volatile。

● 其他:new、instanceof、this、super、void、assert、const*、enum、goto*、strictfp。

注意:打上“*”的关键字,Java没有使用。

2.1.3 常量

Java程序运行时值不可修改的量称为常量,分为字面量(即常数)与标识符常量两种。标识符常量实际上是一个变量,但它的值一旦初始化以后,就不允许再发生改变,因此标识符常量要先定义后使用,一般用于给一个常数取一个有意义的名字。字面量即是Java源程序中表示的常数值,如12.6、123、0x12、false、"hello"等,表示一个个具体的值。常量是有类型的,有如下5种。

1.布尔型常量(boolean)

布尔型常量值只有true或false。true和false可以看成是Java语言的关键字,不能挪作他用且必须要小写。true表示“逻辑真”,false表示“逻辑假”。

注意:不能认为“非零值或1”是true、“零值”是false。

2.整型常量(int或long)

整形常量有十进制、八进制、十六进制三种表示法:

十进制:十进制整数,如123,-48等。

八进制:以数字0开头的八进制整数,如017,-021等。

十六进制:以0x或0X开头的十六进制整数,如0x12a6,0XAB12,-0x1a0等。

整型常量(int)在机器中占32位,即4个字节。故最大的整型常量是2147483647。该值由Integer.MAX_VALUE表示,最小的整型常量是-2147483648,该值由Integer.MIN_VALUE表示。若程序中出现一个整型常量,其值超出上述范围,则会产生一个编译错误。为避免该错误,要在该值后加上L或l,使它成为一个long型的常量。最大的长整型常量值由Long.MAX_VALUE表示,最小的长整型常量值由Long.MIN_VALUE表示。若要表示long型的常量值,则在整数值后加上L或l,该类型的常量占64位,如20000L。若要表示byte或short型的常量值,通常用强制类型转换,例如,(byte)0x12a6,(short)2147483640等。

3.浮点型常量(float或double)

浮点型常量有两种表示形式:十进制小数形式和科学记数法形式。

(1)十进制小数形式:小数点两边的数字不能同时省略且小数点不能省略。合法的double型浮点数如3.14、1.0。

注意:1.0 是double类型常量值,而1 是int类型常量值,1.0f才是float类型的常量值,在内存中占用字节数以及表示的格式是不同的。

(2)科学记数法形式:如1.26×10-21在Java中表示为1.26e-21或1.26E-21,这是一个double型的浮点数。E或e的前面必须有数字且E或e后边必须是一个正/负整数(正号可省略)。由于E或e的后边必须是一个整数,那么1.26×10-2.65该如何表示?可用java.lang.Math类中的方法pow(),表示为:Math.pow(1.26,-2.65)。

数值后边加上d或D表示是double型的浮点数,在机器中占64位。只有在数值后边加上F或f才表示是float型的浮点数,在机器中占32位,例如,12.34f,3.14f等,其有效精度是6 位,这在表示较大数值时精度不够,故Java语言将默认的浮点数的精度规定为double型。

4.字符型常量(char)

Java中的字符采用Unicode字符集的编码方案,是16 位的无符号整数,占2个字节,表示的字符从0~65535。字符型常量值有两种表示方法。对于可输入字符,用单引号将字符括起来,如,'a'、'啊'等。

对于不可输入字符,常采用转义字符表示。‘\n’表示换行,其Unicode值是10。‘\r’表示回车,其Unicode值是13。‘\t’表示Tab键,其Unicode值是9。‘\ddd’表示用三位八进制数代表的ASCII字符,从‘\000’~‘\377’,可表示256个ASCII字符。‘\uxxxx’表示用四位十六进制数代表Unicode字符,从‘\u0000’~‘\uffff’,可表示所有的Unicode字符。其中的u可以任意多个连续,例如,‘\uuuu5de5’与‘\u5de5’相同。

‘\'’表示字符“'”,即‘\u0027’。‘\"’表示字符“"”,即‘\u0022’。‘\\’表示字符“\”,即‘\u005c’。例如,字符串"a汉b\u0067t\067\'\"\\字"表示“a汉bgt7'"\字”。

注意:Java中Unicode转义字符序列的处理时机。编译时,编译程序首先将组成源程序的原始的Unicode字符流进行Unicode转义字符序列处理变成Unicode流,然后再进行编译处理。故下边的换行用法不正确:企图用\u000a代替\n。

String s = "abc\u000adef";

编译程序会报错:unclosed string literal。正确用法应该是:String s = "abc\ndef";。

5.字符串常量(String)

Java中字符串实际上是字符串类java.lang.String的一个对象,所有字符串常量值都用双引号括起来,如"abc"。由于"abc"是一个对象,故它可以使用类String中的方法,如"a汉b字c".length()返回该串的长度5,而"a汉b字c".charAt(3)返回下标值3 所对应的字符:'字'(下标值的起点从0开始)。

注意:null可以简单看成是一个引用类型的常量值,表示不引用任何对象。在Java规范中,规定null是所谓null类型的唯一值,且规定null可转换到任何引用类型。

除了上边介绍的字面量常量外,Java中主要的是标识符常量。一个标识符常量是一个变量,一旦它的值初始化后,就再不能改变。通常用于给一个常数取一个有意义的名字。标识符常量可以是任何类型,其定义格式是在变量定义的前面加上final保留字,如定义一个double型的常量PI:final double PI = 3.14;。

按Java编码规范,常量名字通常用大写表示,若常量名由两个或以上单词组成,则单词间用下划线连接,例如,final int MAX_VALUE = 100;。

2.1.4 变量

与常量不同,变量是程序运行时值发生改变的量。变量对应着内存空间中的一个或几个单元,变量的值就存放在所对应的内存单元中。变量名就是给对应的内存单元取一个有意义的名称,这样在程序中,可以按变量名称来区分和使用这些内存单元。

1.boolean类型

boolean类型的变量的取值只能是true或false。在Java虚拟机内部,用int或byte类型来表示boolean,用整数零表示false,用任意一个非零整数来表示true。只不过这些对程序员是透明的。

2.byte、short、int和long类型

byte、short、int和long都是整数类型,并且都是有符号整数。在定义这4 种类型的整型变量时,Java编译器给它们分配的内存空间大小是不相同的。byte类型占用的内存空间最小,为1个字节大小,long类型占用的内存空间最大,占8个字节空间。如果一个整数值在某种整数类型的取值范围内,就可以把它直接赋给这种类型的变量,否则必须进行强制类型转换。例如,整数13 在byte类型的取值范围(-128~127)内,因此可以把它直接赋给byte类型变量。例如,129 不在byte类型的取值范围内,若想赋给byte类型变量,则必须进行强制类型转换。

byte b=(byte)129                       //变量b的取值为-127

以上代码中的“(byte)”表示把129强制转换为byte类型。129的二进制数据形式为:

00000000000000000000000010000001。“(byte)”运算符截取后8位为10000001,其值为-127。

如果在整数后面加上后缀:大写“L”或小写“l”,就表示它是一个long类型的整数。

3.char类型与字符编码

char是字符类型,Java语言对字符采用Unicode字符编码,这是一种双字节编码,表示字符范围‘\u0000’~‘\uffff’。由于计算机只能存储二进制数据,因此必须为各个字符进行编码。所谓字符编码,是指用一串二进制数据来表示特定的字符。Unicode字符编码由国际Unicode协会编制,收录了全世界所有语言文字中的字符,是一种跨平台的字符编码。

4.float和double类型

float和double类型都遵循IEEE 754标准,该标准分别为32位和64位浮点数规定了二进制数据表示形式。IEEE 754 采用二进制数据的科学记数法来表示浮点数。对于float浮点数,用1 位表示数字的符号,用8 位来表示指数(底数为2),用23 位来表示尾数。对于double浮点数,用1 位表示数字的符号,用11 位来表示指数(底数为2),用52 位来表示尾数。

2.1.5 类型转换

每一个表达式都有一个类型。表达式在使用过程中,当表达式所处的上下文要求的类型与表达式类型不一致时,就会发生类型转换。常见的发生类型转换的上下文有:赋值时类型转换;方法调用时类型转换;强制类型转换;字符串类型转换;数值类型提升。

当上下文中要求类型转换时,转换应遵循什么规则呢?对于Java基本数据类型,类型转换有如下3种。

1.宽转换

所谓的宽转换又称自动类型转换或隐式转换。转换规则如下:

(1)byte可直接转换到short、int、long、float、double;

(2)short可直接转换到int、long、float、double;

(3)char可直接转换到int、long、float、double;

(4)int可直接转换到long、float、double;

(5)long可直接转换到float、double;

(6)float可直接转换到double。

注意:①byte、short、int都是有符号的数,因而宽转换时(如转换到long)要进行符号位扩展。char实际上是0~65535之间的无符号的数,其符号位可认为是0,因而转换到int或long时,永远是用0(二进制位零)进行扩展。如int i=(char) (byte)(-1);i的值为65535。

②int转换到float或long转换到double,很可能会造成精度丢失。如int big =1234567891; float f = big;此时f中小数点之后已不能精确表示891了,即精度丢失了。

2.窄转换

窄转换的转换规则如下:

(1)short可直接转换到byte、char;

(2)char可直接转换到byte、short;

(3)int可直接转换到byte、short、char;

(4)long可直接转换到byte、short、char、int;

(5)float可直接转换到byte、short、char、int、long;

(6)double可直接转换到byte、short、char、int、long、float。

注意:①窄转换在大多数情况下会丢失信息。当int窄转换到byte时,会丢弃掉int的高3个字节(24位),将最低的1个字节(8位)放入byte中。因此,大多数情况下窄转换要求程序员自己明确地指明。

②char窄转换到short,将char的两个字节(16位)直接放入到short中。虽然char与short都是16位,但窄转换到short时,其结果可能会由正数变成了一个负数了。

3.宽窄同时转换

宽窄同时转换发生在byte转换到char期间。其转换过程为:先将byte宽转换到int,再将int窄转换到char。

上述所讲的基本数据类型的三种转换在Java程序中经常发生。例如,当将一个表达式的值赋给一个变量时(称为赋值类型转换上下文),会自动进行“宽转换”和某些特定的“窄转换”。例如下面的代码片段:

byte b = 2;
short s;
s = ++ b;

表达式++b是byte类型,而s是short类型,因而期望赋值符“=”右部出现的short类型。自动执行“宽转换”,将byte转换为short。

赋值时允许的特定的“窄转换”是:若常量表达式(或常数)的类型是byte、short、char、int,要将值赋给一个变量V(变量V的类型是byte、short、char)。若常量值处于变量V的数据类型范围之内,则编译程序自动进行这种特定的“窄转换”。如byte v =‘\uff00’,则必须要程序员自己明确进行“窄转换”。

另外一种经常发生的类型转换上下文是“数值提升”。当使用算术运算符(+,-,*,/,%),关系运算符(<,<=,>,>=,==,!=),位运算符(&,|,^,~,>>,>>>,<<)及条件运算符(?:)时,编译程序会按“宽转换”自动进行“数值提升”。例如下面的代码片段:

byte b = 10;
long l = 20;

对于表达式b+l,首先将b“宽转换”成long,然后按long进行“+”运算。结果类型是long型。再如

byte b = 10;
char c = ‘\u0065’;

对于表达式b+c,首先将b与c按“宽转换”自动提升为int,然后按int进行计算,结果类型为int。

说明:若是在byte、short、char三者之间进行运算,则首先将它们全部按“宽转换”自动提升为int,然后按int再进行运算。

其他类型转换上下文如方法调用类型转换(如3.2.4节方法重载)参见相关章节。

【例2.1】 Java基本数据类型转换。

TypeCast.java

public class TypeCast {
    public static void main(String[] args){
      byte b=1;                                 // 自动类型转换
      int i=b;
      long l=b;
      float f=b;
      double d=b;
      char ch='c';
      int i2=ch;                                // 转换为对应的ASCII码值
      System.out.println("i2:"+i2);
      short s = 99;
      char c=(char)s;                           // 强制类型转换
      System.out.println("c:"+c);
      byte b1 = (byte)129;
      System.out.println("b1:"+b1);
  }
}

程序运行结果:

      i2:99
      c:c
      b1:-127