
2.3 数据类型划分

任何程序严格来讲都属于一个数据的处理游戏。所以对于数据的保存必须有严格的限制,具体体现在数据类型的划分上,即不同的数据类型可以保存不同的数据内容。Java的数据类型可分为基本数据类型与引用数据类型两大类型。其中基本数据类型包括了最基本的byte、short、int、long、float、double、char、boolean等类型。另一种为引用数据类型(类似于C、C++语言的指针),这类数据在操作时必须进行内存的开辟,数据类型的划分如图2-1所示。
提示:本章将重点讲解基本数据类型。
首先,对于Java的数据类型划分,读者必须清楚地记住。其次,考虑到学习阶段的问题,本章主要以讲解各个基本数据类型为主,而引用数据类型将在面向对象部分为读者进行详细的讲解。最后,需要再次说明的是,基本数据类型不牵扯到内存的开辟问题,引用数据类型牵扯到内存的开辟,并且引用数据类型作为整个Java入门的第一大难点,本书将在面向对象部分为读者进行深入分析。
同时还需要提醒读者的是,对于数据类型的划分以及数据类型的名称关键字都要求记住。
基本数据类型不牵扯到内存分配问题,而引用数据类型需要由开发者为其分配内存空间,而后进行关系的匹配。Java的基本数据类型主要是以数值的方式进行定义,这些基本数据类型的保存数据范围与默认值如表2-2所示。
表2-2 Java基本数据类型的大小、范围与默认值

通过表2-2读者可以发现,long保存的整数范围是最大的,而double保存的浮点数范围也是最大的,相比较起来,double可以保存更多的内容。

图2-1 Java数据类型划分
提示:关于基本数据类型的选择。
在编程初期许多读者会对选择哪种基本数据类型出现犹豫,包括也会思考是否要记住这些数据类型所表示的数据范围,而最终却发现可能根本就记不下来。考虑到各种因素,下面来与大家分享一些基本数据类型的选择经验。
表示整数就使用int(例如,表示一个人的年龄),涉及小数就使用double(例如,表示一个人的成绩或者是工资)。
描述日期时间数字、文件、内存大小(程序中是以字节为单元统计大小的)使用long,而较大的数据(超过了int范围,例如,数据库之中的自动增长列)长度也使用long。
实现内容传递(I/O操作、网络编程)或者是编码转换时使用byte。
实现逻辑的控制,可以使用boolean描述(boolean只有true和false两种值)。
处理中文时使用char可以避免乱码问题。
由于现在的计算机硬件不断升级,对于数据类型的选择也不像早期编程那样受到严格的限制,因而像short、float等数据类型已经很少使用了。
有了数据类型的划分后就可以进行变量的定义与赋值处理操作,可以采用如图2-2所示的结构实现。

图2-2 变量定义与赋值处理格式
考虑到程序语法的严谨性,Java需要为每一个变量进行数据类型的定义,这样才方便进行内存空间的开辟,同时在进行变量定义的时候可以通过赋值表达式“=”为变量设置初始化的内容。
提示:关于初始化内容与默认值。
通过表2-2读者可以发现,不同的数据类型均有对应的默认值,但是这些默认值只在定义类结构的过程中起作用,如果在进行方法定义时则都需要进行明确的初始化内容。关于类与方法的定义,读者可以通过后续的章节进行完整学习,暂不急于了解相关内容。
另外,考虑到对不同JDK版本的支持,需要对赋值使用做出两个区分:在JDK 1.4及以前版本中方法定义的变量必须要求赋值,而从JDK 1.5后开始方法中定义的变量可以在声明时不赋值,而在使用之前进行赋值,如下所示。
范例:JDK 1.5后的变量声明与赋值支持

同样的程序代码,如果放在JDK 1.4以及以版本时就会出现错误,而所有版本通用的定义形式为:

