第 5 章 数据类型

优质
小牛编辑
127浏览
2023-12-01

在声明变量或常量时会用到数据类型,在前面已经用到一些数据类型,例如int、double和String等。Java语言的数据类型分为:基本类型和引用类型。

5.1 基本数据类型

基本类型表示简单的数据,基本类型分为4大类,共8种数据类型。

  • 整数类型:byte、short、int和long

  • 浮点类型:float和double

  • 字符类型:char

  • 布尔类型:boolean

基本数据类型如图5-1所示,其中整数类型、浮点类型和字符类型都属于数值类型,它们之间可以互相转换。

{%}

图5-1 基本数据类型

5.2 整型类型

从图5-1可见Java中整数类型包括:byte、short、int和long ,它们之间的区别仅仅是宽度和范围的不同。Java中整数都是有符号,与C不同没有无符号的整数类型。

Java的数据类型是跨平台的(与平台无关),无论你计算机是32位的还是64位的,byte类型整数都是一个字节(8位)。这些整数类型的宽度和范围如表5-1所示。

表 5-1 整数类型

Java语言的整型类型默认是int类型,例如16表示为int类型常量,而不是short或byte,更不是long,long类型需要在数值后面加l(小写英文字母)或L(大写英文字母),示例代码如下:

public class HelloWorld {

    public static void main(String[] args) {
        // 声明整数变量
        // 输出一个默认整数常量
        System.out.println("默认整数常量    =  " + 16);         ①
        byte a = 16;                                           ②
        short b = 16;                                          ③
        int c = 16;                                            ④
        long d = 16L;                                          ⑤
        long e = 16l;                                          ⑥

        System.out.println("byte整数        =  " + a);
        System.out.println("short整数       =  " + b);
        System.out.println("int整数         =  " + c);
        System.out.println("long整数        =  " + d);
        System.out.println("long整数        =  " + e);

    }
}

上述代码多次用到了16整数,但它们是有所区别的。其中代码①行的16是默认整数类型,即int类型常量。代码②行16是byte整数类型。代码③行的16是short类型。代码第④行的16是int类型。代码第⑤行的16后加了L,这是说明long类型整数。代码第⑥行的16后加了l(小写英文字母l),这是也long类型整数。

提示 在程序代码中,尽量不用小写英文字母l,因为它容易与数字1混淆,特别是在Java中表示long类型整数时候很少使用小写英文字母l,而是使用大写的英文字母L。例如:16L要比16l可读性更好。

5.3 浮点类型

浮点类型主要用来储存小数数值,也可以用来储存范围较大的整数。它分为浮点数(float)和双精度浮点数(double)两种,双精度浮点数所使用的内存空间比浮点数多,可表示的数值范围与精确度也比较大。浮点类型说明如表5-2所示。

表 5-2 浮点类型

{%}

Java语言的浮点类型默认是double类型,例如0.0表示double类型常量,而不是float类型。如果想要表示float类型,则需要在数值后面加f或F,示例代码如下:

public class HelloWorld {

    public static void main(String[] args) {
        // 声明浮点数
        // 输出一个默认浮点常量
        System.out.println("默认浮点常量    =  " + 360.66);          ①
        float myMoney = 360.66f;                                    ②
        double yourMoney = 360.66;                                  ③
        final double PI = 3.14159d;                                 ④

        System.out.println("float整数     =  " + myMoney);
        System.out.println("double整数    =  " + yourMoney);
        System.out.println("PI        =  " + PI);

    }
}

上述代码①行的360.66是默认浮点类型double。代码②行360.66f是float浮点类型,float浮点类型常量表示时,数值后面需要加f或F。代码第③行的360.66表示是double浮点类型。事实上double浮点数值后面也可以加字母d或D,以表示是double浮点数,代码第④行是声明一个double类型常量,数值后面加了d字母。

5.4 数字表示方式

整数类型和浮点类型都表示数字类型,那么在给这些类型的变量或常量赋值时,应该如何表示这些数字的值呢?下面介绍一下数字和指数等的表示方式。

5.4.1 进制数字表示

如果为一个整数变量赋值,使用二进制数、八进制数和十六进制数表示,它们的表示方式分别如下:

  • 二进制数:以 0b 或0B为前缀,注意0是阿拉伯数字,不要误认为是英文字母o。

  • 八进制数:以0为前缀,注意0是阿拉伯数字。

  • 十六进制数:以 0x 或0X为前缀,注意0是阿拉伯数字。

例如下面几条语句都是表示int整数28。

int decimalInt = 28;
int binaryInt1 = 0b11100;
int binaryInt2 = 0B11100;
int octalInt = 034;
int hexadecimalInt1 = 0x1C;
int hexadecimalInt2 = 0X1C;

5.4.2 指数表示

进行数学计算时往往会用到指数表示的数值。如果采用十进制表示指数,需要使用大写或小写的e表示幂,e2表示102

