《Java中的数据类型和字面值》源站链接,阅读体验更佳~
《高级语言基本世界观——类型系统》一文中我们提到过,类型系统是一门高级程序设计语言基本世界观的体现。我们通常会通过动/静和强/弱这两个我维度来描述一门语言的类型系统。对于Java而言,它是一门静态强类型的编程语言。
同时我们也提到了标量数据类型的概念,标量数据类型是一门语言中最小的不可再分的数据类型,它通常是由编程语言内置的,是组成程序世界中信息的最小单位和最终来源。
Java中的最基本的标量数据类型有9个,分别是我们挂在嘴边的8大基本数据类型和java.lang.String类型,而且对于这9中标量数据类型,Java都为他们提供了字面值语法以及基本的运算符。其实,标量数据类型和字面值是密切相关的,大家可以做一个粗略的总结,几乎所有的标量数据类型都会配备对应的字面值语法。
而《高级语言基本世界观——类型系统》一文中我们也提到过,最基本的字面值其实就三类,分别是数字值、布尔值和字符串值。在Java中,数字值和布尔值对应的数据类型都是原始数据类型,而字符串比较特殊,它是一个引用类型。
关于Java中原始类型和引用类型的区别(其实不单单是Java语言,原始类型和引用类型的概念可以扩展到任何一门语言中),这是最基本的知识,这里就不做过多说明了。
布尔类型
对于布尔类型,其对应的就是boolean
这种数据类型,它的值集只包含两个单值,分别是true
和false
。boolean
可以使用的操作符如下所示:
-
判等操作符
==
和!=
-
逻辑取反操作符
!
-
逻辑操作符
&
、^
和!
-
条件与和条件或操作符
&&
、||
-
条件操作符
? :
-
字符串拼接操作符
+
当给定一个String类型操作数和一个boolean操作数时,该操作符会将boolean操作数**转换为String(true或false),**然后将两个字符串拼接起来形成一个新的字符串。
boolean表达式在下列几种语句中可用来确定控制流:
- if语句
- while语句
- do语句
- for语句
boolean表达式还可以确定条件操作符? :
中哪一个表达式会用来计算,且只用boolean或Boolean表达式才能用做?:
操作符的第一个操作数
整数类型
对于数字类型,有7个原始类型与之对应,其中又分为整型和浮点型。对于整形数字而言,有5个:
类型 | 描述 |
---|---|
byte | 占8位, -128~127 的闭区间 |
short | 占16位, -32768~32767 的闭区间 |
int | 占32位, -2147483648~2147483647 的闭区间 |
long | 占64位, -9223372036854775808~922337203685477580 的闭区间, 注意,不对称 |
char | 占16位, ‘\u0000’ ~ ‘\uFFFF’ 的闭区间,即从 0 到 65535 |
其中char比较特殊,也是一个使用频率比较低的数据类型。char是字符型,同时我们可以将其当做无符号整数来进行使用。
它们可进行的操作如下:
-
比较操作符,它们会产生boolean类型的值
- 数字比较操作符
<
、<=
、>
和>=
- 数字判等操作符
==
和!=
- 数字比较操作符
-
数字操作符,它们会产生int或long类型的值
- 一元正负操作符
+
和-
- 乘除操作符
*
、/
、%
- 加减操作符
+
、-
- 递增操作符
++
,包括前缀式和后缀式 - 递减操作符
--
,包括前缀式和后缀式 - 有符号和无符号移位操作符
<<
、>>
、<<<
- 位运算补码操作符
~
- 整数位运算操作符
&
、^
、|
- 一元正负操作符
-
条件操作符
? :
-
类型转换操作符
可以将整数值转换为任何指定的数字类型
-
字符串拼接操作符
+
当给定一个String类型操作数和一个整数操作数时,该操作符会将整数操作数**转换为以十进制形式表示这个数值的String,**然后将两个字符串拼接起来形成一个新的字符串。
可以看到,对于整数,我们除了把它们当成一个数字之外,还可以把它们当做一个寄存器来进行位运算。
整数字面值
Java中的整数字面值默认对应的数据类型是int
,只不过当我们在将一个int类型的字面量赋值给byte或者是short类型的变量的时候,如果这个int类型的字面量的值没有超出byte或者是short类型的变量所能表示的数值范围,是可以成功的(编译器帮我们自动进行了处理),但是如果超出了byte或者short类型的变量所能表达的数值范围的时候,就会发生编译错误
而如果我们想指定一个整数字面值的数据类型是long
,则需要在字面量的末尾添加l/L
尾缀。
int类型的字面量有四种表示方式,分别是二进制、八进制、十进制和十六进制,不同的形式的字面量用不同的前缀进行区分,如下:
进制 | 前缀 |
---|---|
二级制 | 0b/0B |
八进制 | 0 |
十进制 | 不需要任何前缀 |
十六进制 | 0x/0X |
同时,为了增强整数字面量的可读性,Java中的数字字面量可以用_
进行分割而不会造成任何影响,如下:
int a = 35_3234_888;//分割之后可以提高可读性
同时,我们需要注意的是,只要加了前缀,就必须符合对应进制的规则,否则无法通过编译,如下:
int a = 08; //报错了,因为这里加了前缀`0`,所以这个数字应该是8进制的,而八进制中并不包含数字8
char类型字面值
上文我们提到char类型比较特殊,它其实是用来ACSII码表中的字符的,所以我们除了可以使用0~65535之内的整数字面值对其进行赋值之外,它还有如下三种形式的字面值:
''
包裹的字符,例如'a'
''
包裹的转义字符,例如'\n'
,因为Java代码本身将一些特殊字符作为字符串字面量的界定和特殊的程序元素,所以Java也提供了转义字符的机制''
包裹的Unicode值,如'\u0061'
,需要注意被包裹的Unicode值的前缀。
浮点数
对于浮点数而言,有两个原始数据类型与之对应:
类型 | 描述 |
---|---|
float | 占32位,其中有效数字占23位,指数位占8位,符号位占1位 |
double | 占64位,其中有效数字占52位,指数位占11位,符号位占1位 |
浮点数类型包括float和double,它们在概念上分别对应单精度32位和双精度64位IEEE 754数值以及有IEEE二进制浮点数算数运算标准指定的操作。
IEEE 754标准不仅包含由一个符号位和一个量值构成的正负数字,而且还包含正负0、正负无穷以及特殊的NaN
值(表示非数字,Not a Number)。NaN值用来表示某些无效操作的结果,例如0/0。float和double的NaN常量分别被定义为Folat.NaN和Double.NaN。
关于浮点数的值集这里不会叙述过多的内容,我们需要特别注意的是NaN,除了NaN,浮点数都是有序的,按照从小到大排列分别是负无穷、富有穷非0值、正0和负0、正有穷非0值以及正无穷。
特别的,因为NaN是无序的,因此:
如果操作数中有一个或两个同时是NaN,则数字比较操作符
<
、<=
、>
和>=
返回false如果操作数中有一个是NaN,则判等操作符
==
返回false特别地,如果x或y是NaN,则(x<y)==(!(x>=y))为false
如果操作数中有一个是NaN,则判不等操作符
!=
返回true特别地,当且仅当x是NaN时,x != x 为true
浮点数可以进行的操作如下所示:
-
比较操作符,它们会产生boolean类型的值
- 数字比较操作符
<
、<=
、>
和>=
- 数字判等操作符
==
和!=
- 数字比较操作符
-
数字操作符,它们会产生float或double类型的值
- 一元正负操作符
+
和-
- 乘除操作符
*
、/
、%
- 加减操作符
+
、-
- 递增操作符
++
,包括前缀式和后缀式 - 递减操作符
--
,包括前缀式和后缀式
- 一元正负操作符
-
条件操作符
? :
-
类型转换操作符
可以将整数值转换为任何指定的数字类型
-
字符串拼接操作符
+
当给定一个String类型操作数和一个浮点数操作数时,该操作符会将浮点数操作数**转换为以十进制形式表示这个数值(不丢失任何信息)的String,**然后将两个字符串拼接起来形成一个新的字符串。
可以看到,由于特殊的编码方式,对于浮点数我们不能和整数那样进行位运算。
浮点数字面值
在Java中,浮点数字面值默认对应的数据类型是double
,如果我们想要指定一个浮点数字面值对应的数据类型为float
,则需要在浮点数字面值的末尾添加f/F
尾缀。
在Java中,实数字面量有两种表示方式,一种是直接书写小数,另一种就是e表示法(科学计数法)
。
同样的,实数字面量也可以利用_
进行分割以提高可读性,**但是需要注意的是,小数点前后不能加_
**如下:
double b = 123.3534_34;
double c = 123_.3534_34; //错误,不能在小数点前面加_
double d = 123._3534_34; //错误,不能在小数点后面加_
原始数据类型的自动类型转换
我们这里要提到的是基本类型中的数字类型之间在进行运算的时候,其实是会根据其值集进行自动提升的。自动提升的规则包含以下两个方面:
-
对于值集范围小于int类型的基本数据类型(byte、short和char),在这些值之间进行算数运算或者是位运算之前,这些值会被自动提升为int类型,当然他们的运算结果也是int类型的。如果要把运算结果赋值给值集小于int的类型,则需要进行强制类型转换,而且由于把值赋给了值集较小的类型,进行强制类型转换有可能会发生信息的丢失。
-
对于一个表达式而言,表达式中出现的值集最大的数据类型,决定了表达式最终结果的数据类型。比如short类型和float类型之间的运算结果的数据类型则为float;float和double之间的运算结果为double。
这种数据类型的提升是发生在编译阶段的。在编译的时候,编译器会扫描表达式中各个部分的数据类型,把所有的变量和值的数据类型提升为值集最大的那个数据类型,把数据类型拉齐。
数字原始类型的值集大小如下(因为byte、short和char类型在运算的时候会被无脑提升为int,所以我们这里的值集大小的排名是从int开始的):
int < long < float < double
需要注意的是值集的大小指的是可以表示的数据范围的大小,和数据所占的位数无关,比如long占64为,float只占32位,但是float可以表示的数据的范围却是比long要大得多。所以上面示例的第21行代码中long和float的运算结果是float。
引用类型
Java中,除了上面提到的8大原始数据类型之外,其他的数据类型均为引用类型,引用类型本质上是一种数据结构,是一种向量。Java中的引用类型可以分为如下几种情况:
-
类类型
这就不用多说了,每一个实例必定有一个对应的类。
-
枚举类型
枚举类型是一种特殊的类类型,后面会有专门的文章介绍
-
接口类型
接口是对外声明的一组行为,一个类可以实现一个或者多个接口,同时这些类的实例就具有了这个类所实现的接口所定义的行为。
-
注解类型
注解是SE5之后加入的特性,其可以在一定程度上支持元编程。如果说上面所说的接口是一种行为上的契约,那么注解就是一种属性上的契约,被注解所标注的某个程序元素实际上就具有了某些特征,但是这并不会对对象的行为有所影响。
注解有不同的生存期,但是对于我们来说最常用的就是在运行时通过反射对注解进行分析,利用注解中提供的数据来组织我们的程序上下文。我们后面会有专门介绍注解的文章。
-
数组类型
数组是比较特殊的,因为数组类型并不是我们自己声明的,我们直接拿来用就可以了,数组类型的生成是JVM帮我们完成的。
-
类型变量
类型变量是ES5加入泛型编程特性之后引入的一个概念,在Java中,类型变量只存在于编译时,当然我们也可以通过使用内部类的方式在运行时传递类型信息,比如fastJSON中的TypeReference
关于泛型我们也会有专门的文章进行介绍。
我们上面列出了6类引用类型,其实真正意义上的分类一共就三种——类、接口或者是数组。
其中注解类型和枚举类型其实都是类类型的,不过注解更像是一种特殊的接口,即属性接口,而且其复用了关键字
interface
,我们声明一个注解类型使用的是关键字@interface
。而声明了类型变量的程序元素在被使用的时候一定要接受一个类型实参,同时,Java中在实现泛型的时候使用了类型擦除。
至于数组,那就比较特殊了,其实数组是一种合成类型,且支持协变,我们使用数组的时候直接书写类型就可以了,而不需要我们单独去声明一个数组类型,类型合成的动作是JVM帮我们完成的,由于数组的特殊性,我也会有专门的一篇文章来介绍数组。
Java中所有的引用类型都是派生自Object这个公共父类的。对于引用类型,我们可以进行的操作如下:
-
域访问
-
方法调用
-
类型转换操作符
-
字符串拼接操作符
+
当给定一个String类型操作符和一个引用时,该操作符会调用被引用对象的
toString()
方法,将引用转换为String(如果该引用为null或者对其调用toStirng()
的结果是null,则使用"null"),然后将两个字符串拼接起来形成一个新的字符串。 -
instanceof
操作符 -
引用判等操作符
==
、!=
-
条件操作符
? :
引用类型的字面值
对于引用类型而言,只有String类型被提供了标准的字面值语法,用""
进行界定。其他的引用类型则需要使用构造方法进行创建(当然还有内部类的相关语法,这个后面会有文章进行介绍)。对于数组这种特殊的引用类型,Java还提供了数组初始化表达式这样的语法。
除了字符串的字面值之外,对于引用类型,Java还提供了两个比较特殊的字面值:
-
null字面值
其实严格来说,null并不是一个字面值,但是其在使用形式上和字面值非常相似,因此也被放在了这里。
Java中所有的引用类型都可以接收字面量
null
,但是简单类型的变量不能接收null
需要注意的是,Java中的null==null返回true
-
class字面值
在《Java语言规范 基于SE8》一书中,称形如A.class这样的表达式为class字面值,但是个人认为这并不是一个真正意义上的字面值,因为它的值是计算出来的,而不是其本身就是一个值,它只是代表了一个值而已。
但是它在形式上确实像是一个字面值,因为在任何的类型中都没有class这样一个属性,而且我们可以通过简单类型或String类型的字面值来计算class字面值,如下:
1.class "213".class
总结
Java作为一门面向对象的语言,但是其数据类型却分为了基本数据类型和引用数据类型两类。同时它也提供了数字、布尔和字符串这三种最基本的字面值语法。
同时,在数字原始类型的运算中,会根据把值集较小的变量或者字面值的数据类型自动进行提升,这种提升是类型安全的。
以上就是我对Java中数据类型和字面值的基本理解,感谢你耐心读完。本人深知技术水平和表达能力有限,如果文中有什么地方不合理或者你有其他不同的思考和看法,欢迎随时和我进行讨论(laomst@163.com)。