考虑到读者概念学习的层次性,为了避免更多的概念造成混乱,本书给出一个良好的建议,在进行变量定义时建议都为每个变量设置默认值。
另外,考虑到程序代码的开发标准性问题,Java中的变量也有明确的命名要求:“第一个单词的首字母小写,随后每个单词的首字母大写”,例如,studentName、mldnInfo全部都是正确的变量名称。
2.3.1 整型

整型数据一共有4种类型,按照保存的范围由小到大分别为byte、short、int、long,在Java里面任何一个整型常量(例如,30、100这样的数字),其默认的类型都是int型。
范例:定义int型变量

在本程序中定义了一个整型变量x,并且在声明变量时为其赋值为数字10。由于变量x属于int型,所以在进行计算后x * x最终的结果也是int型。
注意:保持良好的编程习惯。
以上程序是一个相对而言比较容易理解的代码,但是在实际的开发中,除了保证代码的正确性外,拥有良好的编程习惯也同样重要。细心的读者可以发现在编写代码“int x=10;”时,每一个操作之中都加上一个“ ”(空格),如图2-3所示,这样做的目的是避免由于编译器bug所造成的非正常性语法的编译错误。

图2-3 每个操作之间使用空格分开
提问:变量和常量的区别是什么?
书中一直强调的变量和常量有什么区别?如何区分?
回答:变量的内容可以改变,常量的内容不可以改变。
所谓常量,指的就是一个个具体的内容,例如,一个数字10,内容始终都是无法改变的,这样的内容就被称为常量。
变量一般都需要定义相应的数据类型。而且这个变量一般都可以保存不同的内容,既然里面的内容可变那么就称为变量。
范例:理解变量与常量

在本程序中的数字10和20就属于一个常量,这些内容永远都不会改变,而num内容可以改变就称为变量。如果换个通俗点的方式来理解,变量就好比一个杯子,里面可以倒入咖啡或茶水(常量)。
任何数据类型都有其对应的数据保存范围,但是在一些特殊环境下有可能计算的结果会超过这个限定的范围,此时就会出现数据的溢出问题。
范例:观察数据溢出

本程序分别定义了两个变量:max(保存int最大值)、min(保存int最小值),由于int型变量与int型常量计算后的数据类型依然是int型,所以此时出现了数据的溢出问题,如图2-4所示。

图2-4 数据溢出
提示:关于数据类型的溢出问题解释。
如果学习过汇编语言的读者应该知道,在计算机中二进制是基本的组成单元,而int型数据一共占32位的长度,也就是说第1位是符号位,其余的31位都是数据位,当已经是该数据类型保存的最大值时,如果继续进行“+1”的操作就会造成符号位的变更,最终就会形成这种数据溢出的问题。但是笔者也需要告诉读者,不用过于担心开发中出现的数据溢出问题,只要控制得当并且合乎实际逻辑(例如,定义一个人年龄的时候是绝对不应该出现数据溢出问题,如果真出现了数据溢出,那么已经不是“万年老妖”这样表示年龄的词语可以描述的“物种”了),自然也很少会出现此类情况。
如果要想解决这种溢出的问题,就只能够通过扩大数据范围的方式来实现,比int范围更大的是long数据类型。而要将int型的变量或常量变为long数据类型有以下两种形式。
形式1:int型常量转换为long型常量,使用“数字L”“数字l(小写的字母l)”完成。
形式2:int型变量转换为long型变量,使用“(long)变量名称”。实际上可以用此类方式实现各种数据类型的转换。例如,如果将int型变量变为double型变量,可以使用“(double)变量名称”,即数据类型转换的通用格式为“(目标数据类型)变量”。
范例:解决数据溢出(在操作时需要预估数据范围,如果发现数据保存范围不够就使用更大范围的数据类型)

本程序为了获取正确的计算结果使用了long类型定义了max与min两个变量,这样计算的数据即使超过了int数据范围(但没有超过long数据类型)也可以获取正确的计算结果。
提示:另一种解决数据溢出问题。
对于数据溢出的问题除了以上的处理方式之外,也可以在计算时进行强制类型转换。