采用十进制指数表示的浮点数示例如下:

double myMoney = 3.36e2;
double interestRate = 1.56e-2;

其中3.36e2表示的是3.36×102,1.56e-2表示的是1.56×10-2

5.5 字符类型

字符类型表示单个字符,Java中char声明字符类型,Java中的字符常量必须用单引号括起来的单个字符,如下所示:

char c = 'A';

Java字符采用双字节Unicode编码,占两个字节(16位),因而可用十六进制(无符号的)编码形式表示,它们的表现形式是un,其中n为16位十六进制数,所以'A'字符也可以用Unicode编码'u0041'表示,如果对字符编码感兴趣可以到维基百科(https://zh.wikipedia.org/wiki/Unicode字符列表)查询。

示例代码如下:

public class HelloWorld {

    public static void main(String[] args) {

        char c1 = 'A';
        char c2 = 'u0041';
        char c3 = '花';

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
    }
}

上述代码变量c1和c2都是保存的'A',所以输出结果如下:

A
A
花

提示 字符类型也属于是数值类型,可以与int等数值类型进行数学计算或进行转换。这是因为字符类型在计算机中保存的是Unicode编码,双字节Unicode的存储范围在u0000~uFFFF,所以char类型取值范围0~216-1。

在Java中,为了表示一些特殊字符,前面要加上反斜杠(),这称为字符转义。常见的转义符的含义参见表5-3。

表 5-3 转义符

{%}

示例如下:

//在Hello和World插入制表符
String specialCharTab1 = "HellotWorld.";
//在Hello和World插入制表符,制表符采用Unicode编码u0009表示
String specialCharTab2 = "Hellou0009World.";
//在Hello和World插入换行符
String specialCharNewLine = "HellonWorld.";
//在Hello和World插入回车符
String specialCharReturn = "HellorWorld.";
//在Hello和World插入双引号
String specialCharQuotationMark = "Hello"World".";
//在Hello和World插入单引号
String specialCharApostrophe = "Hello'World'.";
//在Hello和World插入反斜杠
String specialCharReverseSolidus = "Hello\World.";

System.out.println("水平制表符tab1: " + specialCharTab1);
System.out.println("水平制表符tab2: " + specialCharTab2);
System.out.println("换行: " + specialCharNewLine);
System.out.println("回车: " + specialCharReturn);
System.out.println("双引号: " + specialCharQuotationMark);
System.out.println("单引号: " + specialCharApostrophe);
System.out.println("反斜杠: " + specialCharReverseSolidus);

输出结果如下:

水平制表符tab1: Hello    World.
水平制表符tab2: Hello    World.
换行: Hello
World.
回车: Hello
World.
双引号: Hello"World".
单引号: Hello'World'.
反斜杠: HelloWorld.

5.6 布尔类型

在Java语言中声明布尔类型的关键字是boolean,它只有两个值:true和false。

提示 在C语言中布尔类型是数值类型,它有两个取值:1和0。而在Java中的布尔类型取值不能用1和0替代,也不属于数值类型,不能与int等数值类型之间进行数学计算或类型转化。

示例代码如下:

boolean isMan = true;
boolean isWoman = false;

如果试图给它们赋值true和false之外的常量,如下所示。

boolean isMan = 1;
boolean isWoman = 'A';

则发生类型不匹配编译错误。

5.7 数值类型相互转换

学习了前面的数据类型后,大家会思考一个问题,数据类型之间是否可以转换呢?数据类型的转换情况比较复杂。基本数据类型中数值类型之间可以互相转换,布尔类型不能与它们之间进行转换。但有些不兼容类型之间,如String(字符串)转换为int整数等,可以借助于一些类的方法实现。本节只讨论数值类型的互相转换。

从图5-1可见数值类型包括了byte、short、char、int、long、float和double,这些数值类型之间的转换有两个方向:自动类型转换和强制类型转换。

5.7.1 自动类型转换

自动类型转换就是需要类型之间转换是自动的,不需要采取其他手段,总的原则是小范围数据类型可以自动转换为大范围数据类型,列类型转换顺序如图5-2所示,从左到右是自动。

{%}

图5-2 数据类型转换顺序

注意 如图5-2所示,char类型比较特殊,char自动转换为int、long、float和double,但byte和short不能自动转换为char,而且char也不能自动转换为byte或short。

自动类型转换不仅发生在赋值过程中,在进行数学计算时也会发生自动类型转换,在运算中往往是先将数据类型转换为同一类型,然后再进行计算。计算规则如表5-4所示。

表 5-4 计算过程中自动类型转换规则

{%}

示例如下:

// 声明整数变量
byte byteNum = 16;
short shortNum = 16;
int intNum = 16;
long longNum = 16L;

// byte类型转换为int类型
intNum = byteNum;
// 声明char变量
char charNum = '花';
// char类型转换为int类型
intNum = charNum;

// 声明浮点变量
// long类型转换为float类型
float floatNum = longNum;
// float类型转换为double类型
double doubleNum = floatNum;

//表达式计算后类型是double
double result = floatNum * intNum + doubleNum / shortNum;    ①

上述代码第①行中表达式loatNum * intNum + doubleNum / shortNum进行数学计算,该表达式是由4个完全不同的数据类型组成,范围最大的是double,所以在计算过程中它们先转换成double,所以最后的结果是double。

5.7.2 强制类型转换

在数值类型转换过程中,除了需要自动类型转换外,有时还需要强制类型转换,强制类型转换是在变量或常量之前加上“(目标类型)”实现,示例代码如下:

//int型变量
int i = 10;
//把int变量i强制转换为byte
byte b = (byte) i;

上述代码(byte) i表达式实现强制类型转换。强制类型转换主要用于大宽度类型转换为小宽度类型情况,如把int转换为byte。示例代码如下:

//int型变量
int i = 10;
//把int变量i强制转换为byte
byte b = (byte) i;
int i2 = (int)i;              ①
int i3 = (int)b;              ②

上述代码第①行是将int类型的i变量“强制类型转换”为int类型,这显然是没有必要,但是语法也是允许的。代码第②行是将byte类型的b变量强制转换为int类型,从图5-2可见这个转换是自动,不需要强制转换,本例中这个转换没有实际意义,但有时为了提高精度需要种转换。示例代码如下:

//int型变量
int i = 10;
float c1 = i / 3;                  ①
System.out.println(c1);            ②
//把int变量i强制转换为float
float c2 = (float)i / 3;           ③
System.out.println(c2);            ④

输出结果:

3.0
3.3333333

上述代码比较输出结果发现c1和c2变量小数部分差别比较大的,这种差别在一些金融系统中是不允许的。在代码第①行i除以3从结果是小数,但由于两个操作数都是整数int类型,小数部分被截掉了,结果是3,然后再赋值给float 类型的c1变量,最后c1保存的是3.0。为了防止两个整数进行除法等运算导致小数位被截掉问题,可以将其中一个操作数强制类型转换为float,见代码第③行,这样计算过程中操作数是float类型,结果也是float不会截掉小数部分。

再看一个强制类型转换与精度丢失的示例。

long yourNumber = 6666666666L;
System.out.println(yourNumber);
int myNuber = (int)yourNumber;
System.out.println(myNuber);

输出结果:

6666666666
-1923267926

上述代码输出结果可见,经过强制类型转换后,原本的6666666666L变成了负数。当大宽度数值转换为小宽度数值时,大宽度数值的高位被截掉,这样就会导致数据精度丢失。除非大宽度数值的高位没有数据,就是这个数比较小的情况,例如将6666666666L换为6L就不会丢失精度。

5.8 引用数据类型

在Java中除了8种基本数据类型外,其他数据类型全部都是引用(reference)数据类型,引用数据类型用了表示复杂数据类型,如图5-3所示,包含:类、接口和数组声明的数据类型。

{%}

图5-3 引用数据类型

提示 Java中的引用类型,相当于C等语言中指针(pointer)类型,引用事实上就是指针,是指向一个对象的内存地址。引用类型变量中保持的是指向对象的内存地址。很多资料上提到Java不支持指针,事实上是不支持指针计算,而指针类型还是保留了下来,只是在Java中称为引用类型。

引用数据类型示例如下:

int x = 7;                         ①
int y = x;                         ②

String str1 = "Hello";             ③
String str2 = str1;                ④
str2 = "World";                    ⑤

上述代码声明了两个基本数据类型(int)和两个引用数据类型(String)。当程序执行完第②行代码后,x值为7,x赋值给y,这时y的值也是7,它们的保持方式如图5-4所示,x和y两个变量值都是7,但是它们之间是独立的,任何一个变化都不会影响另一个。

当程序执行完第③行时,字符串“Hello”对象被创建,保持到内存地址0x12345678中,str1是引用类型变量,它保存的是内存地址0x12345678,这个地址指向“Hello”对象。

当程序执行完第④行时,str1变量内容(0x12345678)被赋值给str2是引用类型变量,这样一来str1和str2保存了相同的内存地址,都指向“Hello”对象。见图5-4所示,此时str1和str2本质上是引用一个对象,通过任何一个引用都可以修改对象本身。

图5-4 引用数据类型赋值过程1

当程序执行完第⑤行时,字符串“World”对象被创建,保持到内存地址0x23455678中,地址保存到str2变量中,此时,str1和str2不再指向相同内存地址,见图5-5所示。

图5-5 引用数据类型赋值过程2

本章小结

本章主要介绍了Java中的数据类型,读者需要重点掌握基本数据类型,理解基本数据类型与引用数据类型的区别,熟悉数值类型如何互相转换。