Java | Java 基础语法
I、注释、标识符、关键字
1.1 注释
- 平时我们编写代码,在代码量比较少的时候,我们还可以看懂自己写的,但是当项目结构一旦复杂起来,我们就需要用到注释了。
- 注释并不会被执行,是我们写代码的人看的。
- 注释的内容不参与编译,换句话说,编译后生成的
.class
文件中不包含注释掉的信息。 - 注释提高了代码的阅读性,是调试代码的重要方法。
- 书写注释是一个非常好的习惯。
Java
中的注释有三种:- 单行注释:
// 单行注释只能注释一行
- 多行注释:可以注释一段文字
/* 多行注释 多行注释 多行注释 多行注释 多行注释 多行注释 */
- 文档注释(
Java
特有):后续JavaDoc
中详细讲解。- 格式:
/** * @author 指定Java程序的作者 * @version 指定源文件的版本 */
- 注释内容可以被
JDK
提供的工具javadoc
所解析,生成一套以网页文件形式体现的该程序的说明文档。 - 操作方式:
javadoc -d mydoc -author -version HelloWorld.java
,其中mydoc
是说明文档所在目录。
- 格式:
- 单行注释:
1.2 标识符
Java
语言中的变量、常量、函数、类、语句块等组成部分都需要名字,我们将这些名字统统称为Java
标识符。Java
标识符由数字、字母(A-Z 或 a-z)、下划线(_
)和美元符号($
)或人民币符号(¥
)组成。Java
标识符是区分大小写的,并且要求首位不能是数字。Java
标识符不能包含空格。Java
关键字和保留字不能用作Java
标识符。- 可以使用中文命名,但是一般不建议这样去使用,也不建议使用拼音。
- 合法的标识符:
myName
,My_name
,Points
,$points
,_sys_ta
,OK
,_23b
,_3_
非法的标识符:#name
,25name
,class
,&time
,if
- 如果标识符不符合上述规定,编译则不通过。
- 命名约定:在起名字时,为了提高阅读性,要尽量有意义,做到“见名知意”。
- 包名:多单词组成时所有字母都小写。例:
mypackage
。 - 类和接口名:每个单词的首字母大写,含有大小写。例:
MyClass
,HelloWorld
等。这种命名方式叫做大驼峰式命名。 - 方法名、变量名:第一个单词的首字母小写,其余单词首字母大写,含有大小写,尽量少用下划线,少用美元符号。例:
myName
,setTime
等。这种命名方式叫做小驼峰式命名。 - 常量名:基本数据类型常量名的全部字母都大写,单词与单词之间使用下划线分隔。对象常量可大小混写。例:
SIZE_NAME
。
- 包名:多单词组成时所有字母都小写。例:
1.3 关键字
\quad
Java
关键字是Java
语言种事先定义的,有特别意义的标识符,有时又叫保留字,是具有特殊含义的变量。Java
关键字是对 Java
编译器有特殊含义的字符串,是编译器和程序员的一个约定,程序员利用关键字来告诉编译器其声明的变量类型、类、方法特性等信息。Java
中一共有
53
53
53 个关键字,字母均为小写。
- 访问权限控制(
3
3
3 个):
private
,protected
,public
; - 类,方法和变量修饰符(
13
13
13 个):
abstract
,class
,extends
,final
,implements
,interface
,native
,new
,static
,strictfp
,synchronized
,transient
,volatile
; - 程序控制(
12
12
12 个):
break
,continue
,return
,do
,while
,if
,else
,for
,instanceof
,switch
,case
,default
; - 异常处理(
6
6
6 个):
assert
,try
,catch
,finally
,throw
,throws
; - 包相关(
2
2
2 个):
import
,package
; - 基本类型(
12
12
12 个):
boolean
,byte
,char
,double
,float
,int
,long
,short
,null
,true
,false
,enum
; - 变量引用(
3
3
3 个):
super
,this
,void
; - 保留字(
2
2
2 个):
goto
,const
。现有Java
版本尚未使用。
II、数据类型
2.1 强弱类型语言
\quad 强类型语言和弱类型语言其判断的根本为是否隐形进行语言类型转变。
2.1.1 强类型语言
\quad
强类型是针对类型检查的严格程度而言的,它指任何变量在使用的时候必须要指定这个变量的类型,而且在程序的运行过程中这个变量只能存储这个类型的数据。因此,对于强类型语言,一个变量不经过强制转换,它永远是这个数据类型,不允许隐式的类型转换。常用的强类型语言有 Java
、.net
、C#
、Apex
和 Python
等。
2.1.2 弱类型语言
\quad
弱类型语言是一种数据类型可以被忽略的语言,它与强类型定义语言相反,一个变量可以赋不同数据类型的值,当某一个变量被定义数据类型,该变量可以根据环境变化自动进行转换,不需要经过强制转换。常用的弱类型语言有 VB
, PHP
, JavaScript
等。
2.2 动静态语言
2.2.1 动态性语言
\quad
动态性语言是指在运行期间才去做数据类型检查的语言,也就是说动态类型语言编程时,永远不用给任何变量指定数据类型,该语言会在第一次赋值给变量时,在内部将数据类型记录下来。Python
和 Ruby
就是一种典型的动态类型语言。
2.2.2 静态性语言
\quad
静态性语言是指编译期间做数据类型检查的语言,即写程序时要声明所有变量的数据类型。使用数据之前,必须先声明数据类型(int
, float
, double
等)。相当于在使用之前,首先要为它们分配好内存空间。C/C++
是静态类型语言的典型代表。Java
也是一种静态性语言。
2.3 Java 数据类型
2.3.1 原码、反码和补码
\quad
在介绍 Java
数据类型之前,让我们先了解一下计算机是如何对数值进行存储的。数值在计算机中的表示形式统称为机器数。计算机中处理数据及运算都是采用二进制,通常规定机器数用八位二进制表示。实用的数据有正数和负数,因为计算机只能表示 0、1
两种状态,数据的正号“+
”或负号“-
”,在计算机里就用一位二进制的 0
或 1
来区别,通常放在最高位,称为符号位。 符号位数值化之后,为能方便的对机器数进行算术运算、提高运算速度,计算机设计了多种符号位与数值一起编码的方法,最常用的机器数表示方法有:原码、反码和补码,下面就分别介绍一下它们的表示方法。
(1)用法
- 原码:原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数为
0
,负数为1
(0
有两种表示:+0
和-0
),其余位表示数值的大小。例:-7
原码10000111
.。 - 反码:正数的反码和原码一样,负数的反码就是在原码的基础上符号位保持不变,其他位按位取反。对于单个数值(二进制的
0
和1
)而言,对其进行取反操作就是将0
变为1
,1
变为0
。例:-7
反码11111000
。- 负数反码转原码:符号位保持不变,数值位按位取反。
- 补码:正数的补码和原码,反码相同;负数的补码则是在原码的基础上符号位保持不变,其他位按位取反,然后在末尾加
1
。在计算机系统中,数值一律用补码来表示和存储。例:-7
补码11111001
。- 负数补码转原码:补码的补码即是原码。
(2)为什么要使用原码、反码和补码?
\quad
现在我们知道了计算机可以有三种编码方式表示一个数。对于正数因为三种编码方式的结果都相同:[+1] =[0000 0001]
原
_原
原=[0000 0001]
反
_反
反=[0000 0001]
补
_补
补,所以不需要过多解释。
\quad
但是对于负数:[-1] =[1000 0001]
原
_原
原=[1111 1110]
反
_反
反=[1111 1111]
补
_补
补,可见原码、反码和补码是完全不同的。
\quad
对于计算机来说,加减乘除已经是最基础的运算,要设计的尽量简单。计算机辨别“符号位”会让计算机的基础电路设计变得十分复杂,于是人们想出了将符号位也参与运算的方法。同时,根据运算法则减去一个正数等于加上一个负数,即:1-1=1+(-1)=0
,所以机器可以只有加法而没有减法,这样计算机运算的设计就更简单了。于是,人们开始探索将符号位参与运算,并且只保留加法的方法.。
- 首先来看原码计算:
- 计算十进制的表达式:
1 - 1 = 0
- 二进制的表达式:
1 - 1 = 1 + (-1) = [0000 0001]
原 _原 原+ [1000 0001]
原 _原 原= [1000 0010]
原 _原 原 [1000 0010]
原 _原 原 转换成十进制即为-2
,由此可见如果使用原码表示,让符号位也参与计算,显然对于减法来说,结果是不正确的,这也就是为何计算机内部不使用原码表示一个数。为了解决原码做减法的问题,出现了反码。
- 计算十进制的表达式:
- 反码操作:
- 计算十进制的表达式:
1 - 1 = 0
- 二进制的表达式:
1 - 1 = 1 + (-1) = [0000 0001]
原 _原 原+ [1000 0001]
原 _原 原= [0000 0001]
反 _反 反+ [1111 1110]
反 _反 反= [1111 1111]
反 _反 反= [1000 0000]
原 _原 原 [1000 0000]
原 _原 原 转换成十进制即为-0
,由此发现使用反码计算减法,结果的真值部分是正确的。而唯一的问题其实就出现在“0
”这个特殊的数值上。虽然人们理解上+0
和-0
是一样的, 但是0
带符号是没有任何意义的,而且会有[0000 0000]
原 _原 原 和[1000 0000]
原 _原 原 两个编码表示0
。于是补码的出现,解决了0
的符号以及两个编码的问题。
- 计算十进制的表达式:
- 补码运算:
- 计算十进制的表达式:
1 - 1 = 0
- 二进制的表达式:
1 - 1 = 1 + (-1) = [0000 0001]
原 _原 原+ [1000 0001]
原 _原 原= [0000 0001]
补 _补 补+ [1111 1111]
补 _补 补= [0000 0000]
补 _补 补=[0000 0000]
原 _原 原 [0000 0000]
原 _原 原 转换成十进制即为0
,这样0
用[0000 0000]
表示,而以前出现问题的-0
则不存在了。- 可以用
[1000 0000]
表示-128
:-1 - 127 = (-1) + (-127) = [1000 0001]
原 _原 原+ [1111 1111]
原 _原 原= [1111 1111]
补 _补 补+ [1000 0001]
补 _补 补= [1000 0000]
补 _补 补 - 在使用补码运算的结果中,
[1000 0000]
补 _补 补 就是-128
,但是注意因为实际上是使用以前的-0
的补码来表示-128
,所以-128
并没有原码和反码表示。(对-128
的补码表示[1000 0000]
补 _补 补 算出来的原码是[0000 0000]
原 _原 原,这是不正确的)
- 计算十进制的表达式:
\quad
使用补码,不仅仅修复了 0
的符号以及存在两个编码的问题,而且还能够多表示一个最低数。这就是为什么 8
位二进制,使用原码或反码表示的范围为 -127 ~ 127
,而使用补码表示的范围为 -128 ~ 127
。
\quad
也正因为机器使用补码来表示和存储,所以对于编程中常用到的 32
位 int
类型,可以表示范围是: [
−
2
31
,
2
31
−
1
-2^{31}, 2^{31}-1
−231,231−1],因为第一位表示的是符号位,而使用补码表示时又可以多保存一个最小值。
2.3.2 基本数据类型
(1) 数值类型(有符号数值需要保留一个二进制位存储符号)
1)整数类型(1 个字节 [byte] 为 8 个二进制位 [bit] )
\quad 整数在计算机中一般使用整型处理,其在内存中的存储形式为二进制补码。
byte
:占用1
个字节,范围 [ − 2 7 , 2 7 − 1 -2^7, 2^7-1 −27,27−1],即-128 ~ 127
。short
:占用2
个字节,范围 [ − 2 15 , 2 15 − 1 -2^{15}, 2^{15}-1 −215,215−1],即-32,768 ~ 32,767
。int
:占用4
个字节,范围 [ − 2 31 , 2 31 − 1 -2^{31}, 2^{31}-1 −231,231−1],即-2,147,483,648 ~ 2,147,483,647
。long
:占用8
个字节,范围 [ − 2 63 , 2 63 − 1 -2^{63}, 2^{63}-1 −263,263−1],即-9,223,372,036,854,774,808 ~ 9,223,372,036,854,774,807
。声明时须在数值后加 “L
”,“L
” 理论上不区分大小写,但是若写成 “l
” 容易与数字 “1
” 混淆,不容易分辩。所以最好大写。
2)浮点类型(浮点数是对实数的一种近似表示)
① 浮点数表示法(表示)
\quad
浮点数表示法采用了科学计数法来表示实数,即用一个有效数字、一个基数、一个指数以及一个表示正负的符号来表达实数。例:666.66
用十进制科学计数法可以表示为
6.6666
×
1
0
2
6.6666\times10^2
6.6666×102(6.6666
为有效数字,10
为基数,2
为指数)。浮点数利用指数达到了浮动小数点的效果,从而可以灵活地表达更大范围的实数。
\quad
当然,对实数的浮点表示仅作如上的规定是不够的,因为同一实数的浮点表示还不是唯一的。例:上面例子中的666.66
可以表示为
0.66666
×
1
0
3
0.66666×10^3
0.66666×103、
6.6666
×
1
0
2
6.6666×10^2
6.6666×102或
66.666
×
1
0
1
66.666×10^1
66.666×101三种方式。因为这种表示的多样性,因此有必要对其规范化以达到统一表示。规范的浮点数表示方式:
±
d
.
d
d
⋯
d
×
β
e
(
0
≤
d
i
<
β
)
\pm d.dd\cdots d\times\beta^e(0\leq d_i<\beta)
±d.dd⋯d×βe(0≤di<β),其中
d
.
d
d
⋯
d
d.dd\cdots d
d.dd⋯d 为有效数字,
β
β
β 为基数,
e
e
e 为指数。
\quad 有效数字中数字的个数称为精度,我们可以用 p p p 来表示,即可称为 p p p 位有效数字精度。每个数字 d d d 介于 0 0 0 和基数 β β β 之间,包括 0 0 0。更精确地说, ± d 0 . d 1 d 2 ⋯ d p − 1 × β e \pm d_0.d_1d_2\cdots d_{p-1}\times\beta^e ±d0.d1d2⋯dp−1×βe 表示数: ± ( d 0 + d 1 β − 1 + d 2 β − 2 + ⋯ + d p − 1 β − ( p − 1 ) ) β e ( 0 ≤ d i < β ) \pm(d_0+d_1\beta^{-1}+d_2\beta^{-2}+\cdots+d_{p-1}\beta^{-(p-1)})\beta^e\ (0\leq d_i<\beta) ±(d0+d1β−1+d2β−2+⋯+dp−1β−(p−1))βe (0≤di<β)。
\quad
其中,对十进制的浮点数,即基数
β
β
β 等于10
的浮点数而言,上面的表达式非常容易理解。例:12.34
可以根据上面的表达式表示为:
1
×
1
0
1
+
2
×
1
0
0
+
3
×
1
0
−
1
+
4
×
1
0
−
2
1×10^{1}+2×10^{0}+3×10^{-1}+4×10^{-2}
1×101+2×100+3×10−1+4×10−2,其规范化的浮点数表达为
1.234
×
1
0
1
1.234×10^1
1.234×101。对于二进制的浮点数来说,唯一不同之处在于:二进制的
β
β
β 等于2
,而每个数字
d
d
d 只能在0
和1
之间取值。例:1001.101
可以根据上面的表达式表示为:
1
×
2
3
+
0
×
2
2
+
0
×
2
1
+
1
×
2
0
+
1
×
2
−
1
+
0
×
2
−
2
+
1
×
2
−
3
1×2^{3}+0×2^{2}+0×2^{1}+1×2^{0}+1×2^{-1}+0×2^{-2}+1×2^{-3}
1×23+0×22+0×21+1×20+1×2−1+0×2−2+1×2−3,其规范化的浮点数表达为
1.001101
×
2
3
1.001101×2^3
1.001101×23。
\quad
对于二进制的浮点数来说,向左移动二进制小数点一位相当于这个数除以2
,而向右移动二进制小数点一位相当于这个数乘以2
。例:101.11=5.75
,而10.111=2.875
。除此之外,我们还可以得到这样一个基本规律:一个十进制小数要能用浮点数精确地表示,最后一位必须是5
(当然这是必要条件,并非充分条件)。
② IEEE 754(存储)
\quad
IEEE
于1985
年制订了二进制浮点算术标准IEEE 754
(IEEE Standard for Binary Floating-Point Arithmetic
,ANSI
/IEEE Std 754-1985
),又称IEC 60559:1989
,同年被美国引用为ANSI
标准。该标准提供了如何在计算机内存中以二进制的方式存储十进制浮点数的具体标准,它定义了表示浮点数的格式(包括负零-0
)与反常值(denormal number
)),一些特殊数值(无穷INF
与非数值NaN
),以及这些数值的 “浮点数运算符”。目前,几乎所有的计算机都支持IEEE 754
标准。
\quad
IEEE 754
标准规定一个浮点数 (Value
) 可以表示为:
V
a
l
u
e
=
(
−
1
)
S
i
g
n
×
S
i
g
n
i
f
i
c
a
n
d
×
2
E
x
p
o
n
e
n
t
Value=(-1)^{Sign}×Significand×2^{Exponent}
Value=(−1)Sign×Significand×2Exponent。
- 符号位(
Sign
)决定浮点数是正数(Sign=0
)还是负数(Sign=1
),而对于数值0
的符号位则作为特殊情况处理; - 指数位
(Exponent)
指定了小数点在数据中的位置,指数位的长度越长则数值的表达范围就越大。指数可以是负数,也可以是正数,所以指数为采用了移码存储的方式,后文详解。现在只需了解计算机中存储的是指数位偏移后的值(指数位的值 + 偏移量)即可,IEEE 754
标准规定该偏移量为 2 e − 1 − 1 2^{e-1}-1 2e−1−1(e
为指数位的bit
长度),float
为127
,double
为1023
。 - 有效数字位
(Significand)
是规范化表达的二进制小数,它的取值范围为 1 ≤ 1. M < 2 1\leq1.M<2 1≤1.M<2(规约化)或 0 < 0. M < 1 0<0.M<1 0<0.M<1(非规约化),其中M
表示有效数字位中的尾数(Mantissa
),也称为小数(fraction
),即小数点右侧的数字。
\quad
因此,每一个浮点数在计算机中的存储都可以分成下面三个部分,由于规范化表达的二进制小数的最高位都是1
,所以计算机中只存储尾数位:
符号位(Sign) | 指数位偏移后的值(Exponent bias) | 尾数位(Mantissa) |
---|
③ 规约形式浮点数(normal number)
\quad
如果IEEE 754
标准浮点数中指数位偏移后的值在
[
1
,
2
e
−
2
]
[1, 2^e - 2]
[1,2e−2](e
为指数位的bit
长度),即指数位不全为0
(单精度float
:二进制0000 0000
,数值0
;双精度double
:二进制000 0000 0000
,数值0
),也不全为1
(单精度float
:二进制1111 1111
,数值255
;双精度double
:二进制111 1111 1111
,数值2047
),那么这个浮点数就被称为规约形式浮点数。“规约”是指用唯一确定的浮点形式去表示一个值。对于规约浮点数,其有效数(significand
=1
+Mantissa
)大于等于1
且小于2
(隐式前导位为1
)。
- 单精度浮点数
float
的指数位-127
、128
和双精度浮点数double
的指数位值-1023
、1024
被用作特殊值处理。
\quad
因此,规约形式的单精度浮点数float
的指数位偏移后的值的范围为[1, 254]
,这也可以反推出规约形式的单精度浮点数对应的指数部分实际取值是[-126, 127]
。同理,规约形式的双精度浮点数double
的指数位偏移后的值的范围为[1, 2046]
,双精度浮点数double
对应的指数部分实际取值是[-1022, 1023]
。
④ 非规约形式浮点数(subnormal number)
- 由规约形式的浮点数部分的内容,我们可以推算出,规约形式的单精度浮点数
float
的取值范围为:
\quad V a l u e = ( − 1 ) S i g n × S i g n i f i c a n d × 2 E x p o n e n t = ± [ 1 , 2 ) × 2 [ − 126 , 127 ] = ( − 2 × 2 127 , − 1 × 2 − 126 ] ∪ [ 1 × 2 − 126 , 2 × 2 127 ) Value=(-1)^{Sign}×Significand×2^{Exponent}=\pm[1,2)×2^{[-126,\ 127]}=(-2×2^{127},\ -1×2^{-126}]\cup[1×2^{-126},\ 2×2^{127}) Value=(−1)Sign×Significand×2Exponent=±[1,2)×2[−126, 127]=(−2×2127, −1×2−126]∪[1×2−126, 2×2127)
转换为十进制后的大致范围是: [ − 3.40 × 1 0 38 , − 1.18 × 1 0 − 38 ] ∪ [ 1.18 × 1 0 − 38 , 3.40 × 1 0 38 ] [-3.40×10^{38},-1.18×10^{−38}]\cup[1.18×10^{−38},3.40×10^{38}] [−3.40×1038,−1.18×10−38]∪[1.18×10−38,3.40×1038] - 规约形式的双精度浮点数
double
的取值范围为:
\quad V a l u e = ( − 1 ) S i g n × S i g n i f i c a n d × 2 E x p o n e n t = ± [ 1 , 2 ) × 2 [ − 1022 , 1023 ] = ( − 2 × 2 1023 , − 1 × 2 − 1022 ] ∪ [ 1 × 2 − 1022 , 2 × 2 1023 ) Value=(-1)^{Sign}×Significand×2^{Exponent}=\pm[1,2)×2^{[-1022, 1023]}=(-2×2^{1023},\ -1×2^{-1022}]\cup[1×2^{-1022},\ 2×2^{1023}) Value=(−1)Sign×Significand×2Exponent=±[1,2)×2[−1022,1023]=(−2×21023, −1×2−1022]∪[1×2−1022, 2×21023)
转换为十进制后的大致范围是: [ − 1.79 × 1 0 308 , − 2.23 × 1 0 − 308 ] ∪ [ 2.23 × 1 0 − 308 , 1.79 × 1 0 308 ] [-1.79×10^{308},\ -2.23×10^{-308}]\cup[2.23×10^{-308},\ 1.79×10^{308}] [−1.79×10308, −2.23×10−308]∪[2.23×10−308, 1.79×10308]
\quad
由此可见,使用规约形式的浮点数我们无法表示非常接近0
的极小的数(即
(
−
2
−
126
,
2
−
126
)
(-2^{-126},2^{-126})
(−2−126,2−126) 或
(
−
2
−
1022
,
2
−
1022
)
(-2^{-1022},2^{-1022})
(−2−1022,2−1022) 之间的数),非规约形式的浮点数的出现就将解决这个问题。如果浮点数中指数位偏移后的值是0
(二进制0000 0000
),有效数字中的最高有效位为0
且尾数部分非0
,那么这个浮点数将被称为非规约形式的浮点数。一般是某个数字相当接近零时才会使用非规约型式来表示。
\quad
IEEE754
标准规定:非规约形式的浮点数的指数偏移值比规约形式的浮点数的指数偏移值小1
,即非规约的单精度浮点数float
的偏移值为126
,非规约的双精度浮点数double
的偏移值为1022
。
\quad
因此,非规约形式的单精度浮点数float
的指数的实际值为-126
,非规约形式的双精度浮点数double
的指数的实际值为-1022
。
\quad
因此,非规约形式的单精度浮点数float
的取值范围为:
V
a
l
u
e
=
(
−
1
)
S
i
g
n
×
S
i
g
n
i
f
i
c
a
n
d
×
2
E
x
p
o
n
e
n
t
=
±
(
0
,
1
)
×
2
−
126
=
(
−
2
−
126
,
0
)
∪
(
0
,
2
−
126
)
Value=(-1)^{Sign}×Significand×2^{Exponent}=\pm(0,1)×2^{-126}=(-2^{-126},\ 0)\cup(0,\ 2^{-126})
Value=(−1)Sign×Significand×2Exponent=±(0,1)×2−126=(−2−126, 0)∪(0, 2−126);非规约形式的双精度浮点数double
的取值范围为:
V
a
l
u
e
=
(
−
1
)
S
i
g
n
×
S
i
g
n
i
f
i
c
a
n
d
×
2
E
x
p
o
n
e
n
t
=
±
(
0
,
1
)
×
2
−
1022
=
(
−
2
−
1022
,
0
)
∪
(
0
,
2
−
1022
)
Value=(-1)^{Sign}×Significand×2^{Exponent}=\pm(0,1)×2^{-1022}=(-2^{-1022},\ 0)\cup(0,\ 2^{-1022})
Value=(−1)Sign×Significand×2Exponent=±(0,1)×2−1022=(−2−1022, 0)∪(0, 2−1022)。
\quad
仔细看下,非规约形式的单精度浮点数的区间范围和规约形式的单精度浮点数的区间范围正好构成了
(
−
2
×
2
127
,
0
)
∪
(
0
,
2
×
2
127
)
(-2×2^{127},\ 0)\cup(0,\ 2×2^{127})
(−2×2127, 0)∪(0, 2×2127);非规约形式的双精度浮点数的区间范围和规约形式的双精度浮点数的区间范围正好构成了
(
−
2
×
2
1023
,
0
)
∪
(
0
,
2
×
2
1023
)
(-2×2^{1023},\ 0)\cup(0,\ 2×2^{1023})
(−2×21023, 0)∪(0, 2×21023)。0
是作为特殊值存在的。
⑤ 特殊值(non-number)
- 如果浮点数的指数位偏移后的值是
0
(实际指数值-127
或-1023
),且有效数的尾数位部分是0
,则这个数是 ± 0 \pm 0 ±0(和符号位相关)。 - 如果浮点数的指数位偏移后的值是
2
e
−
1
2^e - 1
2e−1(实际指数值
128
或1024
),且有效数的尾数位部分是0
,则这个数是 ± ∞ \pm \infty ±∞(和符号位相关)。当float
或double
存储的值超过了它的范围,就会是 ± ∞ \pm \infty ±∞。 - 如果浮点数的指数位偏移后的值是
2
e
−
1
2^e - 1
2e−1(实际指数值
128
或1024
),且有效数的尾数位部分非0
,则这个数是非数(NaN
)。
⑥ float
\quad
float
占用4
个字节,即 32
个二进制位。声明float
型时须在数值后加 “f
”或“F
”,“f
” 不区分大小写。因为float
占用的存储空间与int
相同,同样是4
字节32
位,所以同样只能表示4,294,967,296
个数值。但float
类型可表示的数值范围却要远大于int
,这是因为float
存储数据的间隔不是等间距的,而是在0
的附近间距小,在远离0
的位置间距大,当要表示间距中间的一个数字时,只能找它附近离它最近的一个可以表示的数字来代替,这也相应的产生了精度问题。
float
的存储:float
将4
字节32
位划分为符号位、指数位和尾数位三个部分来存储。-
1 b i t 1bit 1bit(
Sign
符号位):占据最高位(第31
位),用于表示浮点数是正数还是负数,为0
表示正数,为1
表示负数。 -
8 b i t s 8bits 8bits(
Exponent
指数位):占据第30
位到第23
位这8
位,这里采用了移码存储的方式来存储指数,float
的指数范围为-127
~128
。- 移码存储:浮点型的指数位都有一个固定的偏移量(
bias
),用于使指数 + 这个偏移量 = 一个非负整数
,这样指数位就无需考虑如何表示负数了。IEEE
规定了在32
位单精度类型中,偏移量是127
,在64
位双精度类型中,偏移量是1023
。后文针对移码存储进行了详解。
- 移码存储:浮点型的指数位都有一个固定的偏移量(
-
23 b i t s 23bits 23bits(尾数位,占据剩余的第
22
位到第0
位这23
位,尾数位用的是原码)- 尾数位决定了计算精度。因为浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“
1
”,所以float
虽然尾数只有23
位,但实际上可用来表示尾数的有24
位。精度问题就相当于变成这样的问题:24
位的二进制可以精确表示几位十进制?因为 lg 2 24 = 7.22 \lg2^{24}=7.22 lg224=7.22。所以float
的精度为7
,即可以精确表示7
位有效数字,第8
位为近似位。- 从
1
到0xFFFFFF
(24
位全1
,16777215
)的整数均可以表示为不超过24
位的二进制整数,因为二进制尾数部分不超过23
位,因而可以准确的用浮点数表示。 - 还有一部分整数,在表示为二进制的科学计数法时,尾数部分不超过
23
位,也可以准确表示。除此之外,其他整数在转化为float
后均会损失精度。
- 从
- 尾数位决定了计算精度。因为浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“
-
示例:
float
存储8250
(10000000111010
)
-
⑦ double
\quad
double
占用8
个字节,即64
个二进制位。double
的取值范围为
−
2
1024
-2^{1024}
−21024 ~
2
1024
2^{1024}
21024,也即
−
1.79
e
+
308
-1.79e+308
−1.79e+308 ~
1.79
e
+
308
1.79e+308
1.79e+308。
double
的存储:double
将8
字节64
位划分为符号位、指数位和尾数位三个部分来存储。-
1
b
i
t
1bit
1bit(符号位,占据最高位(第
63
位),用于表示浮点数是正数还是负数,为0
表示正数,为1
表示负数) -
11
b
i
t
s
11bits
11bits(指数位,占据第
62
位到第52
位这11
位),double
的指数范围为-1023 ~ 1024
。 -
52
b
i
t
s
52bits
52bits(尾数位,占据剩余的第
51
位到第0
位这52
位,尾数位用的是原码)- 尾数位决定了计算精度。对于双精度浮点数而言,
lg
2
53
=
15.95
\lg2^{53}=15.95
lg253=15.95。所以
double
的精度为16
,即可以表示16
位有效数字,第17
位为近似位。
- 尾数位决定了计算精度。对于双精度浮点数而言,
lg
2
53
=
15.95
\lg2^{53}=15.95
lg253=15.95。所以
- 示例:
double
存储8250(10000000111010)
-
1
b
i
t
1bit
1bit(符号位,占据最高位(第
⑧ 舍入误差
\quad
由于尾数位长度有限,浮点数能够精确表示的数是有限的,因而也是离散的。在两个可以精确表示的相邻浮点数之间,必定存在无穷多实数是IEEE
浮点数所无法精确表示的。如何用浮点数表示这些数,IEEE 754
的方法是用距离该实数最近的浮点数来近似表示。
\quad
为了将一个无法精确表示的数字舍入到其最接近的浮点数,IEEE 754
提供了四种舍入方式:向偶数舍入、向下舍入、向上舍入以及向0
舍入。
- 向偶数舍入:向偶数舍入是计算机的默认舍入方式,也称为向最接近的值舍入,会将结果舍入为最接近且可以表示的值。 向偶数舍入确保舍入后的最低有效数字是偶数, 对于二进制而言,就代表舍入后最低有效数字必须为
0
。向偶数舍入只有两条规则:- 如果最接近的值唯一,则直接向最接近的值舍入,针对十进制就是四舍六入;
- 如果是处在“中间值”,那么要看保留位是否是偶数,如果是偶数则直接舍去后面的数不进位,如果是奇数则进位后再舍去后面的数。
- 求中间值的方法:1)保留位和左边的数字保持不变;2)近似位(保留位右一位)改写为
N
2
\displaystyle\frac{N}{2}
2N(
N
为进制数,十进制就是10
,二进制就是2
);3)粘滞位(近似位右侧所有位)全部写0
。
- 求中间值的方法:1)保留位和左边的数字保持不变;2)近似位(保留位右一位)改写为
N
2
\displaystyle\frac{N}{2}
2N(
- 示例:
-
十进制数保留一位小数。
原始值 中间值 向偶数舍入 备注 1.36 1.35 1.4 原始值比中间值大,也就是“四舍六入”中的“六入”,所以向十分位进一。 1.751 1.75 1.8 原始值比中间值大,也就是“四舍六入”中的“六入”,所以向十分位进一。 1.74 1.75 1.7 原始值比中间值小,也就是“四舍六入”中的“四舍”,所以直接舍弃,不进位。 1.82 1.85 1.8 原始值比中间值小,也就是“四舍六入”中的“四舍”,所以直接舍弃,不进位。 1.75 1.75 1.8 原始值和中间值相等,保留位是奇数,则先向保留位进一,之后舍弃近似位和粘滞位。 1.85 1.85 1.8 原始值和中间值相等,保留位是偶数,则直接舍弃近似位和粘滞位。 -
二进制数保留一位小数。
原始值 中间值 向偶数舍入 备注 1.111 1.11 10.0 原始值比中间值大,也就是“四舍六入”中的“六入”,所以向小数点右边第 1
位进一。1.0101 1.01 1.1 原始值比中间值大,也就是“四舍六入”中的“六入”,所以向小数点右边第 1
位进一。1.001 1.01 1.0 原始值比中间值小,也就是“四舍六入”中的“四舍”,所以直接舍弃,不进位。 1.10 1.11 1.1 原始值比中间值小,也就是“四舍六入”中的“四舍”,所以直接舍弃,不进位。 1.110 1.11 10.0 原始值和中间值相等,保留位是奇数,则先向保留位进一,之后舍弃近似位和粘滞位。 1.010 1.01 1.0 原始值和中间值相等,保留位是偶数,则直接舍弃近似位和粘滞位。
-
- 向下舍入:向负无穷
−
∞
-\infty
−∞方向舍入。对负数而言,多余位全为
0
则直接截尾,不全为0
则向最低有效位进1
;正数的话不管多余位是多少直接截尾即可。 - 向上舍入:向正无穷
+
∞
+\infty
+∞方向舍入。对正数而言,多余位全为
0
则直接截尾,不全为0
则向最低有效位进1
;负数的话不管多余位是多少直接截尾即可。 - 向
0
舍入:向数轴零点方向舍入,直接截尾即可。
⑨ 详解精度丢失
\quad
存在一些小数在转换成二进制小数的过程中无法准确的用二进制进行表达,例:11.11
的小数部分转成二进制如下所示
\qquad
0.11
×
\times
×2
=0.22
⋯
\cdots
⋯0
\qquad
0.22
×
\times
×2
=0.44
⋯
\cdots
⋯0
\qquad
0.44
×
\times
×2
=0.88
⋯
\cdots
⋯0
\qquad
0.88
×
\times
×2
=1.76
⋯
\cdots
⋯1
\qquad
0.76
×
\times
×2
=1.52
⋯
\cdots
⋯1
\qquad
0.52
×
\times
×2
=1.04
⋯
\cdots
⋯1
\qquad
0.04
×
\times
×2
=0.08
⋯
\cdots
⋯0
\qquad
0.08
×
\times
×2
=0.16
⋯
\cdots
⋯0
\qquad
0.16
×
\times
×2
=0.32
⋯
\cdots
⋯0
\qquad
0.32
×
\times
×2
=0.64
⋯
\cdots
⋯0
\qquad
0.64
×
\times
×2
=1.28
⋯
\cdots
⋯1
\qquad
0.28
×
\times
×2
=0.56
⋯
\cdots
⋯0
\qquad
0.56
×
\times
×2
=1.12
⋯
\cdots
⋯1
\qquad
⋯
⋯
\cdots\cdots
⋯⋯
\qquad
// 无限循环下去
\quad
所以,11.11
用二进制表示为1011.000111000010100011110101110......
,用”科学计数法“表示为:
1.011000111000010100011110101110......
×
2
3
1.011000111000010100011110101110......\times2^3
1.011000111000010100011110101110......×23,使用float
类型存储的23
位尾数为0110 0011 1000 0101 0001 111
,后面的数字被截断,由此就导致了精度的丢失。
\quad 很多浮点数在二进制环境下都无法完整的表示,只能截取部分数据来近似的表示,两个数相加的话,就是两个近似的数相加的和,如果相加次数足够多,精确度就会越来越低。
⑩ 详解移位存储
\quad
以 32
位单精度 float
内存模型为例
\quad
如图所示,通常情况下 8
位存储空间可以存储的数值范围为00000000 ~ 11111111
,也就是从 0
开始到 255
结束,一共 256
个数。但是浮点数的幂指数(阶码:在机器中表示一个浮点数时需要给出指数,这个指数用整数形式表示,这个整数叫做阶码)是有必要使用负数的。
\quad
既要表示正数,又要表示负数,我们就需要拿出一位来表示正负号。按照传统的方式,使用一个字节的最高位(最左边那位)表示正负,最高位为1
时表示负数,为0
时表示正数,那么我们就可以得到两个区间:0 0000000 ~ 0 1111111
,即+0
到127
;1 0000000 ~ 1 1111111
,即-0
到-127
。这样就会出现2
个0
,一个+0
,一个-0
。
\quad
使用移位存储方式会有什么效果呢? 这里我们使用单精度类型(float
)举例,float
移位存储要在原指数基础上加偏移量127(0111 1111)
。
- 使用移位存储方式表示正数:
- 如果我们要表示
0
,则有0 + 127 = 127
,即00000000 + 01111111 = 01111111
。 - 如果我们要表示
1
,则有1 + 127 = 128
,即00000001 + 01111111 = 10000000
。 - 如果我们要表示
2
,则有2 + 127 = 129
,即00000010 + 01111111 = 10000001
。 - ⋯ ⋯ \cdots\cdots ⋯⋯
- 如果我们要表示
128
,则有128 + 127 = 255
,即10000000 + 01111111 = 11111111
。128
是我们能够使用8
位二进制移位存储算法表示的最大正数,再大就会溢出。
- 如果我们要表示
- 同样,我们来看看负数:
- 如果我们要表示
-1
时,则有(-1) + 127 = 126
,即-00000001 + 01111111 = 01111110
。 - 如果我们要表示
-2
时,则有(-2) + 127 = 125
,即-00000010 + 01111111 = 01111101
。 - ⋯ ⋯ \cdots\cdots ⋯⋯
- 如果我们要表示
-127
时,则有(-127) + 127 = 0
,即-01111111 + 01111111 = 00000000
。-127
是我们能够使用8
位二进制移位存储算法表示的最小负数,再小就会溢出。
- 如果我们要表示
\quad
由上面的例子,我们可以得出规律:采用移位存储技术,我们可以使用8
位二进制来表示从-127 ~ 128
,共计:127
个负数 + 零(0
) + 128
个正数 = 256
个数。
\quad 为什么要用移码表示阶码,而不用补码表示阶码? 采用这种方式表示的目的是为了简化比较。因为指数必须是有符号数才能表达很大或很小的数值,如果采用补码表示的话,首先,尾数位是有符号的,如果指数也是有符号的,那么就不能简单的对浮点数进行大小比较,因为根据补码比较大小的话,要先将补码转换成原码再进行比较。正因为如此,将指数的值调整到一个正值的范围内以便进行比较。所以移码不存在符号位,我们直接可以由移码的表示形式看出对应数值的大小。
2. 字符(char)类型
Java
中的char
类型占用2
个字节(16bits
),可以存放单个汉字、单个字母、单个数字和其他Unicode
字符等(例:A='中'
、A=‘s’
)。- 定义
char
类型变量时,通常使用一对单引号' '
,单引号内部只能写一个字符。 char
类型变量必须进行赋值(空格字符也可以),不可以是空字符''
。- 可以使用
ASCII
码或Unicode
码对char
类型进行赋值,例:a=97
、a='\u0061'
。 - 所有字符的本质还是数字,最小值是
\u0000
(十进制等效值为0
);最大值是\uffff
(十进制等效值为65535
)。 - 字符前面加上斜线"
\
"来表示常见的那些不能显示的ASCII
字符,称为转义字符。如:\0(空字符(NULL))
,\t
,\n
等,就称为转义字符,因为后面的字符,都不是它本来的ASCII
字符意思了。
3. 布尔(boolean)类型
Java
将boolean
视为完全独立的数据类型,该数据类型具有2
个不同的值:true
和false
。- 值
1
和0
为int
类型,不能隐式转换为boolean
,所以不可以直接赋给boolean
数据类型。 boolean
数据类型常用于条件判断和循环结构。boolean
类型的变量在底层实际会调用int
,那么既然int
占用4
个字节,boolean
也自然占用4
个字节。boolean
类型的数组在底层实际会调用byte
,那么既然byte
占用1
个字节,boolean
数组中的boolean
也就占用1
个字节。
2.3.2 引用数据类型(后续详解)
- 类(
class
) - 接口(
interface
) - 数组(
[]
)