在将int常量转为long类型的时候可以使用字母L(大写)或l(小写)进行定义,也可以直接进行强制转换,由于int与long类型的计算结果依然是long类型,所以可以得到正确的计算结果。
不同的数据类型之间是可以转换的,即范围小的数据类型可以自动转为范围大的数据类型,但是如果反过来,范围大的数据类型要转为范围小的数据类型,就必须采用强制性的处理模式,同时还需要考虑可能带来的数据溢出。
范例:强制类型转换

本程序定义了一个超过int范围的long变量,所以在进行强制类型转换时就出现了数据溢出问题。
字节是一种存储容量的基本单位,在Java中可以使用关键字byte进行定义,并且byte也属于整型定义,其保存的范围是-128~127,下面通过程序说明。
范例:定义byte变量

本程序定义了byte变量num,并且其设置的数据的范围在byte允许范围内。
提问:为什么此时没有进行强制转型?
在本程序执行“byte num=20;”语句时,20是一个int型的常量,但是为什么在为byte赋值时没有进行强制类型的转换?
回答:在byte范围内可以自动将int常量转为byte常量。
在Java语言中为了方便开发者为byte变量赋值,所以进行了专门的定义。如果所赋值的数据在byte范围内将自动转换;如果超过了byte范围则必须强制转换,如下代码所示。
范例:int常量强制转为byte类型

由于数字200超过了byte类型,所以必须进行强制转换,所以此时出现了数据溢出问题。
2.3.2 浮点型

浮点型数据描述的是小数,而在Java里面任意一个小数常量对应的类型为double,所以在以后描述小数的时候建议直接使用double来进行定义。
范例:定义double变量

所有的数据类型进行自动转型的时候都是由小范围数据类型向大范围数据类型进行自动转换处理,所以int型变量会自动转换为double类型后才可以进行计算,这样最终计算完成的结果就是double型。
由于Java默认的浮点数类型为double,如果定义为位数相对较少的float变量,在赋值时就必须采用强制类型转换。
范例:定义float型变量

本程序利用了两个float型的变量进行乘法运算,但是通过最终的结果可以发现多出了一些小数位,而这个问题也是Java长期以来一直存在的漏洞。
通过分析可以发现整型与浮点型最大的区别在于,整型无法保存小数位,也就是说在整型数据进行计算时小数点的内容将被抹掉。
范例:观察整型除法计算

通过此时的计算可以发现,当前使用的类型为int,在进行除法计算后只保留了整数位(正确的结果应该是2.5),而要想解决当前的问题就必须将其中一个变量的类型转为double或float。
范例:解决除法计算中小数点问题

本程序在进行除法计算中为了保证计算结果的正确性,将计算中的数据类型转为了double(或float)类型,从而实现了小数位的数据保存。
注意:关于var关键字的使用。
Java最初是一种静态语言,这就要求在进行变量定义时都必须明确地为其定义数据类型,并且在随后的变量使用中也要求为变量赋值正确类型的数据。但是从JDK 1.10后为了迎合市场需求,Java也出现了动态语言的支持,提供有var关键字,即可以通过设置的内容自动识别对应类型。
范例:使用var关键字

本程序利用var关键字定义了num动态变量,由于为其赋值的常量为10.2属于double类型,所以num的类型就为double;随后赋值的常量100虽然是整型,但由于num的类型为double,所以自动转型为double。
虽然Java提供这样的动态语法,但是从本质上讲,Java的动态变量定义并不如其他语言强大(例如,JavaScript或Python),所以本书不建议开发者使用此类定义形式。
2.3.3 字符型

