本质上说,程序就是对数据的处理。不同的编程语言为了满足自己的语言优势和特色,结合设计理念制定了不同的数据类型以及存储方式。本文主要介绍数据类型、字面值、变量和常量等相关内容。
1. 数据类型
1.1 数据类型的作用
程序中有很多数据,每一个数据都是有类型的,不同类型的数据占用内存空间大小不同,数据类型的作用是指导JVM在运行程序的时候给该数据分配多大的内存空间。
Java中的数据类型包括两种:
- 基本数据类型:如int,char等;
- 引用数据类型,如数组,字符串,类对象等。(后面面向对象部分介绍)
1.2 基本数据类型
和C/C++类似,Java中的基本数据类型包括四大类八小类,如下所示:
- 整数型:byte, short, int, long
- 浮点型:float, double
- 布尔型:boolean
- 字符型:char
具体每个类型所占内存空间大小相关信息如下表所示:
数据类型 | 占用空间大小(字节) | 取值范围(无符号) | 取值范围(有符号) | 默认值 |
---|---|---|---|---|
byte | 1 | 0 ~ 255 | -128 ~ 127 | 0 |
short | 2 | 0 ~ 65535 | -32768 ~ 32767 | 0 |
int | 4 | 0 | ||
long | 8 | 0L | ||
float | 4 | 0.0 | ||
double | 8 | 0.0 | ||
boolean | 1 | true/false | false | |
char | 2 | 0 ~ 65535 | \u0000 | |
引用数据类型 | null |
1.2.1 整数型
Java语言中的整数型有四种,取值范围从小到大分别是byte, short, int, long。除了数据类型,还有不同的表示方式,Java语言中整数型一共有三种表示方式:
十进制(默认):普通数字。
如:10,15,20等等。
八进制:以 0 开头。
如:010,011,012,分别表示十进制的8,9,10。
十六进制:以 0x 开头。
如:0x10,0x11,0x12,分别表示十进制的16,17,18。
既然存在四种整数类型,那么对于一个整数型数据,比如 732,那么它是哪个类型呢?在Java语言中,默认被当做int类型来处理,即使这个数值超过了int的取值范围,它仍然是被当做int类型,只不过会报错,这个时候需要在数值后面加上字母L,如1234567L,即代表long类型,见后面的字面值部分。对于byte和short,见后续的变量以及类型转换。
1.2.2 浮点型
Java语言中的浮点型有两种,取值范围从小到大分别是float, double。float是单精度,double是双精度。但是相对来说,double的精度也不是很高,对于财务类数据,double仍然不够,为此,Java提供了java.math.BigDecimal
类型。
在Java语言当中,所有的浮点型数据默认被当做是double类型处理,如果想让Java当做float类型来处理,需要在字面值后面加上字母f或F。
注意,double和float在计算机内部二进制存储的时候存储的都是近似值。在现实世界当中有一些数字是无限循环的,例如:3.3333333333…。计算机的资源是有限的,用有限的资源存储无限的数据只能存储近似值。
1.2.3 布尔型
Java语言中的布尔型只有一种boolean,取值为true和false两个。布尔类型数据在实际开发中非常重要,经常用在逻辑运算和条件控制语句当中。
1.2.4 字符型
Java语言中的字符型只有一种char,占用两个字节,可以表示65535个数字,将字符编码并转换成对应的数字,即char可表示65535个字符。
1.3 字符编码
有了数据类型,那么计算机是如何存储这些数据呢?
众所周知,计算机是根据电脉冲的有无来执行指令的,而电脉冲的有无则表示0和1,也就是只能表示二进制。那么对于上述的整数型(十进制),则需要进制转换。对于浮点型,本质上也是十进制,只不过需要分别表示整数位和小数位。对于布尔型,因为只有两个取值,直接一一映射即可。对于字符型,一方面字符的取值较多,另一方面,字符的取值和二进制之间没有关系,所以需要对字符进行编码(使其和二进制有关系),然后进行一一映射。
编码不是什么了不起的东西,就是同一种事物的另一种表示形式而已。比如,人,不可能实际存储在计算机中,就用他的身份证号表示。对于字符,计算机无法存储,只能先存储它的编码,然后在显示器显示的时候,进行转换。
最早的编码是ASCII码,只能编码英文。后来随着计算机的发展,出现了一种编码方式,它是国际化标准组织ISO制定的,这种编码方式支持西欧语言,向上兼容ASCII码,仍然不支持中文。这种编码方式是ISO-8859-1,又被称为Latin-1。
随机计算机向亚洲发展,计算机开始支持中文、日文、韩文等国家文字,其中支持简体中文的编码方式有:GB2312 , GBK , GB18030。支持数量由少到多,其中GB2312支持的中文数量最少。支持繁体中文的有:Big5。
后来又出现了一种编码方式统一了全球所有的文字,容量较大,这种编码方式叫做:Unicode编码。Unicode编码方式有多种具体的实现:比如UTF-8,UTF-16,UTF-32等等。Java语言源代码采用的是Unicode编码方式,所以“标识符”可以用中文。
JDK自带的native2ascii命令工具可以将中文转换成Unicode编码。(似乎JDK9及以后的就没有了该命令)。
1.4 类型转换
既然存在不同的数据类型,那么Java也提供了数据类型之间的转换(具体的例子见后面的字面值以及变量部分)。类型转换分为两种:
自动类型转换
从小容量到大容量的数据转换,Java会自动执行。其实相当于直接在数字的前面填充0即可,使其满足特定字节的长度即可。
强制类型转换
从大容量到小容量的数据转换,需要手动执行,但是这仅仅是编译通过了,在实际运行过程中,可能会损失精度。其实相当于直接从前面截断,比如long类型转换到int类型,直接将前面四个字节截断,取后四个字节。
语法:
int a = (long)1234567L;
1.4.1 基本数据类型之间的互相转换
八种基本数据类型当中除布尔类型之外剩下的7种类型之间都可以互相转换。
小容量向大容量转换,称为自动类型转换,容量从小到大排序:(注意是容量,不是占的字节数)
byte < short < int < long < float < double
char <
注意:任何浮点类型不管占用多少个字节,都比整数型容量大。
char和short可表示的种类数量相同,但是char可以取更大的正整数(因为char没有符号,不分正负)。
大容量转换成小容量,叫做强制类型转换,需要加强制类型转换符,程序才能编译通过,但是在运行阶段可能会损失精度,所以谨慎使用。
当整数字面值没有超出byte,short,char的取值范围,可以直接赋值给byte,short,char类型的变量。
byte,short,char混合运算的时候,各自先转换成int类型再做运算。
多种数据类型混合运算,先转换成容量最大的那种类型再做运算。
注意:编译的时候仅仅检查语法,而不会运算检查结果。比如,int a = 10; short b = a;
,这样就会出错,因为a是int类型的,赋值给b,需要强转,而没有强转,所以报错。(不会因为a是10,在short类型的范围内,就不报错,不会检查结果)。
2. 字面值
字面值就是程序中实际存在的值,字面值就是数据,看到它就知道它具体的值。比如3.14
,'a'
,100
等等。根据数据类型的不同,字面值也分为不同的种类:
- 整数型字面值
- 浮点型字面值
- 字符型字面值
- 字符串型字面值
- 布尔型字面值
注意,Java语言当中所有的字符串型字面值必须使用双引号括起来;Java语言当中所有的字符型字面值必须使用单引号括起来。在程序中,如果有字面值,那么就会开辟一块内存空间,(不管字面值的值是否一样)。
3. 变量
程序本质上是对数据的处理,数据需要开辟内存空间存储,为了高效地利用内存空间,在一些数据不用之后,可以重新在这块内存空间上存储其他同种类型数据,为了找到这块内存空间,给这块内存空间起了一个名字,名字+内存空间存储的值+数据类型=变量。
变量本质上来说是内存中的一块空间,这块空间有“数据类型”、“名字”、“字面值”。即变量包含三部分:数据类型,名称,字面值(数据)
变量的相关语法如下:
1 | // 声明变量,即说明这个变量的数据类型和名称,还没有赋值 |
变量是内存中存储数据的基本单元。有了变量之后,内存空间得到了重复使用。比如int a = 10;
和int b = 10
;那么此时两个10字面值都是指的是一块内存空间。注意和字面值的区别,如果字面值单独存在,那么即使值相同,也会单独开辟空间,而赋值给变量后,就会仅开辟一块空间。
注意:在方法体中的Java代码,是遵守自上而下的顺序依次执行的,逐行执行。所以必须先声明变量才能使用该变量。
4. 常量
常量就是定义之后,其值不会再变化的变量。比如在程序中定义了PI,值为3.1415926。这个值是公认的,为了避免后续误操作被修改,可以在变量前添加修饰符final。
5. 例子
1 | public class Example{ |
6. 备注
参考B站《动力节点》。