在计算机的世界里,一切都是以编码的形式出现。Java使用的是十六进制的Unicode编码,此类编码可以保存任意的文字,所以在Java中进行字符处理时就可以避免由于位数长度不同所造成的乱码问题。如果要定义字符变量则可以使用char关键字进行定义。
提示:关于Java中字符编码问题。
在Unicode编码设计过程之中,考虑到与其他语言的结合问题(C/C++),那么在此编码里与ASCII编码的部分编码重叠,以下面内容的编码为例。
大写字母范围:65('A')~90('Z')。
小写字母范围:97('a')~122('z'),大写字母和小写字母之间差了32。
数字字符范围:48('0')~57('9')。
如果读者之前有过类似开发,那么此处就可以无缝衔接。
范例:定义char变量


在Java中使用“'”可以定义字符常量,每一位字符常量都只能包含有一位字符,同时字符类型与整型也可以实现相互转换。
范例:char与int转换

此时程序中可以直接使用int型接收char型变量,这样就可以获取相应字符的编码信息,由于大小写字母之间差了32个长度,所以利用这一特点实现了大小写转换处理。
提示:使用char还可以保存中文。
由于Unicode编码可以保存任何文字,所以在定义char类型时也可以将内容设置为中文。
范例:设置中文字符

在Unicode编码中每一位中文字符也都有各自的编码,所以在中文语言环境下当前的程序是没有任何问题的,但是需要注意的是,此时只允许保存一位中文字符。
2.3.4 布尔型

布尔型(boolean)是一种逻辑结果,主要保存两类数据:true和false,这类数据主要用于一些程序的逻辑使用上。
提示:“布尔”是一位数学家的名字。
乔治·布尔(George Boole,1815—1864),1815年11月2日生于英格兰的林肯,是19世纪最重要的数学家之一。
范例:观察boolean类型

本程序使用boolean定义了变量,并且设置内容为true,所以if判断才满足执行条件。
提示:关于0与非0描述布尔型的问题。
在许多程序设计语言中,由于设计的初期没有考虑到布尔型的问题,就使用数字0表示false,而非0数字表示true(例如,1、2、3都表示true)。但是这样的设计对于代码开发比较混乱,Java里面不允许使用0或1来填充布尔型的变量内容。
2.3.5 String字符串

字符串是在实际项目中所使用的一种类型,利用字符串可以保存更多的字符内容,Java中使用“"”来实现字符串常量定义,而对应的类型为String。
提示:String为引用数据类型。
String是Java中提供的一个系统类,其并不是基本数据类型,但是由于此类的使用较为特殊,所以可以像基本数据类型那样直接定义并且使用。关于String类的更多描述将在第7章讲解。
范例:定义字符串变量

本程序定义了一个String型的变量,利用“"”可以定义字符串中的组成内容。按照此种模式可以定义更多的字符串,而字符串之间可以使用“+”进行连接。
范例:字符串连接

本程序首先定义了字符串变量str,随后利用“+”实现了字符串内容的连接处理。
提示:关于字符“+”在连接字符串与数值加法计算上的使用。
在字符串上使用“+”可以实现字符串的连接功能,但是需要注意的是,“+”也可以用于两个数值的加法计算,如果混合使用,则所有的数据类型将全部变为字符串类型,而后实现连接处理。
范例:错误的“+”使用

在本程序中原本的含义是希望可以直接输出加法计算的结果,但是由于存在字符串常量,所以所有的数据类型全部变为了字符串,而“+”就成为字符串连接的处理。如果要想解决此类问题,那么可以使用“()”修改执行优先级。
范例:解决错误的连接使用

由于“()”执行的优先级最高,所以会先执行基本数据类型的加法操作,而将最终的加法计算结果与字符串连接。
在进行字符或字符串描述的时候也可以使用转义字符来实现一些特殊符号的定义,例如,换行(\n)、制表符(\t)、\(\\)、双引号(\")、单引号(\')。
范例:使用转义字符

在本程序定义的字符串里由于使用了转义字符,所以在屏幕输出的时候将根据不同转义字符进行显示转换。